brick/geo
brick/geo is a PHP geometry library for working with points, lines, polygons, and other shapes. Provides common spatial operations, parsing/formatting, and robust value objects to model geo data cleanly in applications and services.
Installation
composer require brick/geo
Add to composer.json if using a monorepo or constrained environment:
"require": {
"brick/geo": "^2.0"
}
Basic Usage Create a point in the simplest form:
use Brick\Geo\Point;
$point = Point::fromDegrees(40.7128, -74.0060); // NYC coordinates
echo $point; // "POINT (40.7128 -74.0060)"
Key Classes to Explore
Point, LineString, Polygon, MultiPoint, MultiLineString, MultiPolygonGeometryCollectionCircle (for radius-based queries)First Use Case: Distance Calculation
$pointA = Point::fromDegrees(40.7128, -74.0060);
$pointB = Point::fromDegrees(34.0522, -118.2437); // LA
$distance = $pointA->distance($pointB); // Returns distance in meters
$point = Point::fromDegrees(40.7128, -74.0060);
$radians = $point->toRadians();
use Brick\Geo\Coordinate\Coordinate;
$utm = $point->toUtm(); // Returns UTM zone and coordinates
$buffered = $point->buffer(1000); // 1km radius polygon
$polygon1 = Polygon::fromPoints([...]);
$polygon2 = Polygon::fromPoints([...]);
$intersection = $polygon1->intersection($polygon2);
if ($polygon->contains($point)) { ... }
if ($point->within($polygon)) { ... }
$geohash = $point->geohash(); // e.g., "dr5reg"
$bounds = Geohash::bounds("dr5reg");
$webMercator = $point->toWebMercator();
ST_GeomFromText in PostgreSQL with the geometry column type:
$geometry = $polygon->toText(); // "POLYGON ((...))"
DB::table('locations')->insert(['geom' => $geometry]);
$results = DB::table('locations')
->whereRaw('ST_Contains(geom, ST_GeomFromText(?))', [$point->toText()])
->get();
$geoJson = $polygon->toGeoJson();
return response()->json(['geometry' => $geoJson]);
$cachedPolygon = Cache::remember("polygon_{$key}", now()->addHours(1), function () {
return $this->buildComplexPolygon();
});
GeometryCollection for bulk operations:
$collection = GeometryCollection::fromGeometries([$point1, $line1, $polygon1]);
$totalLength = $collection->totalLength(); // Sum of all lengths
contains() checks to fail. Use a small epsilon for comparisons:
if ($polygon->contains($point, 0.00001)) { ... }
within() for point-in-polygon checks when precision is critical.Polygon::fromPoints() carefully).Polygon::isValid() to check:
if (!$polygon->isValid()) {
$polygon = $polygon->buffer(0); // Fix invalid polygons
}
try {
$utm = $point->toUtm();
} catch (InvalidArgumentException $e) {
// Fallback to a nearby zone or use degrees
}
toGeoJson() and toText() (WKT) may produce different representations. Validate output:
$wkt = $polygon->toText();
$geoJson = $polygon->toGeoJson();
assert($wkt !== $geoJson); // Expected: formats differ
MultiPolygon collections into memory. Use spatial indexes (e.g., PostGIS) for filtering first.$geoJson = $geometry->toGeoJson();
file_put_contents('debug.json', json_encode($geoJson));
echo $polygon->toText() . "\n";
\Log::debug('Polygon WKT:', ['wkt' => $polygon->toText()]);
InvalidArgumentException: Invalid coordinates (e.g., latitude > 90°).RuntimeException: Unsupported operations (e.g., buffering a LineString).Projection for niche use cases:
class CustomProjection extends Projection {
public function forward(Coordinate $coordinate): Coordinate { ... }
public function inverse(Coordinate $coordinate): Coordinate { ... }
}
class CityBoundaryFactory {
public static function createFromZipCodes(array $zipCodes): Polygon { ... }
}
$index = new RTree();
$index->insert($polygon, $polygon->getBounds());
trait LogsGeometryChanges {
private $originalWkt;
public function __construct() {
$this->originalWkt = $this->toText();
}
public function modified(): bool {
return $this->toText() !== $this->originalWkt;
}
}
toArray() for custom serialization:
class CustomPoint extends Point {
public function toArray(): array {
return [
'latitude' => $this->latitude->degrees,
'longitude' => $this->longitude->degrees,
'altitude' => $this->altitude?->degrees,
];
}
}
How can I help you explore Laravel packages today?