Short answer
KML and GeoJSON solve different problems. KML is an XML format created for Google Earth: it bundles geometry and presentation — icon styles, line colours, label rules, balloon HTML, altitude, 3D extrusions, and time animation — so the file looks the same wherever it opens. GeoJSON is a compact JSON format built for the web: it carries geometry and plain attribute properties with no styling, leaving appearance to the consuming application. Both store coordinates in WGS84 longitude-latitude.
Rule of thumb: if the deliverable is "open this and see exactly what I see" — a field map, a stakeholder view in Google Earth — send KML (or KMZ, its zipped form). If the deliverable is data that an app, API, or analysis will consume and style itself, send GeoJSON.
Structure: XML vs JSON
KML is verbose XML. A single styled point:
<Placemark>
<name>Adit 3</name>
<styleUrl>#redIcon</styleUrl>
<ExtendedData>
<Data name="status"><value>collapsed</value></Data>
</ExtendedData>
<Point><coordinates>-2.41,53.48,310</coordinates></Point>
</Placemark>
The same feature in GeoJSON is leaner and machine-friendly:
{
"type": "Feature",
"geometry": { "type": "Point", "coordinates": [-2.41, 53.48, 310] },
"properties": { "name": "Adit 3", "status": "collapsed" }
}
Note both write coordinates as longitude, latitude, (altitude) — longitude first. Reversing them is the single most common error when hand-editing either format, and it silently drops features into the wrong hemisphere or the ocean.
Coordinate reference system
Neither format lets you choose a projection for interchange:
- KML is defined against WGS84 (longitude, latitude, altitude in metres above the WGS84 ellipsoid or relative to ground, depending on
altitudeMode). - GeoJSON under RFC 7946 mandates WGS84 (the CRS84 variant, longitude before latitude). The old
crsmember from the 2008 spec is deprecated; modern parsers assume WGS84 regardless.
The practical consequence: if your data is in a projected CRS — say UTM or a national grid — you must reproject to WGS84 before exporting to either format. ogr2ogr -t_srs EPSG:4326 handles this. Skipping it produces coordinates like 512000, 4601000 that both formats will interpret as absurd longitudes and latitudes.
Styling and attributes
This is the real dividing line.
KML embeds presentation. <Style> and <StyleMap> define icons, line width and colour (in aabbggrr hex — alpha first, then blue-green-red, a frequent gotcha), polygon fill, label scale, and <BalloonStyle> HTML for pop-ups. Altitude, <gx:Track> time animation, and <Polygon> extrusion give it genuine 3D and temporal capability. This is why KML is the lingua franca of Google Earth and field-mapping exports.
GeoJSON carries data, not looks. Attributes live in a flat properties object — clean key-value pairs ideal for filtering, joining, and binding to symbology in a web map (Leaflet, MapLibre, OpenLayers) or processing in PostGIS, GDAL, or geopandas. There is no styling member in the spec; if appearance must travel, you encode it as properties and the client maps them, or you adopt a convention like simplestyle-spec (not part of RFC 7946, supported by some viewers such as GitHub and Mapbox).
So: KML is better when the picture must be self-contained. GeoJSON is better when the data must be queried, transformed, or restyled.
Size and performance
KML's XML tags are heavy; for large datasets it bloats quickly, which is why KMZ (a zipped KML, often with packaged icons and overlays) exists and is usually what you actually ship. GeoJSON is lighter per feature but is still uncompressed text with no spatial index, so a multi-megabyte GeoJSON will choke a browser. Neither is a good analytical or large-scale storage format — for that, move to GeoPackage, PostGIS, or vector tiles. Both KML and GeoJSON are exchange and presentation formats, not working datastores.
Worked conversion with ogr2ogr
KML to GeoJSON (and reprojecting defensively, though KML is already WGS84):
ogr2ogr -f GeoJSON sites.geojson sites.kml -t_srs EPSG:4326
GeoJSON to KML:
ogr2ogr -f KML sites.kml sites.geojson
Reproject a UTM shapefile straight to GeoJSON for the web:
ogr2ogr -f GeoJSON web.geojson utm.shp -t_srs EPSG:4326
Inspect what you produced before sending it:
ogrinfo -so -al sites.geojson # geometry type, feature count, fields, extent
Watch for: lost styling (GeoJSON drops all KML <Style>); KML folders mapping to separate layers; ExtendedData schema mapping to GeoJSON properties; and the colour-order quirk (aabbggrr) if you round-trip styling through KML.
Common pitfalls and why they happen
- Lng/lat swap. Editors and some non-compliant tools assume lat/long; both KML and GeoJSON are long/lat. Features land in the wrong place. Verify against a basemap after export.
- Exporting projected coordinates without reprojecting. UTM eastings/northings written into a WGS84-only format. Always
-t_srs EPSG:4326. - Expecting styling to survive the round trip. KML → GeoJSON → KML loses the original styles because GeoJSON never held them.
- KML colour confusion.
<color>isaabbggrr, notrrggbbaa. Reds and blues swap if you assume web hex order. - Using either for big data. Tens of MB of GeoJSON or KML will stall a browser; move analytical and large layers to GeoPackage or PostGIS and serve tiles to the web.
- GeoJSON
crsmember. Adding a non-WGS84crsblock is deprecated under RFC 7946 and ignored by modern parsers — reproject the data instead.
QA and validation
- Run
ogrinfo -so -aland confirm feature count, geometry type, and extent are sane (coordinates within −180..180 and −90..90). - Open the output over a known basemap to catch a coordinate swap visually.
- For KML, open in Google Earth and confirm styling, altitude mode, and balloons render as intended.
- For GeoJSON, validate against RFC 7946 (right-hand rule for polygon winding, WGS84 coordinates) —
ogr2ogrwrites compliant output, hand-edited files may not.
Bathyl perspective
We pick the exchange format from the destination, not habit: KML or KMZ when a stakeholder needs a self-contained Google Earth view, GeoJSON when a web app or API will consume and restyle the data. For anything large or query-heavy we treat both as the wrong tool and reach for GeoPackage or a PostGIS-backed tile service instead.
Related Bathyl reading
- Spatial Indexes in GIS Files
- CSV Coordinates to GIS Layers
- Why Your GIS Layers Do Not Line Up
- Spatial data products