Merge pull request #24157 from denisname/topojson-types

TopoJSON: Improve types
This commit is contained in:
Armando Aguirre
2018-03-09 16:30:36 -08:00
committed by GitHub
8 changed files with 667 additions and 31 deletions

View File

@@ -1,49 +1,217 @@
// Type definitions for topojson 3.0
// Type definitions for topojson 3.2
// Project: https://github.com/topojson/topojson
// Definitions by: Ricardo Mello <https://github.com/ricardo-mello>
// Zhutian Chen <https://github.com/chenzhutian>
// Zhutian Chen <https://github.com/chenzhutian>
// denisname <https://github.com/denisname>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.5
export function bbox(topology: any): any;
import * as GeoJSON from "geojson";
export function feature(topology: any, o: any): { features: any[]; type: string };
export as namespace topojson;
export function filter(topology: any, filter: any): any;
// ---------------------------------------------------------------
// TopoJSON Format Specification
// ---------------------------------------------------------------
export function filterAttached(topology: any): any;
// See: https://github.com/topojson/topojson-specification/
export function filterAttachedWeight(topology: any, minWeight: any, weight: any): any;
// 2. TopoJSON Objects
export interface TopoJSON {
type: "Topology" | GeoJSON.GeoJsonGeometryTypes | null;
bbox?: GeoJSON.BBox;
}
export function filterWeight(topology: any, minWeight: any, weight: any): any;
// 2.1. Topology Objects
export interface Topology<T extends Objects<Properties> = Objects<Properties>> extends TopoJSON {
type: "Topology";
objects: T;
arcs: Arc[];
transform?: Transform;
}
export function merge(topology: any, ...args: any[]): { type: any, coordinates: any[] };
// 2.1.1. Positions
export type Positions = number[]; // at least two elements
export function mergeArcs(topology: any, objects: any): any;
// 2.1.2. Transforms
export interface Transform {
scale: [number, number];
translate: [number, number];
}
export function mesh(topology: any, ...args: any[]): { type: any; coordinates: any[]; };
// 2.1.3. Arcs
export type Arc = Positions[]; // at least two elements
export function meshArcs(topology: any, object$$1: any, filter: any, ...args: any[]): { type: any; coordinates: any[]; };
// 2.1.4. Arc Indexes
export type ArcIndexes = number[];
export function neighbors(objects: any): any[];
// 2.1.5. Objects
export type Properties = GeoJSON.GeoJsonProperties;
export function planarRingArea(ring: any): any;
export interface Objects<P extends Properties = {}> {
[key: string]: GeometryObject<P>;
}
export function planarTriangleArea(triangle: any): any;
// 2.2. Geometry Objects
export interface GeometryObjectA<P extends Properties = {}> extends TopoJSON {
type: GeoJSON.GeoJsonGeometryTypes | null;
id?: number | string;
properties?: P;
}
export function presimplify(topology: any, weight: any): any[];
export type GeometryObject<P extends Properties = {}> =
Point<P> | MultiPoint<P> |
LineString<P> | MultiLineString<P> |
Polygon<P> | MultiPolygon<P> |
GeometryCollection<P> |
NullObject;
export function quantile(topology: any, p: any): any;
// 2.2.1. Point
export interface Point<P extends Properties = {}> extends GeometryObjectA<P> {
type: "Point";
coordinates: Positions;
}
export function quantize(topology: any, transform: any): any;
// 2.2.2. MultiPoint
export interface MultiPoint<P extends Properties = {}> extends GeometryObjectA<P> {
type: "MultiPoint";
coordinates: Positions[];
}
export function simplify(topology: any, minWeight: any): any;
// 2.2.3. LineString
export interface LineString<P extends Properties = {}> extends GeometryObjectA<P> {
type: "LineString";
arcs: ArcIndexes;
}
export function sphericalRingArea(ring: any, interior: any): any;
// 2.2.4. MultiLineString
export interface MultiLineString<P extends Properties = {}> extends GeometryObjectA<P> {
type: "MultiLineString";
arcs: ArcIndexes[];
}
export function sphericalTriangleArea(t: any): any;
// 2.2.5. Polygon
export interface Polygon<P extends Properties = {}> extends GeometryObjectA<P> {
type: "Polygon";
arcs: ArcIndexes[];
}
export function topology(objects: any, quantization: any): any;
// 2.2.6. MultiPolygon
export interface MultiPolygon<P extends Properties = {}> extends GeometryObjectA<P> {
type: "MultiPolygon";
arcs: ArcIndexes[][];
}
export function transform(transform: any): any;
// 2.2.7. Geometry Collection
export interface GeometryCollection<P extends Properties = {}> extends GeometryObjectA<P> {
type: "GeometryCollection";
geometries: Array<GeometryObject<P>>;
}
export function untransform(transform: any): any;
// More
export interface NullObject extends GeometryObjectA {
type: null;
}
export type OrNull<T extends Objects> = {
[P in keyof T]: T[P] | NullObject;
};
// ---------------------------------------------------------------
// TopoJSON Server
// ---------------------------------------------------------------
export function topology(objects: {[k: string]: GeoJSON.GeoJsonObject}, quantization?: number): Topology;
// ---------------------------------------------------------------
// TopoJSON Simplify
// ---------------------------------------------------------------
export type Triangle = [[number, number], [number, number], [number, number]];
export type TriangleWeighter = (triangle: Triangle) => number;
export type Ring = Array<[number, number]>;
export type RingWeighter = (triangle: Ring) => number;
export type Filter = (ring: Ring, interior: boolean) => boolean;
export function presimplify<T extends Objects>(topology: Topology<T>, weight?: TriangleWeighter): Topology<T>;
export function simplify<T extends Objects>(topology: Topology<T>, minWeight?: number): Topology<T>;
export function quantile(topology: Topology, p: number): number;
export function filter<K extends Objects>(topology: Topology<K>, filter: Filter): Topology<OrNull<K>>;
export function filterAttached(topology: Topology): Filter;
export function filterAttachedWeight(topology: Topology, minWeight?: number, weight?: RingWeighter): Filter;
export function filterWeight(topology: Topology, minWeight?: number, weight?: RingWeighter): Filter;
export function planarRingArea(ring: Ring): number;
export function planarTriangleArea(triangle: Triangle): number;
export function sphericalRingArea(ring: Ring, interior: true): number;
export function sphericalTriangleArea(triangle: Triangle): number;
// ---------------------------------------------------------------
// TopoJSON Client
// ---------------------------------------------------------------
export type Transformer = (point: number[], index?: boolean) => number[];
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: Point<P>): GeoJSON.Feature<GeoJSON.Point, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: MultiPoint<P>): GeoJSON.Feature<GeoJSON.MultiPoint, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: LineString<P>): GeoJSON.Feature<GeoJSON.LineString, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: MultiLineString<P>): GeoJSON.Feature<GeoJSON.MultiLineString, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: Polygon<P>): GeoJSON.Feature<GeoJSON.Polygon, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: MultiPolygon<P>): GeoJSON.Feature<GeoJSON.MultiPolygon, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: GeometryCollection<P>): GeoJSON.FeatureCollection<GeoJSON.GeometryObject, P>;
export function feature<P = GeoJSON.GeoJsonProperties>(topology: Topology, object: GeometryObject<P>)
: GeoJSON.Feature<GeoJSON.GeometryObject, P> | GeoJSON.FeatureCollection<GeoJSON.GeometryObject, P>;
export function merge(topology: Topology, objects: Array<Polygon | MultiPolygon>): GeoJSON.MultiPolygon;
export function mergeArcs(topology: Topology, objects: Array<Polygon | MultiPolygon>): MultiPolygon;
export function mesh(topology: Topology, obj?: GeometryObject, filter?: (a: GeometryObject, b: GeometryObject) => boolean): GeoJSON.MultiLineString;
export function meshArcs(topology: Topology, obj?: GeometryObject, filter?: (a: GeometryObject, b: GeometryObject) => boolean): MultiLineString;
export function neighbors(objects: GeometryObject[]): number[][];
export function bbox(topology: Topology): GeoJSON.BBox;
export function quantize<T extends Objects>(topology: Topology<T>, transform: Transform | number): Topology<T>;
export function transform(transform: Transform | null): Transformer;
export function untransform(transform: Transform | null): Transformer;
// ---------------------------------------------------------------
// U.S. Atlas TopoJSON
// ---------------------------------------------------------------
export interface UsAtlas extends topojson.Topology {
objects: {
counties: {type: "GeometryCollection", geometries: Array<Polygon | MultiPolygon>};
states: {type: "GeometryCollection", geometries: Array<Polygon | MultiPolygon>};
nation: topojson.GeometryCollection;
};
bbox: [number, number, number, number];
transform: topojson.Transform;
}
// ---------------------------------------------------------------
// World Atlas TopoJSON
// ---------------------------------------------------------------
export interface WorldAtlas extends topojson.Topology {
objects: {
countries: {type: "GeometryCollection", geometries: Array<Polygon | MultiPolygon>};
land: topojson.GeometryCollection;
};
bbox: [number, number, number, number];
transform: topojson.Transform;
}

