Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GeoJSON to flat binary arrays #690

Merged
merged 27 commits into from
Mar 19, 2020
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions modules/gis/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# @loaders.gl/gis

This module contains helper classes for the GIS category of loaders.

[loaders.gl](https://loaders.gl/docs) is a collection of framework independent visualization-focused loaders (parsers).
15 changes: 15 additions & 0 deletions modules/gis/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# @loaders.gl/gis

This module contains helper classes for the GIS category of loaders.

## Installation

```bash
npm install @loaders.gl/gis
```

kylebarron marked this conversation as resolved.
Show resolved Hide resolved
## Loaders and Writers

| Loader |
| --------------------------------------------------------- |
| [`MVTLoader`](modules/mvts/docs/api-reference/mvt-loader) |
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
31 changes: 31 additions & 0 deletions modules/gis/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "@loaders.gl/gis",
"description": "Helpers for GIS category data",
"version": "2.1.0-beta.2",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "https://github.com/uber-web/loaders.gl"
},
"keywords": [
"geometry",
"GeoJSON"
],
"main": "dist/es5/index.js",
"module": "dist/esm/index.js",
"esnext": "dist/es6/index.js",
"sideEffects": false,
"files": [
"src",
"dist",
"README.md"
],
"dependencies": {
"@loaders.gl/loader-utils": "2.1.0-beta.2",
"@mapbox/vector-tile": "^1.3.1",
"pbf": "^3.2.1"
}
}
7 changes: 7 additions & 0 deletions modules/gis/src/bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* global window, global */
const moduleExports = require('./index');
const _global = typeof window === 'undefined' ? global : window;
// @ts-ignore
_global.loaders = _global.loaders || {};
// @ts-ignore
module.exports = Object.assign(_global.loaders, moduleExports);
1 change: 1 addition & 0 deletions modules/gis/src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {featuresToBinary} from './lib/geojson-to-binary';
205 changes: 205 additions & 0 deletions modules/gis/src/lib/geojson-to-binary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
export function featuresToBinary(features, options = {}) {
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
const firstPassData = firstPass(features);
return secondPass(features, firstPassData, options);
}

function firstPass(features) {
// Counts the number of _positions_, so [x, y, z] counts as one
let pointPositions = 0;
let linePositions = 0;
let linePaths = 0;
let polygonPositions = 0;
let polygonObjects = 0;
let polygonRings = 0;
let maxCoordLength = 2;

for (const feature of features) {
const geometry = feature.geometry;
switch (geometry.type) {
case 'Point':
pointPositions++;
if (geometry.coordinates.length === 3) maxCoordLength = 3;
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
break;
case 'MultiPoint':
pointPositions += geometry.coordinates.length;
for (const point of geometry.coordinates) {
if (point.length === 3) maxCoordLength = 3;
}
break;
case 'LineString':
linePositions += geometry.coordinates.length;
linePaths++;

for (const coord of geometry.coordinates) {
if (coord.length === 3) maxCoordLength = 3;
}
break;
case 'MultiLineString':
for (const line of geometry.coordinates) {
linePositions += line.length;
linePaths++;

for (const coord of line.coordinates) {
if (coord.length === 3) maxCoordLength = 3;
}
}
break;
case 'Polygon':
polygonObjects++;
polygonRings += geometry.coordinates.length;
polygonPositions += geometry.coordinates.flat(1).length;

for (const coord of geometry.coordinates.flat()) {
if (coord.length === 3) maxCoordLength = 3;
}
break;
case 'MultiPolygon':
for (const polygon of geometry.coordinates) {
polygonObjects++;
polygonRings += polygon.length;
polygonPositions += polygon.flat(1).length;

for (const coord of geometry.coordinates.flat()) {
if (coord.length === 3) maxCoordLength = 3;
}
}
break;
default:
throw new Error(`Unsupported geometry type: ${geometry.type}`);
}
};

return {
pointPositions,
linePositions,
linePaths,
coordLength: maxCoordLength,
polygonPositions,
polygonObjects,
polygonRings
};
}

