GeoJSON
How to prepare a GeoJSON file for a Mosaic Region layer - including per-region colors.
Overview
A Region layer gets its shapes from a GeoJSON file and its data from Salesforce. The two are joined by one value: you nominate a property on each GeoJSON feature - the Region ID Property - and Mosaic matches it against the Region ID on your Mosaic_Byte__c records.
GeoJSON feature Mosaic_Byte__c
properties.region_id = "77002" ⟷ Region_Id__c = "77002"
properties.color = "#2E86AB" ⟷ Color__c = "#2E86AB"
You can carry the color in the file itself. When you upload a GeoJSON through the map, Mosaic reads each feature's color property, creates one Salesforce record per region with that color, and stores the file against the layer - so the map draws the same colors on every later visit without anyone touching a record by hand.
Requirements
| Requirement | Detail |
|---|---|
| FeatureCollection | The top-level object must be {"type": "FeatureCollection", "features": [...]} with at least one feature. A bare geometry or a single Feature won't load. |
Every feature has properties | Even if it only holds the ID. |
| Every feature has the ID property | The property you nominate must be present on every feature, spelled identically. |
| ID values are unique | Duplicates are skipped on upload - the first feature with a given ID wins. |
| ID values are ≤ 40 characters | The matching Salesforce field is 40 characters. |
| ID property name is ≤ 40 characters | Short, simple names work best: region_id, zip, fips. |
| Polygons only | Polygon or MultiPolygon. Points and lines aren't supported in a Region layer. |
| WGS84 coordinates | [longitude, latitude] order, EPSG:4326 (plain degrees). Any crs declaration in the file is ignored. |
Quote your ID values.
"06", not6. Numeric IDs are tolerated, but JSON numbers silently drop leading zeros - and"06"will never match a byte holding6. Zip codes, FIPS, and ZCTA codes must be strings.
Minimal valid example
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"region_id": "77002",
"color": "#2E86AB",
"region_name": "Downtown Houston"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[[-95.372, 29.758], [-95.360, 29.758], [-95.360, 29.750], [-95.372, 29.750], [-95.372, 29.758]]
]
}
}
]
}
Colors
Give a feature a property named exactly color holding a hex code, and Mosaic draws the region in it.
| Aspect | Detail |
|---|---|
| Property name | color - lowercase, exactly. No other property is read, and there is nothing to configure. |
| Accepted values | #RRGGBB, #RGB, or #RRGGBBAA. The leading # is optional - 2E86AB works. |
| Invalid or missing | The region draws transparent - no color is invented for it. Anything unparseable (a color name, an rgb() string, an empty value) counts as missing. |
| Where it ends up | On the Color__c field of the region's byte record, so it can be set or changed in Salesforce afterward without re-uploading. |
Color the file however you like - a category palette, a choropleth ramp you computed in QGIS or a spreadsheet, a single brand color repeated. Mosaic doesn't interpret the values; it draws them.
Note: Transparent regions are still real: the record exists, it's searchable and filterable, and giving its byte a
Color__cmakes it appear. That's deliberate - a file can carry every boundary in a state while only the ones you've colored show on the map.
Note: A region is only drawn when a byte matches it. Uploading a file creates those bytes for you. If you're configuring a layer by hand against a hosted URL, a region with no matching byte - or a byte with no color - stays invisible.
Uploading a file
Regions & Points → the upload icon next to Regions.
- Choose a
.geojsonor.jsonfile. - Pick the Region ID property.
- Optionally set a Region ID label (
Zip Code,County Name). - Upload. The layer renders immediately with the colors from the file, as a session layer.
- Save to Salesforce from the layer's action menu to make it permanent.
Saving writes three things:
- The layer record, linked to the current map.
- One byte per feature - region ID, color, and every other feature property stored as metadata (which is what makes regions searchable and filterable).
- The GeoJSON file itself, attached to the layer record as a Salesforce file. The map reads it back the next time someone opens that layer.
So the shapes, the colors, and the data all survive a reload. No hosting, no CORS setup, no URL to manage.
Note: The file uploads through the Flourish service and is attached to the layer a moment after the save toast appears. If you reload instantly and the layer looks empty, give it a few seconds.
Size limit
Uploads are capped at 3 MB - the picker rejects anything larger and tells you so before you get as far as saving. Above that, the file has to be hosted and referenced from the layer record instead; see the hosted URL alternative. That route has no size ceiling, because the file goes straight from your host to the browser.
Either way, simplify: the whole file is downloaded and parsed by every viewer's browser.
- Simplify boundaries - 10-20% of original detail is usually indistinguishable at map zoom levels.
- Trim coordinates to 5 decimal places (about 1 metre). Public boundary files often ship with 15.
- Strip properties you don't need. Keep the ID, the color, and anything you want searchable.
- Publish one file per boundary type (zip codes, counties, districts) rather than one combined file.
The hosted URL alternative
Instead of uploading, host the file and set Region GeoJSON URL on the Mosaic_Layer__c record. This is the route for anything over 3 MB, and for files you update on a schedule from somewhere else. It's configured on the layer record rather than through the map, so it's an admin task.
Mosaic loads the file from the browser, so:
- HTTPS, publicly readable. Test it in a private window with no Salesforce session - if it prompts for login, it won't work.
- CORS enabled. The host must allow cross-origin requests from your Salesforce domain. Object storage buckets require this to be turned on explicitly.
- Host allowed in Salesforce. It must be registered as a CSP Trusted Site with the connect-src context enabled. Ask your administrator before adopting a new host.
- No query strings. Anything after a
?is dropped, so presigned or token-bearing links fail. Use a plain, permanent path. - Colors still come from bytes. The URL route draws shapes only; create the byte records (region ID + color) separately, or upload once to generate them and then switch the layer to a URL.
Republishing at the same URL updates the map on the next page load.
Preparing a file
Both toolchains target the same end state: WGS84, polygons, a string ID, a hex color, trimmed precision.
mapshaper - simplify, keep the two fields that matter, force the ID to a string:
npx mapshaper input.shp \
-proj wgs84 \
-simplify 15% keep-shapes \
-filter-fields ZCTA5CE20,COLOR \
-rename-fields region_id=ZCTA5CE20,color=COLOR \
-each 'region_id = String(region_id)' \
-o precision=0.00001 format=geojson regions.geojson
To compute colors from a value rather than carrying them in the source data, add a -each step:
-each 'color = density > 5000 ? "#08519C" : density > 2000 ? "#3182BD" : "#9ECAE1"'
ogr2ogr - reproject and cast in one pass:
ogr2ogr -f GeoJSON regions.geojson input.shp \
-t_srs EPSG:4326 \
-lco RFC7946=YES \
-lco COORDINATE_PRECISION=5 \
-sql "SELECT CAST(GEOID AS character(40)) AS region_id, HEX AS color FROM input"
Pre-flight checklist
- Top level is a
FeatureCollectionwith a non-emptyfeaturesarray. - Every feature has
propertiescontaining the ID property. - Every ID value is quoted, unique, and 40 characters or fewer.
- The color property is named
colorand holds hex values (#2E86AB) - not color names orrgb(). - Geometries are
PolygonorMultiPolygon. - Coordinates read longitude-first and land in the right part of the world (
[-95.37, 29.76]for Houston, not[29.76, -95.37]). - No
crsblock declaring a projected coordinate system. - Unused properties stripped, coordinate precision trimmed, file under 3 MB.
Troubleshooting
| Symptom | Likely cause |
|---|---|
| Nothing appears after uploading | No feature carries a valid color, so every region drew transparent. Check the property is named color and holds hex values. |
| Some regions are invisible, others aren't | Those features have a missing or invalid color. Fix the file and re-upload, or set Color__c on the byte records. |
| Regions are invisible and the layer has no records | The chosen ID property is empty on most features, so no regions were created. |
| The layer says it has no boundary file | It was configured by hand without a GeoJSON URL, or the file didn't finish attaching. Upload one, or set Region GeoJSON URL. |
| Regions render, but the wrong ones are missing | ID mismatch - check leading zeros, casing, and stray spaces. |
| Fewer regions than features in the file | Duplicate ID values; only the first feature with each ID is kept. |
| Shapes appear in the ocean or far from where they belong | Coordinates are latitude-first, or the file is in a projected coordinate system. Reproject to EPSG:4326. |
| The upload rejects the file for being too large | Over the 3 MB upload cap. Simplify it, or host it and set Region GeoJSON URL on the layer record. |
| A hosted file opens in a browser tab but not in Mosaic | Missing CORS headers, the host isn't a CSP Trusted Site, or the URL depends on a query string. |
| The map is slow to load or the browser stalls | The file is too detailed - simplify geometry, trim precision, remove unused properties. |
| An updated file still shows old boundaries | Reload the page; boundary files are cached for the session. |