View File

@@ -0,0 +1,66 @@
// Tests for: https://github.com/topojson/us-atlas and https://github.com/topojson/world-atlas
const world: topojson.WorldAtlas = {
type: "Topology",
objects: {
countries: {
type: "GeometryCollection",
geometries: [{
type: "Polygon",
arcs: [[]],
id: "004",
}],
},
land: {
type: "GeometryCollection",
geometries: [{
type: "MultiPolygon",
arcs: [[[]]],
}],
},
},
arcs: [[]],
bbox: [-180, -85, 180, 83],
transform: {
scale: [0, 0],
translate: [-180, -85],
},
};
const us: topojson.UsAtlas = {
type: "Topology",
bbox: [-56, 12, 942, 596],
transform: {
scale: [0, 0],
translate: [-56, 12],
},
objects: {
counties: {
type: "GeometryCollection",
geometries: [{
type: "Polygon",
arcs: [[]],
id: "05089",
}],
},
states: {
type: "GeometryCollection",
geometries: [{
type: "MultiPolygon",
arcs: [[[]]],
id: "11",
}],
},
nation: {
type: "GeometryCollection",
geometries: [{
type: "MultiPolygon",
arcs: [[[]]],
}],
},
},
arcs: [[[]]],
};
// $ExpectError
us.objects.xxx;