function secondPass(features, firstPassData, options = {}) {
const {
pointPositions,
linePositions,
linePaths,
coordLength,
polygonPositions,
polygonObjects,
polygonRings
} = firstPassData;
const {PositionDataType = Float32Array} = options;
const points = {
positions: new PositionDataType(pointPositions * coordLength),
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
objectIds: new Uint32Array(pointPositions)
};
const lines = {
pathIndices: new Uint32Array(linePaths),
positions: new PositionDataType(linePositions * coordLength),
objectIds: new Uint32Array(linePositions)
};
const polygons = {
polygonIndices: new Uint32Array(polygonObjects),
primitivePolygonIndices: new Uint32Array(polygonRings),
positions: new PositionDataType(polygonPositions * coordLength),
objectIds: new Uint32Array(polygonPositions)
};

const index = {
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
pointPosition: 0,
linePosition: 0,
linePath: 0,
polygonPosition: 0,
polygonObject: 0,
polygonRing: 0,
feature: 0
};

for (const feature of features) {
const geometry = feature.geometry;

switch (geometry.type) {
case 'Point':
handlePoint({coords: geometry.coordinates, points, index, coordLength});
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
break;
case 'MultiPoint':
handleMultiPoint({coords: geometry.coordinates, points, index, coordLength});
break;
case 'LineString':
handleLineString({coords: geometry.coordinates, lines, index, coordLength});
break;
case 'MultiLineString':
handleMultiLineString({coords: geometry.coordinates, lines, index, coordLength});
break;
case 'Polygon':
handlePolygon({coords: geometry.coordinates, polygons, index, coordLength});
break;
case 'MultiPolygon':
handleMultiPolygon({coords: geometry.coordinates, polygons, index, coordLength});
break;
default:
throw new Error('Invalid geometry type');
}

index.feature++;
};

return {
points,
lines,
polygons
};
}

function handlePoint({coords, points, index, coordLength}) {
points.positions.set(coords, index.pointPosition * coordLength);
points.objectIds[index.pointPosition] = index.feature;
index.pointPosition++;
}

function handleMultiPoint({coords, points, index, coordLength}) {
for (const point of coords) {
handlePoint({coords: point, points, index, coordLength});
}
}

function handleLineString({coords, lines, index, coordLength}) {
lines.pathIndices[index.linePath] = index.linePosition * coordLength;
index.linePath++;

lines.positions.set(coords.flat(), index.linePosition * coordLength);

const nPositions = coords.length;
lines.objectIds.set(new Uint32Array(nPositions).fill(index.feature), index.linePosition);
index.linePosition += nPositions;
}

function handleMultiLineString({coords, lines, index, coordLength}) {
for (const line of coords) {
handleLineString({coords: line, lines, index, coordLength});
}
}

function handlePolygon({coords, polygons, index, coordLength}) {
polygons.polygonIndices[index.polygonObject] = index.polygonPosition * coordLength;
index.polygonObject++;

for (const {} of coords) {
kylebarron marked this conversation as resolved.
Show resolved Hide resolved
polygons.primitivePolygonIndices[index.polygonRing] = index.polygonPosition * coordLength;
index.polygonRing++;
}

polygons.positions.set(coords.flat(2), index.polygonPosition * coordLength);

const nPositions = coords.flat(1).length;
polygons.objectIds.set(new Uint32Array(nPositions).fill(index.feature), index.polygonPosition);
index.polygonPosition += nPositions;
}

function handleMultiPolygon({coords, lines, index, coordLength}) {
for (const polygon of coords) {
handlePolygon({coords: polygon, lines, index, coordLength});
}
}
Empty file.
1 change: 1 addition & 0 deletions modules/gis/test/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './geojson-to-binary.spec';