Skip to main content

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

RequirementDetail
FeatureCollectionThe 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 propertiesEven if it only holds the ID.
Every feature has the ID propertyThe property you nominate must be present on every feature, spelled identically.
ID values are uniqueDuplicates are skipped on upload - the first feature with a given ID wins.
ID values are ≤ 40 charactersThe matching Salesforce field is 40 characters.
ID property name is ≤ 40 charactersShort, simple names work best: region_id, zip, fips.
Polygons onlyPolygon 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", not 6. Numeric IDs are tolerated, but JSON numbers silently drop leading zeros - and "06" will never match a byte holding 6. 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.

AspectDetail
Property namecolor - 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 missingThe 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 upOn 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__c makes 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.

  1. Choose a .geojson or .json file.
  2. Pick the Region ID property.
  3. Optionally set a Region ID label (Zip Code, County Name).
  4. Upload. The layer renders immediately with the colors from the file, as a session layer.
  5. 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:

  1. HTTPS, publicly readable. Test it in a private window with no Salesforce session - if it prompts for login, it won't work.
  2. CORS enabled. The host must allow cross-origin requests from your Salesforce domain. Object storage buckets require this to be turned on explicitly.
  3. 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.
  4. No query strings. Anything after a ? is dropped, so presigned or token-bearing links fail. Use a plain, permanent path.
  5. 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 FeatureCollection with a non-empty features array.
  • Every feature has properties containing the ID property.
  • Every ID value is quoted, unique, and 40 characters or fewer.
  • The color property is named color and holds hex values (#2E86AB) - not color names or rgb().
  • Geometries are Polygon or MultiPolygon.
  • 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 crs block declaring a projected coordinate system.
  • Unused properties stripped, coordinate precision trimmed, file under 3 MB.

Troubleshooting

SymptomLikely cause
Nothing appears after uploadingNo 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'tThose 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 recordsThe chosen ID property is empty on most features, so no regions were created.
The layer says it has no boundary fileIt 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 missingID mismatch - check leading zeros, casing, and stray spaces.
Fewer regions than features in the fileDuplicate ID values; only the first feature with each ID is kept.
Shapes appear in the ocean or far from where they belongCoordinates are latitude-first, or the file is in a projected coordinate system. Reproject to EPSG:4326.
The upload rejects the file for being too largeOver 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 MosaicMissing 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 stallsThe file is too detailed - simplify geometry, trim precision, remove unused properties.
An updated file still shows old boundariesReload the page; boundary files are cached for the session.