Map Markers

Place points, draw lines, and define polygons on the shared map with real-time sync.

10 min read

Marker Types

GroundWave supports three geometry types for map markers. All types are stored in PostgreSQL using PostGIS GEOGRAPHY columns with SRID 4326, which enables accurate distance and area calculations in meters without coordinate system conversion.

Point Markers

Single coordinate markers with name, category, description, and optional photo attachments. Ideal for rally points, hazards, medical stations, water sources, shelters, and any discrete location of interest.

Line Markers

Multi-vertex polylines with label and color for routes of march, patrol paths, cordons, phase lines, and linear features.

Polygon Markers

Closed polygon areas with label, color, and fill for objective areas, engagement zones, exclusion zones, grid squares, and any bounded region requiring area representation.

All marker geometry is validated against the GeoJSON specification on the server before being written to the database. Invalid geometry -- self-intersecting polygons, unclosed rings, or out-of-range coordinates -- is rejected with a descriptive error response.

Categories

Point markers belong to one of the predefined categories. Categories drive the visual appearance of markers on the map and give operators an immediate, at-a-glance understanding of what a marker represents without reading its label.

Category Intended Use Default Color
rally_point Assembly areas, rendezvous locations Blue
water_source Rivers, wells, resupply points Cyan
hazard Obstacles, danger zones, known threats Red
shelter Structures, safe houses, bivouac sites Green
medical Aid stations, casualty collection points Amber

Additional context beyond the category belongs in the description field. Use it for grid references, access notes, capacity, or any other free-form operational detail.

Creating Markers

Markers can be created through two paths: the interactive map drawing tool in the client UI, or directly via the REST API or Socket.IO for programmatic or external integrations.

Interactive Drawing Tool

The client implements a custom MapLibre drawing state machine that manages the full lifecycle of geometry construction without depending on a third-party drawing library. The interaction model is:

  • Point -- select the Point tool, then click anywhere on the map. A create form opens to set name, category, and description. Submitting creates the marker immediately.
  • Line -- select the Line tool, click to place the first vertex, click again to add subsequent vertices, and double-click to finish and submit the line.
  • Polygon -- select the Polygon tool, click to place vertices around the area, and double-click to close the ring and submit the polygon.

During drawing, a live preview renders the in-progress geometry on the map so operators can see the shape forming before they commit it. The Escape key cancels an in-progress drawing without creating a marker.

API Creation

Markers can also be created directly via REST:

POST /api/markers Operator+
Create a new marker. The geometry field must be a valid GeoJSON geometry object (Point, LineString, or Polygon). Point markers require name and category.
// POST /api/markers -- point marker example
{
  "marker_type":  "point",
  "name":         "Alpha Rally Point",
  "category":     "rally_point",
  "description":  "Grid 4QFJ 123 456 -- clear LZ to the north",
  "geometry": {
    "type":        "Point",
    "coordinates": [-118.243, 34.052]
  }
}
// POST /api/markers -- polygon marker example
{
  "marker_type":        "polygon",
  "name":               "Objective Alpha",
  "marker_properties": {
    "color":     "#ef4444",
    "opacity":   0.35,
    "lineWidth": 2
  },
  "geometry": {
    "type":        "Polygon",
    "coordinates": [[[-118.25, 34.05], [-118.24, 34.05],
                       [-118.24, 34.06], [-118.25, 34.06],
                       [-118.25, 34.05]]]
  }
}

Markers can also be submitted via Socket.IO for lower-latency creation:

// Socket.IO event: marker:create (client -> server)
{
  "marker_type": "point",
  "name":        "RP Delta",
  "category":    "rally_point",
  "geometry": {
    "type":        "Point",
    "coordinates": [-118.243, 34.052]
  }
}

Real-Time Sync

All marker CRUD operations are broadcast to every connected client via Socket.IO immediately after the database write completes. Changes appear on all maps simultaneously without any polling or manual refresh.

