Skip to content

Commit a1dac72

Browse files
authored
Merge pull request #49 from constructive-io/devin/1774221043-geotypes-rename-add-geography
feat(geotypes): rename domains and add geography variants
2 parents 83be2dd + 822c363 commit a1dac72

22 files changed

Lines changed: 210 additions & 74 deletions

packages/geotypes/README.md

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ Geographic data types and spatial functions for PostgreSQL.
1616

1717
## Overview
1818

19-
`@pgpm/geotypes` provides PostgreSQL domain types for geographic data, built on top of PostGIS geometry types. This package enables type-safe storage and validation of geographic coordinates and polygons with proper SRID (Spatial Reference System Identifier) enforcement.
19+
`@pgpm/geotypes` provides PostgreSQL domain types for geographic data, built on top of PostGIS geometry and geography types. This package enables type-safe storage and validation of geographic coordinates and polygons with proper SRID (Spatial Reference System Identifier) enforcement.
2020

2121
## Features
2222

23-
- **geolocation**: A domain type for geographic points (latitude/longitude) using WGS84 (SRID 4326)
24-
- **geopolygon**: A domain type for geographic polygons using WGS84 (SRID 4326)
23+
- **geo_point**: A geometry domain for geographic points (latitude/longitude) using WGS84 (SRID 4326) — planar coordinates, fast computation
24+
- **geo_polygon**: A geometry domain for geographic polygons using WGS84 (SRID 4326) — planar coordinates
25+
- **geography_point**: A geography domain for geographic points using WGS84 (SRID 4326) — geodetic calculations on the sphere, distances in meters
26+
- **geography_polygon**: A geography domain for geographic polygons using WGS84 (SRID 4326) — geodetic calculations on the sphere
2527
- Automatic SRID validation to ensure coordinate system consistency
2628
- Integration with PostGIS spatial functions
2729

@@ -82,30 +84,44 @@ pgpm deploy --createdb --database mydb1
8284
### Creating Tables with Geographic Types
8385

8486
```sql
87+
-- Geometry (planar) types
8588
CREATE TABLE places (
8689
id serial PRIMARY KEY,
87-
loc geolocation,
88-
area geopolygon
90+
loc geo_point,
91+
area geo_polygon
92+
);
93+
94+
-- Geography (spherical) types
95+
CREATE TABLE places_geo (
96+
id serial PRIMARY KEY,
97+
loc geography_point,
98+
area geography_polygon
8999
);
90100
```
91101

92102
### Inserting Geographic Data
93103

94104
```sql
95-
-- Insert a point location (San Francisco)
105+
-- Insert a geometry point (San Francisco)
96106
INSERT INTO places (loc)
97107
VALUES (
98108
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)
99109
);
100110

101-
-- Insert a polygon area
111+
-- Insert a geometry polygon
102112
INSERT INTO places (area)
103113
VALUES (
104114
ST_SetSRID(
105115
ST_GeomFromText('POLYGON((-122.5 37.7, -122.4 37.7, -122.4 37.8, -122.5 37.8, -122.5 37.7))'),
106116
4326
107117
)
108118
);
119+
120+
-- Insert a geography point (distances in meters, accounts for curvature)
121+
INSERT INTO places_geo (loc)
122+
VALUES (
123+
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography
124+
);
109125
```
110126

