Документация

QazGeo integration

QazGeo is the shared GIS backend for Crisis Monitor. The project keeps its domain objects locally: incidents, watch zones, resource points, static resources and escalation rules remain in this application. QazGeo is used as the common source for geocoding, reverse geocoding, text-to-region extraction, layer discovery and shared map defaults.

Runtime configuration

QAZGEO_BASE_URL=https://qgeo.tech
QAZGEO_API_KEY=...
QAZGEO_TIMEOUT_SECONDS=5
TILE_SERVER_URL=https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png
TILE_SERVER_URL_DARK=https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png
MAP_DEFAULT_CENTER_LAT=48.0
MAP_DEFAULT_CENTER_LON=67.7
MAP_DEFAULT_ZOOM=5
MAP_TILE_ATTRIBUTION=© OpenStreetMap

QAZGEO_API_KEY must stay server-side. Browser code should use local server-side proxy endpoints and must not call QazGeo directly with the key.

The COP also resolves the optional public nasa_viirs_true_color TileJSON contract through QazGeo. Operators can turn the NASA VIIRS background on only when visual context is useful; it is off by default, keeps the QazGeo/NASA attribution, and does not make QazGeo or Crisis Monitor store raster tiles.

Server-side endpoints

Authenticated API clients can use:

  • GET /api/v1/geo/qazgeo/health
  • GET /api/v1/geo/qazgeo/layers
  • POST /api/v1/geo/qazgeo/locate-texts

These endpoints delegate to container.geo_service() and keep fallback behavior inside services/geo_service.py.

The COP calls GeoService.operational_catalog() and shows a compact layer summary: contract status, stable/beta counts, public scope, provenance label, declared refresh cadence and the temporal field supplied by QazGeo when present. An unknown update time stays explicit as unknown; Crisis does not infer freshness from reachability or from the declared cadence. It also consumes the public /api/v1/mapregion/public/regions-geojson contract server-side: official ADM1 boundaries are drawn behind incident markers, and active points are counted against those geometries for regional context. This does not replace local watch-zones or write incident data into QazGeo. A missing registry, incomplete provenance or unavailable boundary contract is shown as unavailable/degraded; the UI does not fabricate owner, licence, freshness or quality values that QazGeo has not supplied.

The regional map context deliberately uses a public, no-key boundary contract. The QazGeo integration key remains server-side for protected geocode, reverse-geocode and locate-texts calls; browser code never receives it.

When the public layer registry advertises h3_poi_density, the COP exposes a separate operator toggle for the read-only H3 projection through the local authenticated /cop/api/qazgeo/poi-density proxy. It loads only after an operator enables it, is capped at 5,000 cells, displays the QazGeo snapshot metadata and labels the result as aggregated POI context. It is not an incident, population or impact verdict, and an unavailable/empty response leaves the incident map unchanged.

Coordinate coverage maintenance

Existing incidents with a canonical region, city or address but no coordinates can be enriched from QazGeo. The command is dry-run by default:

python -m scripts.backfill_ticket_coordinates
python -m scripts.backfill_ticket_coordinates --apply

The backfill never overwrites existing coordinates. It accepts only QazGeo results inside Kazakhstan's geographic bounds and prints every proposed change as JSON before the final summary. Region-only records use the QazGeo region centroid and remain region-precision observations rather than invented street locations.

The production apply pass on 2026-07-10 enriched 35 of 37 incidents. The two remaining records do not contain a canonical region, city or address and are intentionally left without invented coordinates. Re-running the dry-run after the apply pass reports zero additional candidates.

Map UI standard

All Leaflet maps must use window.CMMap from static/map-runtime.js.

Use:

const map = CMMap.createMap('map-id');
CMMap.addTileLayer(map);
CMMap.fitKazakhstan(map);
CMMap.invalidateLater(map);

Do not add new direct L.tileLayer('https://...') calls in templates. If a page needs a different tile source, pass it through TILE_SERVER_URL / TILE_SERVER_URL_DARK or add a server-side QazGeo proxy.

Boundaries

  • QazGeo does not replace Crisis Monitor watch zones.
  • QazGeo does not own incident/resource records.
  • QazGeo layer registry is read-only for this project.
  • MapLibre or qazmap.js can be introduced later only for a dedicated GIS workspace; existing operator maps should remain compact Leaflet screens until there is a clear product reason to migrate them.