Skip to content

Commit c90bdd5

Browse files
committed
feat(geojson): Rect encoding
1 parent e8fff31 commit c90bdd5

2 files changed

Lines changed: 64 additions & 8 deletions

File tree

geojson/geometry.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@ import { Rect } from '../s2/Rect'
77
import * as point from './point'
88
import * as linestring from './linestring'
99
import * as polygon from './polygon'
10-
import { Loop } from '../s2/Loop'
10+
import * as rect from './rect'
1111

12-
export type Decodable = Point | Polyline | Polygon | Point[] | Polyline[] | Polygon[]
13-
export type Encodable = bigint | Cell | Rect | Decodable
12+
export type Decodable = Point | Polyline | Polygon | Rect | Point[] | Polyline[] | Polygon[]
13+
export type Encodable = bigint | Cell | Decodable
1414

1515
/**
1616
* Returns a geojson Geometry given a s2 shape(s).
@@ -19,14 +19,11 @@ export type Encodable = bigint | Cell | Rect | Decodable
1919
export const toGeoJSON = (shape: Encodable): geojson.Geometry => {
2020
if (typeof shape === 'bigint') shape = Cell.fromCellID(shape)
2121
if (shape instanceof Cell) shape = Polygon.fromCell(shape)
22-
else if (shape instanceof Rect) {
23-
const loop = new Loop(Array(4).map((_, i) => Point.fromLatLng((shape as Rect).vertex(i))))
24-
shape = Polygon.fromOrientedLoops([loop])
25-
}
2622

2723
if (shape instanceof Point) return point.marshal(shape)
2824
if (shape instanceof Polyline) return linestring.marshal(shape)
2925
if (shape instanceof Polygon) return polygon.marshal(shape)
26+
if (shape instanceof Rect) return rect.marshal(shape)
3027

3128
if (Array.isArray(shape) && shape.length) {
3229
if (shape.every((g: any) => g instanceof Point)) return point.marshalMulti(shape as Point[])
@@ -46,7 +43,10 @@ export const fromGeoJSON = (geometry: geojson.Geometry): Decodable => {
4643

4744
if (t === 'Point') return point.unmarshal(geometry as geojson.Point)
4845
if (t === 'LineString') return linestring.unmarshal(geometry as geojson.LineString)
49-
if (t === 'Polygon') return polygon.unmarshal(geometry as geojson.Polygon)
46+
if (t === 'Polygon') {
47+
if (rect.valid(geometry as geojson.Polygon)) return rect.unmarshal(geometry as geojson.Polygon)
48+
return polygon.unmarshal(geometry as geojson.Polygon)
49+
}
5050

5151
if (t === 'MultiPoint') return point.unmarshalMulti(geometry as geojson.MultiPoint)
5252
if (t === 'MultiLineString') return linestring.unmarshalMulti(geometry as geojson.MultiLineString)

geojson/rect.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type * as geojson from 'geojson'
2+
import { Rect } from '../s2/Rect'
3+
import { Interval as R1Interval } from '../r1/Interval'
4+
import { Interval as S1Interval } from '../s1/Interval'
5+
import { Point } from '../s2/Point'
6+
import { Loop } from '../s2/Loop'
7+
import { Polygon } from '../s2/Polygon'
8+
import * as polygon from './polygon'
9+
import { DEGREE } from '../s1/angle_constants'
10+
11+
/**
12+
* Returns a geojson Polygon geometry given an s2 Rect.
13+
* @category Constructors
14+
*/
15+
export const marshal = (rect: Rect): geojson.Polygon => {
16+
const loop = new Loop(Array.from({ length: 4 }, (_, i) => Point.fromLatLng(rect.vertex(i))))
17+
return polygon.marshal(Polygon.fromOrientedLoops([loop]))
18+
}
19+
20+
/**
21+
* Constructs an s2 Rect given a geojson Polygon geometry.
22+
* @category Constructors
23+
*/
24+
export const unmarshal = (geometry: geojson.Polygon): Rect => {
25+
const ring = geometry.coordinates[0]
26+
const lngLo = Math.min(ring[0][0], ring[2][0])
27+
const lngHi = Math.max(ring[0][0], ring[2][0])
28+
const latLo = Math.min(ring[0][1], ring[2][1])
29+
const latHi = Math.max(ring[0][1], ring[2][1])
30+
31+
return new Rect(
32+
new R1Interval(latLo * DEGREE, latHi * DEGREE),
33+
S1Interval.fromEndpoints(lngLo * DEGREE, lngHi * DEGREE)
34+
)
35+
}
36+
37+
/**
38+
* Returns true iff the geojson Polygon represents a valid Rect.
39+
* @category Constructors
40+
*/
41+
export const valid = (geometry: geojson.Polygon): boolean => {
42+
if (geometry?.type !== 'Polygon') return false
43+
if (geometry?.coordinates.length !== 1) return false
44+
const ring = geometry.coordinates[0]
45+
if (ring.length !== 5) return false
46+
if (!pointsEqual(ring[0], ring[4])) return false
47+
if (!lngEqual(ring[0], ring[3])) return false
48+
if (!lngEqual(ring[1], ring[2])) return false
49+
if (!latEqual(ring[0], ring[1])) return false
50+
if (!latEqual(ring[2], ring[3])) return false
51+
return true
52+
}
53+
54+
const lngEqual = (a: geojson.Position, b: geojson.Position) => a[0] === b[0]
55+
const latEqual = (a: geojson.Position, b: geojson.Position) => a[1] === b[1]
56+
const pointsEqual = (a: geojson.Position, b: geojson.Position) => lngEqual(a, b) && latEqual(a, b)

0 commit comments

Comments
 (0)