Merge pull request #32548 from Manc/supercluster-5.0

[supercluster] Upgrade to 5.0; stricter types
This commit is contained in:
Armando Aguirre
2019-02-05 16:04:43 -08:00
committed by GitHub
3 changed files with 214 additions and 110 deletions

View File

@@ -1,6 +1,7 @@
// Type definitions for supercluster 3.0
// Type definitions for supercluster 5.0
// Project: https://github.com/mapbox/supercluster
// Definitions by: Denis Carriere <https://github.com/DenisCarriere>
// Nick Zahn <https://github.com/Manc>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
@@ -8,113 +9,164 @@ import * as GeoJSON from 'geojson';
export as namespace supercluster;
export interface Options {
export interface Options<P, C> {
/**
* Minimum zoom level at which clusters are generated.
*
* @default 0
*/
minZoom?: number;
/**
* Maximum zoom level at which clusters are generated.
*
* @default 16
*/
maxZoom?: number;
/**
* Cluster radius, in pixels.
*
* @default 40
*/
radius?: number;
/**
* (Tiles) Tile extent. Radius is calculated relative to this value.
*
* @default 512
*/
extent?: number;
/**
* Size of the KD-tree leaf node. Affects performance.
*
* @default 64
*/
nodeSize?: number;
/**
* Whether timing info should be logged.
*
* @default false
*/
log?: boolean;
/**
* a reduce function for calculating custom cluster properties
* A function that returns cluster properties corresponding to a single point.
*
* @example
* function (accumulated, props) { accumulated.sum += props.sum; }
* (props) => ({sum: props.myValue})
*/
reduce?: (accumulated: any, props: any) => void;
map?: (props: P) => C;
/**
* initial properties of a cluster (before running the reducer)
* A reduce function that merges properties of two clusters into one.
*
* @example
* function () { return {sum: 0}; }
* (accumulated, props) => { accumulated.sum += props.sum; }
*/
initial?: () => any;
/**
* properties to use for individual points when running the reducer
*
* @example
* function (props) { return {sum: props.my_value}; }
*/
map?: (props: any) => any;
}
export class Supercluster {
/**
* Loads an array of GeoJSON.Feature objects. Each feature's geometry must be a GeoJSON.Point. Once loaded, index is immutable.
*/
load(points: Points): Supercluster;
/**
* For the given bbox array ([westLng, southLat, eastLng, northLat]) and integer zoom, returns an array of clusters and points as GeoJSON.Feature objects.
*/
getClusters(bbox: BBox, zoom: number): Clusters;
/**
* For a given zoom and x/y coordinates, returns a geojson-vt-compatible JSON tile object with cluster/point features.
*/
getTile(z: number, x: number, y: number): Tile;
/**
* Returns the children of a cluster (on the next zoom level) given its id (cluster_id value from feature properties) and zoom the cluster was from.
*/
getChildren(clusterId: number, clusterZoom: number): Clusters;
/**
* Returns all the points of a cluster (given its cluster_id and zoom),
* with pagination support: limit is the number of points to return (set to Infinity for all points),
* and offset is the amount of points to skip (for pagination).
*/
getLeaves(clusterId: number, clusterZoom: number, limit?: number, offset?: number): Clusters;
/**
* Returns the zoom on which the cluster expands into several children (useful for "click to zoom" feature), given the cluster's cluster_id and zoom.
*/
getClusterExpansionZoom(clusterId: number, clusterZoom: number): number;
reduce?: (accumulated: C, props: Readonly<C>) => void;
}
/**
* A very fast JavaScript library for geospatial point clustering for browsers and Node.
* Default properties type, allowing any properties.
* Try to avoid this for better typesafety by using proper types.
*/
export default function supercluster(options: Options): Supercluster;
export interface AnyProps {
[name: string]: any;
}
/**
* A very fast geospatial point clustering library for browsers and Node.
*/
export default class Supercluster<P extends GeoJSON.GeoJsonProperties = AnyProps, C extends GeoJSON.GeoJsonProperties = AnyProps> {
constructor(options?: Options<P, C>);
/**
* Loads an array of GeoJSON Feature objects. Each feature's geometry
* must be a GeoJSON Point. Once loaded, index is immutable.
*
* @param points Array of GeoJSON Features, the geometries being GeoJSON Points.
*/
load(points: Array<PointFeature<P>>): Supercluster<P, C>;
/**
* Returns an array of clusters and points as `GeoJSON.Feature` objects
* for the given bounding box (`bbox`) and zoom level (`zoom`).
*
* @param bbox Bounding box (`[westLng, southLat, eastLng, northLat]`).
* @param zoom Zoom level.
*/
getClusters(bbox: GeoJSON.BBox, zoom: number): Array<ClusterFeature<C> | PointFeature<P>>;
/**
* For a given zoom and x/y coordinates, returns a
* [geojson-vt](https://github.com/mapbox/geojson-vt)-compatible JSON
* tile object with cluster any point features.
*/
getTile(zoom: number, x: number, y: number): Tile<C, P> | null;
/**
* Returns the children of a cluster (on the next zoom level).
*
* @param clusterId Cluster ID (`cluster_id` value from feature properties).
* @throws {Error} If `clusterId` does not exist.
*/
getChildren(clusterId: number): Array<ClusterFeature<C> | PointFeature<P>>;
/**
* Returns all the points of a cluster (with pagination support).
*
* @param clusterId Cluster ID (`cluster_id` value from feature properties).
* @param limit The number of points to return (set to `Infinity` for all points).
* @param offset The amount of points to skip (for pagination).
*/
getLeaves(clusterId: number, limit?: number, offset?: number): Array<ClusterFeature<C> | PointFeature<P>>; // Cluster[];
/**
* Returns the zoom level on which the cluster expands into several
* children (useful for "click to zoom" feature).
*
* @param clusterId Cluster ID (`cluster_id` value from feature properties).
*/
getClusterExpansionZoom(clusterId: number): number;
}
/**
* [GeoJSON Feature](https://tools.ietf.org/html/rfc7946#section-3.2),
* with the geometry being a
* [GeoJSON Point](https://tools.ietf.org/html/rfc7946#section-3.1.2).
*/
export type PointFeature<P> = GeoJSON.Feature<GeoJSON.Point, P>;
export type Point = GeoJSON.Feature<GeoJSON.Point>;
export type Points = Point[];
export type Clusters = Cluster[];
export type TileFeatures = TileFeature[];
export type BBox = [number, number, number, number];
export interface ClusterProperties {
cluster?: boolean;
cluster_id?: number;
point_count?: number;
point_count_abbreviated?: number;
sum?: number;
[key: string]: any;
/**
* Always `true` to indicate that the Feature is a Cluster and not
* an individual point.
*/
cluster: true;
/** Cluster ID */
cluster_id: number;
/** Number of points in the cluster. */
point_count: number;
/**
* Abbreviated number of points in the cluster as string if the number
* is 1000 or greater (e.g. `1.3k` if the number is 1298).
*
* For less than 1000 points it is the same value as `point_count`.
*/
point_count_abbreviated: string | number;
}
export interface Cluster extends Point {
properties: ClusterProperties;
}
export interface TileFeature {
export type ClusterFeature<C> = PointFeature<ClusterProperties & C>;
export interface TileFeature<C, P> {
type: 1;
geometry: Array<[number, number]>;
tags: ClusterProperties;
tags: (ClusterProperties & C) | P;
}
export interface Tile {
features: TileFeatures;
export interface Tile<C, P> {
features: Array<TileFeature<C, P>>;
}

View File

@@ -1,43 +1,101 @@
import supercluster, { Point } from 'supercluster';
import Supercluster, { PointFeature, ClusterProperties } from 'supercluster';
const point1: Point = {
type: 'Feature',
properties: {my_value: 2},
geometry: { type: 'Point', coordinates: [10, 20] }
};
const point2: Point = {
type: 'Feature',
properties: {my_value: 3},
geometry: { type: 'Point', coordinates: [20, 30] }
};
const points = [point1, point2];
//
// Test 1: strictly typed
//
const initialFunction = () => {
return {sum: 0};
};
interface TestPointProps {
myTestFeatureName: string;
}
const mapFunction = (props: any) => {
return {sum: props.my_value};
};
interface TestClusterProps {
myTestClusterName: string;
}
const reduceFunction = (accumulated: any, props: any) => {
accumulated.sum += props.sum;
};
const points: Array<PointFeature<TestPointProps>> = [
{
type: 'Feature',
properties: { myTestFeatureName: 'a' },
geometry: { type: 'Point', coordinates: [10, 20] }
},
{
type: 'Feature',
properties: { myTestFeatureName: 'b' },
geometry: { type: 'Point', coordinates: [20, 30] }
}
];
const index = supercluster({
radius: 40,
maxZoom: 16,
extent: 256,
log: true,
initial: initialFunction,
map: mapFunction,
reduce: reduceFunction,
// construct()
new Supercluster();
new Supercluster({});
const index = new Supercluster({
minZoom: 5,
maxZoom: 16,
radius: 40,
extent: 256,
nodeSize: 64,
log: true,
map: (props: TestPointProps): TestClusterProps => ({
myTestClusterName: props.myTestFeatureName.toUpperCase()
}),
reduce: (accumulated, props) => {
accumulated.myTestClusterName += ` & ${props.myTestClusterName}`;
},
});
// load()
index.load(points);
// getClusters()
const clusters = index.getClusters([-180, -85, 180, 85], 2);
clusters[0].properties.cluster_id;
index.getTile(0, 0, 0);
index.getChildren(0, 0);
index.getLeaves(0, 0, 10, 5);
index.getClusterExpansionZoom(0, 0);
index.getTile(0, 0, 0).features[0].tags.sum;
const firstProps = clusters[0].properties; // Either cluster or point properties
// Generic cluster properties
(<ClusterProperties> firstProps).cluster;
(<ClusterProperties> firstProps).point_count;
// Custom cluster properties
(<TestClusterProps> firstProps).myTestClusterName;
// Custom point properties
(<TestPointProps> firstProps).myTestFeatureName;
// getTile()
const tile = index.getTile(0, 0, 0);
if (tile) {
const tileProps = tile.features[0].tags; // Either cluster or point properties
// Generic cluster properties
(<ClusterProperties> tileProps).cluster;
(<ClusterProperties> tileProps).point_count;
// Custom cluster properties
(<TestClusterProps> tileProps).myTestClusterName;
// Custom point properties
(<TestPointProps> tileProps).myTestFeatureName;
}
// Other methods
index.getChildren(0);
index.getLeaves(0);
index.getLeaves(0, Infinity);
index.getLeaves(0, 0, 10);
index.getClusterExpansionZoom(0);
//
// Test 2: loosely typed
//
const index2 = new Supercluster();
index2.load([
{
type: 'Feature',
properties: { testPropertyA: 100 },
geometry: { type: 'Point', coordinates: [10, 20] }
},
{
type: 'Feature',
properties: { testPropertyB: 'test' },
geometry: { type: 'Point', coordinates: [20, 30] }
}
]);
const clusters2 = index2.getClusters([-180, -85, 180, 85], 2);
const firstProps2 = clusters2[0].properties;
(<ClusterProperties> firstProps2).cluster_id;
firstProps2.testPropertyA;
firstProps2.testPropertyB;

View File

@@ -1,7 +1 @@
{
"extends": "dtslint/dt.json",
"rules": {
// TODO
"no-duplicate-imports": false
}
}
{ "extends": "dtslint/dt.json" }