111127
### SRID Validation
@@ -118,28 +134,48 @@ INSERT INTO places (loc)
118134
VALUES (
119135
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 3857)
120136
);
121-
-- ERROR: value for domain geolocation violates check constraint
137+
-- ERROR: value for domain geo_point violates check constraint
122138
```
123139

124140
## Domain Types
125141

126-
### geolocation
142+
### Geometry Domains (Planar)
127143

128-
A PostgreSQL domain based on `geometry(Point, 4326)` that stores geographic point coordinates.
144+
#### geo_point
145+
146+
A PostgreSQL domain based on `geometry(Point, 4326)` that stores geographic point coordinates using planar (flat) coordinates.
129147

130148
- **Base Type**: `geometry(Point, 4326)`
131-
- **Use Case**: Storing latitude/longitude coordinates for locations
149+
- **Use Case**: Storing latitude/longitude coordinates for locations, fast computation
132150
- **SRID**: 4326 (WGS84 - World Geodetic System 1984)
133151

134-
### geopolygon
152+
#### geo_polygon
135153

136-
A PostgreSQL domain based on `geometry(Polygon, 4326)` that stores geographic polygon areas.
154+
A PostgreSQL domain based on `geometry(Polygon, 4326)` that stores geographic polygon areas using planar coordinates.
137155

138156
- **Base Type**: `geometry(Polygon, 4326)`
139157
- **Use Case**: Storing geographic boundaries, regions, or areas
140158
- **SRID**: 4326 (WGS84)
141159
- **Validation**: Ensures valid polygon geometry (closed rings, proper vertex count)
142160

161+
### Geography Domains (Spherical)
162+
163+
#### geography_point
164+
165+
A PostgreSQL domain based on `geography(Point, 4326)` that stores geographic point coordinates using geodetic (spherical) calculations.
166+
167+
- **Base Type**: `geography(Point, 4326)`
168+
- **Use Case**: Real-world GPS data where distances should be in meters and account for Earth's curvature
169+
- **SRID**: 4326 (WGS84)
170+
171+
#### geography_polygon
172+
173+
A PostgreSQL domain based on `geography(Polygon, 4326)` that stores geographic polygon areas using geodetic calculations.
174+
175+
- **Base Type**: `geography(Polygon, 4326)`
176+
- **Use Case**: Real-world geographic boundaries where area/distance calculations need to account for Earth's curvature
177+
- **SRID**: 4326 (WGS84)
178+
143179
## Dependencies
144180

145181
- `@pgpm/types`: Core PostgreSQL type definitions
@@ -153,9 +189,10 @@ pnpm test
153189
```
154190

155191
The test suite validates:
156-
- Successful insertion of valid points and polygons
192+
- Successful insertion of valid geometry and geography points and polygons
157193
- SRID validation and rejection of incorrect coordinate systems
158194
- Polygon geometry validation
195+
- Geography distance calculations return values in meters
159196

160197
## Related Tooling
161198

packages/geotypes/__tests__/geotypes.test.ts

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,16 @@ beforeAll(async () => {
1111
await pg.any(`
1212
CREATE TABLE places (
1313
id serial PRIMARY KEY,
14-
loc geolocation,
15-
area geopolygon
14+
loc geo_point,
15+
area geo_polygon
16+
);
17+
`);
18+
19+
await pg.any(`
20+
CREATE TABLE places_geo (
21+
id serial PRIMARY KEY,
22+
loc geography_point,
23+
area geography_polygon
1624
);
1725
`);
1826
});
@@ -21,9 +29,9 @@ afterAll(async () => {
2129
await teardown();
2230
});
2331