View File

@@ -0,0 +1,97 @@
// Tests for: https://github.com/topojson/topojson-client
let geoMP: GeoJSON.MultiPolygon;
let geoMLS: GeoJSON.MultiLineString;
let topoMP: topojson.MultiPolygon;
let topoMLS: topojson.MultiLineString;
let bbox: GeoJSON.BBox;
let transformer: topojson.Transformer;
let color: string;
let size: number;
interface TestProp {
color: string;
size: number;
}
const selectedGeometries: Array<topojson.Polygon | topojson.MultiPolygon> =
us.objects.states.geometries.filter((g) => ["be", 2, undefined].indexOf(g.id) >= 0);
const topoWithProp = {
type: "Topology" as "Topology",
transform: {scale: [1, 1] as [number, number], translate: [0, 0] as [number, number]},
objects: {
foo: {type: "Polygon" as "Polygon", properties: {color: "orange", size: 42}, arcs: [[0]]},
bar: {type: "Polygon" as "Polygon", properties: {name: "hello"}, arcs: [[0]]},
more: {type: "Polygon" as "Polygon", arcs: [[0]]},
coll: {
type: "GeometryCollection" as "GeometryCollection", geometries: [
{type: "Polygon" as "Polygon", properties: {color: "orange", size: 42}, arcs: [[0]]},
],
},
},
arcs: [
[[0, 0], [1, 0], [0, 1], [-1, 0], [0, -1]],
[[0, 0], [1, 0], [0, 1]],
[[1, 1], [-1, 0], [0, -1]],
[[1, 1]],
[[0, 0]]
],
};
const featurePolygon: GeoJSON.Feature<GeoJSON.Polygon> =
topojson.feature(topoWithProp, topoWithProp.objects.foo);
const featurePolygonWithProp: GeoJSON.Feature<GeoJSON.Polygon, TestProp> =
topojson.feature(topoWithProp, topoWithProp.objects.foo);
const featureCollection: GeoJSON.FeatureCollection<GeoJSON.GeometryObject> =
topojson.feature(us, us.objects.counties);
const featureObject: GeoJSON.Feature<GeoJSON.GeometryObject> | GeoJSON.FeatureCollection<GeoJSON.GeometryObject, {}> =
topojson.feature(topoWithProp, topoWithProp.objects.foo as topojson.GeometryObject);
const propColor = topojson.feature(topoWithProp, topoWithProp.objects.foo).properties;
color = propColor.color;
size = propColor.size;
const propName = topojson.feature(topoWithProp, topoWithProp.objects.bar).properties;
const aName: string = propName.name;
const propCollection = topojson.feature(topoWithProp, topoWithProp.objects.coll).features[0].properties;
color = propCollection.color;
size = propCollection.size;
const prop3: GeoJSON.GeoJsonProperties = topojson.feature(topoWithProp, topoWithProp.objects.more).properties;
geoMP = topojson.merge(us, selectedGeometries);
topoMP = topojson.mergeArcs(us, selectedGeometries);
geoMLS = topojson.mesh(us);
geoMLS = topojson.mesh(us, us.objects.states);
geoMLS = topojson.mesh(us, us.objects.states, (a, b) => a !== b);
topoMLS = topojson.meshArcs(us);
topoMLS = topojson.meshArcs(us, us.objects.states);
topoMLS = topojson.meshArcs(us, us.objects.states, (a, b) => a !== b);
const n: number[][] = topojson.neighbors(world.objects.countries.geometries);
// Transforms
const usTransform: topojson.Transform = us.transform;
bbox = topojson.bbox(us);
bbox = topojson.bbox({type: "Topology", objects: {}, arcs: []});
newUs = topojson.quantize(us, 1e4);
newUs = topojson.quantize(us, {scale: [1, 1], translate: [0, 0]});
newUs = topojson.quantize(us, us.transform);
transformer = topojson.transform(null);
transformer = topojson.transform({scale: [1, 1], translate: [0, 0]});
transformer = topojson.transform(usTransform);
transformer = topojson.untransform(null);
transformer = topojson.untransform({scale: [1, 1], translate: [0, 0]});
transformer = topojson.untransform(usTransform);