Event Direction Payload
marker:created Server → All Full marker object including geometry, properties, generated id, and created_at
marker:updated Server → All Full updated marker object
marker:deleted Server → All { id } -- clients remove the feature from their map source

Markers are included in the offline IndexedDB cache. When the client reconnects after a connectivity gap, the cache is refreshed from the server and any queued marker operations are replayed in order.

Styling

MapLibre renders markers using a data-driven style stack that provides visual differentiation across marker types and states:

  1. Polygon fill -- semi-transparent filled area using the marker's color and opacity properties.
  2. Polygon outline -- solid border using the marker's color and lineWidth properties.
  3. Line stroke -- rendered for LineString geometry using color and lineWidth.
  4. Point circle -- rendered for Point geometry as a circle whose color is determined by the marker's category.
  5. Label -- the marker name rendered as a text symbol above the feature centroid.

Color, opacity, and line width are stored in the marker's marker_properties JSON column and applied at render time. Users can adjust these values through the marker edit panel in the slide-out UI. Changes persist to the database and broadcast to all clients via marker:updated.

Photo Attachments

Point markers support photo attachments through the existing file upload system. Any image file uploaded to POST /api/files can be associated with a marker by including the marker's identifier in the upload metadata.

Photos are displayed in the marker detail view within the slide-out panel. Clicking a thumbnail opens the full-resolution image using an authenticated download URL. Multiple photos per marker are supported; they are displayed in a scrollable gallery ordered by upload timestamp.

Photo uploads follow the same MIME allowlist and size limits as the File Sharing system. Only authenticated operators and admins can attach photos; observers can view them.

Filtering

The GET /api/markers endpoint supports multiple independent filtering mechanisms that can be combined in a single request.

GET /api/markers
Retrieve markers with optional filtering.
Query params: marker_type, category, bbox=west,south,east,north, lat + lng + radius

Type Filter

Filter markers by geometry type to retrieve only points, lines, or polygons:

GET /api/markers?marker_type=point

Category Filter

Pass a comma-separated list of categories to restrict results to specific point marker types:

GET /api/markers?category=hazard,medical

Bounding Box Filter

Pass a bbox query parameter as four comma-separated decimal values in minLon,minLat,maxLon,maxLat order (GeoJSON convention):

GET /api/markers?bbox=-118.5,33.9,-118.1,34.2

The server translates the bbox into a PostGIS ST_MakeEnvelope call and filters using the spatial index on the geometry column, making this query efficient even with large datasets.

Radius Filter

Supply a center point and a radius in meters to fetch all markers within a given distance:

GET /api/markers?lat=34.052&lng=-118.243&radius=5000

Internally this executes a PostGIS ST_DWithin call against the GEOGRAPHY column. Because the column type is GEOGRAPHY rather than GEOMETRY, the radius unit is always meters regardless of the underlying coordinate system.

Category Visibility Filtering

The marker panel includes a visibility toggle row for each category. Toggling a category off applies a MapLibre filter expression that hides all markers of that type without removing them from the data source. This is a client-side display preference and does not affect what other users see.

Permissions

Marker access follows GroundWave's role-based access control model. Permission levels are enforced on both the REST API and Socket.IO event handlers.

Action Observer Operator Admin
View markers
Create markers
Edit own markers
Delete own markers
Edit any marker
Delete any marker

Observers can view all markers on the map and in the marker panel but cannot create, modify, or delete any marker. This is appropriate for personnel who need situational awareness without permission to alter the shared map picture.

When authentication is disabled (AUTH_REQUIRED=false), all connected users have full create, edit, and delete access to markers regardless of role. Enable authentication for any deployment requiring access control.

Markers can be exported as a GeoJSON FeatureCollection from the admin Data Export tab and re-imported via POST /api/import/markers, enabling faithful round-trip transfer of marker data between GroundWave deployments.