24-
describe('places table (geotypes)', () => {
32+
describe('geometry domains (geo_point, geo_polygon)', () => {
2533
it('inserts valid point and polygon', async () => {
26-
await expect(pg.any(`
34+
await expect(pg.any(`
2735
INSERT INTO places (loc, area)
2836
VALUES (
2937
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326),
@@ -36,7 +44,7 @@ describe('places table (geotypes)', () => {
3644
});
3745

3846
it('fails if point SRID is incorrect', async () => {
39-
await expect(pg.any(`
47+
await expect(pg.any(`
4048
INSERT INTO places (loc)
4149
VALUES (
4250
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 3857)
@@ -45,11 +53,44 @@ describe('places table (geotypes)', () => {
4553
});
4654

4755
it('fails if polygon is invalid', async () => {
48-
await expect(pg.any(`
56+
await expect(pg.any(`
4957
INSERT INTO places (area)
5058
VALUES (
5159
ST_SetSRID(ST_GeomFromText('POLYGON((0 0, 1 1, 2 2))'), 4326)
5260
);
5361
`)).rejects.toThrow();
5462
});
5563
});
64+
65+
describe('geography domains (geography_point, geography_polygon)', () => {
66+
it('inserts valid geography point and polygon', async () => {
67+
await expect(pg.any(`
68+
INSERT INTO places_geo (loc, area)
69+
VALUES (
70+
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography,
71+
ST_GeogFromText('POLYGON((-122.5 37.7, -122.4 37.7, -122.4 37.8, -122.5 37.8, -122.5 37.7))')
72+
);
73+
`)).resolves.not.toThrow();
74+
});
75+
76+
it('computes distance in meters for geography_point', async () => {
77+
const result = await pg.one(`
78+
SELECT ST_Distance(
79+
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 4326)::geography,
80+
ST_SetSRID(ST_MakePoint(-73.9857, 40.7484), 4326)::geography
81+
) AS dist_meters;
82+
`);
83+
// SF to NYC is approximately 4,000 km
84+
expect(result.dist_meters).toBeGreaterThan(4000000);
85+
expect(result.dist_meters).toBeLessThan(5000000);
86+
});
87+
88+
it('fails if geography point SRID is incorrect', async () => {
89+
await expect(pg.any(`
90+
INSERT INTO places_geo (loc)
91+
VALUES (
92+
ST_SetSRID(ST_MakePoint(-122.4194, 37.7749), 3857)::geography
93+
);
94+
`)).rejects.toThrow();
95+
});
96+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Deploy schemas/public/domains/geo_point to pg
2+
3+
-- requires: schemas/public/schema
4+
5+
BEGIN;
6+
7+
CREATE DOMAIN geo_point AS geometry (Point, 4326);
8+
COMMENT ON DOMAIN geo_point IS E'@name pgpmInternalTypeGeoPoint';
9+
10+
COMMIT;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Deploy schemas/public/domains/geo_polygon to pg
2+
3+
-- requires: schemas/public/schema
4+
5+
BEGIN;
6+
7+
CREATE DOMAIN geo_polygon AS geometry (Polygon, 4326);
8+
COMMENT ON DOMAIN geo_polygon IS E'@name pgpmInternalTypeGeoPolygon';
9+
10+
COMMIT;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Deploy schemas/public/domains/geography_point to pg
2+
3+
-- requires: schemas/public/schema
4+
5+
BEGIN;
6+
7+
CREATE DOMAIN geography_point AS geography (Point, 4326);
8+
COMMENT ON DOMAIN geography_point IS E'@name pgpmInternalTypeGeographyPoint';
9+
10+
COMMIT;
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
-- Deploy schemas/public/domains/geography_polygon to pg
2+
3+
-- requires: schemas/public/schema
4+
5+
BEGIN;
6+
7+
CREATE DOMAIN geography_polygon AS geography (Polygon, 4326);
8+
COMMENT ON DOMAIN geography_polygon IS E'@name pgpmInternalTypeGeographyPolygon';
9+
10+
COMMIT;

packages/geotypes/deploy/schemas/public/domains/geolocation.sql

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/geotypes/deploy/schemas/public/domains/geopolygon.sql

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/geotypes/pgpm.plan

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,7 @@
33
%uri=pgpm-geo-types
44

55
schemas/public/schema 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/schema
6-
schemas/public/domains/geolocation [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geolocation
7-
schemas/public/domains/geopolygon [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geopolygon
6+
schemas/public/domains/geo_point [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geo_point
7+
schemas/public/domains/geo_polygon [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geo_polygon
8+
schemas/public/domains/geography_point [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geography_point
9+
schemas/public/domains/geography_polygon [schemas/public/schema] 2017-08-11T08:11:51Z skitch <skitch@5b0c196eeb62> # add schemas/public/domains/geography_polygon
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- Revert schemas/public/domains/geo_point from pg
2+
3+
BEGIN;
4+
5+
DROP TYPE public.geo_point;
6+
7+
COMMIT;

0 commit comments

Comments
 (0)