Skip to main content

Best practices

Cartography Best Practices

Operational guidelines for developers integrating geographic data into AI, RAG, and GIS applications.

Always declare the datum and CRS

Practice: In any system handling geographic coordinates, the datum (WGS84, ETRS89, Roma40) and CRS (EPSG code) must be explicitly stated and saved with the data. Never assume that "coordinates are in WGS84" without verification.

Do: Save the CRS in the file (gpkg_spatial_ref_sys in GeoPackage, .prj file in Shapefile, GeoTIFF metadata), document it in the dataset README, verify it with ogrinfo -al -so file.gpkg or gdalinfo file.tif.

Don't: Distribute files with an unknown or implicitly assumed CRS. A dataset without a declared CRS requires forensic investigation to determine it.

Use GeoPackage instead of Shapefile for new projects

Practice: Shapefile has limitations that cause silent errors: field names truncated to 10 characters, file max 2 GB, ambiguous encoding, single geometry type per file. GeoPackage (.gpkg) is the modern OGC format that resolves all these limitations.

Do: Use ogr2ogr -f GPKG output.gpkg input.shp to convert existing Shapefiles. Prefer GeoPackage for all new vector datasets.

Don't: Create new Shapefiles when GeoPackage is available. Do not ignore silent field name truncation: a field "district_name" becomes "district_na" without warning.

Separate storage CRS from display CRS

Practice: Data can be stored in ETRS89/UTM (metric coordinates, suitable for spatial analysis) and displayed in Web Mercator (EPSG:3857, suitable for web tiles). On-the-fly conversion is handled by GIS libraries (pyproj, PROJ, PostGIS ST_Transform).

Do: Store data in the CRS appropriate for analysis (UTM for metric measurements, ETRS89 for INSPIRE compliance); convert on the fly for web display.

Don't: Store data in Web Mercator (EPSG:3857): this spherical Mercator projection distorts areas and distances and is suitable only for web raster visualization.

Always cite the source of coordinates, never generate them

Practice: In any cartographic content (documentation, FAQ, dataset examples), geographic coordinates must be cited from authoritative sources (IGM, OpenStreetMap, EPSG Registry, SRTM data), never approximated or calculated without a source.

Do: Explicitly cite the source ("EPSG:4326 coordinates from OpenStreetMap Nominatim for Florence: latitude 43.7696°N, longitude 11.2558°E, accessed 2024"). Use verified geocoding services (OSM Nominatim, GeocodeAPI, HERE Geocoder) for example coordinates.

Don't: Generate or invent "plausible" coordinates for examples or documentation. A coordinate error in a cartographic pack is the primary failure mode: users relying on it for GIS reference will immediately detect the inaccuracy.

Handle latitude-longitude order correctly

Practice: Latitude-longitude vs longitude-latitude order is a perennial source of bugs. OGC specs use lat-lon; GeoJSON (RFC 7946) uses lon-lat; Leaflet uses lat-lon; Mapbox GL JS uses lon-lat. Always document the order used in APIs.

Do: Use explicit types ({lat: number, lng: number} instead of array [number, number]), comment the order, add sanity-check tests (verify latitude is in range [-90, 90]).

Don't: Use coordinate arrays without documenting the order. Do not assume all libraries use the same convention.

Use the PostGIS geography type for geodetic distances

Practice: PostGIS has two types for geographic geometries: geometry (Cartesian plane, Euclidean calculations) and geography (WGS84 spheroid, geodetic calculations). For accurate distances and areas on WGS84 data, use the geography type.

Do: SELECT ST_Distance(a.geom::geography, b.geom::geography) FROM ... for distances in meters on spheroid. Index with CREATE INDEX ON table USING GIST(geom::geography).

Don't: Calculate distances in degrees with the geometry type in WGS84: ST_Distance(p1, p2) returns the difference in degrees, not meters. Multiplying by 111,319 m/degree is a rough approximation unsuitable for distances greater than a few km.

Validate geometries before database insertion

Practice: Invalid geometries (self-intersections in polygons, unclosed rings, empty geometries) cause errors in spatial queries and topological calculations. Validate before inserting.

Do: SELECT ST_IsValid(geom), ST_IsValidReason(geom) FROM ... to diagnose; UPDATE table SET geom = ST_MakeValid(geom) WHERE NOT ST_IsValid(geom) for automatic correction. In QGIS: Vector → Geometry Tools → Fix Geometries.

Don't: Insert geometries without validation: an invalid geometry can block ST_Intersection, ST_Union and topological analysis functions with obscure GEOS errors.

Use spatial indexes for geographic queries

Practice: Spatial queries without an index (full scan) are orders of magnitude slower on large datasets. GiST indexes (PostGIS) and R-Tree indexes (SQLite/GeoPackage) are essential for acceptable performance.

Do: CREATE INDEX ON table USING GIST(geom); in PostGIS after loading. SELECT CreateSpatialIndex('table', 'geom'); in GeoPackage. Verify index use with EXPLAIN ANALYZE in PostgreSQL.

Don't: Run spatial joins or ST_Contains/ST_Within on millions of geometries without an index. The PostgreSQL query planner uses the index automatically if present.

Choose the projection based on use case

Practice: There is no universally correct projection. The choice must be guided by use: UTM for linear measurements in a limited area, Equal Area for area statistics, Robinson or Mollweide for global visualization maps, Web Mercator for web tiles.

Do: For analysis in Italy: UTM zone 32N or 33N (EPSG:32632 or 32633) for metric measurements. For INSPIRE compliance: ETRS89 / LAEA Europe (EPSG:3035) for statistical maps. For web tiles: Web Mercator (EPSG:3857).

Don't: Use Web Mercator (EPSG:3857) for area or distance calculations. This projection distorts areas: Greenland appears as large as Africa in the Mercator projection, but is actually 14 times smaller.

Document data resolution and accuracy

Practice: The spatial resolution of a raster (e.g., 25 m/pixel for SRTM3) and the planimetric accuracy of a vector dataset (e.g., ±5 m for CTR 1:10,000 data) determine the applicability of data to the intended use. Document these in metadata.

Do: Specify in the README or ISO 19115 metadata: spatial resolution (for rasters), planimetric and vertical accuracy (for topographic data), acquisition date, producing authority, license.

Don't: Present SRTM data (30 m resolution, about 10 m RMSE vertical accuracy) as high-resolution DEM for applications requiring centimeter precision. The difference between SRTM and LiDAR is 2-3 orders of magnitude in resolution.

Handle HGT/SRTM no-data values correctly

Practice: HGT files (SRTM format) use the value -32768 as a missing data indicator (no-data). If not handled, this value appears as an elevation of -32,768 m, causing severe errors in terrain analysis.

Do: Check and set the no-data value: gdal_translate -a_nodata -32768 input.hgt output.tif. Use gdalinfo to verify the current no-data value. Filter no-data pixels in GDAL/numpy analysis.

Don't: Use SRTM data for elevation analysis without filtering no-data values: contour interpolation or elevation profile calculation with -32768 values produces completely erroneous results.

Use UTC timestamps for geographic data

Practice: GPS tracking data and acquisition logs contain timestamps. Always use UTC as the timezone for timestamps in geographic datasets. Local time conversion is a presentation operation, not a storage operation.

Do: Store timestamps as UTC with explicit timezone (ISO 8601 with Z suffix). Document the timezone in metadata.

Don't: Store timestamps without timezone or in local time: during daylight saving changes or in cross-timezone analyses, ambiguous timestamps cause sorting errors and duration calculation bugs.