geojson-to-gml-3
Version:
a package to translate geojson to GML 3.2.1
467 lines (433 loc) • 11.7 kB
text/typescript
// ©️ Steven Kalt
// Spdx-License-Identifier: PolyForm-Noncommercial-1.0.0 OR PolyForm-Free-Trial-1.0.0
/**
* A library of functions to convert geojson to GML.
* @module geojson-to-gml-3
*/
import type {
BBox,
Geometry,
GeometryCollection,
LineString,
MultiLineString,
MultiPoint,
MultiPolygon,
Point,
Polygon,
} from 'geojson';
import {
type Name,
NsRegistry,
tag,
attrs,
type Xml,
type Text,
} from 'minimxml';
import {
type CoordOrderParam,
gmlGeometry,
gmlGeometryCollection,
gmlLineString,
gmlMultiLineString,
gmlMultiPoint,
gmlMultiPolygon,
gmlPoint,
gmlPolygon,
type IdParam,
type NsParam,
type SrsDimensionParam,
type SrsNameParam,
useCoords,
withGmlNamespace,
} from './internal.js';
export const GML = 'http://www.opengis.net/gml/3.2' as const;
export type Gml = Xml<typeof GML>;
export type ToGml = (r?: NsRegistry) => Gml;
// TODO: document, test
export const bbox =
/* @__PURE__ */
(bbox: BBox, params: Params): ToGml =>
(namespaces?: NsRegistry): Gml => {
namespaces = namespaces ?? new NsRegistry();
const { ns = 'gml' as Name, srsDimension, srsName } = params;
const gml = namespaces.getOrInsert(ns, GML);
return tag(
gml.qualify('Envelope' as Name),
attrs({
srsDimension,
srsName,
}),
tag(
gml.qualify('lowerCorner'),
[],
bbox
.slice(0, bbox.length / 2) // safe since `BBox["length"]` must be either 4 or 6
.join(' ') as Text, // safe since `BBox` is composed entirely of numbers
),
tag(
gml.qualify('upperCorner'),
[],
bbox.slice(bbox.length / 2).join(' ') as Text,
),
)(namespaces);
};
/**
The signature of all public geojson-to-gml conversion functions.
Note this can only convert what geojson can store: simple feature types, not GML
coverage, topology, etc.
*/
export type Converter<Geom extends Geometry = Geometry> = (
geom: Geom,
params?: Params,
) => ToGml;
/** Optional parameters for conversion of geojson to gml geometries */
export type Params = Partial<
NsParam & IdParam & CoordOrderParam & SrsNameParam & SrsDimensionParam
> & {};
/**
* geojson coordinates are in longitude/easting, latitude/northing [,elevation]
* order by [RFC-7946 § 3.1.1]{@link https://tools.ietf.org/html/rfc7946#section-3.1.1}.
* however, you may use a CRS that follows a latitude/easting,
* longitude/northing, [,elevation, [...etc]] order.
* @see https://macwright.com/lonlat/
*/
export const enum CoordinateOrder {
/** Geojson's default order */
LON_LAT = 'lon,lat',
LAT_LON = 'lat,lon',
}
export type MultiGeometry =
| MultiPoint
| MultiLineString
| MultiPolygon
| GeometryCollection;
/*!! use-example file://./../tests/point.example.ts */
/**
Converts an input geojson Point geometry to GML
@function
@param geom the {@link Point} to render to GML
@param params @see Params
@returns a GML string representing the input Point
@example
```ts
import { type Point } from 'geojson';
import { expect, test } from 'vitest';
import { point } from 'geojson-to-gml-3';
test('point', () => {
const geom: Point = { type: 'Point', coordinates: [102.0, 0.5] };
expect(point(geom)()).toBe(''
+ `<gml:Point>`
+ `<gml:pos>`
+ `102 0.5`
+ `</gml:pos>`
+ `</gml:Point>`
);
});
```
*/
export const point: Converter<Point> = /* @__PURE__ */ withGmlNamespace<Point>(
useCoords(gmlPoint),
);
/*!! use-example file://./../tests/lineString.example.ts */
/**
Convert an input geojson LineString geometry to gml
@function
@param line the coordinates member of the geojson geometry
@param gmlId the gml:id
@param params optional parameters; @see {@link Params}
@returns a string containing gml representing the input geometry
@example
```ts
import { type LineString } from 'geojson';
import { expect, test } from 'vitest';
import { lineString } from 'geojson-to-gml-3';
test('lineString', () => {
const geom: LineString = {
type: 'LineString',
coordinates: [
[102.0, 0.0],
[103.0, 1.0],
[104.0, 0.0],
[105.0, 1.0],
],
};
expect(lineString(geom)()).toBe(''
+ `<gml:LineString>`
+ `<gml:posList>`
+ `102 0 103 1 104 0 105 1`
+ `</gml:posList>`
+ `</gml:LineString>`
);
});
```
*/
export const lineString: Converter<LineString> =
/* @__PURE__ */ withGmlNamespace<LineString>(useCoords(gmlLineString));
/*!! use-example file://./../tests/polygon.example.ts */
/**
Converts an input geojson Polygon geometry to gml
@function
@param geom the {@link Polygon} to convert to gml
@param params @see Params
@returns a string containing gml representing the input geometry
@example
```ts
import { type Polygon } from 'geojson';
import { expect, test } from 'vitest';
import { polygon } from 'geojson-to-gml-3';
test('polygon', () => {
const geom: Polygon = {
type: 'Polygon',
coordinates: [
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
],
};
expect(polygon(geom)()).toBe(''
+ `<gml:Polygon>`
+ `<gml:exterior>`
+ `<gml:LinearRing>`
+ `<gml:posList>`
+ `100 0 101 0 101 1 100 1 100 0`
+ `</gml:posList>`
+ `</gml:LinearRing>`
+ `</gml:exterior>`
+ `</gml:Polygon>`
);
});
```
*/
export const polygon: Converter<Polygon> =
/* @__PURE__ */ withGmlNamespace<Polygon>(useCoords(gmlPolygon));
/*!! use-example file://./../tests/multiPoint.example.ts */
/**
Converts an input geojson MultiPoint geometry to gml
@function
@param geom the {@link MultiPoint} to convert to gml
@param params @see Params
@returns a string containing gml representing the input geometry
@example
```ts
import { type MultiPoint } from 'geojson';
import { expect, test } from 'vitest';
import { multiPoint } from 'geojson-to-gml-3';
test('multiPoint', () => {
const geom: MultiPoint = {
type: 'MultiPoint',
coordinates: [
[100.0, 0.0],
[101.0, 1.0],
],
};
expect(multiPoint(geom)()).toBe(''
+ `<gml:MultiPoint>`
+ `<gml:pointMembers>`
+ `<gml:Point>`
+ `<gml:pos>`
+ `100 0`
+ `</gml:pos>`
+ `</gml:Point>`
+ `<gml:Point>`
+ `<gml:pos>`
+ `101 1`
+ `</gml:pos>`
+ `</gml:Point>`
+ `</gml:pointMembers>`
+ `</gml:MultiPoint>`
);
});
```
*/
export const multiPoint: Converter<MultiPoint> =
/* @__PURE__ */
withGmlNamespace<MultiPoint>(gmlMultiPoint);
/*!! use-example file://./../tests/multiLineString.example.ts */
/**
Converts an input geojson MultiLineString geometry to gml
@function
@param coords the coordinates member of the geojson geometry
@param params @see Params
@returns a string containing gml representing the input geometry
@example
```ts
import { MultiLineString } from 'geojson';
import { expect, test } from 'vitest';
import { multiLineString } from '../src';
test('multiLineString', () => {
const geom: MultiLineString = {
type: 'MultiLineString',
coordinates: [
[
[100.0, 0.0],
[101.0, 1.0],
],
[
[102.0, 2.0],
[103.0, 3.0],
],
],
};
expect(multiLineString(geom)()).toBe(''
+ `<gml:MultiCurve>`
+ `<gml:curveMembers>`
+ `<gml:LineString>`
+ `<gml:posList>`
+ `100 0 101 1`
+ `</gml:posList>`
+ `</gml:LineString>`
+ `<gml:LineString>`
+ `<gml:posList>`
+ `102 2 103 3`
+ `</gml:posList>`
+ `</gml:LineString>`
+ `</gml:curveMembers>`
+ `</gml:MultiCurve>`
);
});
```
*/
export const multiLineString: Converter<MultiLineString> =
/* @__PURE__ */
withGmlNamespace<MultiLineString>(gmlMultiLineString);
/*!! use-example file://./../tests/multiPolygon.example.ts */
/**
Converts an input geojson `MultiPolygon` geometry to GML
@function
@param geom the {@link MultiPolygon} to convert to GML
@param params @see Params
@returns a string containing GML representing the input geometry
@example
```ts
import { type MultiPolygon } from 'geojson';
import { expect, test } from 'vitest';
import { multiPolygon } from 'geojson-to-gml-3';
import { point } from 'geojson-to-gml-3';
test('multiPolygon', () => {
const geom: MultiPolygon = {
type: 'MultiPolygon',
coordinates: [
[
[
[102.0, 2.0],
[103.0, 2.0],
[103.0, 3.0],
[102.0, 3.0],
[102.0, 2.0],
],
],
[
[
[100.0, 0.0],
[101.0, 0.0],
[101.0, 1.0],
[100.0, 1.0],
[100.0, 0.0],
],
[
[100.2, 0.2],
[100.8, 0.2],
[100.8, 0.8],
[100.2, 0.8],
[100.2, 0.2],
],
],
],
};
expect(multiPolygon(geom)()).toBe(''
+ `<gml:MultiSurface>`
+ `<gml:surfaceMembers>`
+ `<gml:Polygon>`
+ `<gml:exterior>`
+ `<gml:LinearRing>`
+ `<gml:posList>`
+ `102 2 103 2 103 3 102 3 102 2`
+ `</gml:posList>`
+ `</gml:LinearRing>`
+ `</gml:exterior>`
+ `</gml:Polygon>`
+ `<gml:Polygon>`
+ `<gml:exterior>`
+ `<gml:LinearRing>`
+ `<gml:posList>`
+ `100 0 101 0 101 1 100 1 100 0`
+ `</gml:posList>`
+ `</gml:LinearRing>`
+ `</gml:exterior>`
+ `<gml:interior>`
+ `<gml:LinearRing>`
+ `<gml:posList>`
+ `100.2 0.2 100.8 0.2 100.8 0.8 100.2 0.8 100.2 0.2`
+ `</gml:posList>`
+ `</gml:LinearRing>`
+ `</gml:interior>`
+ `</gml:Polygon>`
+ `</gml:surfaceMembers>`
+ `</gml:MultiSurface>`
);
});
```
*/
export const multiPolygon =
/* @__PURE__ */ withGmlNamespace<MultiPolygon>(gmlMultiPolygon);
/**
* Converts an any input geojson geometry into a GML string.
* @function
* @param geom the {@link Geometry} to convert to GML
*/
export const geometry: Converter<Geometry> =
/* @__PURE__ */
withGmlNamespace<Geometry>(gmlGeometry);
export default geometry;
/*!! use-example file://./../tests/geometryCollection.example.ts */
/**
Converts an input geojson GeometryCollection geometry to GML
@function
@param geom the {@link GeometryCollection} to convert to GML
@param params @see Params
@returns a string containing GML representing the input geometry\
@example
```ts
import { type GeometryCollection } from 'geojson';
import { expect, test } from 'vitest';
import { geometryCollection } from 'geojson-to-gml-3';
test('geometryCollection', () => {
const geom: GeometryCollection = {
type: 'GeometryCollection',
geometries: [
{ type: 'Point', coordinates: [100.0, 0.0] },
{
type: 'LineString',
coordinates: [
[101.0, 0.0],
[102.0, 1.0],
],
},
],
};
expect(geometryCollection(geom)()).toBe(''
+ `<gml:MultiGeometry>`
+ `<gml:geometryMembers>`
+ `<gml:Point>`
+ `<gml:pos>`
+ `100 0`
+ `</gml:pos>`
+ `</gml:Point>`
+ `<gml:LineString>`
+ `<gml:posList>`
+ `101 0 102 1`
+ `</gml:posList>`
+ `</gml:LineString>`
+ `</gml:geometryMembers>`
+ `</gml:MultiGeometry>`
);
});
```
*/
export const geometryCollection: Converter<GeometryCollection> =
/* @__PURE__ */
withGmlNamespace<GeometryCollection>(gmlGeometryCollection);