All articles
September 22, 20254 min read

GeoJSON, PostGIS, and Filament Maps: A Practical Guide

From GeoJSON parsing to interactive map widgets, a practical guide to geospatial features in web apps.

GeoJSONPHPMapsFilament

GeoJSON is the lingua franca of geospatial data on the web. Simple, human-readable, and supported by every major mapping library and database with spatial extensions. I have built GeoJSON tooling, integrated PostGIS in Laravel apps, and created the Filament Maps plugin. Here is what I learned.

Why GeoJSON

GeoJSON is JSON. A point is {"type":"Point","coordinates":[-46.6333,-23.5505]}. A polygon is a list of coordinate rings. Features can have properties. It is easy to parse, easy to generate, and easy to debug. No binary formats, no proprietary schemas.

When you need to store or transmit geographic data, GeoJSON is usually the right choice. APIs return it. Mapping libraries consume it. Databases can index it.

Building a GeoJSON PHP Library

I created a PHP class collection for working with GeoJSON because I needed a type-safe way to construct and validate geographic data in Laravel applications. The GeoJSON spec has rules: a Point has exactly two or three coordinates, a Polygon's first and last ring coordinates must match, and so on. Raw arrays do not enforce that. Classes do.

php
1use Dmandrade\GeoJson\Geometry\Point;
2use Dmandrade\GeoJson\Feature\Feature;
3
4$point = new Point([-46.6333, -23.5505]);
5$feature = new Feature($point, ['name' => 'São Paulo']);
6$json = $feature->toJson();
7

The library validates on construction. Invalid coordinates throw. Serialization produces valid GeoJSON. When you integrate with external APIs, you can parse into these types and know the structure is correct before you store or display.

Database Integration: PostGIS and Beyond

PostgreSQL with PostGIS is the gold standard for storing geospatial data. You get real spatial types, spatial indexes (GIST), and functions like ST_DWithin, ST_Contains, and ST_Intersects. For "find all points within 5km of this location," PostGIS is built for it.

Laravel does not ship with PostGIS support out of the box, but you can add it. Use raw queries or a package like matanyadaev/laravel-eloquent-spatial for a nicer API. The key is choosing the right spatial index for your query patterns. A B-tree on coordinates works for "points in this bounding box." For distances and complex shapes, GIST is what you want.

MySQL 8+ has spatial support too. It is improving. For new projects with heavy geo workloads, I still prefer PostGIS. For existing MySQL deployments with light geo needs, MySQL can work.

Filament Maps: Bridging Data and UI

The Filament Maps plugin came from a need to visualize location data in admin panels. We had stores, delivery zones, and survey responses with coordinates. Tables and forms were not enough. We needed maps.

Filament Maps renders Leaflet maps with markers, circles, polygons, and GeoJSON layers. You pass coordinates or GeoJSON. The plugin handles the rest. It fits Filament's resource and form system. Add a map field to a form, and users see an interactive map. Add a map to a table, and you get a spatial overview.

The plugin supports multiple tile providers (OpenStreetMap, Mapbox, Google) and lets you customize markers and popups. For admin panels, that is usually enough. For fully custom map UIs, you might use Leaflet or Mapbox GL directly.

Performance Considerations

Geospatial queries can be expensive. A few rules:

Spatial indexes. Without them, the database scans every row. With a GIST index on your geometry column, bounding box and distance queries become fast.

Bounding box pre-filtering. Before running a complex spatial query, filter by a bounding box. "Points in this map view" is often a box query first, then a more precise filter if needed.

Tile-based rendering. For large datasets, loading every point at once is a bad idea. Use clustering or vector tiles. Show clusters at low zoom, individual points at high zoom. Libraries like Leaflet.markercluster help.

Caching. If the same geospatial query runs often (e.g. "stores near this user"), cache the result. Invalidate when data changes.

A Real Example: Delivery Zones

We had a project with delivery zones as polygons. Admins drew zones on a map. We stored them as GeoJSON in PostGIS. The app needed to check if an address fell inside any zone.

Flow: geocode the address to coordinates, then ST_Contains(zone_geom, point). PostGIS handles it. We indexed the zone geometry with GIST. Queries were fast even with hundreds of zones.

The lesson: match the right tool to the problem. PostGIS for storage and querying. GeoJSON for interchange. Filament Maps (or Leaflet) for editing and display.

The Future

Browser APIs like the Geolocation API are getting more precise. WebGL enables rich 3D map experiences. Geospatial features are becoming a standard expectation. If your app has location data, invest in it. Start small. Add a map. Add a spatial index. Iterate.