UNPKG

@placemarkio/turf-jsts

Version:

A JavaScript library of spatial predicates and functions for processing geometry

1,835 lines (1,661 loc) 432 kB
class BufferParameters { constructor() { BufferParameters.constructor_.apply(this, arguments); } static constructor_() { this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS; this._endCapStyle = BufferParameters.CAP_ROUND; this._joinStyle = BufferParameters.JOIN_ROUND; this._mitreLimit = BufferParameters.DEFAULT_MITRE_LIMIT; this._isSingleSided = false; this._simplifyFactor = BufferParameters.DEFAULT_SIMPLIFY_FACTOR; if (arguments.length === 0) ; else if (arguments.length === 1) { const quadrantSegments = arguments[0]; this.setQuadrantSegments(quadrantSegments); } else if (arguments.length === 2) { const quadrantSegments = arguments[0], endCapStyle = arguments[1]; this.setQuadrantSegments(quadrantSegments); this.setEndCapStyle(endCapStyle); } else if (arguments.length === 4) { const quadrantSegments = arguments[0], endCapStyle = arguments[1], joinStyle = arguments[2], mitreLimit = arguments[3]; this.setQuadrantSegments(quadrantSegments); this.setEndCapStyle(endCapStyle); this.setJoinStyle(joinStyle); this.setMitreLimit(mitreLimit); } } static bufferDistanceError(quadSegs) { const alpha = Math.PI / 2.0 / quadSegs; return 1 - Math.cos(alpha / 2.0) } getEndCapStyle() { return this._endCapStyle } isSingleSided() { return this._isSingleSided } setQuadrantSegments(quadSegs) { this._quadrantSegments = quadSegs; if (this._quadrantSegments === 0) this._joinStyle = BufferParameters.JOIN_BEVEL; if (this._quadrantSegments < 0) { this._joinStyle = BufferParameters.JOIN_MITRE; this._mitreLimit = Math.abs(this._quadrantSegments); } if (quadSegs <= 0) this._quadrantSegments = 1; if (this._joinStyle !== BufferParameters.JOIN_ROUND) this._quadrantSegments = BufferParameters.DEFAULT_QUADRANT_SEGMENTS; } getJoinStyle() { return this._joinStyle } setJoinStyle(joinStyle) { this._joinStyle = joinStyle; } setSimplifyFactor(simplifyFactor) { this._simplifyFactor = simplifyFactor < 0 ? 0 : simplifyFactor; } getSimplifyFactor() { return this._simplifyFactor } getQuadrantSegments() { return this._quadrantSegments } setEndCapStyle(endCapStyle) { this._endCapStyle = endCapStyle; } getMitreLimit() { return this._mitreLimit } setMitreLimit(mitreLimit) { this._mitreLimit = mitreLimit; } setSingleSided(isSingleSided) { this._isSingleSided = isSingleSided; } } BufferParameters.CAP_ROUND = 1; BufferParameters.CAP_FLAT = 2; BufferParameters.CAP_SQUARE = 3; BufferParameters.JOIN_ROUND = 1; BufferParameters.JOIN_MITRE = 2; BufferParameters.JOIN_BEVEL = 3; BufferParameters.DEFAULT_QUADRANT_SEGMENTS = 8; BufferParameters.DEFAULT_MITRE_LIMIT = 5.0; BufferParameters.DEFAULT_SIMPLIFY_FACTOR = 0.01; class Exception extends Error { constructor(message) { super(message); this.name = Object.keys({ Exception })[0]; } toString() { return this.message } } class IllegalArgumentException extends Exception { constructor(message) { super(message); this.name = Object.keys({ IllegalArgumentException })[0]; } } class GeometryComponentFilter { filter(geom) {} } function Comparable() {} function Clonable() {} function Serializable() {} class NumberUtil { static equalsWithTolerance(x1, x2, tolerance) { return Math.abs(x1 - x2) <= tolerance } } class Long { constructor(high, low) { this.low = low || 0; this.high = high || 0; } static toBinaryString(i) { let mask; let result = ''; for (mask = 0x80000000; mask > 0; mask >>>= 1) result += (i.high & mask) === mask ? '1' : '0'; for (mask = 0x80000000; mask > 0; mask >>>= 1) result += (i.low & mask) === mask ? '1' : '0'; return result } } function Double() { } Double.NaN = NaN; Double.isNaN = n => Number.isNaN(n); Double.isInfinite = n => !Number.isFinite(n); Double.MAX_VALUE = Number.MAX_VALUE; Double.POSITIVE_INFINITY = Number.POSITIVE_INFINITY; Double.NEGATIVE_INFINITY = Number.NEGATIVE_INFINITY; if (typeof Float64Array === 'function' && typeof Int32Array === 'function') // Simple and fast conversion between double and long bits // using TypedArrays and ArrayViewBuffers. (function() { const EXP_BIT_MASK = 0x7ff00000; const SIGNIF_BIT_MASK = 0xFFFFF; const f64buf = new Float64Array(1); const i32buf = new Int32Array(f64buf.buffer); Double.doubleToLongBits = function(value) { f64buf[0] = value; let low = i32buf[0] | 0; let high = i32buf[1] | 0; // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if (((high & EXP_BIT_MASK) === EXP_BIT_MASK) && ((high & SIGNIF_BIT_MASK) !== 0) && (low !== 0)) { low = 0 | 0; high = 0x7ff80000 | 0; } return new Long(high, low) }; Double.longBitsToDouble = function(bits) { i32buf[0] = bits.low; i32buf[1] = bits.high; return f64buf[0] }; })(); else // More complex and slower fallback implementation using // math and the divide-by-two and multiply-by-two algorithms. (function() { const BIAS = 1023; const log2 = Math.log2; const floor = Math.floor; const pow = Math.pow; const MAX_REL_BITS_INTEGER = (function() { for (let i = 53; i > 0; i--) { const bits = pow(2, i) - 1; if (floor(log2(bits)) + 1 === i) return bits } return 0 })(); Double.doubleToLongBits = function(value) { let x, y, f, bits, skip; let sign, exp, high, low; // Get the sign bit and absolute value. if (value < 0 || 1 / value === Number.NEGATIVE_INFINITY) { sign = (1 << 31); value = (-value); } else { sign = 0; } // Handle some special values. if (value === 0) { // Handle zeros (+/-0). low = 0 | 0; high = sign; // exponent: 00..00, significand: 00..00 return new Long(high, low) } if (value === Infinity) { // Handle infinity (only positive values for value possible). low = 0 | 0; high = sign | 0x7ff00000; // exponent: 11..11, significand: 00..00 return new Long(high, low) } if (value !== value) { // eslint-disable-line // Handle NaNs (boiled down to only one distinct NaN). low = 0 | 0; high = 0x7ff80000; // exponent: 11..11, significand: 10..00 return new Long(high, low) } // Preinitialize variables, that are not neccessarily set by // the algorithm. bits = 0; low = 0 | 0; // Get the (always positive) integer part of value. x = floor(value); // Process the integer part if it's greater than 1. Zero requires // no bits at all, 1 represents the implicit (hidden) leading bit, // which must not be written as well. if (x > 1) // If we can reliably determine the number of bits required for // the integer part, if (x <= MAX_REL_BITS_INTEGER) { // get the number of bits required to represent it minus 1 bits = floor(log2(x)); /* + 1 - 1 */ // and simply copy/shift the integer bits into low and high. // That's much faster than the divide-by-two algorithm (saves // up to ~60%). // We always need to mask out the most significant bit, which // is the implicit (aka hidden) bit. if (bits <= 20) { // The simple case in which the integer fits into the // lower 20 bits of the high word is worth to be handled // separately (saves ~25%). low = 0 | 0; high = (x << (20 - bits)) & 0xfffff; } else { // Here, the integer part is split into low and high. // Since its value may require more than 32 bits, we // cannot use bitwise operators (which implicitly cast // to Int32), but use arithmetic operators % and / to // get low and high parts. The uppper 20 bits go to high, // the remaining bits (in f) to low. f = bits - 20; // Like (1 << f) but safe with even more than 32 bits. y = pow(2, f); low = (x % y) << (32 - f); high = (x / y) & 0xfffff; } } else { // For greater values, we must use the much slower divide-by-two // algorithm. Bits are generated from right to left, that is from // least to most significant bit. For each bit, we left-shift both // low and high by one and carry bit #0 from high to #31 in low. // The next bit is then copied into bit #19 in high, the leftmost // bit of the double's significand. // Preserve x for later user, so work with f. f = x; low = 0 | 0; for (;;) { y = f / 2; f = floor(y); if (f === 0) // We just found the most signigicant (1-)bit, which // is the implicit bit and so, not stored in the double // value. So, it's time to leave the loop. break // Count this bit, shift low and carry bit #0 from high. bits++; low >>>= 1; low |= (high & 0x1) << 31; // Shift high. high >>>= 1; if (y !== f) // Copy the new bit into bit #19 in high (only required if 1). high |= 0x80000; } } // Bias the exponent. exp = bits + BIAS; // If the integer part is zero, we've not yet seen the implicit // leading bit. Variable skip is later used while processing the // fractional part (if any). skip = (x === 0); // Get fraction only into x. x = value - x; // If some significand bits are still left to be filled and // the fractional part is not zero, convert the fraction using // the multiply-by-2 algorithm. if (bits < 52 && x !== 0) { // Initialize 'buffer' f, into which newly created bits get // shifted from right to left. f = 0; for (;;) { y = x * 2; if (y >= 1) { // This is a new 1-bit. Add and count this bit, if not // prohibited by skip. x = y - 1; if (!skip) { f <<= 1; f |= 1; bits++; } else { // Otherwise, decrement the exponent and unset // skip, so that all following bits get written. exp--; skip = false; } } else { // This is a new 0-bit. Add and count this bit, if not // prohibited by skip. x = y; if (!skip) { f <<= 1; bits++; } else if (--exp === 0) { // Otherwise we've just decremented the exponent. If the // biased exponent is zero now (-1023), we process a // subnormal number, which has no impled leading 1-bit. // So, count this 0-bit and unset skip to write out // all the following bits. bits++; skip = false; } } if (bits === 20) { // When 20 bits have been created in total, we're done with // the high word. Copy the bits from 'buffer' f into high // and reset 'buffer' f. Following bits will end up in the // low word. high |= f; f = 0; } else if (bits === 52) { // When 52 bits have been created in total, we're done with // low word as well. Copy the bits from 'buffer' f into low // and exit the loop. low |= f; break } if (y === 1) { // When y is exactly 1, there is no remainder and the process // is complete (the number is finite). Copy the bits from // 'buffer' f into either low or high and exit the loop. if (bits < 20) high |= (f << (20 - bits)); else if (bits < 52) low |= (f << (52 - bits)); break } } } // Copy/shift the exponent and sign bits into the high word. high |= (exp << 20); high |= sign; return new Long(high, low) }; Double.longBitsToDouble = function(bits) { let i; let x, exp, fract; const high = bits.high; const low = bits.low; // Extract the sign. const sign = (high & (1 << 31)) ? -1 : 1; // Extract the unbiased exponent. exp = ((high & 0x7ff00000) >> 20) - BIAS; // Calculate the fraction from left to right. Start // off with the 20 lower bits from the high word. fract = 0; x = (1 << 19); for (i = 1; i <= 20; i++) { if (high & x) fract += pow(2, -i); x >>>= 1; } // Continue with all 32 bits from the low word. x = (1 << 31); for (i = 21; i <= 52; i++) { if (low & x) fract += pow(2, -i); x >>>= 1; } // Handle special values. // Check for zero and subnormal values. if (exp === -BIAS) { if (fract === 0) // +/-1.0 * 0.0 => +/-0.0 return sign * 0 exp = -1022; } else if (exp === BIAS + 1) { // Check for +/-Infinity or NaN. if (fract === 0) // +/-1.0 / 0.0 => +/-Infinity return sign / 0 return NaN } else { // Nothing special? Seems to be a normal number. // Add the implicit leading bit (1*2^0). fract += 1; } return sign * fract * pow(2, exp) }; })(); function Comparator() {} class RuntimeException extends Exception { constructor(message) { super(message); this.name = Object.keys({ RuntimeException })[0]; } } class AssertionFailedException extends RuntimeException { constructor() { super(); AssertionFailedException.constructor_.apply(this, arguments); } static constructor_() { if (arguments.length === 0) { RuntimeException.constructor_.call(this); } else if (arguments.length === 1) { const message = arguments[0]; RuntimeException.constructor_.call(this, message); } } } class Assert { static shouldNeverReachHere() { if (arguments.length === 0) { Assert.shouldNeverReachHere(null); } else if (arguments.length === 1) { const message = arguments[0]; throw new AssertionFailedException('Should never reach here' + (message !== null ? ': ' + message : '')) } } static isTrue() { if (arguments.length === 1) { const assertion = arguments[0]; Assert.isTrue(assertion, null); } else if (arguments.length === 2) { const assertion = arguments[0], message = arguments[1]; if (!assertion) if (message === null) throw new AssertionFailedException() else throw new AssertionFailedException(message) } } static equals() { if (arguments.length === 2) { const expectedValue = arguments[0], actualValue = arguments[1]; Assert.equals(expectedValue, actualValue, null); } else if (arguments.length === 3) { const expectedValue = arguments[0], actualValue = arguments[1], message = arguments[2]; if (!actualValue.equals(expectedValue)) throw new AssertionFailedException('Expected ' + expectedValue + ' but encountered ' + actualValue + (message !== null ? ': ' + message : '')) } } } const kBuf = new ArrayBuffer(8); const kBufAsF64 = new Float64Array(kBuf); const kBufAsI32 = new Int32Array(kBuf); class Coordinate { constructor() { Coordinate.constructor_.apply(this, arguments); } static constructor_() { this.x = null; this.y = null; this.z = null; if (arguments.length === 0) { Coordinate.constructor_.call(this, 0.0, 0.0); } else if (arguments.length === 1) { const c = arguments[0]; Coordinate.constructor_.call(this, c.x, c.y, c.getZ()); } else if (arguments.length === 2) { const x = arguments[0], y = arguments[1]; Coordinate.constructor_.call(this, x, y, Coordinate.NULL_ORDINATE); } else if (arguments.length === 3) { const x = arguments[0], y = arguments[1], z = arguments[2]; this.x = x; this.y = y; this.z = z; } } static hashCode(n) { kBufAsF64[0] = n; return kBufAsI32[0] ^ kBufAsI32[1] } getM() { return Double.NaN } setOrdinate(ordinateIndex, value) { switch (ordinateIndex) { case Coordinate.X: this.x = value; break case Coordinate.Y: this.y = value; break case Coordinate.Z: this.setZ(value); break default: throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex) } } equals2D() { if (arguments.length === 1) { const other = arguments[0]; if (this.x !== other.x) return false if (this.y !== other.y) return false return true } else if (arguments.length === 2) { const c = arguments[0], tolerance = arguments[1]; if (!NumberUtil.equalsWithTolerance(this.x, c.x, tolerance)) return false if (!NumberUtil.equalsWithTolerance(this.y, c.y, tolerance)) return false return true } } setM(m) { throw new IllegalArgumentException('Invalid ordinate index: ' + Coordinate.M) } getZ() { return this.z } getOrdinate(ordinateIndex) { switch (ordinateIndex) { case Coordinate.X: return this.x case Coordinate.Y: return this.y case Coordinate.Z: return this.getZ() } throw new IllegalArgumentException('Invalid ordinate index: ' + ordinateIndex) } equals3D(other) { return this.x === other.x && this.y === other.y && (this.getZ() === other.getZ() || Double.isNaN(this.getZ()) && Double.isNaN(other.getZ())) } equals(other) { if (!(other instanceof Coordinate)) return false return this.equals2D(other) } equalInZ(c, tolerance) { return NumberUtil.equalsWithTolerance(this.getZ(), c.getZ(), tolerance) } setX(x) { this.x = x; } compareTo(o) { const other = o; if (this.x < other.x) return -1 if (this.x > other.x) return 1 if (this.y < other.y) return -1 if (this.y > other.y) return 1 return 0 } getX() { return this.x } setZ(z) { this.z = z; } clone() { try { const coord = null; return coord } catch (e) { if (e instanceof CloneNotSupportedException) { Assert.shouldNeverReachHere('this shouldn\'t happen because this class is Cloneable'); return null } else { throw e } } finally {} } copy() { return new Coordinate(this) } toString() { return '(' + this.x + ', ' + this.y + ', ' + this.getZ() + ')' } distance3D(c) { const dx = this.x - c.x; const dy = this.y - c.y; const dz = this.getZ() - c.getZ(); return Math.sqrt(dx * dx + dy * dy + dz * dz) } getY() { return this.y } setY(y) { this.y = y; } distance(c) { const dx = this.x - c.x; const dy = this.y - c.y; return Math.sqrt(dx * dx + dy * dy) } hashCode() { let result = 17; result = 37 * result + Coordinate.hashCode(this.x); result = 37 * result + Coordinate.hashCode(this.y); return result } setCoordinate(other) { this.x = other.x; this.y = other.y; this.z = other.getZ(); } get interfaces_() { return [Comparable, Clonable, Serializable] } } class DimensionalComparator { constructor() { DimensionalComparator.constructor_.apply(this, arguments); } static constructor_() { this._dimensionsToTest = 2; if (arguments.length === 0) { DimensionalComparator.constructor_.call(this, 2); } else if (arguments.length === 1) { const dimensionsToTest = arguments[0]; if (dimensionsToTest !== 2 && dimensionsToTest !== 3) throw new IllegalArgumentException('only 2 or 3 dimensions may be specified') this._dimensionsToTest = dimensionsToTest; } } static compare(a, b) { if (a < b) return -1 if (a > b) return 1 if (Double.isNaN(a)) { if (Double.isNaN(b)) return 0 return -1 } if (Double.isNaN(b)) return 1 return 0 } compare(c1, c2) { const compX = DimensionalComparator.compare(c1.x, c2.x); if (compX !== 0) return compX const compY = DimensionalComparator.compare(c1.y, c2.y); if (compY !== 0) return compY if (this._dimensionsToTest <= 2) return 0 const compZ = DimensionalComparator.compare(c1.getZ(), c2.getZ()); return compZ } get interfaces_() { return [Comparator] } } Coordinate.DimensionalComparator = DimensionalComparator; Coordinate.NULL_ORDINATE = Double.NaN; Coordinate.X = 0; Coordinate.Y = 1; Coordinate.Z = 2; Coordinate.M = 3; class Envelope { constructor() { Envelope.constructor_.apply(this, arguments); } static constructor_() { this._minx = null; this._maxx = null; this._miny = null; this._maxy = null; if (arguments.length === 0) { this.init(); } else if (arguments.length === 1) { if (arguments[0] instanceof Coordinate) { const p = arguments[0]; this.init(p.x, p.x, p.y, p.y); } else if (arguments[0] instanceof Envelope) { const env = arguments[0]; this.init(env); } } else if (arguments.length === 2) { const p1 = arguments[0], p2 = arguments[1]; this.init(p1.x, p2.x, p1.y, p2.y); } else if (arguments.length === 4) { const x1 = arguments[0], x2 = arguments[1], y1 = arguments[2], y2 = arguments[3]; this.init(x1, x2, y1, y2); } } static intersects() { if (arguments.length === 3) { const p1 = arguments[0], p2 = arguments[1], q = arguments[2]; if (q.x >= (p1.x < p2.x ? p1.x : p2.x) && q.x <= (p1.x > p2.x ? p1.x : p2.x) && (q.y >= (p1.y < p2.y ? p1.y : p2.y) && q.y <= (p1.y > p2.y ? p1.y : p2.y))) return true return false } else if (arguments.length === 4) { const p1 = arguments[0], p2 = arguments[1], q1 = arguments[2], q2 = arguments[3]; let minq = Math.min(q1.x, q2.x); let maxq = Math.max(q1.x, q2.x); let minp = Math.min(p1.x, p2.x); let maxp = Math.max(p1.x, p2.x); if (minp > maxq) return false if (maxp < minq) return false minq = Math.min(q1.y, q2.y); maxq = Math.max(q1.y, q2.y); minp = Math.min(p1.y, p2.y); maxp = Math.max(p1.y, p2.y); if (minp > maxq) return false if (maxp < minq) return false return true } } getArea() { return this.getWidth() * this.getHeight() } equals(other) { if (!(other instanceof Envelope)) return false const otherEnvelope = other; if (this.isNull()) return otherEnvelope.isNull() return this._maxx === otherEnvelope.getMaxX() && this._maxy === otherEnvelope.getMaxY() && this._minx === otherEnvelope.getMinX() && this._miny === otherEnvelope.getMinY() } intersection(env) { if (this.isNull() || env.isNull() || !this.intersects(env)) return new Envelope() const intMinX = this._minx > env._minx ? this._minx : env._minx; const intMinY = this._miny > env._miny ? this._miny : env._miny; const intMaxX = this._maxx < env._maxx ? this._maxx : env._maxx; const intMaxY = this._maxy < env._maxy ? this._maxy : env._maxy; return new Envelope(intMinX, intMaxX, intMinY, intMaxY) } isNull() { return this._maxx < this._minx } getMaxX() { return this._maxx } covers() { if (arguments.length === 1) { if (arguments[0] instanceof Coordinate) { const p = arguments[0]; return this.covers(p.x, p.y) } else if (arguments[0] instanceof Envelope) { const other = arguments[0]; if (this.isNull() || other.isNull()) return false return other.getMinX() >= this._minx && other.getMaxX() <= this._maxx && other.getMinY() >= this._miny && other.getMaxY() <= this._maxy } } else if (arguments.length === 2) { const x = arguments[0], y = arguments[1]; if (this.isNull()) return false return x >= this._minx && x <= this._maxx && y >= this._miny && y <= this._maxy } } intersects() { if (arguments.length === 1) { if (arguments[0] instanceof Envelope) { const other = arguments[0]; if (this.isNull() || other.isNull()) return false return !(other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny) } else if (arguments[0] instanceof Coordinate) { const p = arguments[0]; return this.intersects(p.x, p.y) } } else if (arguments.length === 2) { if (arguments[0] instanceof Coordinate && arguments[1] instanceof Coordinate) { const a = arguments[0], b = arguments[1]; if (this.isNull()) return false const envminx = a.x < b.x ? a.x : b.x; if (envminx > this._maxx) return false const envmaxx = a.x > b.x ? a.x : b.x; if (envmaxx < this._minx) return false const envminy = a.y < b.y ? a.y : b.y; if (envminy > this._maxy) return false const envmaxy = a.y > b.y ? a.y : b.y; if (envmaxy < this._miny) return false return true } else if (typeof arguments[0] === 'number' && typeof arguments[1] === 'number') { const x = arguments[0], y = arguments[1]; if (this.isNull()) return false return !(x > this._maxx || x < this._minx || y > this._maxy || y < this._miny) } } } getMinY() { return this._miny } getDiameter() { if (this.isNull()) return 0 const w = this.getWidth(); const h = this.getHeight(); return Math.sqrt(w * w + h * h) } getMinX() { return this._minx } expandToInclude() { if (arguments.length === 1) { if (arguments[0] instanceof Coordinate) { const p = arguments[0]; this.expandToInclude(p.x, p.y); } else if (arguments[0] instanceof Envelope) { const other = arguments[0]; if (other.isNull()) return null if (this.isNull()) { this._minx = other.getMinX(); this._maxx = other.getMaxX(); this._miny = other.getMinY(); this._maxy = other.getMaxY(); } else { if (other._minx < this._minx) this._minx = other._minx; if (other._maxx > this._maxx) this._maxx = other._maxx; if (other._miny < this._miny) this._miny = other._miny; if (other._maxy > this._maxy) this._maxy = other._maxy; } } } else if (arguments.length === 2) { const x = arguments[0], y = arguments[1]; if (this.isNull()) { this._minx = x; this._maxx = x; this._miny = y; this._maxy = y; } else { if (x < this._minx) this._minx = x; if (x > this._maxx) this._maxx = x; if (y < this._miny) this._miny = y; if (y > this._maxy) this._maxy = y; } } } minExtent() { if (this.isNull()) return 0.0 const w = this.getWidth(); const h = this.getHeight(); if (w < h) return w return h } getWidth() { if (this.isNull()) return 0 return this._maxx - this._minx } compareTo(o) { const env = o; if (this.isNull()) { if (env.isNull()) return 0 return -1 } else { if (env.isNull()) return 1 } if (this._minx < env._minx) return -1 if (this._minx > env._minx) return 1 if (this._miny < env._miny) return -1 if (this._miny > env._miny) return 1 if (this._maxx < env._maxx) return -1 if (this._maxx > env._maxx) return 1 if (this._maxy < env._maxy) return -1 if (this._maxy > env._maxy) return 1 return 0 } translate(transX, transY) { if (this.isNull()) return null this.init(this.getMinX() + transX, this.getMaxX() + transX, this.getMinY() + transY, this.getMaxY() + transY); } copy() { return new Envelope(this) } toString() { return 'Env[' + this._minx + ' : ' + this._maxx + ', ' + this._miny + ' : ' + this._maxy + ']' } setToNull() { this._minx = 0; this._maxx = -1; this._miny = 0; this._maxy = -1; } disjoint(other) { if (this.isNull() || other.isNull()) return true return other._minx > this._maxx || other._maxx < this._minx || other._miny > this._maxy || other._maxy < this._miny } getHeight() { if (this.isNull()) return 0 return this._maxy - this._miny } maxExtent() { if (this.isNull()) return 0.0 const w = this.getWidth(); const h = this.getHeight(); if (w > h) return w return h } expandBy() { if (arguments.length === 1) { const distance = arguments[0]; this.expandBy(distance, distance); } else if (arguments.length === 2) { const deltaX = arguments[0], deltaY = arguments[1]; if (this.isNull()) return null this._minx -= deltaX; this._maxx += deltaX; this._miny -= deltaY; this._maxy += deltaY; if (this._minx > this._maxx || this._miny > this._maxy) this.setToNull(); } } contains() { if (arguments.length === 1) { if (arguments[0] instanceof Envelope) { const other = arguments[0]; return this.covers(other) } else if (arguments[0] instanceof Coordinate) { const p = arguments[0]; return this.covers(p) } } else if (arguments.length === 2) { const x = arguments[0], y = arguments[1]; return this.covers(x, y) } } centre() { if (this.isNull()) return null return new Coordinate((this.getMinX() + this.getMaxX()) / 2.0, (this.getMinY() + this.getMaxY()) / 2.0) } init() { if (arguments.length === 0) { this.setToNull(); } else if (arguments.length === 1) { if (arguments[0] instanceof Coordinate) { const p = arguments[0]; this.init(p.x, p.x, p.y, p.y); } else if (arguments[0] instanceof Envelope) { const env = arguments[0]; this._minx = env._minx; this._maxx = env._maxx; this._miny = env._miny; this._maxy = env._maxy; } } else if (arguments.length === 2) { const p1 = arguments[0], p2 = arguments[1]; this.init(p1.x, p2.x, p1.y, p2.y); } else if (arguments.length === 4) { const x1 = arguments[0], x2 = arguments[1], y1 = arguments[2], y2 = arguments[3]; if (x1 < x2) { this._minx = x1; this._maxx = x2; } else { this._minx = x2; this._maxx = x1; } if (y1 < y2) { this._miny = y1; this._maxy = y2; } else { this._miny = y2; this._maxy = y1; } } } getMaxY() { return this._maxy } distance(env) { if (this.intersects(env)) return 0 let dx = 0.0; if (this._maxx < env._minx) dx = env._minx - this._maxx; else if (this._minx > env._maxx) dx = this._minx - env._maxx; let dy = 0.0; if (this._maxy < env._miny) dy = env._miny - this._maxy; else if (this._miny > env._maxy) dy = this._miny - env._maxy; if (dx === 0.0) return dy if (dy === 0.0) return dx return Math.sqrt(dx * dx + dy * dy) } hashCode() { let result = 17; result = 37 * result + Coordinate.hashCode(this._minx); result = 37 * result + Coordinate.hashCode(this._maxx); result = 37 * result + Coordinate.hashCode(this._miny); result = 37 * result + Coordinate.hashCode(this._maxy); return result } get interfaces_() { return [Comparable, Serializable] } } class Geometry { constructor() { Geometry.constructor_.apply(this, arguments); } isGeometryCollection() { return this.getTypeCode() === Geometry.TYPECODE_GEOMETRYCOLLECTION } getFactory() { return this._factory } getGeometryN(n) { return this } getArea() { return 0.0 } isRectangle() { return false } equalsExact(other) { return this === other || this.equalsExact(other, 0) } geometryChanged() { this.apply(Geometry.geometryChangedFilter); } geometryChangedAction() { this._envelope = null; } equalsNorm(g) { if (g === null) return false return this.norm().equalsExact(g.norm()) } getLength() { return 0.0 } getNumGeometries() { return 1 } compareTo() { let other; if (arguments.length === 1) { const o = arguments[0]; other = o; if (this.getTypeCode() !== other.getTypeCode()) return this.getTypeCode() - other.getTypeCode() if (this.isEmpty() && other.isEmpty()) return 0 if (this.isEmpty()) return -1 if (other.isEmpty()) return 1 return this.compareToSameClass(o) } else if (arguments.length === 2) { const o = arguments[0]; const comp = arguments[1]; other = o; if (this.getTypeCode() !== other.getTypeCode()) return this.getTypeCode() - other.getTypeCode() if (this.isEmpty() && other.isEmpty()) return 0 if (this.isEmpty()) return -1 if (other.isEmpty()) return 1 return this.compareToSameClass(o, comp) } } getUserData() { return this._userData } getSRID() { return this._SRID } getEnvelope() { return this.getFactory().toGeometry(this.getEnvelopeInternal()) } checkNotGeometryCollection(g) { if (g.getTypeCode() === Geometry.TYPECODE_GEOMETRYCOLLECTION) throw new IllegalArgumentException('This method does not support GeometryCollection arguments') } equal(a, b, tolerance) { if (tolerance === 0) return a.equals(b) return a.distance(b) <= tolerance } norm() { const copy = this.copy(); copy.normalize(); return copy } reverse() { const res = this.reverseInternal(); if (this.envelope != null) res.envelope = this.envelope.copy(); res.setSRID(this.getSRID()); return res } copy() { const copy = this.copyInternal(); copy.envelope = this._envelope == null ? null : this._envelope.copy(); copy._SRID = this._SRID; copy._userData = this._userData; return copy } getPrecisionModel() { return this._factory.getPrecisionModel() } getEnvelopeInternal() { if (this._envelope === null) this._envelope = this.computeEnvelopeInternal(); return new Envelope(this._envelope) } setSRID(SRID) { this._SRID = SRID; } setUserData(userData) { this._userData = userData; } compare(a, b) { const i = a.iterator(); const j = b.iterator(); while (i.hasNext() && j.hasNext()) { const aElement = i.next(); const bElement = j.next(); const comparison = aElement.compareTo(bElement); if (comparison !== 0) return comparison } if (i.hasNext()) return 1 if (j.hasNext()) return -1 return 0 } hashCode() { return this.getEnvelopeInternal().hashCode() } isEquivalentClass(other) { return this.getClass() === other.getClass() } isGeometryCollectionOrDerived() { if (this.getTypeCode() === Geometry.TYPECODE_GEOMETRYCOLLECTION || this.getTypeCode() === Geometry.TYPECODE_MULTIPOINT || this.getTypeCode() === Geometry.TYPECODE_MULTILINESTRING || this.getTypeCode() === Geometry.TYPECODE_MULTIPOLYGON) return true return false } get interfaces_() { return [Clonable, Comparable, Serializable] } getClass() { return Geometry } static hasNonEmptyElements(geometries) { for (let i = 0; i < geometries.length; i++) if (!geometries[i].isEmpty()) return true return false } static hasNullElements(array) { for (let i = 0; i < array.length; i++) if (array[i] === null) return true return false } } Geometry.constructor_ = function(factory) { if (!factory) return this._envelope = null; this._userData = null; this._factory = factory; this._SRID = factory.getSRID(); }; Geometry.TYPECODE_POINT = 0; Geometry.TYPECODE_MULTIPOINT = 1; Geometry.TYPECODE_LINESTRING = 2; Geometry.TYPECODE_LINEARRING = 3; Geometry.TYPECODE_MULTILINESTRING = 4; Geometry.TYPECODE_POLYGON = 5; Geometry.TYPECODE_MULTIPOLYGON = 6; Geometry.TYPECODE_GEOMETRYCOLLECTION = 7; Geometry.TYPENAME_POINT = 'Point'; Geometry.TYPENAME_MULTIPOINT = 'MultiPoint'; Geometry.TYPENAME_LINESTRING = 'LineString'; Geometry.TYPENAME_LINEARRING = 'LinearRing'; Geometry.TYPENAME_MULTILINESTRING = 'MultiLineString'; Geometry.TYPENAME_POLYGON = 'Polygon'; Geometry.TYPENAME_MULTIPOLYGON = 'MultiPolygon'; Geometry.TYPENAME_GEOMETRYCOLLECTION = 'GeometryCollection'; Geometry.geometryChangedFilter = { get interfaces_() { return [GeometryComponentFilter] }, filter(geom) { geom.geometryChangedAction(); } }; class Location { static toLocationSymbol(locationValue) { switch (locationValue) { case Location.EXTERIOR: return 'e' case Location.BOUNDARY: return 'b' case Location.INTERIOR: return 'i' case Location.NONE: return '-' } throw new IllegalArgumentException('Unknown location value: ' + locationValue) } } Location.INTERIOR = 0; Location.BOUNDARY = 1; Location.EXTERIOR = 2; Location.NONE = -1; /** * @see http://download.oracle.com/javase/6/docs/api/java/util/Collection.html */ class Collection { /** * Ensures that this collection contains the specified element (optional * operation). * @param {Object} e * @return {boolean} */ add() { } /** * Appends all of the elements in the specified collection to the end of this * list, in the order that they are returned by the specified collection's * iterator (optional operation). * @param {javascript.util.Collection} c * @return {boolean} */ addAll() { } /** * Returns true if this collection contains no elements. * @return {boolean} */ isEmpty() { } /** * Returns an iterator over the elements in this collection. * @return {javascript.util.Iterator} */ iterator() { } /** * Returns an iterator over the elements in this collection. * @return {number} */ size() { } /** * Returns an array containing all of the elements in this collection. * @return {Array} */ toArray() { } /** * Removes a single instance of the specified element from this collection if it * is present. (optional) * @param {Object} e * @return {boolean} */ remove() { } } class NoSuchElementException extends Exception { constructor(message) { super(message); this.name = Object.keys({ NoSuchElementException })[0]; } } class UnsupportedOperationException extends Exception { constructor(message) { super(message); this.name = Object.keys({ UnsupportedOperationException })[0]; } } /** * @see http://download.oracle.com/javase/6/docs/api/java/util/Set.html * * @extends {Collection} * @constructor * @private */ class Set extends Collection { /** * Returns true if this set contains the specified element. More formally, * returns true if and only if this set contains an element e such that (o==null ? * e==null : o.equals(e)). * @param {Object} e * @return {boolean} */ contains() { } } /** * @see http://docs.oracle.com/javase/6/docs/api/java/util/HashSet.html */ class HashSet extends Set { constructor(o) { super(); this.map = new Map(); if (o instanceof Collection) this.addAll(o); } contains(o) { const hashCode = o.hashCode ? o.hashCode() : o; if (this.map.has(hashCode)) return true return false } add(o) { const hashCode = o.hashCode ? o.hashCode() : o; if (this.map.has(hashCode)) return false return !!this.map.set(hashCode, o) } addAll(c) { for (const e of c) this.add(e); return true } remove() { throw new UnsupportedOperationException() } size() { return this.map.size } isEmpty() { return this.map.size === 0 } toArray() { return Array.from(this.map.values()) } iterator() { return new Iterator$3(this.map) } [Symbol.iterator]() { return this.map } } class Iterator$3 { constructor(map) { this.iterator = map.values(); const { done, value } = this.iterator.next(); this.done = done; this.value = value; } next() { if (this.done) throw new NoSuchElementException() const current = this.value; const { done, value } = this.iterator.next(); this.done = done; this.value = value; return current } hasNext() { return !this.done } remove() { throw new UnsupportedOperationException() } } class Position { static opposite(position) { if (position === Position.LEFT) return Position.RIGHT if (position === Position.RIGHT) return Position.LEFT return position } } Position.ON = 0; Position.LEFT = 1; Position.RIGHT = 2; class EmptyStackException extends Exception { constructor(message) { super(message); this.name = Object.keys({ EmptyStackException })[0]; } } class IndexOutOfBoundsException extends Exception { constructor(message) { super(message); this.name = Object.keys({ IndexOutOfBoundsException })[0]; } } /** * @see http://download.oracle.com/javase/6/docs/api/java/util/List.html */ class List extends Collection { /** * Returns the element at the specified position in this list. * @param {number} index * @return {Object} */ get() { } /** * Replaces the element at the specified position in this list with the * specified element (optional operation). * @param {number} index * @param {Object} e * @return {Object} */ set() { } /** * Returns true if this collection contains no elements. * @return {boolean} */ isEmpty() { } } /** * @see http://download.oracle.com/javase/6/docs/api/java/util/Stack.html */ class Stack extends List { constructor() { super(); this.array = []; } add(e) { this.array.push(e); return true } get(index) { if (index < 0 || index >= this.size()) throw new IndexOutOfBoundsException() return this.array[index] } /** * Pushes an item onto the top of this stack. * @param {Object} e * @return {Object} */ push(e) { this.array.push(e); return e } /** * Removes the object at the top of this stack and returns that object as the value of this function. * @return {Object} */ pop() { if (this.array.length === 0) throw new EmptyStackException() return this.array.pop() } /** * Looks at the object at the top of this stack without removing it from the * stack. * @return {Object} */ peek() { if (this.array.length === 0) throw new EmptyStackException() return this.array[this.array.length - 1] } /** * Tests if this stack is empty. * @return {boolean} true if and only if this stack contains no items; false * otherwise. */ empty() { return this.array.length === 0 } /** * @return {boolean} */ isEmpty() { return this.empty() } /** * Returns the 1-based position where an object is on this stack. If the object * o occurs as an item in this stack, this method returns the distance from the * top of the stack of the occurrence nearest the top of the stack; the topmost * item on the stack is considered to be at distance 1. The equals method is * used to compare o to the items in this stack. * * NOTE: does not currently actually use equals. (=== is used) * * @param {Object} o * @return {number} the 1-based position from the top of the stack where the * object is located; the return value -1 indicates that the object is * not on the stack. */ search(o) { return this.array.indexOf(o) } /** * @return {number} */ size() { return this.array.length } /** * @return {Array} */ toArray() { return this.array.slice() } } function hasInterface(o, i) { return o.interfaces_ && o.interfaces_.indexOf(i) > -1 } class StringBuffer { constructor(str) { this.str = str; } append(e) { this.str += e; } setCharAt(i, c) { this.str = this.str.substr(0, i) + c + this.str.substr(i + 1); } toString() { return this.str } } class Integer { constructor(value) { this.value = value; } intValue() { return this.value } compareTo(o) { if (this.value < o) return -1 if (this.value > o) return 1 return 0 } static compare(x, y) { if (x < y) return -1 if (x > y) return 1 return 0 } static isNan(n) { return Number.isNaN(n) } static valueOf(value) { return new Integer(value) } } class Character { static isWhitespace(c) { return ((c <= 32 && c >= 0) || c === 127) } static toUpperCase(c) { return c.toUpperCase() } } class DD { constructor() { DD.constructor_.apply(this, arguments); } static constructor_() { this._hi = 0.0; this._lo = 0.0; if (arguments.length === 0) { this.init(0.0); } else if (arguments.length === 1) { if (typeof arguments[0] === 'number') { const x = arguments[0]; this.init(x); } else if (arguments[0] instanceof DD) { const dd = arguments[0]; this.init(dd); } else if (typeof arguments[0] === 'string') { const str = arguments[0]; DD.constructor_.call(this, DD.parse(str)); } } else if (arguments.length === 2) { const hi = arguments[0], lo = arguments[1]; this.init(hi, lo); } } static determinant() { if (typeof arguments[3] === 'number' && (typeof arguments[2] === 'number' && (typeof arguments[0] === 'number' && typeof arguments[1] === 'number'))) { const x1 = arguments[0], y1 = arguments[1], x2 = arguments[2], y2 = arguments[3]; return DD.determinant(DD.valueOf(x1), DD.valueOf(y1), DD.valueOf(x2), DD.valueOf(y2)) } else if (arguments[3] instanceof DD && (arguments[2] instanceof DD && (arguments[0] instanceof DD && arguments[1] instanceof DD))) { const x1 = arguments[0], y1 = arguments[1], x2 = arguments[2], y2 = arguments[3]; const det = x1.multiply(y2).selfSubtract(y1.multiply(x2)); return det } } static sqr(x) { return DD.valueOf(x).selfMultiply(x) } static valueOf() { if (typeof arguments[0] === 'string') { const str = arguments[0]; return DD.parse(str) } else if (typeof arguments[0] === 'number') { const x = arguments[0]; return new DD(x) } } static sqrt(x) { return DD.valueOf(x).sqrt() } static parse(str) { let i = 0; const strlen = str.length; while (Character.isWhitespace(str.charAt(i))) i++; let isNegative = false; if (i < strlen) { const signCh = str.charAt(i); if (signCh === '-' || signCh === '+') { i++; if (signCh === '-') isNegative = true; } } const val = new DD(); let numDigits = 0; let numBeforeDec = 0; let exp = 0; let hasDecimalChar = false; while (true) { if (i >= strlen) break const ch = str.charAt(i); i++; if (Character.isDigit(ch)) { const d = ch - '0'; val.selfMultiply(DD.TEN); val.selfAdd(d); numDigits++; continue } if (ch === '.') { numBeforeDec = numDigits; hasDecimalChar = true; continue } if (ch === 'e' || ch === 'E') { const expStr = str.substring(i); try { exp = Integer.parseInt(expStr); } catch (ex) { if (ex instanceof NumberFormatException) throw new NumberFormatException('Invalid exponent ' + expStr + ' in string ' + str) else throw ex } finally {} break } throw new NumberFormatException('Unexpected character \'' + ch + '\' at position ' + i + ' in string ' + str) } let val2 = val; if (!hasDecimalChar) numBeforeDec = numDigits; const numDecPlaces = numDigits - numBeforeDec - exp; if (numDecPlaces === 0) { val2 = val; } else if (numDecPlaces > 0) { const scale = DD.TEN.pow(numDecPlaces); val2 = val.divide(scale); } else if (numDecPlaces < 0) { const scale = DD.TEN.pow(-numDecPlaces); val2 = val.multiply(scale); } if (isNegative) return val2.negate() return val2 } static createNaN() { return new DD(Double.NaN, Double.NaN) } static copy(dd) { return new DD(dd) } static magnitude(x) { const xAbs = Math.abs(x); const xLog10 = Math.log(xAbs) / Math.log(10); let xMag = Math.trunc(Math.floor(xLog10)); const xApprox = Math.pow(10, xMag); if (xApprox * 10 <= xAbs) xMag += 1; return xMag } static stringOfChar(ch, len) { const buf = new StringBuffer(); for (let i = 0; i < len; i++) buf.append(ch); return buf.toString() } le(y) { return this._hi < y._hi || this._hi === y._hi && this._lo <= y._lo } extractSignificantDigits(insertDecimalPoint, magnitude) { let y = this.abs(); let mag = DD.magnitude(y._hi); const scale = DD.TEN.pow(mag); y = y.divide(scale); if (y.gt(DD.TEN)) { y = y.divide(DD.TEN); mag += 1; } e