View File

@@ -0,0 +1,14 @@
// Tests for: https://github.com/topojson/topojson-server
let topo: topojson.Topology;
const aPoint: GeoJSON.Point = {type: "Point", coordinates: [30, 10]};
const aPolygon: GeoJSON.Polygon = {type: "Polygon", coordinates: [[[30, 10], [40, 40], [20, 40], [30, 10]]]};
const objects = {
aPoint,
aPolygon,
};
topo = topojson.topology(objects);
topo = topojson.topology(objects, 1e4);

View File

@@ -0,0 +1,92 @@
// Tests for: https://github.com/topojson/topojson-simplify
interface UsAtlasObjects extends topojson.Objects {
counties: {type: "GeometryCollection", geometries: Array<topojson.Polygon | topojson.MultiPolygon>};
states: {type: "GeometryCollection", geometries: Array<topojson.Polygon | topojson.MultiPolygon>};
nation: topojson.GeometryCollection;
}
interface UsEmpty extends topojson.Objects {
counties: topojson.NullObject;
states: topojson.NullObject;
nation: topojson.NullObject;
}
let aTopology: topojson.Topology;
let presimplifiedUs: topojson.Topology<UsAtlasObjects>;
let newUs: topojson.Topology<UsAtlasObjects>;
let emptyUs: topojson.Topology<UsEmpty>;
let geomCollection: topojson.GeometryCollection;
let geomCollectionOrNull: topojson.GeometryCollection | topojson.NullObject;
let aNullObject: topojson.NullObject;
let filter: topojson.Filter;
presimplifiedUs = topojson.presimplify(us);
presimplifiedUs = topojson.presimplify(us, topojson.planarTriangleArea);
presimplifiedUs = topojson.presimplify(us, topojson.sphericalTriangleArea);
presimplifiedUs = topojson.presimplify(us, (points: Array<[number, number]>) => 1.5);
geomCollection = topojson.presimplify(us).objects.counties;
geomCollection = topojson.presimplify(us).objects.nation;
geomCollection = topojson.presimplify(us).objects.states;
let minWeight = topojson.quantile(presimplifiedUs, 0.5);
newUs = topojson.simplify(presimplifiedUs);
newUs = topojson.simplify(presimplifiedUs, 1.23);
newUs = topojson.simplify(presimplifiedUs, minWeight);
geomCollection = topojson.simplify(presimplifiedUs).objects.counties;
geomCollection = topojson.simplify(presimplifiedUs).objects.nation;
geomCollection = topojson.simplify(presimplifiedUs).objects.states;
// Filtering
filter = topojson.filterAttached(us);
filter = topojson.filterAttachedWeight(us);
filter = topojson.filterAttachedWeight(us, 0.5);
filter = topojson.filterAttachedWeight(us, 0.5, topojson.planarRingArea);
filter = topojson.filterAttachedWeight(us, 0.5, (points: Array<[number, number]>) => 1.5);
filter = topojson.filterWeight(us);
filter = topojson.filterWeight(us, 0.5);
filter = topojson.filterWeight(us, 0.5, topojson.planarRingArea);
filter = topojson.filterWeight(us, 0.5, (points: Array<[number, number]>) => 1.5);
aTopology = topojson.filter(us, filter);
newUs = topojson.filter(us, (ring: topojson.Ring, interior: boolean) => true) as topojson.UsAtlas;
emptyUs = topojson.filter(us, () => false) as topojson.Topology<UsEmpty>;
geomCollectionOrNull = topojson.filter(us, () => Math.random() > 0.9).objects.nation;
aNullObject = topojson.filter(us, () => false).objects.nation as topojson.NullObject;
geomCollection = topojson.filter(us, () => true).objects.nation as topojson.GeometryCollection;
// Geometry
let area: number;
area = topojson.planarRingArea([[0, 0], [0, 1], [1, 1], [1, 0], [0, 0]]);
area = topojson.planarTriangleArea([[0, 0], [0, 1], [1, 1]]);
area = topojson.sphericalRingArea([[0, 0], [0, 90], [90, 180], [0, 0]], true);
area = topojson.sphericalTriangleArea([[0, 0], [0, 90], [90, 180]]);
// Fails
interface MyAtlas extends topojson.Topology {
objects: {
obj: topojson.GeometryCollection;
};
more: "hello";
}
let myAtlas: MyAtlas = null as any; // shortcut...
console.log(myAtlas.more);
let s: string;
// $ExpectError
s = topojson.presimplify(myAtlas).more; // must fail
// $ExpectError
s = topojson.simplify(myAtlas).more; // must fail
// // $ExpectError
// area = topojson.planarTriangleArea([[0, 0], [0, 1], [1, 1], [1, 0]]); // must fail in 2.7

