sharedstreets
Version:
SharedStreets, a 'digital commons' for the street
405 lines (404 loc) • 19.9 kB
JavaScript
"use strict";
//import redis = require('redis');
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TileIndex = exports.createGeometry = exports.getReferenceLength = exports.createIntersectionGeometry = void 0;
//import { SharedStreetsMetadata, SharedStreetsIntersection, SharedStreetsGeometry, SharedStreetsReference, RoadClass } from 'sharedstreets-types';
const turfHelpers = __importStar(require("@turf/helpers"));
const buffer_1 = __importDefault(require("@turf/buffer"));
const along_1 = __importDefault(require("@turf/along"));
const line_slice_along_1 = __importDefault(require("@turf/line-slice-along"));
const distance_1 = __importDefault(require("@turf/distance"));
const line_offset_1 = __importDefault(require("@turf/line-offset"));
const rbush_1 = __importDefault(require("rbush"));
const index_1 = require("../src/index");
const tiles_1 = require("./tiles");
const geom_1 = require("./geom");
const helpers_1 = require("@turf/helpers");
const SHST_ID_API_URL = 'https://api.sharedstreets.io/v0.1.0/id/';
// maintains unified spaital and id indexes for tiled data
function createIntersectionGeometry(data) {
var point = turfHelpers.point([data.lon, data.lat]);
return turfHelpers.feature(point.geometry, { id: data.id });
}
exports.createIntersectionGeometry = createIntersectionGeometry;
function getReferenceLength(ref) {
var refLength = 0;
for (var locationRef of ref.locationReferences) {
if (locationRef.distanceToNextRef)
refLength = refLength = locationRef.distanceToNextRef;
}
return refLength / 100;
}
exports.getReferenceLength = getReferenceLength;
function createGeometry(data) {
var line = turfHelpers.lineString(index_1.lonlatsToCoords(data.lonlats));
var feature = turfHelpers.feature(line.geometry, { id: data.id });
return feature;
}
exports.createGeometry = createGeometry;
class TileIndex {
constructor() {
this.additionalTileTypes = [];
this.tiles = new Set();
this.objectIndex = new Map();
this.featureIndex = new Map();
this.metadataIndex = new Map();
this.osmNodeIntersectionIndex = new Map();
this.osmNodeIndex = new Map();
this.osmWayIndex = new Map();
this.binIndex = new Map();
this.intersectionIndex = new rbush_1.default(9);
this.geometryIndex = new rbush_1.default(9);
}
addTileType(tileType) {
this.additionalTileTypes.push(tileType);
}
isIndexed(tilePath) {
if (this.tiles.has(tilePath.toPathString()))
return true;
else
return false;
}
indexTilesByPathGroup(tilePathGroup) {
return __awaiter(this, void 0, void 0, function* () {
for (var tilePath of tilePathGroup) {
yield this.indexTileByPath(tilePath);
}
return false;
});
}
indexTileByPath(tilePath) {
return __awaiter(this, void 0, void 0, function* () {
if (this.isIndexed(tilePath))
return true;
var data = yield tiles_1.getTile(tilePath);
if (tilePath.tileType === tiles_1.TileType.GEOMETRY) {
var geometryFeatures = [];
for (var geometry of data) {
if (!this.objectIndex.has(geometry.id)) {
this.objectIndex.set(geometry.id, geometry);
var geometryFeature = createGeometry(geometry);
this.featureIndex.set(geometry.id, geometryFeature);
var bboxCoords = geom_1.bboxFromPolygon(geometryFeature);
bboxCoords['id'] = geometry.id;
geometryFeatures.push(bboxCoords);
}
}
this.geometryIndex.load(geometryFeatures);
}
else if (tilePath.tileType === tiles_1.TileType.INTERSECTION) {
var intersectionFeatures = [];
for (var intersection of data) {
if (!this.objectIndex.has(intersection.id)) {
this.objectIndex.set(intersection.id, intersection);
var intesectionFeature = createIntersectionGeometry(intersection);
this.featureIndex.set(intersection.id, intesectionFeature);
this.osmNodeIntersectionIndex.set(intersection.nodeId, intersection);
var bboxCoords = geom_1.bboxFromPolygon(intesectionFeature);
bboxCoords['id'] = intersection.id;
intersectionFeatures.push(bboxCoords);
}
}
this.intersectionIndex.load(intersectionFeatures);
}
else if (tilePath.tileType === tiles_1.TileType.REFERENCE) {
for (var reference of data) {
this.objectIndex.set(reference.id, reference);
}
}
else if (tilePath.tileType === tiles_1.TileType.METADATA) {
for (var metadata of data) {
this.metadataIndex.set(metadata.geometryId, metadata);
if (metadata.osmMetadata) {
for (var waySection of metadata.osmMetadata.waySections) {
if (!this.osmWayIndex.has("" + waySection.wayId))
this.osmWayIndex.set("" + waySection.wayId, []);
var ways = this.osmWayIndex.get("" + waySection.wayId);
ways.push(metadata);
this.osmWayIndex.set("" + waySection.wayId, ways);
for (var nodeId of waySection.nodeIds) {
if (!this.osmNodeIndex.has("" + nodeId))
this.osmNodeIndex.set("" + nodeId, []);
var nodes = this.osmNodeIndex.get("" + nodeId);
nodes.push(metadata);
this.osmNodeIndex.set("" + nodeId, nodes);
}
}
}
}
}
this.tiles.add(tilePath.toPathString());
});
}
getGraph(polygon, params) {
return __awaiter(this, void 0, void 0, function* () {
return null;
});
}
intersects(polygon, searchType, buffer, params) {
return __awaiter(this, void 0, void 0, function* () {
var tilePaths = tiles_1.TilePathGroup.fromPolygon(polygon, buffer, params);
if (searchType === tiles_1.TileType.GEOMETRY)
tilePaths.addType(tiles_1.TileType.GEOMETRY);
else if (searchType === tiles_1.TileType.INTERSECTION)
tilePaths.addType(tiles_1.TileType.INTERSECTION);
else
throw "invalid search type must be GEOMETRY or INTERSECTION";
if (this.additionalTileTypes.length > 0) {
for (var type of this.additionalTileTypes) {
tilePaths.addType(type);
}
}
yield this.indexTilesByPathGroup(tilePaths);
var data = helpers_1.featureCollection([]);
if (searchType === tiles_1.TileType.GEOMETRY) {
var bboxCoords = geom_1.bboxFromPolygon(polygon);
var rbushMatches = this.geometryIndex.search(bboxCoords);
for (var rbushMatch of rbushMatches) {
var matchedGeom = this.featureIndex.get(rbushMatch.id);
data.features.push(matchedGeom);
}
}
else if (searchType === tiles_1.TileType.INTERSECTION) {
var bboxCoords = geom_1.bboxFromPolygon(polygon);
var rbushMatches = this.intersectionIndex.search(bboxCoords);
for (var rbushMatch of rbushMatches) {
var matchedGeom = this.featureIndex.get(rbushMatch.id);
data.features.push(matchedGeom);
}
}
return data;
});
}
nearby(point, searchType, searchRadius, params) {
return __awaiter(this, void 0, void 0, function* () {
var tilePaths = tiles_1.TilePathGroup.fromPoint(point, searchRadius * 2, params);
if (searchType === tiles_1.TileType.GEOMETRY)
tilePaths.addType(tiles_1.TileType.GEOMETRY);
else if (searchType === tiles_1.TileType.INTERSECTION)
tilePaths.addType(tiles_1.TileType.INTERSECTION);
else
throw "invalid search type must be GEOMETRY or INTERSECTION";
if (this.additionalTileTypes.length > 0) {
for (var type of this.additionalTileTypes) {
tilePaths.addType(type);
}
}
yield this.indexTilesByPathGroup(tilePaths);
var bufferedPoint = buffer_1.default(point, searchRadius, { 'units': 'meters' });
var data = helpers_1.featureCollection([]);
if (searchType === tiles_1.TileType.GEOMETRY) {
var bboxCoords = geom_1.bboxFromPolygon(bufferedPoint);
var rbushMatches = this.geometryIndex.search(bboxCoords);
for (var rbushMatch of rbushMatches) {
var matchedGeom = this.featureIndex.get(rbushMatch.id);
data.features.push(matchedGeom);
}
}
else if (searchType === tiles_1.TileType.INTERSECTION) {
var bboxCoords = geom_1.bboxFromPolygon(bufferedPoint);
var rbushMatches = this.intersectionIndex.search(bboxCoords);
for (var rbushMatch of rbushMatches) {
var matchedGeom = this.featureIndex.get(rbushMatch.id);
data.features.push(matchedGeom);
}
}
return data;
});
}
geomFromOsm(wayId, nodeId1, nodeId2, offset = 0) {
return __awaiter(this, void 0, void 0, function* () {
if (this.osmNodeIntersectionIndex.has(nodeId1) && this.osmNodeIntersectionIndex.has(nodeId2)) {
var intersection1 = this.osmNodeIntersectionIndex.get(nodeId1);
var intersection2 = this.osmNodeIntersectionIndex.get(nodeId2);
var referenceCandidates = new Set();
for (var refId of intersection1.outboundReferenceIds) {
referenceCandidates.add(refId);
}
for (var refId of intersection2.inboundReferenceIds) {
if (referenceCandidates.has(refId)) {
var geom = yield this.geom(refId, null, null, offset);
if (geom) {
geom.properties['referenceId'] = refId;
return geom;
}
}
}
}
else if (this.osmWayIndex.has(wayId)) {
var metadataList = this.osmWayIndex.get(wayId);
for (var metadata of metadataList) {
var nodeIds = [];
var previousNode = null;
var nodeIndex = 0;
var startNodeIndex = null;
var endNodeIndex = null;
for (var waySection of metadata.osmMetadata.waySections) {
for (var nodeId of waySection.nodeIds) {
var nodeIdStr = nodeId + "";
if (previousNode != nodeIdStr) {
nodeIds.push(nodeIdStr);
if (nodeIdStr == nodeId1)
startNodeIndex = nodeIndex;
if (nodeIdStr == nodeId2)
endNodeIndex = nodeIndex;
nodeIndex++;
}
previousNode = nodeIdStr;
}
}
if (startNodeIndex != null && endNodeIndex != null) {
var geometry = this.objectIndex.get(metadata.geometryId);
var geometryFeature = this.featureIndex.get(metadata.geometryId);
var reference = this.objectIndex.get(geometry.forwardReferenceId);
if (startNodeIndex > endNodeIndex) {
if (geometry.backReferenceId) {
nodeIds.reverse();
startNodeIndex = (nodeIds.length - 1) - startNodeIndex;
endNodeIndex = (nodeIds.length - 1) - endNodeIndex;
reference = this.objectIndex.get(geometry.backReferenceId);
geometryFeature = JSON.parse(JSON.stringify(geometryFeature));
geometryFeature.geometry.coordinates = geometryFeature.geometry.coordinates.reverse();
}
}
var startLocation = 0;
var endLocation = 0;
var previousCoord = null;
for (var j = 0; j <= endNodeIndex; j++) {
if (previousCoord) {
try {
var coordDistance = distance_1.default(previousCoord, geometryFeature.geometry.coordinates[j], { units: 'meters' });
if (j <= startNodeIndex)
startLocation += coordDistance;
endLocation += coordDistance;
}
catch (e) {
console.log(e);
}
}
previousCoord = geometryFeature.geometry.coordinates[j];
}
//console.log(wayId + " " + nodeId1 + " " + nodeId2 + ": " + reference.id + " " + startLocation + " " + endLocation);
var geom = yield this.geom(reference.id, startLocation, endLocation, offset);
if (geom) {
geom.properties['referenceId'] = reference.id;
geom.properties['section'] = [startLocation, endLocation];
return geom;
}
}
}
}
return null;
});
}
referenceToBins(referenceId, numBins, offset, sideOfStreet) {
var binIndexId = referenceId + ':' + numBins + ':' + offset;
if (this.binIndex.has(binIndexId))
return this.binIndex.get(binIndexId);
var ref = this.objectIndex.get(referenceId);
var geom = this.objectIndex.get(ref.geometryId);
var feature = this.featureIndex.get(ref.geometryId);
var binLength = getReferenceLength(ref) / numBins;
var binPoints = {
"type": "Feature",
"properties": { "id": referenceId },
"geometry": {
"type": "MultiPoint",
"coordinates": []
}
};
try {
if (offset) {
if (referenceId === geom.forwardReferenceId)
feature = line_offset_1.default(feature, offset, { units: 'meters' });
else {
var reverseGeom = geom_1.reverseLineString(feature);
feature = line_offset_1.default(reverseGeom, offset, { units: 'meters' });
}
}
for (var binPosition = 0; binPosition < numBins; binPosition++) {
try {
var point = along_1.default(feature, (binLength * binPosition) + (binLength / 2), { units: 'meters' });
point.geometry.coordinates[0] = Math.round(point.geometry.coordinates[0] * 10000000) / 10000000;
point.geometry.coordinates[1] = Math.round(point.geometry.coordinates[1] * 10000000) / 10000000;
binPoints.geometry.coordinates.push(point.geometry.coordinates);
}
catch (e) {
console.log(e);
}
}
this.binIndex.set(binIndexId, binPoints);
}
catch (e) {
console.log(e);
}
return binPoints;
}
geom(referenceId, p1, p2, offset = 0) {
return __awaiter(this, void 0, void 0, function* () {
if (this.objectIndex.has(referenceId)) {
var ref = this.objectIndex.get(referenceId);
var geom = this.objectIndex.get(ref.geometryId);
var geomFeature = JSON.parse(JSON.stringify(this.featureIndex.get(ref.geometryId)));
if (geom.backReferenceId && geom.backReferenceId === referenceId) {
geomFeature.geometry.coordinates = geomFeature.geometry.coordinates.reverse();
}
if (offset) {
geomFeature = line_offset_1.default(geomFeature, offset, { units: 'meters' });
}
if (p1 < 0)
p1 = 0;
if (p2 < 0)
p2 = 0;
if (p1 == null && p2 == null) {
return geomFeature;
}
else if (p1 && p2 == null) {
return along_1.default(geomFeature, p1, { "units": "meters" });
}
else if (p1 != null && p2 != null) {
try {
return line_slice_along_1.default(geomFeature, p1, p2, { "units": "meters" });
}
catch (e) {
//console.log(p1, p2)
}
}
}
// TODO find missing IDs via look up
return null;
});
}
}
exports.TileIndex = TileIndex;