123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838 |
- // https://github.com/topojson/topojson-server v3.0.1 Copyright 2019 Mike Bostock
- (function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
- (global = global || self, factory(global.topojson = global.topojson || {}));
- }(this, function (exports) { 'use strict';
- var hasOwnProperty = Object.prototype.hasOwnProperty;
- // Computes the bounding box of the specified hash of GeoJSON objects.
- function bounds(objects) {
- var x0 = Infinity,
- y0 = Infinity,
- x1 = -Infinity,
- y1 = -Infinity;
- function boundGeometry(geometry) {
- if (geometry != null && hasOwnProperty.call(boundGeometryType, geometry.type)) boundGeometryType[geometry.type](geometry);
- }
- var boundGeometryType = {
- GeometryCollection: function(o) { o.geometries.forEach(boundGeometry); },
- Point: function(o) { boundPoint(o.coordinates); },
- MultiPoint: function(o) { o.coordinates.forEach(boundPoint); },
- LineString: function(o) { boundLine(o.arcs); },
- MultiLineString: function(o) { o.arcs.forEach(boundLine); },
- Polygon: function(o) { o.arcs.forEach(boundLine); },
- MultiPolygon: function(o) { o.arcs.forEach(boundMultiLine); }
- };
- function boundPoint(coordinates) {
- var x = coordinates[0],
- y = coordinates[1];
- if (x < x0) x0 = x;
- if (x > x1) x1 = x;
- if (y < y0) y0 = y;
- if (y > y1) y1 = y;
- }
- function boundLine(coordinates) {
- coordinates.forEach(boundPoint);
- }
- function boundMultiLine(coordinates) {
- coordinates.forEach(boundLine);
- }
- for (var key in objects) {
- boundGeometry(objects[key]);
- }
- return x1 >= x0 && y1 >= y0 ? [x0, y0, x1, y1] : undefined;
- }
- function hashset(size, hash, equal, type, empty) {
- if (arguments.length === 3) {
- type = Array;
- empty = null;
- }
- var store = new type(size = 1 << Math.max(4, Math.ceil(Math.log(size) / Math.LN2))),
- mask = size - 1;
- for (var i = 0; i < size; ++i) {
- store[i] = empty;
- }
- function add(value) {
- var index = hash(value) & mask,
- match = store[index],
- collisions = 0;
- while (match != empty) {
- if (equal(match, value)) return true;
- if (++collisions >= size) throw new Error("full hashset");
- match = store[index = (index + 1) & mask];
- }
- store[index] = value;
- return true;
- }
- function has(value) {
- var index = hash(value) & mask,
- match = store[index],
- collisions = 0;
- while (match != empty) {
- if (equal(match, value)) return true;
- if (++collisions >= size) break;
- match = store[index = (index + 1) & mask];
- }
- return false;
- }
- function values() {
- var values = [];
- for (var i = 0, n = store.length; i < n; ++i) {
- var match = store[i];
- if (match != empty) values.push(match);
- }
- return values;
- }
- return {
- add: add,
- has: has,
- values: values
- };
- }
- function hashmap(size, hash, equal, keyType, keyEmpty, valueType) {
- if (arguments.length === 3) {
- keyType = valueType = Array;
- keyEmpty = null;
- }
- var keystore = new keyType(size = 1 << Math.max(4, Math.ceil(Math.log(size) / Math.LN2))),
- valstore = new valueType(size),
- mask = size - 1;
- for (var i = 0; i < size; ++i) {
- keystore[i] = keyEmpty;
- }
- function set(key, value) {
- var index = hash(key) & mask,
- matchKey = keystore[index],
- collisions = 0;
- while (matchKey != keyEmpty) {
- if (equal(matchKey, key)) return valstore[index] = value;
- if (++collisions >= size) throw new Error("full hashmap");
- matchKey = keystore[index = (index + 1) & mask];
- }
- keystore[index] = key;
- valstore[index] = value;
- return value;
- }
- function maybeSet(key, value) {
- var index = hash(key) & mask,
- matchKey = keystore[index],
- collisions = 0;
- while (matchKey != keyEmpty) {
- if (equal(matchKey, key)) return valstore[index];
- if (++collisions >= size) throw new Error("full hashmap");
- matchKey = keystore[index = (index + 1) & mask];
- }
- keystore[index] = key;
- valstore[index] = value;
- return value;
- }
- function get(key, missingValue) {
- var index = hash(key) & mask,
- matchKey = keystore[index],
- collisions = 0;
- while (matchKey != keyEmpty) {
- if (equal(matchKey, key)) return valstore[index];
- if (++collisions >= size) break;
- matchKey = keystore[index = (index + 1) & mask];
- }
- return missingValue;
- }
- function keys() {
- var keys = [];
- for (var i = 0, n = keystore.length; i < n; ++i) {
- var matchKey = keystore[i];
- if (matchKey != keyEmpty) keys.push(matchKey);
- }
- return keys;
- }
- return {
- set: set,
- maybeSet: maybeSet, // set if unset
- get: get,
- keys: keys
- };
- }
- function equalPoint(pointA, pointB) {
- return pointA[0] === pointB[0] && pointA[1] === pointB[1];
- }
- // TODO if quantized, use simpler Int32 hashing?
- var buffer = new ArrayBuffer(16),
- floats = new Float64Array(buffer),
- uints = new Uint32Array(buffer);
- function hashPoint(point) {
- floats[0] = point[0];
- floats[1] = point[1];
- var hash = uints[0] ^ uints[1];
- hash = hash << 5 ^ hash >> 7 ^ uints[2] ^ uints[3];
- return hash & 0x7fffffff;
- }
- // Given an extracted (pre-)topology, identifies all of the junctions. These are
- // the points at which arcs (lines or rings) will need to be cut so that each
- // arc is represented uniquely.
- //
- // A junction is a point where at least one arc deviates from another arc going
- // through the same point. For example, consider the point B. If there is a arc
- // through ABC and another arc through CBA, then B is not a junction because in
- // both cases the adjacent point pairs are {A,C}. However, if there is an
- // additional arc ABD, then {A,D} != {A,C}, and thus B becomes a junction.
- //
- // For a closed ring ABCA, the first point A’s adjacent points are the second
- // and last point {B,C}. For a line, the first and last point are always
- // considered junctions, even if the line is closed; this ensures that a closed
- // line is never rotated.
- function join(topology) {
- var coordinates = topology.coordinates,
- lines = topology.lines,
- rings = topology.rings,
- indexes = index(),
- visitedByIndex = new Int32Array(coordinates.length),
- leftByIndex = new Int32Array(coordinates.length),
- rightByIndex = new Int32Array(coordinates.length),
- junctionByIndex = new Int8Array(coordinates.length),
- junctionCount = 0, // upper bound on number of junctions
- i, n,
- previousIndex,
- currentIndex,
- nextIndex;
- for (i = 0, n = coordinates.length; i < n; ++i) {
- visitedByIndex[i] = leftByIndex[i] = rightByIndex[i] = -1;
- }
- for (i = 0, n = lines.length; i < n; ++i) {
- var line = lines[i],
- lineStart = line[0],
- lineEnd = line[1];
- currentIndex = indexes[lineStart];
- nextIndex = indexes[++lineStart];
- ++junctionCount, junctionByIndex[currentIndex] = 1; // start
- while (++lineStart <= lineEnd) {
- sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[lineStart]);
- }
- ++junctionCount, junctionByIndex[nextIndex] = 1; // end
- }
- for (i = 0, n = coordinates.length; i < n; ++i) {
- visitedByIndex[i] = -1;
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- var ring = rings[i],
- ringStart = ring[0] + 1,
- ringEnd = ring[1];
- previousIndex = indexes[ringEnd - 1];
- currentIndex = indexes[ringStart - 1];
- nextIndex = indexes[ringStart];
- sequence(i, previousIndex, currentIndex, nextIndex);
- while (++ringStart <= ringEnd) {
- sequence(i, previousIndex = currentIndex, currentIndex = nextIndex, nextIndex = indexes[ringStart]);
- }
- }
- function sequence(i, previousIndex, currentIndex, nextIndex) {
- if (visitedByIndex[currentIndex] === i) return; // ignore self-intersection
- visitedByIndex[currentIndex] = i;
- var leftIndex = leftByIndex[currentIndex];
- if (leftIndex >= 0) {
- var rightIndex = rightByIndex[currentIndex];
- if ((leftIndex !== previousIndex || rightIndex !== nextIndex)
- && (leftIndex !== nextIndex || rightIndex !== previousIndex)) {
- ++junctionCount, junctionByIndex[currentIndex] = 1;
- }
- } else {
- leftByIndex[currentIndex] = previousIndex;
- rightByIndex[currentIndex] = nextIndex;
- }
- }
- function index() {
- var indexByPoint = hashmap(coordinates.length * 1.4, hashIndex, equalIndex, Int32Array, -1, Int32Array),
- indexes = new Int32Array(coordinates.length);
- for (var i = 0, n = coordinates.length; i < n; ++i) {
- indexes[i] = indexByPoint.maybeSet(i, i);
- }
- return indexes;
- }
- function hashIndex(i) {
- return hashPoint(coordinates[i]);
- }
- function equalIndex(i, j) {
- return equalPoint(coordinates[i], coordinates[j]);
- }
- visitedByIndex = leftByIndex = rightByIndex = null;
- var junctionByPoint = hashset(junctionCount * 1.4, hashPoint, equalPoint), j;
- // Convert back to a standard hashset by point for caller convenience.
- for (i = 0, n = coordinates.length; i < n; ++i) {
- if (junctionByIndex[j = indexes[i]]) {
- junctionByPoint.add(coordinates[j]);
- }
- }
- return junctionByPoint;
- }
- // Given an extracted (pre-)topology, cuts (or rotates) arcs so that all shared
- // point sequences are identified. The topology can then be subsequently deduped
- // to remove exact duplicate arcs.
- function cut(topology) {
- var junctions = join(topology),
- coordinates = topology.coordinates,
- lines = topology.lines,
- rings = topology.rings,
- next,
- i, n;
- for (i = 0, n = lines.length; i < n; ++i) {
- var line = lines[i],
- lineMid = line[0],
- lineEnd = line[1];
- while (++lineMid < lineEnd) {
- if (junctions.has(coordinates[lineMid])) {
- next = {0: lineMid, 1: line[1]};
- line[1] = lineMid;
- line = line.next = next;
- }
- }
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- var ring = rings[i],
- ringStart = ring[0],
- ringMid = ringStart,
- ringEnd = ring[1],
- ringFixed = junctions.has(coordinates[ringStart]);
- while (++ringMid < ringEnd) {
- if (junctions.has(coordinates[ringMid])) {
- if (ringFixed) {
- next = {0: ringMid, 1: ring[1]};
- ring[1] = ringMid;
- ring = ring.next = next;
- } else { // For the first junction, we can rotate rather than cut.
- rotateArray(coordinates, ringStart, ringEnd, ringEnd - ringMid);
- coordinates[ringEnd] = coordinates[ringStart];
- ringFixed = true;
- ringMid = ringStart; // restart; we may have skipped junctions
- }
- }
- }
- }
- return topology;
- }
- function rotateArray(array, start, end, offset) {
- reverse(array, start, end);
- reverse(array, start, start + offset);
- reverse(array, start + offset, end);
- }
- function reverse(array, start, end) {
- for (var mid = start + ((end-- - start) >> 1), t; start < mid; ++start, --end) {
- t = array[start], array[start] = array[end], array[end] = t;
- }
- }
- // Given a cut topology, combines duplicate arcs.
- function dedup(topology) {
- var coordinates = topology.coordinates,
- lines = topology.lines, line,
- rings = topology.rings, ring,
- arcCount = lines.length + rings.length,
- i, n;
- delete topology.lines;
- delete topology.rings;
- // Count the number of (non-unique) arcs to initialize the hashmap safely.
- for (i = 0, n = lines.length; i < n; ++i) {
- line = lines[i]; while (line = line.next) ++arcCount;
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- ring = rings[i]; while (ring = ring.next) ++arcCount;
- }
- var arcsByEnd = hashmap(arcCount * 2 * 1.4, hashPoint, equalPoint),
- arcs = topology.arcs = [];
- for (i = 0, n = lines.length; i < n; ++i) {
- line = lines[i];
- do {
- dedupLine(line);
- } while (line = line.next);
- }
- for (i = 0, n = rings.length; i < n; ++i) {
- ring = rings[i];
- if (ring.next) { // arc is no longer closed
- do {
- dedupLine(ring);
- } while (ring = ring.next);
- } else {
- dedupRing(ring);
- }
- }
- function dedupLine(arc) {
- var startPoint,
- endPoint,
- startArcs, startArc,
- endArcs, endArc,
- i, n;
- // Does this arc match an existing arc in order?
- if (startArcs = arcsByEnd.get(startPoint = coordinates[arc[0]])) {
- for (i = 0, n = startArcs.length; i < n; ++i) {
- startArc = startArcs[i];
- if (equalLine(startArc, arc)) {
- arc[0] = startArc[0];
- arc[1] = startArc[1];
- return;
- }
- }
- }
- // Does this arc match an existing arc in reverse order?
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[1]])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (reverseEqualLine(endArc, arc)) {
- arc[1] = endArc[0];
- arc[0] = endArc[1];
- return;
- }
- }
- }
- if (startArcs) startArcs.push(arc); else arcsByEnd.set(startPoint, [arc]);
- if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]);
- arcs.push(arc);
- }
- function dedupRing(arc) {
- var endPoint,
- endArcs,
- endArc,
- i, n;
- // Does this arc match an existing line in order, or reverse order?
- // Rings are closed, so their start point and end point is the same.
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0]])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (equalRing(endArc, arc)) {
- arc[0] = endArc[0];
- arc[1] = endArc[1];
- return;
- }
- if (reverseEqualRing(endArc, arc)) {
- arc[0] = endArc[1];
- arc[1] = endArc[0];
- return;
- }
- }
- }
- // Otherwise, does this arc match an existing ring in order, or reverse order?
- if (endArcs = arcsByEnd.get(endPoint = coordinates[arc[0] + findMinimumOffset(arc)])) {
- for (i = 0, n = endArcs.length; i < n; ++i) {
- endArc = endArcs[i];
- if (equalRing(endArc, arc)) {
- arc[0] = endArc[0];
- arc[1] = endArc[1];
- return;
- }
- if (reverseEqualRing(endArc, arc)) {
- arc[0] = endArc[1];
- arc[1] = endArc[0];
- return;
- }
- }
- }
- if (endArcs) endArcs.push(arc); else arcsByEnd.set(endPoint, [arc]);
- arcs.push(arc);
- }
- function equalLine(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1];
- if (ia - ja !== ib - jb) return false;
- for (; ia <= ja; ++ia, ++ib) if (!equalPoint(coordinates[ia], coordinates[ib])) return false;
- return true;
- }
- function reverseEqualLine(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1];
- if (ia - ja !== ib - jb) return false;
- for (; ia <= ja; ++ia, --jb) if (!equalPoint(coordinates[ia], coordinates[jb])) return false;
- return true;
- }
- function equalRing(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1],
- n = ja - ia;
- if (n !== jb - ib) return false;
- var ka = findMinimumOffset(arcA),
- kb = findMinimumOffset(arcB);
- for (var i = 0; i < n; ++i) {
- if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[ib + (i + kb) % n])) return false;
- }
- return true;
- }
- function reverseEqualRing(arcA, arcB) {
- var ia = arcA[0], ib = arcB[0],
- ja = arcA[1], jb = arcB[1],
- n = ja - ia;
- if (n !== jb - ib) return false;
- var ka = findMinimumOffset(arcA),
- kb = n - findMinimumOffset(arcB);
- for (var i = 0; i < n; ++i) {
- if (!equalPoint(coordinates[ia + (i + ka) % n], coordinates[jb - (i + kb) % n])) return false;
- }
- return true;
- }
- // Rings are rotated to a consistent, but arbitrary, start point.
- // This is necessary to detect when a ring and a rotated copy are dupes.
- function findMinimumOffset(arc) {
- var start = arc[0],
- end = arc[1],
- mid = start,
- minimum = mid,
- minimumPoint = coordinates[mid];
- while (++mid < end) {
- var point = coordinates[mid];
- if (point[0] < minimumPoint[0] || point[0] === minimumPoint[0] && point[1] < minimumPoint[1]) {
- minimum = mid;
- minimumPoint = point;
- }
- }
- return minimum - start;
- }
- return topology;
- }
- // Given an array of arcs in absolute (but already quantized!) coordinates,
- // converts to fixed-point delta encoding.
- // This is a destructive operation that modifies the given arcs!
- function delta(arcs) {
- var i = -1,
- n = arcs.length;
- while (++i < n) {
- var arc = arcs[i],
- j = 0,
- k = 1,
- m = arc.length,
- point = arc[0],
- x0 = point[0],
- y0 = point[1],
- x1,
- y1;
- while (++j < m) {
- point = arc[j], x1 = point[0], y1 = point[1];
- if (x1 !== x0 || y1 !== y0) arc[k++] = [x1 - x0, y1 - y0], x0 = x1, y0 = y1;
- }
- if (k === 1) arc[k++] = [0, 0]; // Each arc must be an array of two or more positions.
- arc.length = k;
- }
- return arcs;
- }
- // Extracts the lines and rings from the specified hash of geometry objects.
- //
- // Returns an object with three properties:
- //
- // * coordinates - shared buffer of [x, y] coordinates
- // * lines - lines extracted from the hash, of the form [start, end]
- // * rings - rings extracted from the hash, of the form [start, end]
- //
- // For each ring or line, start and end represent inclusive indexes into the
- // coordinates buffer. For rings (and closed lines), coordinates[start] equals
- // coordinates[end].
- //
- // For each line or polygon geometry in the input hash, including nested
- // geometries as in geometry collections, the `coordinates` array is replaced
- // with an equivalent `arcs` array that, for each line (for line string
- // geometries) or ring (for polygon geometries), points to one of the above
- // lines or rings.
- function extract(objects) {
- var index = -1,
- lines = [],
- rings = [],
- coordinates = [];
- function extractGeometry(geometry) {
- if (geometry && hasOwnProperty.call(extractGeometryType, geometry.type)) extractGeometryType[geometry.type](geometry);
- }
- var extractGeometryType = {
- GeometryCollection: function(o) { o.geometries.forEach(extractGeometry); },
- LineString: function(o) { o.arcs = extractLine(o.arcs); },
- MultiLineString: function(o) { o.arcs = o.arcs.map(extractLine); },
- Polygon: function(o) { o.arcs = o.arcs.map(extractRing); },
- MultiPolygon: function(o) { o.arcs = o.arcs.map(extractMultiRing); }
- };
- function extractLine(line) {
- for (var i = 0, n = line.length; i < n; ++i) coordinates[++index] = line[i];
- var arc = {0: index - n + 1, 1: index};
- lines.push(arc);
- return arc;
- }
- function extractRing(ring) {
- for (var i = 0, n = ring.length; i < n; ++i) coordinates[++index] = ring[i];
- var arc = {0: index - n + 1, 1: index};
- rings.push(arc);
- return arc;
- }
- function extractMultiRing(rings) {
- return rings.map(extractRing);
- }
- for (var key in objects) {
- extractGeometry(objects[key]);
- }
- return {
- type: "Topology",
- coordinates: coordinates,
- lines: lines,
- rings: rings,
- objects: objects
- };
- }
- // Given a hash of GeoJSON objects, returns a hash of GeoJSON geometry objects.
- // Any null input geometry objects are represented as {type: null} in the output.
- // Any feature.{id,properties,bbox} are transferred to the output geometry object.
- // Each output geometry object is a shallow copy of the input (e.g., properties, coordinates)!
- function geometry(inputs) {
- var outputs = {}, key;
- for (key in inputs) outputs[key] = geomifyObject(inputs[key]);
- return outputs;
- }
- function geomifyObject(input) {
- return input == null ? {type: null}
- : (input.type === "FeatureCollection" ? geomifyFeatureCollection
- : input.type === "Feature" ? geomifyFeature
- : geomifyGeometry)(input);
- }
- function geomifyFeatureCollection(input) {
- var output = {type: "GeometryCollection", geometries: input.features.map(geomifyFeature)};
- if (input.bbox != null) output.bbox = input.bbox;
- return output;
- }
- function geomifyFeature(input) {
- var output = geomifyGeometry(input.geometry), key; // eslint-disable-line no-unused-vars
- if (input.id != null) output.id = input.id;
- if (input.bbox != null) output.bbox = input.bbox;
- for (key in input.properties) { output.properties = input.properties; break; }
- return output;
- }
- function geomifyGeometry(input) {
- if (input == null) return {type: null};
- var output = input.type === "GeometryCollection" ? {type: "GeometryCollection", geometries: input.geometries.map(geomifyGeometry)}
- : input.type === "Point" || input.type === "MultiPoint" ? {type: input.type, coordinates: input.coordinates}
- : {type: input.type, arcs: input.coordinates}; // TODO Check for unknown types?
- if (input.bbox != null) output.bbox = input.bbox;
- return output;
- }
- function prequantize(objects, bbox, n) {
- var x0 = bbox[0],
- y0 = bbox[1],
- x1 = bbox[2],
- y1 = bbox[3],
- kx = x1 - x0 ? (n - 1) / (x1 - x0) : 1,
- ky = y1 - y0 ? (n - 1) / (y1 - y0) : 1;
- function quantizePoint(input) {
- return [Math.round((input[0] - x0) * kx), Math.round((input[1] - y0) * ky)];
- }
- function quantizePoints(input, m) {
- var i = -1,
- j = 0,
- n = input.length,
- output = new Array(n), // pessimistic
- pi,
- px,
- py,
- x,
- y;
- while (++i < n) {
- pi = input[i];
- x = Math.round((pi[0] - x0) * kx);
- y = Math.round((pi[1] - y0) * ky);
- if (x !== px || y !== py) output[j++] = [px = x, py = y]; // non-coincident points
- }
- output.length = j;
- while (j < m) j = output.push([output[0][0], output[0][1]]);
- return output;
- }
- function quantizeLine(input) {
- return quantizePoints(input, 2);
- }
- function quantizeRing(input) {
- return quantizePoints(input, 4);
- }
- function quantizePolygon(input) {
- return input.map(quantizeRing);
- }
- function quantizeGeometry(o) {
- if (o != null && hasOwnProperty.call(quantizeGeometryType, o.type)) quantizeGeometryType[o.type](o);
- }
- var quantizeGeometryType = {
- GeometryCollection: function(o) { o.geometries.forEach(quantizeGeometry); },
- Point: function(o) { o.coordinates = quantizePoint(o.coordinates); },
- MultiPoint: function(o) { o.coordinates = o.coordinates.map(quantizePoint); },
- LineString: function(o) { o.arcs = quantizeLine(o.arcs); },
- MultiLineString: function(o) { o.arcs = o.arcs.map(quantizeLine); },
- Polygon: function(o) { o.arcs = quantizePolygon(o.arcs); },
- MultiPolygon: function(o) { o.arcs = o.arcs.map(quantizePolygon); }
- };
- for (var key in objects) {
- quantizeGeometry(objects[key]);
- }
- return {
- scale: [1 / kx, 1 / ky],
- translate: [x0, y0]
- };
- }
- // Constructs the TopoJSON Topology for the specified hash of features.
- // Each object in the specified hash must be a GeoJSON object,
- // meaning FeatureCollection, a Feature or a geometry object.
- function topology(objects, quantization) {
- var bbox = bounds(objects = geometry(objects)),
- transform = quantization > 0 && bbox && prequantize(objects, bbox, quantization),
- topology = dedup(cut(extract(objects))),
- coordinates = topology.coordinates,
- indexByArc = hashmap(topology.arcs.length * 1.4, hashArc, equalArc);
- objects = topology.objects; // for garbage collection
- topology.bbox = bbox;
- topology.arcs = topology.arcs.map(function(arc, i) {
- indexByArc.set(arc, i);
- return coordinates.slice(arc[0], arc[1] + 1);
- });
- delete topology.coordinates;
- coordinates = null;
- function indexGeometry(geometry) {
- if (geometry && hasOwnProperty.call(indexGeometryType, geometry.type)) indexGeometryType[geometry.type](geometry);
- }
- var indexGeometryType = {
- GeometryCollection: function(o) { o.geometries.forEach(indexGeometry); },
- LineString: function(o) { o.arcs = indexArcs(o.arcs); },
- MultiLineString: function(o) { o.arcs = o.arcs.map(indexArcs); },
- Polygon: function(o) { o.arcs = o.arcs.map(indexArcs); },
- MultiPolygon: function(o) { o.arcs = o.arcs.map(indexMultiArcs); }
- };
- function indexArcs(arc) {
- var indexes = [];
- do {
- var index = indexByArc.get(arc);
- indexes.push(arc[0] < arc[1] ? index : ~index);
- } while (arc = arc.next);
- return indexes;
- }
- function indexMultiArcs(arcs) {
- return arcs.map(indexArcs);
- }
- for (var key in objects) {
- indexGeometry(objects[key]);
- }
- if (transform) {
- topology.transform = transform;
- topology.arcs = delta(topology.arcs);
- }
- return topology;
- }
- function hashArc(arc) {
- var i = arc[0], j = arc[1], t;
- if (j < i) t = i, i = j, j = t;
- return i + 31 * j;
- }
- function equalArc(arcA, arcB) {
- var ia = arcA[0], ja = arcA[1],
- ib = arcB[0], jb = arcB[1], t;
- if (ja < ia) t = ia, ia = ja, ja = t;
- if (jb < ib) t = ib, ib = jb, jb = t;
- return ia === ib && ja === jb;
- }
- exports.topology = topology;
- Object.defineProperty(exports, '__esModule', { value: true });
- }));
|