View File

@@ -0,0 +1,196 @@
// Tests for: https://github.com/topojson/topojson-specification
// Geometry Objects
const point: topojson.Point = {
type: "Point",
coordinates: [0, 0],
};
const multiPoint: topojson.MultiPoint = {
type: "MultiPoint",
coordinates: [[0, 0]],
};
const lineString: topojson.LineString = {
type: "LineString",
arcs: [0],
};
const multiLineString: topojson.MultiLineString = {
type: "MultiLineString",
arcs: [[3], [4]],
};
const polygon: topojson.Polygon = {
type: "Polygon",
arcs: [[0]],
};
const multiPolygon: topojson.MultiPolygon = {
type: "MultiPolygon",
arcs: [[[0]]],
};
const geometryCollection: topojson.GeometryCollection = {
type: "GeometryCollection",
geometries: [
{type: "Polygon", arcs: [[0]]},
{type: "MultiPolygon", arcs: [[[0]]]},
{type: "GeometryCollection", geometries: [
{type: "Point", coordinates: [0, 0]}
]}
],
};
const nullObject: topojson.NullObject = {
type: null,
};
// Properties
interface TestProp {
color: string;
size: number;
}
const pointWithProp: topojson.Point<TestProp> = {
type: "Point",
coordinates: [0, 0],
properties: {color: "orange", size: 42},
};
const str: string = pointWithProp.properties!.color;
const nbr: number = pointWithProp.properties!.size;
// Topology
let topology: topojson.Topology;
topology = {
type: "Topology",
objects: {},
arcs: [],
};
topology = {
type: "Topology",
bbox: [0, 0, 1, 1],
transform: {
scale: [1, 1],
translate: [0, 0],
},
objects: {
foo: {
type: "Polygon",
arcs: [[0]],
},
},
arcs: [[[0, 0], [1, 1]], [[1, 1], [-1, -1]]],
};
topology = {
type: "Topology",
transform: {
scale: [1, 1],
translate: [0, 0],
},
objects: {
point,
lineString,
multiLineString,
multiPoint,
polygon,
multiPolygon,
geometryCollection,
nullObject,
},
arcs: [
[[0, 0], [1, 0], [0, 1], [-1, 0], [0, -1]],
[[0, 0], [1, 0], [0, 1]],
[[1, 1], [-1, 0], [0, -1]],
[[1, 1]],
[[0, 0]]
],
};
topology = {
type: "Topology",
objects: {
example: {
type: "GeometryCollection",
geometries: [
{
type: "Point",
properties: {
prop0: "value0",
},
coordinates: [102, 0.5],
},
{
type: "LineString",
properties: {
prop0: "value0",
prop1: 0,
},
arcs: [0],
},
{
type: "Polygon",
properties: {
prop0: "value0",
prop1: {
this: "that",
},
},
arcs: [[-2]],
}
],
},
},
arcs: [
[[102, 0], [103, 1], [104, 0], [105, 1]],
[[100, 0], [101, 0], [101, 1], [100, 1], [100, 0]]
],
};
// Fails
// $ExpectError
topology = {
type: "Topology",
objects: {
foo: {
type: "hello", // must fail
arcs: [[0]],
},
},
arcs: [],
};
// $ExpectError
topology = {
type: "Topology",
objects: {
foo: {
type: "Point",
// must fail: Property 'coordinates' is missing in type '{ type: "Point"; }'.
},
},
arcs: [],
};
// $ExpectError
topology = {
type: "Topology",
objects: {
foo: {
type: "GeometryCollection",
geometries: [
{type: "Polygon"}
// must fail: Property 'arcs' is missing in type '{ type: "Polygon"; }'
],
},
},
arcs: [],
};

View File

@@ -1,6 +1,4 @@
import * as topojson from 'topojson';
const hello = "world";
// TODO: complete tests.
topojson.feature(null, null); // $ExpectType { features: any[]; type: string; }
topojson.mesh(null, null, (a: any, b: any) => a !== b); // $ExpectType { type: any; coordinates: any[]; }
// NOTE: The standard bundle definition has no particular function.
// See ./test/ for per bundle tests.

View File

@@ -7,7 +7,7 @@
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": false,
"strictNullChecks": true,
"strictFunctionTypes": true,
"baseUrl": "../",
"typeRoots": [
@@ -19,6 +19,11 @@
},
"files": [
"index.d.ts",
"topojson-tests.ts"
"topojson-tests.ts",
"test/atlas-tests.ts",
"test/client-tests.ts",
"test/server-tests.ts",
"test/simplify-tests.ts",
"test/specification-tests.ts"
]
}