Full Code of pissang/geometry-extrude for AI

master b658c3d3f06b cached
36 files
2.9 MB
773.9k tokens
321 symbols
1 requests
Download .txt
Showing preview only (3,096K chars total). Download the full file or copy to clipboard to get everything.
Repository: pissang/geometry-extrude
Branch: master
Commit: b658c3d3f06b
Files: 36
Total size: 2.9 MB

Directory structure:
gitextract_sfii8sv8/

├── .babelrc
├── .gitattributes
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── dist/
│   └── geometry-extrude.js
├── index.d.ts
├── package.json
├── rollup.config.js
├── src/
│   ├── main.js
│   ├── math.js
│   └── simplify.js
└── test/
    ├── asset/
    │   ├── buildings-ny.geojson
    │   ├── pisa.hdr
    │   └── street.geojson
    ├── distortion.html
    ├── extrude-bevel.html
    ├── extrude-dude.html
    ├── extrude-exclude-bottom.html
    ├── extrude-hole.html
    ├── extrude-multipolygon.html
    ├── extrude-normal.html
    ├── extrude-polyline.html
    ├── extrude-simple.html
    ├── extrude-simplify.html
    ├── extrude-star.html
    ├── extrude-uv.html
    ├── geojson.html
    ├── lib/
    │   ├── claygl-advanced-renderer.js
    │   ├── claygl.js
    │   └── dat.gui.js
    ├── polygon-offset.html
    ├── polyline-offset.html
    ├── slerp.html
    └── street.html

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
    "presets": [
      [
        "@babel/preset-env",
        {
          "modules": false
        }
      ]
    ]
  }

================================================
FILE: .gitattributes
================================================
# Auto detect text files and perform LF normalization
* text=auto


================================================
FILE: .gitignore
================================================
node_modules
package-lock.json

================================================
FILE: .npmignore
================================================
test
screenshot

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 pissang

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# Geometry Extrude

A small and fast JavaScript library for extruding 2D polygons and polylines to 3D meshes. It depends on [earcut](https://github.com/mapbox/earcut) to do triangulation.

## Features

+ Extrude polygons with holes.

+ Extrude polylines with specific line thickness.

+ Generate `position` / `uv` / `normal` / `indices` TypedArray.

+ Support bevel style.

## Basic Usage

Install with npm

```
npm i geometry-extrude
```

Extrude a simple square with hole

```js
import {extrudePolygon} from 'geometry-extrude';
const squareWithHole = [
    [[0, 0], [10, 0], [10, 10], [0, 10]],
    // Hole
    [[2, 2], [8, 2], [8, 8], [2, 8]]
];
const {indices, position, uv, normal} = extrudePolygon([squareWithHole], {
    depth: 2
});
```

### Use with ClayGL

```js
const {indices, position, uv, normal} = extrudePolygon(squareWithHole);
const geometry = new clay.Geometry();
geometry.attributes.position.value = position;
geometry.attributes.texcoord0.value = uv;
geometry.attributes.normal.value = normal;
geometry.indices = indices;
```

### Use with ThreeJS

```js
const {indices, position, uv, normal} = extrudePolygon(squareWithHole);
const geometry = new THREE.BufferGeometry();
geometry.addAttribute('position', new THREE.Float32BufferAttribute(position, 3));
geometry.addAttribute('normal', new THREE.Float32BufferAttribute(normal, 3));
geometry.setIndex(new THREE.Uint16BufferAttribute(indices, 1));
```

[Example](https://github.com/pissang/geometry-extrude-example-threejs)

### Use with regl

```js
const {indices, position, uv, normal} = extrudePolygon(squareWithHole);
const draw = regl({
    frag: `...`,
    vert: `...`,

    attributes: {
        position: position,
        uv: uv,
        normal: norma
    },

    elements: indices
});
```

[Example](https://github.com/pissang/geometry-extrude-example-regl)

## Full API List

### extrudePolygon

```js
extrudePolygon(
    // polygons same with coordinates of MultiPolygon type geometry in GeoJSON
    // See http://wiki.geojson.org/GeoJSON_draft_version_6#MultiPolygon
    polygons: GeoJSONMultiPolygonGeometry,
    // Options of extrude
    opts: {
        // Can be a constant value, or a function.
        // Default to be 1.
        depth?: ((idx: number) => number) | number,
        // Size of bevel, default to be 0, which is no bevel.
        bevelSize?: number,
        // Segments of bevel, default to be 2. Larger value will lead to smoother bevel.
        bevelSegments?: number,
        // Polygon or polyline simplification tolerance. Default to be 0.
        // Use https://www.npmjs.com/package/simplify-js to do the simplification. Same with the tolerance parameter in it. The unit is same with depth and bevelSize
        simplify?: number,
        // If has smooth side, default to be false.
        smoothSide?: boolean,
        // If has smooth bevel, default to be false.
        smoothBevel?: boolean,
        // If exclude bottom faces, default to be false.
        // Usefull when bottom side can't be seen.
        excludeBottom?: boolean,
        // Transform the polygon to fit this rect.
        // Will keep polygon aspect if only width or height is given.
        fitRect?: {x?: number, y?: number, width?: number: height?: number},
        // Translate the polygon. Default to be [0, 0]
        // Will be ignored if fitRect is given.
        translate?: ArrayLike<number>,
        // Scale the polygon. Default to be [1, 1]
        // Will be ignored if fitRect is given.
        scale?: ArrayLike<number>
    }
) => {
    indices: Uint16Array|Uint32Array,
    position: Float32Array,
    normal: Float32Array,
    uv: Float32Array,
    boundingRect: {x: number, y: number, width: number, height: number}
}
```

### extrudePolyline

```typescript
extrudePolyline(
    // polylines same with coordinates of MultiLineString type geometry in GeoJSON
    // See http://wiki.geojson.org/GeoJSON_draft_version_6#MultiLineString
    polylines: GeoJSONMultiLineStringGeometry,
    // Options of extrude
    opts: {
        ////// Extended from opts in extrudePolygon

        // Thickness of line, default to be 1
        lineWidth?: number,
        // default to be 2
        miterLimit?: number
    }
) => {
    indices: Uint16Array|Uint32Array,
    position: Float32Array,
    normal: Float32Array,
    uv: Float32Array,
    boundingRect: {x: number, y: number, width: number, height: number}
}
```

### extrudeGeoJSON

```typescript
extrudeGeoJSON(
    // Extrude geojson with Polygon/LineString/MultiPolygon/MultiLineString geometries.
    geojson: GeoJSON,
    // Options of extrude
    opts: {
        ////// Extended from opts in extrudePolygon

        // Can be a constant value, or a function with parameter of each feature in geojson.
        // Default to be 1.
        depth?: ((feature: GeoJSONFeature) => number) | number
        // Thickness of line, default to be 1
        lineWidth?: number,
        // default to be 2
        miterLimit?: number
    }
) => {
    // Same result with extrudePolygon
    polygon: Object,
    // Same result with extrudePolyline
    polyline: Object
}
```


================================================
FILE: dist/geometry-extrude.js
================================================
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
    typeof define === 'function' && define.amd ? define(['exports'], factory) :
    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.geometryExtrude = {}));
})(this, (function (exports) { 'use strict';

    var earcut$2 = {exports: {}};

    earcut$2.exports = earcut;
    earcut$2.exports.default = earcut;

    function earcut(data, holeIndices, dim) {

        dim = dim || 2;

        var hasHoles = holeIndices && holeIndices.length,
            outerLen = hasHoles ? holeIndices[0] * dim : data.length,
            outerNode = linkedList(data, 0, outerLen, dim, true),
            triangles = [];

        if (!outerNode || outerNode.next === outerNode.prev) return triangles;

        var minX, minY, maxX, maxY, x, y, invSize;

        if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);

        // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox
        if (data.length > 80 * dim) {
            minX = maxX = data[0];
            minY = maxY = data[1];

            for (var i = dim; i < outerLen; i += dim) {
                x = data[i];
                y = data[i + 1];
                if (x < minX) minX = x;
                if (y < minY) minY = y;
                if (x > maxX) maxX = x;
                if (y > maxY) maxY = y;
            }

            // minX, minY and invSize are later used to transform coords into integers for z-order calculation
            invSize = Math.max(maxX - minX, maxY - minY);
            invSize = invSize !== 0 ? 1 / invSize : 0;
        }

        earcutLinked(outerNode, triangles, dim, minX, minY, invSize);

        return triangles;
    }

    // create a circular doubly linked list from polygon points in the specified winding order
    function linkedList(data, start, end, dim, clockwise) {
        var i, last;

        if (clockwise === (signedArea(data, start, end, dim) > 0)) {
            for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
        } else {
            for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
        }

        if (last && equals(last, last.next)) {
            removeNode(last);
            last = last.next;
        }

        return last;
    }

    // eliminate colinear or duplicate points
    function filterPoints(start, end) {
        if (!start) return start;
        if (!end) end = start;

        var p = start,
            again;
        do {
            again = false;

            if (!p.steiner && (equals(p, p.next) || area$1(p.prev, p, p.next) === 0)) {
                removeNode(p);
                p = end = p.prev;
                if (p === p.next) break;
                again = true;

            } else {
                p = p.next;
            }
        } while (again || p !== end);

        return end;
    }

    // main ear slicing loop which triangulates a polygon (given as a linked list)
    function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
        if (!ear) return;

        // interlink polygon nodes in z-order
        if (!pass && invSize) indexCurve(ear, minX, minY, invSize);

        var stop = ear,
            prev, next;

        // iterate through ears, slicing them one by one
        while (ear.prev !== ear.next) {
            prev = ear.prev;
            next = ear.next;

            if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
                // cut off the triangle
                triangles.push(prev.i / dim);
                triangles.push(ear.i / dim);
                triangles.push(next.i / dim);

                removeNode(ear);

                // skipping the next vertex leads to less sliver triangles
                ear = next.next;
                stop = next.next;

                continue;
            }

            ear = next;

            // if we looped through the whole remaining polygon and can't find any more ears
            if (ear === stop) {
                // try filtering points and slicing again
                if (!pass) {
                    earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);

                // if this didn't work, try curing all small self-intersections locally
                } else if (pass === 1) {
                    ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
                    earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);

                // as a last resort, try splitting the remaining polygon into two
                } else if (pass === 2) {
                    splitEarcut(ear, triangles, dim, minX, minY, invSize);
                }

                break;
            }
        }
    }

    // check whether a polygon node forms a valid ear with adjacent nodes
    function isEar(ear) {
        var a = ear.prev,
            b = ear,
            c = ear.next;

        if (area$1(a, b, c) >= 0) return false; // reflex, can't be an ear

        // now make sure we don't have other points inside the potential ear
        var p = ear.next.next;

        while (p !== ear.prev) {
            if (pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                area$1(p.prev, p, p.next) >= 0) return false;
            p = p.next;
        }

        return true;
    }

    function isEarHashed(ear, minX, minY, invSize) {
        var a = ear.prev,
            b = ear,
            c = ear.next;

        if (area$1(a, b, c) >= 0) return false; // reflex, can't be an ear

        // triangle bbox; min & max are calculated like this for speed
        var minTX = a.x < b.x ? (a.x < c.x ? a.x : c.x) : (b.x < c.x ? b.x : c.x),
            minTY = a.y < b.y ? (a.y < c.y ? a.y : c.y) : (b.y < c.y ? b.y : c.y),
            maxTX = a.x > b.x ? (a.x > c.x ? a.x : c.x) : (b.x > c.x ? b.x : c.x),
            maxTY = a.y > b.y ? (a.y > c.y ? a.y : c.y) : (b.y > c.y ? b.y : c.y);

        // z-order range for the current triangle bbox;
        var minZ = zOrder(minTX, minTY, minX, minY, invSize),
            maxZ = zOrder(maxTX, maxTY, minX, minY, invSize);

        var p = ear.prevZ,
            n = ear.nextZ;

        // look for points inside the triangle in both directions
        while (p && p.z >= minZ && n && n.z <= maxZ) {
            if (p !== ear.prev && p !== ear.next &&
                pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                area$1(p.prev, p, p.next) >= 0) return false;
            p = p.prevZ;

            if (n !== ear.prev && n !== ear.next &&
                pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
                area$1(n.prev, n, n.next) >= 0) return false;
            n = n.nextZ;
        }

        // look for remaining points in decreasing z-order
        while (p && p.z >= minZ) {
            if (p !== ear.prev && p !== ear.next &&
                pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, p.x, p.y) &&
                area$1(p.prev, p, p.next) >= 0) return false;
            p = p.prevZ;
        }

        // look for remaining points in increasing z-order
        while (n && n.z <= maxZ) {
            if (n !== ear.prev && n !== ear.next &&
                pointInTriangle(a.x, a.y, b.x, b.y, c.x, c.y, n.x, n.y) &&
                area$1(n.prev, n, n.next) >= 0) return false;
            n = n.nextZ;
        }

        return true;
    }

    // go through all polygon nodes and cure small local self-intersections
    function cureLocalIntersections(start, triangles, dim) {
        var p = start;
        do {
            var a = p.prev,
                b = p.next.next;

            if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {

                triangles.push(a.i / dim);
                triangles.push(p.i / dim);
                triangles.push(b.i / dim);

                // remove two nodes involved
                removeNode(p);
                removeNode(p.next);

                p = start = b;
            }
            p = p.next;
        } while (p !== start);

        return filterPoints(p);
    }

    // try splitting polygon into two and triangulate them independently
    function splitEarcut(start, triangles, dim, minX, minY, invSize) {
        // look for a valid diagonal that divides the polygon into two
        var a = start;
        do {
            var b = a.next.next;
            while (b !== a.prev) {
                if (a.i !== b.i && isValidDiagonal(a, b)) {
                    // split the polygon in two by the diagonal
                    var c = splitPolygon(a, b);

                    // filter colinear points around the cuts
                    a = filterPoints(a, a.next);
                    c = filterPoints(c, c.next);

                    // run earcut on each half
                    earcutLinked(a, triangles, dim, minX, minY, invSize);
                    earcutLinked(c, triangles, dim, minX, minY, invSize);
                    return;
                }
                b = b.next;
            }
            a = a.next;
        } while (a !== start);
    }

    // link every hole into the outer loop, producing a single-ring polygon without holes
    function eliminateHoles(data, holeIndices, outerNode, dim) {
        var queue = [],
            i, len, start, end, list;

        for (i = 0, len = holeIndices.length; i < len; i++) {
            start = holeIndices[i] * dim;
            end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
            list = linkedList(data, start, end, dim, false);
            if (list === list.next) list.steiner = true;
            queue.push(getLeftmost(list));
        }

        queue.sort(compareX);

        // process holes from left to right
        for (i = 0; i < queue.length; i++) {
            outerNode = eliminateHole(queue[i], outerNode);
            outerNode = filterPoints(outerNode, outerNode.next);
        }

        return outerNode;
    }

    function compareX(a, b) {
        return a.x - b.x;
    }

    // find a bridge between vertices that connects hole with an outer ring and and link it
    function eliminateHole(hole, outerNode) {
        var bridge = findHoleBridge(hole, outerNode);
        if (!bridge) {
            return outerNode;
        }

        var bridgeReverse = splitPolygon(bridge, hole);

        // filter collinear points around the cuts
        var filteredBridge = filterPoints(bridge, bridge.next);
        filterPoints(bridgeReverse, bridgeReverse.next);

        // Check if input node was removed by the filtering
        return outerNode === bridge ? filteredBridge : outerNode;
    }

    // David Eberly's algorithm for finding a bridge between hole and outer polygon
    function findHoleBridge(hole, outerNode) {
        var p = outerNode,
            hx = hole.x,
            hy = hole.y,
            qx = -Infinity,
            m;

        // find a segment intersected by a ray from the hole's leftmost point to the left;
        // segment's endpoint with lesser x will be potential connection point
        do {
            if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
                var x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
                if (x <= hx && x > qx) {
                    qx = x;
                    if (x === hx) {
                        if (hy === p.y) return p;
                        if (hy === p.next.y) return p.next;
                    }
                    m = p.x < p.next.x ? p : p.next;
                }
            }
            p = p.next;
        } while (p !== outerNode);

        if (!m) return null;

        if (hx === qx) return m; // hole touches outer segment; pick leftmost endpoint

        // look for points inside the triangle of hole point, segment intersection and endpoint;
        // if there are no points found, we have a valid connection;
        // otherwise choose the point of the minimum angle with the ray as connection point

        var stop = m,
            mx = m.x,
            my = m.y,
            tanMin = Infinity,
            tan;

        p = m;

        do {
            if (hx >= p.x && p.x >= mx && hx !== p.x &&
                    pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {

                tan = Math.abs(hy - p.y) / (hx - p.x); // tangential

                if (locallyInside(p, hole) &&
                    (tan < tanMin || (tan === tanMin && (p.x > m.x || (p.x === m.x && sectorContainsSector(m, p)))))) {
                    m = p;
                    tanMin = tan;
                }
            }

            p = p.next;
        } while (p !== stop);

        return m;
    }

    // whether sector in vertex m contains sector in vertex p in the same coordinates
    function sectorContainsSector(m, p) {
        return area$1(m.prev, m, p.prev) < 0 && area$1(p.next, m, m.next) < 0;
    }

    // interlink polygon nodes in z-order
    function indexCurve(start, minX, minY, invSize) {
        var p = start;
        do {
            if (p.z === null) p.z = zOrder(p.x, p.y, minX, minY, invSize);
            p.prevZ = p.prev;
            p.nextZ = p.next;
            p = p.next;
        } while (p !== start);

        p.prevZ.nextZ = null;
        p.prevZ = null;

        sortLinked(p);
    }

    // Simon Tatham's linked list merge sort algorithm
    // http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html
    function sortLinked(list) {
        var i, p, q, e, tail, numMerges, pSize, qSize,
            inSize = 1;

        do {
            p = list;
            list = null;
            tail = null;
            numMerges = 0;

            while (p) {
                numMerges++;
                q = p;
                pSize = 0;
                for (i = 0; i < inSize; i++) {
                    pSize++;
                    q = q.nextZ;
                    if (!q) break;
                }
                qSize = inSize;

                while (pSize > 0 || (qSize > 0 && q)) {

                    if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
                        e = p;
                        p = p.nextZ;
                        pSize--;
                    } else {
                        e = q;
                        q = q.nextZ;
                        qSize--;
                    }

                    if (tail) tail.nextZ = e;
                    else list = e;

                    e.prevZ = tail;
                    tail = e;
                }

                p = q;
            }

            tail.nextZ = null;
            inSize *= 2;

        } while (numMerges > 1);

        return list;
    }

    // z-order of a point given coords and inverse of the longer side of data bbox
    function zOrder(x, y, minX, minY, invSize) {
        // coords are transformed into non-negative 15-bit integer range
        x = 32767 * (x - minX) * invSize;
        y = 32767 * (y - minY) * invSize;

        x = (x | (x << 8)) & 0x00FF00FF;
        x = (x | (x << 4)) & 0x0F0F0F0F;
        x = (x | (x << 2)) & 0x33333333;
        x = (x | (x << 1)) & 0x55555555;

        y = (y | (y << 8)) & 0x00FF00FF;
        y = (y | (y << 4)) & 0x0F0F0F0F;
        y = (y | (y << 2)) & 0x33333333;
        y = (y | (y << 1)) & 0x55555555;

        return x | (y << 1);
    }

    // find the leftmost node of a polygon ring
    function getLeftmost(start) {
        var p = start,
            leftmost = start;
        do {
            if (p.x < leftmost.x || (p.x === leftmost.x && p.y < leftmost.y)) leftmost = p;
            p = p.next;
        } while (p !== start);

        return leftmost;
    }

    // check if a point lies within a convex triangle
    function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
        return (cx - px) * (ay - py) - (ax - px) * (cy - py) >= 0 &&
               (ax - px) * (by - py) - (bx - px) * (ay - py) >= 0 &&
               (bx - px) * (cy - py) - (cx - px) * (by - py) >= 0;
    }

    // check if a diagonal between two polygon nodes is valid (lies in polygon interior)
    function isValidDiagonal(a, b) {
        return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
               (locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
                (area$1(a.prev, a, b.prev) || area$1(a, b.prev, b)) || // does not create opposite-facing sectors
                equals(a, b) && area$1(a.prev, a, a.next) > 0 && area$1(b.prev, b, b.next) > 0); // special zero-length case
    }

    // signed area of a triangle
    function area$1(p, q, r) {
        return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
    }

    // check if two points are equal
    function equals(p1, p2) {
        return p1.x === p2.x && p1.y === p2.y;
    }

    // check if two segments intersect
    function intersects(p1, q1, p2, q2) {
        var o1 = sign(area$1(p1, q1, p2));
        var o2 = sign(area$1(p1, q1, q2));
        var o3 = sign(area$1(p2, q2, p1));
        var o4 = sign(area$1(p2, q2, q1));

        if (o1 !== o2 && o3 !== o4) return true; // general case

        if (o1 === 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1
        if (o2 === 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1
        if (o3 === 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2
        if (o4 === 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2

        return false;
    }

    // for collinear points p, q, r, check if point q lies on segment pr
    function onSegment(p, q, r) {
        return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
    }

    function sign(num) {
        return num > 0 ? 1 : num < 0 ? -1 : 0;
    }

    // check if a polygon diagonal intersects any polygon segments
    function intersectsPolygon(a, b) {
        var p = a;
        do {
            if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i &&
                    intersects(p, p.next, a, b)) return true;
            p = p.next;
        } while (p !== a);

        return false;
    }

    // check if a polygon diagonal is locally inside the polygon
    function locallyInside(a, b) {
        return area$1(a.prev, a, a.next) < 0 ?
            area$1(a, b, a.next) >= 0 && area$1(a, a.prev, b) >= 0 :
            area$1(a, b, a.prev) < 0 || area$1(a, a.next, b) < 0;
    }

    // check if the middle point of a polygon diagonal is inside the polygon
    function middleInside(a, b) {
        var p = a,
            inside = false,
            px = (a.x + b.x) / 2,
            py = (a.y + b.y) / 2;
        do {
            if (((p.y > py) !== (p.next.y > py)) && p.next.y !== p.y &&
                    (px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x))
                inside = !inside;
            p = p.next;
        } while (p !== a);

        return inside;
    }

    // link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits polygon into two;
    // if one belongs to the outer ring and another to a hole, it merges it into a single ring
    function splitPolygon(a, b) {
        var a2 = new Node(a.i, a.x, a.y),
            b2 = new Node(b.i, b.x, b.y),
            an = a.next,
            bp = b.prev;

        a.next = b;
        b.prev = a;

        a2.next = an;
        an.prev = a2;

        b2.next = a2;
        a2.prev = b2;

        bp.next = b2;
        b2.prev = bp;

        return b2;
    }

    // create a node and optionally link it with previous one (in a circular doubly linked list)
    function insertNode(i, x, y, last) {
        var p = new Node(i, x, y);

        if (!last) {
            p.prev = p;
            p.next = p;

        } else {
            p.next = last.next;
            p.prev = last;
            last.next.prev = p;
            last.next = p;
        }
        return p;
    }

    function removeNode(p) {
        p.next.prev = p.prev;
        p.prev.next = p.next;

        if (p.prevZ) p.prevZ.nextZ = p.nextZ;
        if (p.nextZ) p.nextZ.prevZ = p.prevZ;
    }

    function Node(i, x, y) {
        // vertex index in coordinates array
        this.i = i;

        // vertex coordinates
        this.x = x;
        this.y = y;

        // previous and next vertex nodes in a polygon ring
        this.prev = null;
        this.next = null;

        // z-order curve value
        this.z = null;

        // previous and next nodes in z-order
        this.prevZ = null;
        this.nextZ = null;

        // indicates whether this is a steiner point
        this.steiner = false;
    }

    // return a percentage difference between the polygon area and its triangulation area;
    // used to verify correctness of triangulation
    earcut.deviation = function (data, holeIndices, dim, triangles) {
        var hasHoles = holeIndices && holeIndices.length;
        var outerLen = hasHoles ? holeIndices[0] * dim : data.length;

        var polygonArea = Math.abs(signedArea(data, 0, outerLen, dim));
        if (hasHoles) {
            for (var i = 0, len = holeIndices.length; i < len; i++) {
                var start = holeIndices[i] * dim;
                var end = i < len - 1 ? holeIndices[i + 1] * dim : data.length;
                polygonArea -= Math.abs(signedArea(data, start, end, dim));
            }
        }

        var trianglesArea = 0;
        for (i = 0; i < triangles.length; i += 3) {
            var a = triangles[i] * dim;
            var b = triangles[i + 1] * dim;
            var c = triangles[i + 2] * dim;
            trianglesArea += Math.abs(
                (data[a] - data[c]) * (data[b + 1] - data[a + 1]) -
                (data[a] - data[b]) * (data[c + 1] - data[a + 1]));
        }

        return polygonArea === 0 && trianglesArea === 0 ? 0 :
            Math.abs((trianglesArea - polygonArea) / polygonArea);
    };

    function signedArea(data, start, end, dim) {
        var sum = 0;
        for (var i = start, j = end - dim; i < end; i += dim) {
            sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
            j = i;
        }
        return sum;
    }

    // turn a polygon in a multi-dimensional array form (e.g. as in GeoJSON) into a form Earcut accepts
    earcut.flatten = function (data) {
        var dim = data[0][0].length,
            result = {vertices: [], holes: [], dimensions: dim},
            holeIndex = 0;

        for (var i = 0; i < data.length; i++) {
            for (var j = 0; j < data[i].length; j++) {
                for (var d = 0; d < dim; d++) result.vertices.push(data[i][j][d]);
            }
            if (i > 0) {
                holeIndex += data[i - 1].length;
                result.holes.push(holeIndex);
            }
        }
        return result;
    };

    var earcut$1 = earcut$2.exports;

    /*
     (c) 2017, Vladimir Agafonkin
     Simplify.js, a high-performance JS polyline simplification library
     mourner.github.io/simplify-js
    */
    // to suit your point format, run search/replace for '.x' and '.y';
    // for 3D version, see 3d branch (configurability would draw significant performance overhead)
    // square distance between 2 points
    function getSqDist(p1, p2) {
      var dx = p1[0] - p2[0],
          dy = p1[1] - p2[1];
      return dx * dx + dy * dy;
    } // square distance from a point to a segment


    function getSqSegDist(p, p1, p2) {
      var x = p1[0],
          y = p1[1],
          dx = p2[0] - x,
          dy = p2[1] - y;

      if (dx !== 0 || dy !== 0) {
        var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);

        if (t > 1) {
          x = p2[0];
          y = p2[1];
        } else if (t > 0) {
          x += dx * t;
          y += dy * t;
        }
      }

      dx = p[0] - x;
      dy = p[1] - y;
      return dx * dx + dy * dy;
    } // rest of the code doesn't care about point format
    // basic distance-based simplification


    function simplifyRadialDist(points, sqTolerance) {
      var prevPoint = points[0],
          newPoints = [prevPoint],
          point;

      for (var i = 1, len = points.length; i < len; i++) {
        point = points[i];

        if (getSqDist(point, prevPoint) > sqTolerance) {
          newPoints.push(point);
          prevPoint = point;
        }
      }

      if (prevPoint !== point) newPoints.push(point);
      return newPoints;
    }

    function simplifyDPStep(points, first, last, sqTolerance, simplified) {
      var maxSqDist = sqTolerance,
          index;

      for (var i = first + 1; i < last; i++) {
        var sqDist = getSqSegDist(points[i], points[first], points[last]);

        if (sqDist > maxSqDist) {
          index = i;
          maxSqDist = sqDist;
        }
      }

      if (maxSqDist > sqTolerance) {
        if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
        simplified.push(points[index]);
        if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
      }
    } // simplification using Ramer-Douglas-Peucker algorithm


    function simplifyDouglasPeucker(points, sqTolerance) {
      var last = points.length - 1;
      var simplified = [points[0]];
      simplifyDPStep(points, 0, last, sqTolerance, simplified);
      simplified.push(points[last]);
      return simplified;
    } // both algorithms combined for awesome performance


    function simplify(points, tolerance, highestQuality) {
      if (points.length <= 2) return points;
      var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;
      points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
      points = simplifyDouglasPeucker(points, sqTolerance);
      return points;
    }

    function dot(v1, v2) {
      return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
    }
    function v2Dot(v1, v2) {
      return v1[0] * v2[0] + v1[1] * v2[1];
    }
    function normalize(out, v) {
      var x = v[0];
      var y = v[1];
      var z = v[2];
      var d = Math.sqrt(x * x + y * y + z * z);
      out[0] = x / d;
      out[1] = y / d;
      out[2] = z / d;
      return out;
    }
    function v2Normalize(out, v) {
      var x = v[0];
      var y = v[1];
      var d = Math.sqrt(x * x + y * y);
      out[0] = x / d;
      out[1] = y / d;
      return out;
    }
    function scale(out, v, s) {
      out[0] = v[0] * s;
      out[1] = v[1] * s;
      out[2] = v[2] * s;
      return out;
    }
    function scaleAndAdd(out, v1, v2, s) {
      out[0] = v1[0] + v2[0] * s;
      out[1] = v1[1] + v2[1] * s;
      out[2] = v1[2] + v2[2] * s;
      return out;
    }
    function v2Add(out, v1, v2) {
      out[0] = v1[0] + v2[0];
      out[1] = v1[1] + v2[1];
      return out;
    }
    function v3Sub(out, v1, v2) {
      out[0] = v1[0] - v2[0];
      out[1] = v1[1] - v2[1];
      out[2] = v1[2] - v2[2];
      return out;
    }
    function v3Normalize(out, v) {
      var x = v[0];
      var y = v[1];
      var z = v[2];
      var d = Math.sqrt(x * x + y * y + z * z);
      out[0] = x / d;
      out[1] = y / d;
      out[2] = z / d;
      return out;
    }
    function v3Cross(out, v1, v2) {
      var ax = v1[0],
          ay = v1[1],
          az = v1[2],
          bx = v2[0],
          by = v2[1],
          bz = v2[2];
      out[0] = ay * bz - az * by;
      out[1] = az * bx - ax * bz;
      out[2] = ax * by - ay * bx;
      return out;
    }
    var rel = []; // start and end must be normalized

    function slerp(out, start, end, t) {
      // https://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/
      var cosT = dot(start, end);
      var theta = Math.acos(cosT) * t;
      scaleAndAdd(rel, end, start, -cosT);
      normalize(rel, rel); // start and rel Orthonormal basis

      scale(out, start, Math.cos(theta));
      scaleAndAdd(out, out, rel, Math.sin(theta));
      return out;
    }
    function lineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, out, writeOffset) {
      var dx1 = x2 - x1;
      var dx2 = x4 - x3;
      var dy1 = y2 - y1;
      var dy2 = y4 - y3;
      var cross = dy2 * dx1 - dx2 * dy1;
      var tmp1 = y1 - y3;
      var tmp2 = x1 - x3;
      var t1 = (dx2 * tmp1 - dy2 * tmp2) / cross; // const t2 = (dx1 * tmp1 - dy1 * tmp2) / cross;

      if (out) {
        writeOffset = writeOffset || 0;
        out[writeOffset] = x1 + t1 * (x2 - x1);
        out[writeOffset + 1] = y1 + t1 * (y2 - y1);
      }

      return t1;
    }
    function area(points, start, end) {
      // Signed polygon area
      var n = end - start;

      if (n < 3) {
        return 0;
      }

      var area = 0;

      for (var i = (end - 1) * 2, j = start * 2; j < end * 2;) {
        var x0 = points[i];
        var y0 = points[i + 1];
        var x1 = points[j];
        var y1 = points[j + 1];
        i = j;
        j += 2;
        area += x0 * y1 - x1 * y0;
      }

      return area;
    }

    // TODO fitRect x, y are negative?
    function triangulate(vertices, holes) {
      var dimensions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 2;
      return earcut$1(vertices, holes, dimensions);
    }
    function flatten(data) {
      return earcut$1.flatten(data);
    }
    var v1 = [];
    var v2 = [];
    var v = [];

    function innerOffsetPolygon(vertices, out, start, end, outStart, offset, miterLimit, close, removeIntersections // offsetLines
    ) {
      var checkMiterLimit = miterLimit != null;
      var cursor = outStart;
      var indicesMap = null;

      if (checkMiterLimit) {
        indicesMap = new Uint32Array(end - start);
      }

      var prevOffsetX;
      var prevOffsetY;
      var prevCursor;
      var tmpIntersection = [];

      for (var i = start; i < end; i++) {
        var nextIdx = i === end - 1 ? start : i + 1;
        var prevIdx = i === start ? end - 1 : i - 1;
        var x1 = vertices[prevIdx * 2];
        var y1 = vertices[prevIdx * 2 + 1];
        var x2 = vertices[i * 2];
        var y2 = vertices[i * 2 + 1];
        var x3 = vertices[nextIdx * 2];
        var y3 = vertices[nextIdx * 2 + 1];
        v1[0] = x2 - x1;
        v1[1] = y2 - y1;
        v2[0] = x3 - x2;
        v2[1] = y3 - y2;
        v2Normalize(v1, v1);
        v2Normalize(v2, v2);
        checkMiterLimit && (indicesMap[i] = cursor);
        var needCheckIntersection = false;
        var offsetX = void 0;
        var offsetY = void 0;

        if (!close && i === start) {
          v[0] = v2[1];
          v[1] = -v2[0];
          v2Normalize(v, v);
          prevOffsetX = out[cursor * 2] = x2 + v[0] * offset;
          prevOffsetY = out[cursor * 2 + 1] = y2 + v[1] * offset;
          prevCursor = cursor; // offsetLines && offsetLines.push([x2, y2, prevOffsetX, prevOffsetY, cursor])

          cursor++;
        } else if (!close && i === end - 1) {
          v[0] = v1[1];
          v[1] = -v1[0];
          v2Normalize(v, v);
          offsetX = x2 + v[0] * offset;
          offsetY = y2 + v[1] * offset;
          needCheckIntersection = true;
        } else {
          // PENDING Why using sub will lost the direction info.
          v2Add(v, v2, v1);
          var tmp = v[1];
          v[1] = -v[0];
          v[0] = tmp;
          v2Normalize(v, v);
          var cosA = v2Dot(v, v2);
          var sinA = Math.sqrt(1 - cosA * cosA); // PENDING
          // Make sure it's offset lines instead of vertices.

          var miter = offset * Math.min(10, 1 / sinA);
          var isCovex = offset * cosA < 0;

          if (checkMiterLimit && 1 / sinA > miterLimit && isCovex) {
            // No need to check line intersection on the outline.
            var mx = x2 + v[0] * offset;
            var my = y2 + v[1] * offset;
            var halfA = Math.acos(sinA) / 2;
            var dist = Math.tan(halfA) * Math.abs(offset);
            out[cursor * 2] = mx + v[1] * dist;
            out[cursor * 2 + 1] = my - v[0] * dist;
            cursor++;
            out[cursor * 2] = mx - v[1] * dist;
            out[cursor * 2 + 1] = my + v[0] * dist;
            cursor++;
          } else {
            offsetX = x2 + v[0] * miter;
            offsetY = y2 + v[1] * miter;
            needCheckIntersection = true;
          }

          if (needCheckIntersection) {
            // TODO Handle with whole.
            if (removeIntersections && prevOffsetX != null) {
              // Greedy, only check with previous offset line
              // PENDING: Is it necessary to check with other lines?
              var t = lineIntersection(x1, y1, prevOffsetX, prevOffsetY, x2, y2, offsetX, offsetY, tmpIntersection, 0); // Use a eplison

              if (t >= -1e-2 && t <= 1 + 1e-2) {
                // Update previous offset points.
                out[prevCursor * 2] = offsetX = tmpIntersection[0];
                out[prevCursor * 2 + 1] = offsetY = tmpIntersection[1];
              }
            }

            prevOffsetX = out[cursor * 2] = offsetX;
            prevOffsetY = out[cursor * 2 + 1] = offsetY;
            prevCursor = cursor; // offsetLines && offsetLines.push([x2, y2, offsetX, offsetY, cursor])

            cursor++;
          }
        }
      }

      return indicesMap;
    }

    function offsetPolygon(vertices, holes, offset, miterLimit, close) {
      var offsetVertices = miterLimit != null ? [] : new Float32Array(vertices.length);
      var exteriorSize = holes && holes.length ? holes[0] : vertices.length / 2;
      innerOffsetPolygon(vertices, offsetVertices, 0, exteriorSize, 0, offset, miterLimit, close, true);

      if (holes) {
        for (var i = 0; i < holes.length; i++) {
          var start = holes[i];
          var end = holes[i + 1] || vertices.length / 2;
          innerOffsetPolygon(vertices, offsetVertices, start, end, miterLimit != null ? offsetVertices.length / 2 : start, offset, miterLimit, close, false);
        }
      } // TODO holes
      // Remove intersections of offseted polygon
      // let len = offsetLines.length;
      // let tmpIntersection = [];
      // for (let i = 0; i < len; i++) {
      //     const line1 = offsetLines[i];
      //     for (let k = i + 1; k < len; k++) {
      //         const line2 = offsetLines[k];
      //         const t = lineIntersection(
      //             line1[0], line1[1], line1[2], line1[3],
      //             line2[0], line2[1], line2[2], line2[3], tmpIntersection, 0
      //         );
      //         // Use a eplison
      //         if (t >= -1e-2 && t <= 1 + 1e-2) {
      //             const cursor1 = line1[4] * 2;
      //             const cursor2 = line2[4] * 2;
      //             // Update
      //             offsetVertices[cursor1] = offsetVertices[cursor2] = line1[2] = line2[2] = tmpIntersection[0];
      //             offsetVertices[cursor1 + 1] = offsetVertices[cursor2 + 1] = line1[3] = line2[3]= tmpIntersection[1];
      //         }
      //     }
      // }


      return offsetVertices;
    }

    function reversePoints(points, stride, start, end) {
      for (var i = 0; i < Math.floor((end - start) / 2); i++) {
        for (var j = 0; j < stride; j++) {
          var a = (i + start) * stride + j;
          var b = (end - i - 1) * stride + j;
          var tmp = points[a];
          points[a] = points[b];
          points[b] = tmp;
        }
      }

      return points;
    }

    function convertToClockwise(vertices, holes) {
      var polygonVertexCount = vertices.length / 2;
      var start = 0;
      var end = holes && holes.length ? holes[0] : polygonVertexCount;

      if (area(vertices, start, end) > 0) {
        reversePoints(vertices, 2, start, end);
      }

      for (var h = 1; h < (holes ? holes.length : 0) + 1; h++) {
        start = holes[h - 1];
        end = holes[h] || polygonVertexCount;

        if (area(vertices, start, end) < 0) {
          reversePoints(vertices, 2, start, end);
        }
      }
    }

    function normalizeOpts(opts) {
      opts.depth = opts.depth || 1;
      opts.bevelSize = opts.bevelSize || 0;
      opts.bevelSegments = opts.bevelSegments == null ? 2 : opts.bevelSegments;
      opts.smoothBevel = opts.smoothBevel || false;
      opts.simplify = opts.simplify || 0;

      if (opts.smoothSide == null) {
        opts.smoothSide = 'auto';
      }

      if (opts.smoothSideThreshold == null) {
        opts.smoothSideThreshold = 0.9;
      } // Normalize bevel options.


      if (typeof opts.depth === 'number') {
        opts.bevelSize = Math.min(!(opts.bevelSegments > 0) ? 0 : opts.bevelSize, opts.depth / 2);
      }

      if (!(opts.bevelSize > 0)) {
        opts.bevelSegments = 0;
      }

      opts.bevelSegments = Math.round(opts.bevelSegments);
      var boundingRect = opts.boundingRect;
      opts.translate = opts.translate || [0, 0];
      opts.scale = opts.scale || [1, 1];

      if (opts.fitRect) {
        var targetX = opts.fitRect.x == null ? boundingRect.x || 0 : opts.fitRect.x;
        var targetY = opts.fitRect.y == null ? boundingRect.y || 0 : opts.fitRect.y;
        var targetWidth = opts.fitRect.width;
        var targetHeight = opts.fitRect.height;

        if (targetWidth == null) {
          if (targetHeight != null) {
            targetWidth = targetHeight / boundingRect.height * boundingRect.width;
          } else {
            targetWidth = boundingRect.width;
            targetHeight = boundingRect.height;
          }
        } else if (targetHeight == null) {
          targetHeight = targetWidth / boundingRect.width * boundingRect.height;
        }

        opts.scale = [targetWidth / boundingRect.width, targetHeight / boundingRect.height];
        opts.translate = [(targetX - boundingRect.x) * opts.scale[0], (targetY - boundingRect.y) * opts.scale[1]];
      }
    }

    function generateNormal(indices, position) {
      function v3Set(p, a, b, c) {
        p[0] = a;
        p[1] = b;
        p[2] = c;
      }

      var p1 = [];
      var p2 = [];
      var p3 = [];
      var v21 = [];
      var v32 = [];
      var n = [];
      var len = indices.length;
      var normals = new Float32Array(position.length);

      for (var f = 0; f < len;) {
        var i1 = indices[f++] * 3;
        var i2 = indices[f++] * 3;
        var i3 = indices[f++] * 3;
        v3Set(p1, position[i1], position[i1 + 1], position[i1 + 2]);
        v3Set(p2, position[i2], position[i2 + 1], position[i2 + 2]);
        v3Set(p3, position[i3], position[i3 + 1], position[i3 + 2]);
        v3Sub(v21, p1, p2);
        v3Sub(v32, p2, p3);
        v3Cross(n, v21, v32); // Already be weighted by the triangle area

        for (var _i = 0; _i < 3; _i++) {
          normals[i1 + _i] = normals[i1 + _i] + n[_i];
          normals[i2 + _i] = normals[i2 + _i] + n[_i];
          normals[i3 + _i] = normals[i3 + _i] + n[_i];
        }
      }

      for (var i = 0; i < normals.length;) {
        v3Set(n, normals[i], normals[i + 1], normals[i + 2]);
        v3Normalize(n, n);
        normals[i++] = n[0];
        normals[i++] = n[1];
        normals[i++] = n[2];
      }

      return normals;
    } // 0,0----1,0
    // 0,1----1,1


    var quadToTriangle = [[0, 0], [1, 0], [1, 1], [0, 0], [1, 1], [0, 1]]; // Add side vertices and indices. Include bevel.

    function addExtrudeSide(out, _ref, start, end, cursors, opts) {
      var vertices = _ref.vertices,
          topVertices = _ref.topVertices,
          splittedMap = _ref.splittedMap,
          depth = _ref.depth,
          rect = _ref.rect;
      var ringVertexCount = end - start;
      var splitBevel = opts.smoothBevel ? 1 : 2;
      var bevelSize = Math.min(depth / 2, opts.bevelSize);
      var bevelSegments = opts.bevelSegments;
      var vertexOffset = cursors.vertex;
      var size = Math.max(rect.width, rect.height, depth);
      var isDuplicateVertex = splittedMap ? function (idx) {
        var nextIdx = (idx + 1) % ringVertexCount;
        return splittedMap[idx + start] === splittedMap[nextIdx + start];
      } : function (idx) {
        return false;
      }; // Side vertices

      if (bevelSize > 0) {
        var v0 = [0, 0, 1];
        var _v = [];
        var _v2 = [0, 0, -1];
        var _v3 = [];
        var ringCount = 0;
        var vLen = new Float32Array(ringVertexCount);

        for (var k = 0; k < 2; k++) {
          var z = k === 0 ? depth - bevelSize : bevelSize;

          for (var s = 0; s <= bevelSegments * splitBevel; s++) {
            var uLen = 0;
            var prevX = void 0;
            var prevY = void 0;

            for (var i = 0; i < ringVertexCount; i++) {
              var idx = (i % ringVertexCount + start) * 2;
              var rawIdx = splittedMap ? splittedMap[idx / 2] * 2 : idx;
              _v[0] = vertices[idx] - topVertices[rawIdx];
              _v[1] = vertices[idx + 1] - topVertices[rawIdx + 1];
              _v[2] = 0;
              var l = Math.sqrt(_v[0] * _v[0] + _v[1] * _v[1]);
              _v[0] /= l;
              _v[1] /= l;
              var t = (Math.floor(s / splitBevel) + s % splitBevel) / bevelSegments;
              k === 0 ? slerp(_v3, v0, _v, t) : slerp(_v3, _v, _v2, t);
              var t2 = k === 0 ? t : 1 - t;
              var a = bevelSize * Math.sin(t2 * Math.PI / 2);
              var b = l * Math.cos(t2 * Math.PI / 2); // ellipse radius

              var r = bevelSize * l / Math.sqrt(a * a + b * b);
              var x = _v3[0] * r + topVertices[rawIdx];
              var y = _v3[1] * r + topVertices[rawIdx + 1];
              var zz = _v3[2] * r + z;
              out.position[cursors.vertex * 3] = x;
              out.position[cursors.vertex * 3 + 1] = y;
              out.position[cursors.vertex * 3 + 2] = zz; // TODO Cache and optimize

              if (i > 0) {
                uLen += Math.sqrt((prevX - x) * (prevX - x) + (prevY - y) * (prevY - y));
              }

              if (s > 0 || k > 0) {
                var tmp = (cursors.vertex - ringVertexCount) * 3;
                var prevX2 = out.position[tmp];
                var prevY2 = out.position[tmp + 1];
                var prevZ2 = out.position[tmp + 2];
                vLen[i] += Math.sqrt((prevX2 - x) * (prevX2 - x) + (prevY2 - y) * (prevY2 - y) + (prevZ2 - zz) * (prevZ2 - zz));
              }

              out.uv[cursors.vertex * 2] = uLen / size;
              out.uv[cursors.vertex * 2 + 1] = vLen[i] / size;
              prevX = x;
              prevY = y;
              cursors.vertex++; // Just ignore this face if vertex are duplicted in `splitVertices`

              if (isDuplicateVertex(i)) {
                continue;
              }

              if (splitBevel > 1 && s % splitBevel || splitBevel === 1 && s >= 1) {
                for (var f = 0; f < 6; f++) {
                  var m = (quadToTriangle[f][0] + i) % ringVertexCount;
                  var n = quadToTriangle[f][1] + ringCount;
                  out.indices[cursors.index++] = (n - 1) * ringVertexCount + m + vertexOffset;
                }
              }
            }

            ringCount++;
          }
        }
      } else {
        for (var _k = 0; _k < 2; _k++) {
          var _z = _k === 0 ? depth : 0;

          var _uLen = 0;

          var _prevX = void 0;

          var _prevY = void 0;

          for (var _i2 = 0; _i2 < ringVertexCount; _i2++) {
            var _idx = (_i2 + start) * 2;

            var _x = vertices[_idx];
            var _y = vertices[_idx + 1];
            var vtx3 = cursors.vertex * 3;
            var vtx2 = cursors.vertex * 2;
            out.position[vtx3] = _x;
            out.position[vtx3 + 1] = _y;
            out.position[vtx3 + 2] = _z;

            if (_i2 > 0) {
              _uLen += Math.sqrt((_prevX - _x) * (_prevX - _x) + (_prevY - _y) * (_prevY - _y));
            }

            out.uv[vtx2] = _uLen / size;
            out.uv[vtx2 + 1] = _z / size;
            _prevX = _x;
            _prevY = _y;
            cursors.vertex++;
          }
        }
      } // Connect the side


      var sideStartRingN = bevelSize > 0 ? bevelSegments * splitBevel + 1 : 1;

      for (var _i3 = 0; _i3 < ringVertexCount; _i3++) {
        // Just ignore this face if vertex are duplicted in `splitVertices`
        if (isDuplicateVertex(_i3)) {
          continue;
        }

        for (var _f = 0; _f < 6; _f++) {
          var _m = (quadToTriangle[_f][0] + _i3) % ringVertexCount;

          var _n = quadToTriangle[_f][1] + sideStartRingN;

          out.indices[cursors.index++] = (_n - 1) * ringVertexCount + _m + vertexOffset;
        }
      }
    }

    function addTopAndBottom(_ref2, out, cursors, opts) {
      var indices = _ref2.indices,
          topVertices = _ref2.topVertices,
          rect = _ref2.rect,
          depth = _ref2.depth;

      if (topVertices.length <= 4) {
        return;
      }

      var vertexOffset = cursors.vertex; // Top indices

      var indicesLen = indices.length;

      for (var i = 0; i < indicesLen; i++) {
        out.indices[cursors.index++] = vertexOffset + indices[i];
      }

      var size = Math.max(rect.width, rect.height); // Top and bottom vertices

      for (var k = 0; k < (opts.excludeBottom ? 1 : 2); k++) {
        for (var _i4 = 0; _i4 < topVertices.length; _i4 += 2) {
          var x = topVertices[_i4];
          var y = topVertices[_i4 + 1];
          var vtx3 = cursors.vertex * 3;
          var vtx2 = cursors.vertex * 2;
          out.position[vtx3] = x;
          out.position[vtx3 + 1] = y;
          out.position[vtx3 + 2] = (1 - k) * depth;
          out.uv[vtx2] = (x - rect.x) / size;
          out.uv[vtx2 + 1] = (y - rect.y) / size;
          cursors.vertex++;
        }
      } // Bottom indices


      if (!opts.excludeBottom) {
        var vertexCount = topVertices.length / 2;

        for (var _i5 = 0; _i5 < indicesLen; _i5 += 3) {
          for (var _k2 = 0; _k2 < 3; _k2++) {
            out.indices[cursors.index++] = vertexOffset + vertexCount + indices[_i5 + 2 - _k2];
          }
        }
      }
    }
    /**
     * Split vertices for sharp side.
     */


    function splitVertices(vertices, holes, smoothSide, smoothSideThreshold) {
      var isAutoSmooth = smoothSide == null || smoothSide === 'auto';

      if (smoothSide === true) {
        return {
          vertices: vertices,
          holes: holes
        };
      }

      var newVertices = [];
      var newHoles = holes && [];
      var count = vertices.length / 2;
      var v1 = [];
      var v2 = []; // Map of splitted index to raw index

      var splittedMap = [];
      var start = 0;
      var end = 0;
      var polysCount = (holes ? holes.length : 0) + 1;

      for (var h = 0; h < polysCount; h++) {
        if (h === 0) {
          end = holes && holes.length ? holes[0] : count;
        } else {
          start = holes[h - 1];
          end = holes[h] || count;
        }

        for (var i = start; i < end; i++) {
          var x2 = vertices[i * 2];
          var y2 = vertices[i * 2 + 1];
          var nextIdx = i === end - 1 ? start : i + 1;
          var x3 = vertices[nextIdx * 2];
          var y3 = vertices[nextIdx * 2 + 1];

          if (isAutoSmooth) {
            var prevIdx = i === start ? end - 1 : i - 1;
            var x1 = vertices[prevIdx * 2];
            var y1 = vertices[prevIdx * 2 + 1];
            v1[0] = x1 - x2;
            v1[1] = y1 - y2;
            v2[0] = x3 - x2;
            v2[1] = y3 - y2;
            v2Normalize(v1, v1);
            v2Normalize(v2, v2);
            var angleCos = v2Dot(v1, v2) * 0.5 + 0.5;

            if (1 - angleCos > smoothSideThreshold) {
              newVertices.push(x2, y2);
              splittedMap.push(i);
            } else {
              newVertices.push(x2, y2, x2, y2);
              splittedMap.push(i, i);
            }
          } else {
            newVertices.push(x2, y2, x2, y2);
            splittedMap.push(i, i);
          }
        }

        if (h < polysCount - 1 && newHoles) {
          newHoles.push(newVertices.length / 2);
        }
      }

      return {
        vertices: new Float32Array(newVertices),
        splittedMap: splittedMap,
        holes: newHoles
      };
    }

    function innerExtrudeTriangulatedPolygon(preparedData, opts) {
      var indexCount = 0;
      var vertexCount = 0;

      for (var p = 0; p < preparedData.length; p++) {
        var _preparedData$p = preparedData[p],
            indices = _preparedData$p.indices,
            _vertices = _preparedData$p.vertices,
            splittedMap = _preparedData$p.splittedMap,
            topVertices = _preparedData$p.topVertices,
            holes = _preparedData$p.holes,
            depth = _preparedData$p.depth;
        var bevelSize = Math.min(depth / 2, opts.bevelSize);
        var bevelSegments = !(bevelSize > 0) ? 0 : opts.bevelSegments;
        holes = holes || [];
        indexCount += indices.length * (opts.excludeBottom ? 1 : 2);
        vertexCount += topVertices.length / 2 * (opts.excludeBottom ? 1 : 2);
        var ringCount = 2 + bevelSegments * 2;
        var start = 0;
        var end = 0;

        for (var h = 0; h < holes.length + 1; h++) {
          if (h === 0) {
            end = holes.length ? holes[0] : _vertices.length / 2;
          } else {
            start = holes[h - 1];
            end = holes[h] || _vertices.length / 2;
          }

          var faceEnd = splittedMap ? splittedMap[end - 1] + 1 : end;
          var faceStart = splittedMap ? splittedMap[start] : start;
          indexCount += (faceEnd - faceStart) * 6 * (ringCount - 1);
          var sideRingVertexCount = end - start;
          vertexCount += sideRingVertexCount * ringCount // Double the bevel vertex number if not smooth
          + (!opts.smoothBevel ? bevelSegments * sideRingVertexCount * 2 : 0);
        }
      }

      var data = {
        position: new Float32Array(vertexCount * 3),
        indices: new (vertexCount > 0xffff ? Uint32Array : Uint16Array)(indexCount),
        uv: new Float32Array(vertexCount * 2)
      };
      var cursors = {
        vertex: 0,
        index: 0
      };

      for (var d = 0; d < preparedData.length; d++) {
        addTopAndBottom(preparedData[d], data, cursors, opts);
      }

      for (var _d = 0; _d < preparedData.length; _d++) {
        var _preparedData$_d = preparedData[_d],
            _holes = _preparedData$_d.holes,
            _vertices2 = _preparedData$_d.vertices;

        var _vertexCount = _vertices2.length / 2;

        var _start = 0;

        var _end = _holes && _holes.length ? _holes[0] : _vertexCount; // Add exterior


        addExtrudeSide(data, preparedData[_d], _start, _end, cursors, opts); // Add holes

        if (_holes) {
          for (var _h = 0; _h < _holes.length; _h++) {
            _start = _holes[_h];
            _end = _holes[_h + 1] || _vertexCount;
            addExtrudeSide(data, preparedData[_d], _start, _end, cursors, opts);
          }
        }
      } // Wrap uv


      for (var i = 0; i < data.uv.length; i++) {
        var val = data.uv[i];

        if (val > 0 && Math.round(val) === val) {
          data.uv[i] = 1;
        } else {
          data.uv[i] = val % 1;
        }
      }

      data.normal = generateNormal(data.indices, data.position); // PENDING

      data.boundingRect = preparedData[0] && preparedData[0].rect;
      return data;
    }

    function convertPolylineToTriangulatedPolygon(polyline, polylineIdx, opts) {
      var lineWidth = opts.lineWidth;
      var pointCount = polyline.length;
      var points = new Float32Array(pointCount * 2);
      var translate = opts.translate || [0, 0];
      var scale = opts.scale || [1, 1];

      for (var i = 0, k = 0; i < pointCount; i++) {
        points[k++] = polyline[i][0] * scale[0] + translate[0];
        points[k++] = polyline[i][1] * scale[1] + translate[1];
      }

      if (area(points, 0, pointCount) < 0) {
        reversePoints(points, 2, 0, pointCount);
      }

      var insidePoints = [];
      var outsidePoints = [];
      var miterLimit = opts.miterLimit;
      var outsideIndicesMap = innerOffsetPolygon(points, outsidePoints, 0, pointCount, 0, -lineWidth / 2, miterLimit, false, true);
      reversePoints(points, 2, 0, pointCount);
      var insideIndicesMap = innerOffsetPolygon(points, insidePoints, 0, pointCount, 0, -lineWidth / 2, miterLimit, false, true);
      var polygonVertexCount = (insidePoints.length + outsidePoints.length) / 2;
      var polygonVertices = new Float32Array(polygonVertexCount * 2);
      var offset = 0;
      var outsidePointCount = outsidePoints.length / 2;

      for (var _i6 = 0; _i6 < outsidePoints.length; _i6++) {
        polygonVertices[offset++] = outsidePoints[_i6];
      }

      for (var _i7 = 0; _i7 < insidePoints.length; _i7++) {
        polygonVertices[offset++] = insidePoints[_i7];
      } // Built indices


      var indices = new (polygonVertexCount > 0xffff ? Uint32Array : Uint16Array)(((pointCount - 1) * 2 + (polygonVertexCount - pointCount * 2)) * 3);
      var off = 0;

      for (var _i8 = 0; _i8 < pointCount - 1; _i8++) {
        var i2 = _i8 + 1;
        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[_i8];
        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[_i8] - 1;
        indices[off++] = insideIndicesMap[_i8] + 1 + outsidePointCount;
        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[_i8];
        indices[off++] = insideIndicesMap[_i8] + 1 + outsidePointCount;
        indices[off++] = insideIndicesMap[_i8] + outsidePointCount;

        if (insideIndicesMap[i2] - insideIndicesMap[_i8] === 2) {
          indices[off++] = insideIndicesMap[_i8] + 2 + outsidePointCount;
          indices[off++] = insideIndicesMap[_i8] + 1 + outsidePointCount;
          indices[off++] = outsidePointCount - outsideIndicesMap[i2] - 1;
        } else if (outsideIndicesMap[i2] - outsideIndicesMap[_i8] === 2) {
          indices[off++] = insideIndicesMap[i2] + outsidePointCount;
          indices[off++] = outsidePointCount - 1 - (outsideIndicesMap[_i8] + 1);
          indices[off++] = outsidePointCount - 1 - (outsideIndicesMap[_i8] + 2);
        }
      }

      var topVertices = opts.bevelSize > 0 ? offsetPolygon(polygonVertices, [], opts.bevelSize, null, true) : polygonVertices;
      var boundingRect = opts.boundingRect;
      var res = splitVertices(polygonVertices, null, opts.smoothSide, opts.smoothSideThreshold);
      return {
        vertices: res.vertices,
        rawVertices: vertices,
        splittedMap: res.splittedMap,
        indices: indices,
        topVertices: topVertices,
        rect: {
          x: boundingRect.x * scale[0] + translate[0],
          y: boundingRect.y * scale[1] + translate[1],
          width: boundingRect.width * scale[0],
          height: boundingRect.height * scale[1]
        },
        depth: typeof opts.depth === 'function' ? opts.depth(polylineIdx) : opts.depth,
        holes: []
      };
    }

    function removeClosePointsOfPolygon(polygon, epsilon) {
      var newPolygon = [];

      for (var k = 0; k < polygon.length; k++) {
        var points = polygon[k];
        var newPoints = [];
        var len = points.length;
        var x1 = points[len - 1][0];
        var y1 = points[len - 1][1];
        var dist = 0;

        for (var i = 0; i < len; i++) {
          var x2 = points[i][0];
          var y2 = points[i][1];
          var dx = x2 - x1;
          var dy = y2 - y1;
          dist += Math.sqrt(dx * dx + dy * dy);

          if (dist > epsilon) {
            newPoints.push(points[i]);
            dist = 0;
          }

          x1 = x2;
          y1 = y2;
        }

        if (newPoints.length >= 3) {
          newPolygon.push(newPoints);
        }
      }

      return newPolygon.length > 0 ? newPolygon : null;
    }

    function simplifyPolygon(polygon, tolerance) {
      var newPolygon = [];

      for (var k = 0; k < polygon.length; k++) {
        var points = polygon[k];
        points = simplify(points, tolerance, true);

        if (points.length >= 3) {
          newPolygon.push(points);
        }
      }

      return newPolygon.length > 0 ? newPolygon : null;
    }
    /**
     *
     * @param {Array} polygons Polygons array that match GeoJSON MultiPolygon geometry.
     * @param {Object} [opts]
     * @param {number|Function} [opts.depth]
     * @param {number} [opts.bevelSize = 0]
     * @param {number} [opts.bevelSegments = 2]
     * @param {number} [opts.simplify = 0]
     * @param {boolean} [opts.smoothSide = 'auto']
     * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
     * @param {boolean} [opts.smoothBevel = false]
     * @param {boolean} [opts.excludeBottom = false]
     * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
     * @param {Array} [opts.translate]
     * @param {Array} [opts.scale]
     *
     * @return {Object} {indices, position, uv, normal, boundingRect}
     */


    function extrudePolygon(polygons, opts) {
      opts = Object.assign({}, opts);
      var min = [Infinity, Infinity];
      var max = [-Infinity, -Infinity];

      for (var i = 0; i < polygons.length; i++) {
        updateBoundingRect(polygons[i][0], min, max);
      }

      opts.boundingRect = opts.boundingRect || {
        x: min[0],
        y: min[1],
        width: max[0] - min[0],
        height: max[1] - min[1]
      };
      normalizeOpts(opts);
      var preparedData = [];
      var translate = opts.translate || [0, 0];
      var scale = opts.scale || [1, 1];
      var boundingRect = opts.boundingRect;
      var transformdRect = {
        x: boundingRect.x * scale[0] + translate[0],
        y: boundingRect.y * scale[1] + translate[1],
        width: boundingRect.width * scale[0],
        height: boundingRect.height * scale[1]
      };
      var epsilon = Math.min(boundingRect.width, boundingRect.height) / 1e5;

      for (var _i9 = 0; _i9 < polygons.length; _i9++) {
        var newPolygon = removeClosePointsOfPolygon(polygons[_i9], epsilon);

        if (!newPolygon) {
          continue;
        }

        var simplifyTolerance = opts.simplify / Math.max(scale[0], scale[1]);

        if (simplifyTolerance > 0) {
          newPolygon = simplifyPolygon(newPolygon, simplifyTolerance);
        }

        if (!newPolygon) {
          continue;
        }

        var _earcut$flatten = earcut$1.flatten(newPolygon),
            _vertices3 = _earcut$flatten.vertices,
            holes = _earcut$flatten.holes,
            dimensions = _earcut$flatten.dimensions;

        for (var k = 0; k < _vertices3.length;) {
          _vertices3[k] = _vertices3[k++] * scale[0] + translate[0];
          _vertices3[k] = _vertices3[k++] * scale[1] + translate[1];
        }

        convertToClockwise(_vertices3, holes);

        if (dimensions !== 2) {
          throw new Error('Only 2D polygon points are supported');
        }

        var topVertices = opts.bevelSize > 0 ? offsetPolygon(_vertices3, holes, opts.bevelSize, null, true) : _vertices3;
        var indices = triangulate(topVertices, holes, dimensions);
        var res = splitVertices(_vertices3, holes, opts.smoothSide, opts.smoothSideThreshold);
        preparedData.push({
          indices: indices,
          vertices: res.vertices,
          rawVertices: _vertices3,
          topVertices: topVertices,
          holes: res.holes,
          splittedMap: res.splittedMap,
          rect: transformdRect,
          depth: typeof opts.depth === 'function' ? opts.depth(_i9) : opts.depth
        });
      }

      return innerExtrudeTriangulatedPolygon(preparedData, opts);
    }
    /**
     *
     * @param {Array} polylines Polylines array that match GeoJSON MultiLineString geometry.
     * @param {Object} [opts]
     * @param {number} [opts.depth]
     * @param {number} [opts.bevelSize = 0]
     * @param {number} [opts.bevelSegments = 2]
     * @param {number} [opts.simplify = 0]
     * @param {boolean} [opts.smoothSide = 'auto']
     * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
     * @param {boolean} [opts.smoothBevel = false]
     * @param {boolean} [opts.excludeBottom = false]
     * @param {boolean} [opts.lineWidth = 1]
     * @param {boolean} [opts.miterLimit = 2]
     * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
     * @param {Array} [opts.translate]
     * @param {Array} [opts.scale]
     * @param {Object} [opts.boundingRect]
     * @return {Object} {indices, position, uv, normal, boundingRect}
     */

    function extrudePolyline(polylines, opts) {
      opts = Object.assign({}, opts);
      var min = [Infinity, Infinity];
      var max = [-Infinity, -Infinity];

      for (var i = 0; i < polylines.length; i++) {
        updateBoundingRect(polylines[i], min, max);
      }

      opts.boundingRect = opts.boundingRect || {
        x: min[0],
        y: min[1],
        width: max[0] - min[0],
        height: max[1] - min[1]
      };
      normalizeOpts(opts);
      var scale = opts.scale || [1, 1];

      if (opts.lineWidth == null) {
        opts.lineWidth = 1;
      }

      if (opts.miterLimit == null) {
        opts.miterLimit = 2;
      }

      var preparedData = []; // Extrude polyline to polygon

      for (var _i10 = 0; _i10 < polylines.length; _i10++) {
        var newPolyline = polylines[_i10];
        var simplifyTolerance = opts.simplify / Math.max(scale[0], scale[1]);

        if (simplifyTolerance > 0) {
          newPolyline = simplify(newPolyline, simplifyTolerance, true);
        }

        preparedData.push(convertPolylineToTriangulatedPolygon(newPolyline, _i10, opts));
      }

      return innerExtrudeTriangulatedPolygon(preparedData, opts);
    }

    function updateBoundingRect(points, min, max) {
      for (var i = 0; i < points.length; i++) {
        min[0] = Math.min(points[i][0], min[0]);
        min[1] = Math.min(points[i][1], min[1]);
        max[0] = Math.max(points[i][0], max[0]);
        max[1] = Math.max(points[i][1], max[1]);
      }
    }
    /**
     *
     * @param {Object} geojson
     * @param {Object} [opts]
     * @param {number} opts.depth
     * @param {number} [opts.bevelSize = 0]
     * @param {number} [opts.bevelSegments = 2]
     * @param {number} [opts.simplify = 0]
     * @param {boolean} [opts.smoothSide = 'auto']
     * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
     * @param {boolean} [opts.smoothBevel = false]
     * @param {boolean} [opts.excludeBottom = false]
     * @param {boolean} [opts.lineWidth = 1]
     * @param {boolean} [opts.miterLimit = 2]
     * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
     * @param {Array} [opts.translate]
     * @param {Array} [opts.scale]
     * @param {Object} [opts.boundingRect]
     * @return {Object} {polyline: {indices, position, uv, normal}, polygon: {indices, position, uv, normal}}
     */
    // TODO Not merge feature


    function extrudeGeoJSON(geojson, opts) {
      opts = Object.assign({}, opts);
      var polylines = [];
      var polygons = [];
      var polylineFeatureIndices = [];
      var polygonFeatureIndices = [];
      var min = [Infinity, Infinity];
      var max = [-Infinity, -Infinity];

      if (geojson.type === 'LineString' || geojson.type === 'MultiLineString' || geojson.type === 'Polygon' || geojson.type === 'MultiPolygon') {
        geojson = {
          features: [{
            geometry: geojson
          }]
        };
      }

      for (var i = 0; i < geojson.features.length; i++) {
        var feature = geojson.features[i];
        var geometry = feature.geometry;

        if (geometry && geometry.coordinates) {
          switch (geometry.type) {
            case 'LineString':
              polylines.push(geometry.coordinates);
              polylineFeatureIndices.push(i);
              updateBoundingRect(geometry.coordinates, min, max);
              break;

            case 'MultiLineString':
              for (var k = 0; k < geometry.coordinates.length; k++) {
                polylines.push(geometry.coordinates[k]);
                polylineFeatureIndices.push(i);
                updateBoundingRect(geometry.coordinates[k], min, max);
              }

              break;

            case 'Polygon':
              polygons.push(geometry.coordinates);
              polygonFeatureIndices.push(i);
              updateBoundingRect(geometry.coordinates[0], min, max);
              break;

            case 'MultiPolygon':
              for (var _k3 = 0; _k3 < geometry.coordinates.length; _k3++) {
                polygons.push(geometry.coordinates[_k3]);
                polygonFeatureIndices.push(i);
                updateBoundingRect(geometry.coordinates[_k3][0], min, max);
              }

              break;
          }
        }
      }

      opts.boundingRect = opts.boundingRect || {
        x: min[0],
        y: min[1],
        width: max[0] - min[0],
        height: max[1] - min[1]
      };
      var originalDepth = opts.depth;
      return {
        polyline: extrudePolyline(polylines, Object.assign(opts, {
          depth: function depth(idx) {
            if (typeof originalDepth === 'function') {
              return originalDepth(geojson.features[polylineFeatureIndices[idx]]);
            }

            return originalDepth;
          }
        })),
        polygon: extrudePolygon(polygons, Object.assign(opts, {
          depth: function depth(idx) {
            if (typeof originalDepth === 'function') {
              return originalDepth(geojson.features[polygonFeatureIndices[idx]]);
            }

            return originalDepth;
          }
        }))
      };
    }

    exports.extrudeGeoJSON = extrudeGeoJSON;
    exports.extrudePolygon = extrudePolygon;
    exports.extrudePolyline = extrudePolyline;
    exports.flatten = flatten;
    exports.offsetPolygon = offsetPolygon;
    exports.triangulate = triangulate;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
//# sourceMappingURL=geometry-extrude.js.map


================================================
FILE: index.d.ts
================================================
type Rect = {
    x?: number,
    y?: number,
    width?: number,
    height?: number
}

type BasicExtrudeOpt = {
    bevelSize?: number,
    bevelSegments?: number,
    simplify?: number,
    smoothSide?: boolean | 'auto',
    smoothSideThreshold?: number,
    smoothBevel?: boolean,
    excludeBottom?: boolean,
    fitRect?: Rect,
    translate?: ArrayLike<number>,
    scale?: ArrayLike<number>
}

type ExtrudeResult = {
    indices: Uint16Array|Uint32Array,
    position: Float32Array,
    normal: Float32Array,
    uv: Float32Array,
    boundingRect: Rect
}

type Polygon = ArrayLike<ArrayLike<ArrayLike<number>>>;
type Polyline = ArrayLike<ArrayLike<number>>;

type GeoJSONPolygonGeometry = {
    type: 'Polygon',
    coordinates: Polygon
}

type GeoJSONLineStringGeometry = {
    type: 'LineString',
    coordinates: Polyline
}
type GeoJSONMultiPolygonGeometry = {
    type: 'MultiPolygon',
    coordinates: Array<Polygon>
}
type GeoJSONMultiLineStringGeometry = {
    type: 'MultiLineString',
    coordinates: Array<Polyline>
}

type GeoJSONFeature = {
    geometry: GeoJSONPolygonGeometry
        | GeoJSONLineStringGeometry
        | GeoJSONMultiPolygonGeometry
        | GeoJSONMultiLineStringGeometry,
    properties: Object
}

type GeoJSON = {
    features: Array<GeoJSONFeature>
}

interface GeometryExtrudeStatic {

    extrudePolygon(polygons: ArrayLike<Polygon>, opts: BasicExtrudeOpt & {
        depth: ((idx: number) => number) | number
    }): ExtrudeResult

    extrudePolyline(polylines: ArrayLike<Polyline>, opts: BasicExtrudeOpt & {
        depth: ((idx: number) => number) | number
        lineWidth?: number
        miterLimit?: number
    }): ExtrudeResult

    extrudeGeoJSON(
        geojson: GeoJSON | GeoJSONPolygonGeometry | GeoJSONLineStringGeometry | GeoJSONMultiLineStringGeometry | GeoJSONMultiPolygonGeometry,
        opts: BasicExtrudeOpt & {
            depth: ((feature: GeoJSONFeature) => number) | number
            lineWidth?: number
            miterLimit?: number
        }
    ): {polygon: ExtrudeResult, polyline: ExtrudeResult}
}

declare const exports: GeometryExtrudeStatic;
export = exports;
export as namespace geometryExtrude;

================================================
FILE: package.json
================================================
{
  "name": "geometry-extrude",
  "version": "0.2.1",
  "main": "dist/geometry-extrude.js",
  "scripts": {
    "dev": "rollup -c -w",
    "build": "rollup -c && uglifyjs -c -m -- dist/geometry-extrude.js > dist/geometry-extrude.min.js"
  },
  "types": "index.d.ts",
  "repository": "https://github.com/pissang/geometry-extrude",
  "license": "MIT",
  "dependencies": {
    "earcut": "^2.1.3"
  },
  "devDependencies": {
    "@babel/core": "^7.15.8",
    "@babel/preset-env": "^7.15.8",
    "@rollup/plugin-babel": "^5.3.0",
    "@rollup/plugin-commonjs": "^21.0.1",
    "@rollup/plugin-node-resolve": "^13.0.6",
    "rollup": "^2.58.0",
    "uglify-js": "3.3.28"
  }
}


================================================
FILE: rollup.config.js
================================================
import nodeResolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import babel from '@rollup/plugin-babel';

export default {
    input: __dirname + '/src/main.js',
    plugins: [
        nodeResolve(),
        commonjs(),
        babel({
            exclude: ['node_modules/**']
        })
    ],
    // sourceMap: true,
    output: [
        {
            format: 'umd',
            name: 'geometryExtrude',
            sourcemap: true,
            file: 'dist/geometry-extrude.js'
        }
    ]
};

================================================
FILE: src/main.js
================================================
// TODO fitRect x, y are negative?
// TODO Extrude dimensions
// TODO bevel="top"|"bottom"
// TODO Not add top and bottom vertices if area is 0

import earcut from 'earcut';
import doSimplify from './simplify';
import {
    slerp, v2Normalize, v2Dot, v2Add, area,
    v3Normalize, v3Sub, v3Cross, lineIntersection
} from './math';

export function triangulate(vertices, holes, dimensions=2) {
    return earcut(vertices, holes, dimensions);
};

export function flatten(data) {
    return earcut.flatten(data);
}

const v1 = [];
const v2 = [];
const v = [];

function innerOffsetPolygon(
    vertices, out, start, end, outStart, offset, miterLimit, close,
    removeIntersections,
    // offsetLines
) {
    const checkMiterLimit = miterLimit != null;
    let cursor = outStart;
    let indicesMap = null;
    if (checkMiterLimit) {
        indicesMap = new Uint32Array(end - start);
    }
    let prevOffsetX;
    let prevOffsetY;
    let prevCursor;
    let tmpIntersection = [];

    for (let i = start; i < end; i++) {
        const nextIdx = i === end - 1 ? start : i + 1;
        const prevIdx = i === start ? end - 1 : i - 1;
        const x1 = vertices[prevIdx * 2];
        const y1 = vertices[prevIdx * 2 + 1];
        const x2 = vertices[i * 2];
        const y2 = vertices[i * 2 + 1];
        const x3 = vertices[nextIdx * 2];
        const y3 = vertices[nextIdx * 2 + 1];

        v1[0] = x2 - x1;
        v1[1] = y2 - y1;
        v2[0] = x3 - x2;
        v2[1] = y3 - y2;

        v2Normalize(v1, v1);
        v2Normalize(v2, v2);

        checkMiterLimit && (indicesMap[i] = cursor);

        let needCheckIntersection = false;
        let offsetX;
        let offsetY;
        if (!close && i === start) {
            v[0] = v2[1];
            v[1] = -v2[0];
            v2Normalize(v, v);
            prevOffsetX = out[cursor * 2] = x2 + v[0] * offset;
            prevOffsetY = out[cursor * 2 + 1] = y2 + v[1] * offset;
            prevCursor = cursor;

            // offsetLines && offsetLines.push([x2, y2, prevOffsetX, prevOffsetY, cursor])
            cursor++;
        }
        else if (!close && i === end - 1) {
            v[0] = v1[1];
            v[1] = -v1[0];
            v2Normalize(v, v);

            offsetX = x2 + v[0] * offset;
            offsetY = y2 + v[1] * offset;

            needCheckIntersection = true;
        }
        else {
            // PENDING Why using sub will lost the direction info.
            v2Add(v, v2, v1);
            const tmp = v[1];
            v[1] = -v[0];
            v[0] = tmp;

            v2Normalize(v, v);

            const cosA = v2Dot(v, v2);
            const sinA = Math.sqrt(1 - cosA * cosA);
            // PENDING
            // Make sure it's offset lines instead of vertices.
            const miter = offset * Math.min(10, 1 / sinA);

            const isCovex = offset * cosA < 0;

            if (checkMiterLimit && (1 / sinA) > miterLimit && isCovex) {
                // No need to check line intersection on the outline.
                const mx = x2 + v[0] * offset;
                const my = y2 + v[1] * offset;
                const halfA = Math.acos(sinA) / 2;
                const dist = Math.tan(halfA) * Math.abs(offset);
                out[cursor * 2] = mx + v[1] * dist;
                out[cursor * 2 + 1] = my - v[0] * dist;
                cursor++;
                out[cursor * 2] = mx - v[1] * dist;
                out[cursor * 2 + 1] = my + v[0] * dist;
                cursor++;
            }
            else {
                offsetX = x2 + v[0] * miter;
                offsetY = y2 + v[1] * miter;
                needCheckIntersection = true;
            }

            if (needCheckIntersection) {
                // TODO Handle with whole.
                if (removeIntersections && prevOffsetX != null) {
                    // Greedy, only check with previous offset line
                    // PENDING: Is it necessary to check with other lines?
                    const t = lineIntersection(
                        x1, y1, prevOffsetX, prevOffsetY,
                        x2, y2, offsetX, offsetY, tmpIntersection, 0
                    );
                    // Use a eplison
                    if (t >= -1e-2 && t <= 1 + 1e-2) {
                        // Update previous offset points.
                        out[prevCursor * 2] = offsetX = tmpIntersection[0];
                        out[prevCursor * 2 + 1] = offsetY = tmpIntersection[1];
                    }
                }

                prevOffsetX = out[cursor * 2] = offsetX;
                prevOffsetY = out[cursor * 2 + 1] = offsetY;
                prevCursor = cursor;

                // offsetLines && offsetLines.push([x2, y2, offsetX, offsetY, cursor])

                cursor++;
            }
        }
    }


    return indicesMap;
}

export function offsetPolygon(vertices, holes, offset, miterLimit, close) {
    const offsetVertices = miterLimit != null ? [] : new Float32Array(vertices.length);
    const exteriorSize = (holes && holes.length) ? holes[0] : vertices.length / 2;

    const offsetLines = [];

    innerOffsetPolygon(
        vertices, offsetVertices, 0, exteriorSize, 0, offset, miterLimit, close, true
    );

    if (holes) {
        for (let i = 0; i < holes.length; i++) {
            const start = holes[i];
            const end = holes[i + 1] || vertices.length / 2;
            innerOffsetPolygon(
                vertices, offsetVertices, start, end,
                miterLimit != null ? offsetVertices.length / 2 : start,
                offset, miterLimit, close, false
            );
        }
    }

    // TODO holes
    // Remove intersections of offseted polygon
    // let len = offsetLines.length;
    // let tmpIntersection = [];
    // for (let i = 0; i < len; i++) {
    //     const line1 = offsetLines[i];
    //     for (let k = i + 1; k < len; k++) {
    //         const line2 = offsetLines[k];

    //         const t = lineIntersection(
    //             line1[0], line1[1], line1[2], line1[3],
    //             line2[0], line2[1], line2[2], line2[3], tmpIntersection, 0
    //         );
    //         // Use a eplison
    //         if (t >= -1e-2 && t <= 1 + 1e-2) {
    //             const cursor1 = line1[4] * 2;
    //             const cursor2 = line2[4] * 2;
    //             // Update
    //             offsetVertices[cursor1] = offsetVertices[cursor2] = line1[2] = line2[2] = tmpIntersection[0];
    //             offsetVertices[cursor1 + 1] = offsetVertices[cursor2 + 1] = line1[3] = line2[3]= tmpIntersection[1];
    //         }
    //     }
    // }
    return offsetVertices;
}

function reversePoints(points, stride, start, end) {
    for (let i = 0; i < Math.floor((end - start) / 2); i++) {
        for (let j = 0; j < stride; j++) {
            const a = (i + start) * stride + j;
            const b = (end - i - 1) * stride + j;
            const tmp = points[a];
            points[a] = points[b];
            points[b] = tmp;
        }
    }

    return points;
}

function convertToClockwise(vertices, holes) {
    let polygonVertexCount = vertices.length / 2;
    let start = 0;
    let end = holes && holes.length ? holes[0] : polygonVertexCount;
    if (area(vertices, start, end) > 0) {
        reversePoints(vertices, 2, start, end);
    }
    for (let h = 1; h < (holes ? holes.length : 0) + 1; h++) {
        start = holes[h - 1];
        end = holes[h] || polygonVertexCount;
        if (area(vertices, start, end) < 0) {
            reversePoints(vertices, 2, start, end);
        }
    }
}

function normalizeOpts(opts) {

    opts.depth = opts.depth || 1;
    opts.bevelSize = opts.bevelSize || 0;
    opts.bevelSegments = opts.bevelSegments == null ? 2 : opts.bevelSegments;
    opts.smoothBevel = opts.smoothBevel || false;
    opts.simplify = opts.simplify || 0;

    if (opts.smoothSide == null) {
        opts.smoothSide = 'auto'
    }
    if (opts.smoothSideThreshold == null) {
        opts.smoothSideThreshold = 0.9
    }

    // Normalize bevel options.
    if (typeof opts.depth === 'number') {
        opts.bevelSize = Math.min(!(opts.bevelSegments > 0) ? 0 : opts.bevelSize, opts.depth / 2);
    }
    if (!(opts.bevelSize > 0)) {
        opts.bevelSegments = 0;
    }
    opts.bevelSegments = Math.round(opts.bevelSegments);

    const boundingRect = opts.boundingRect;
    opts.translate = opts.translate || [0, 0];
    opts.scale = opts.scale || [1, 1];
    if (opts.fitRect) {
        let targetX = opts.fitRect.x == null
            ? (boundingRect.x || 0)
            : opts.fitRect.x;
        let targetY = opts.fitRect.y == null
            ? (boundingRect.y || 0)
            : opts.fitRect.y;
        let targetWidth = opts.fitRect.width;
        let targetHeight = opts.fitRect.height;
        if (targetWidth == null) {
            if (targetHeight != null) {
                targetWidth = targetHeight / boundingRect.height * boundingRect.width;
            }
            else {
                targetWidth = boundingRect.width;
                targetHeight = boundingRect.height;
            }
        }
        else if (targetHeight == null) {
            targetHeight = targetWidth / boundingRect.width * boundingRect.height;
        }
        opts.scale = [
            targetWidth / boundingRect.width,
            targetHeight / boundingRect.height
        ];
        opts.translate = [
            (targetX - boundingRect.x) * opts.scale[0],
            (targetY - boundingRect.y) * opts.scale[1]
        ];
    }
}

function generateNormal(indices, position) {

    function v3Set(p, a, b, c) {
        p[0] = a; p[1] = b; p[2] = c;
    }

    const p1 = [];
    const p2 = [];
    const p3 = [];

    const v21 = [];
    const v32 = [];

    const n = [];

    const len = indices.length;
    const normals = new Float32Array(position.length);

    for (let f = 0; f < len;) {
        const i1 = indices[f++] * 3;
        const i2 = indices[f++] * 3;
        const i3 = indices[f++] * 3;

        v3Set(p1, position[i1], position[i1 + 1], position[i1 + 2]);
        v3Set(p2, position[i2], position[i2 + 1], position[i2 + 2]);
        v3Set(p3, position[i3], position[i3 + 1], position[i3 + 2]);

        v3Sub(v21, p1, p2);
        v3Sub(v32, p2, p3);
        v3Cross(n, v21, v32);
        // Already be weighted by the triangle area
        for (let i = 0; i < 3; i++) {
            normals[i1 + i] = normals[i1 + i] + n[i];
            normals[i2 + i] = normals[i2 + i] + n[i];
            normals[i3 + i] = normals[i3 + i] + n[i];
        }
    }

    for (var i = 0; i < normals.length;) {
        v3Set(n, normals[i], normals[i+1], normals[i+2]);
        v3Normalize(n, n);
        normals[i++] = n[0];
        normals[i++] = n[1];
        normals[i++] = n[2];

    }

    return normals;
}
// 0,0----1,0
// 0,1----1,1
const quadToTriangle = [
    [0, 0], [1, 0], [1, 1],
    [0, 0], [1, 1], [0, 1]
];

// Add side vertices and indices. Include bevel.
function addExtrudeSide(
    out, {vertices, topVertices, splittedMap, depth, rect}, start, end,
    cursors, opts
) {
    const ringVertexCount = end - start;

    const splitBevel = opts.smoothBevel ? 1 : 2;
    const bevelSize = Math.min(depth / 2, opts.bevelSize);
    const bevelSegments = opts.bevelSegments;
    const vertexOffset = cursors.vertex;
    const size = Math.max(rect.width, rect.height, depth);

    const isDuplicateVertex = splittedMap
        ? (idx) => {
            const nextIdx = (idx + 1) % ringVertexCount;
            return splittedMap[idx + start] === splittedMap[nextIdx + start];
        }
        : (idx) => false;

    // Side vertices
    if (bevelSize > 0) {
        const v0 = [0, 0, 1];
        const v1 = [];
        const v2 = [0, 0, -1];
        const v = [];

        let ringCount = 0;
        let vLen = new Float32Array(ringVertexCount);
        for (let k = 0; k < 2; k++) {
            const z = (k === 0 ? (depth - bevelSize) : bevelSize);
            for (let s = 0; s <= bevelSegments * splitBevel; s++) {
                let uLen = 0;
                let prevX;
                let prevY;
                for (let i = 0; i < ringVertexCount; i++) {
                    const idx = (i % ringVertexCount + start) * 2;
                    const rawIdx = splittedMap ? splittedMap[idx / 2] * 2 : idx;
                    v1[0] = vertices[idx] - topVertices[rawIdx];
                    v1[1] = vertices[idx + 1] - topVertices[rawIdx + 1];
                    v1[2] = 0;
                    const l = Math.sqrt(v1[0] * v1[0] + v1[1] * v1[1]);
                    v1[0] /= l;
                    v1[1] /= l;

                    const t = (Math.floor(s / splitBevel) + (s % splitBevel)) / bevelSegments;
                    k === 0 ? slerp(v, v0, v1, t)
                        : slerp(v, v1, v2, t);

                    const t2 = k === 0  ? t : 1 - t;
                    const a = bevelSize * Math.sin(t2 * Math.PI / 2);
                    const b = l * Math.cos(t2 * Math.PI / 2);

                    // ellipse radius
                    const r = bevelSize * l / Math.sqrt(a * a + b * b);

                    const x = v[0] * r + topVertices[rawIdx];
                    const y = v[1] * r + topVertices[rawIdx + 1];
                    const zz = v[2] * r + z;
                    out.position[cursors.vertex * 3] = x;
                    out.position[cursors.vertex * 3 + 1] = y;
                    out.position[cursors.vertex * 3 + 2] = zz;

                    // TODO Cache and optimize
                    if (i > 0) {
                        uLen += Math.sqrt((prevX - x) * (prevX - x) + (prevY - y) * (prevY - y));
                    }
                    if (s > 0 || k > 0) {
                        let tmp = (cursors.vertex - ringVertexCount) * 3;
                        let prevX2 = out.position[tmp];
                        let prevY2 = out.position[tmp + 1];
                        let prevZ2 = out.position[tmp + 2];

                        vLen[i] += Math.sqrt(
                            (prevX2 - x) * (prevX2 - x)
                            + (prevY2 - y) * (prevY2 - y)
                            + (prevZ2 - zz) * (prevZ2 - zz)
                        );
                    }
                    out.uv[cursors.vertex * 2] = uLen / size;
                    out.uv[cursors.vertex * 2 + 1] = vLen[i] / size;

                    prevX = x;
                    prevY = y;
                    cursors.vertex++;
                    // Just ignore this face if vertex are duplicted in `splitVertices`
                    if (isDuplicateVertex(i)) {
                        continue;
                    }
                    if ((splitBevel > 1 && (s % splitBevel)) || (splitBevel === 1 && s >= 1)) {
                        for (let f = 0; f < 6; f++) {
                            const m = (quadToTriangle[f][0] + i) % ringVertexCount;
                            const n = quadToTriangle[f][1] + ringCount;
                            out.indices[cursors.index++] = (n - 1) * ringVertexCount + m + vertexOffset;
                        }
                    }
                }

                ringCount++;
            }
        }
    }
    else {
        for (let k = 0; k < 2; k++) {
            const z = k === 0 ? depth : 0;
            let uLen = 0;
            let prevX;
            let prevY;
            for (let i = 0; i < ringVertexCount; i++) {
                const idx = (i + start) * 2;
                const x = vertices[idx];
                const y = vertices[idx + 1];
                const vtx3 = cursors.vertex * 3;
                const vtx2 = cursors.vertex * 2;
                out.position[vtx3] = x;
                out.position[vtx3 + 1] = y;
                out.position[vtx3 + 2] = z;
                if (i > 0) {
                    uLen += Math.sqrt((prevX - x) * (prevX - x) + (prevY - y) * (prevY - y));
                }
                out.uv[vtx2] = uLen / size;
                out.uv[vtx2 + 1] = z / size;
                prevX = x;
                prevY = y;

                cursors.vertex++;
            }
        }
    }
    // Connect the side
    const sideStartRingN = bevelSize > 0 ? (bevelSegments * splitBevel + 1) : 1;
    for (let i = 0; i < ringVertexCount; i++) {
        // Just ignore this face if vertex are duplicted in `splitVertices`
        if (isDuplicateVertex(i)) {
            continue;
        }
        for (let f = 0; f < 6; f++) {
            const m = (quadToTriangle[f][0] + i) % ringVertexCount;
            const n = quadToTriangle[f][1] + sideStartRingN;
            out.indices[cursors.index++] = (n - 1) * ringVertexCount + m + vertexOffset;
        }
    }
}

function addTopAndBottom({indices, topVertices, rect, depth}, out, cursors, opts) {
    if (topVertices.length <= 4) {
        return;
    }

    const vertexOffset = cursors.vertex;
    // Top indices
    const indicesLen = indices.length;
    for (let i = 0; i < indicesLen; i++) {
        out.indices[cursors.index++] = vertexOffset + indices[i];
    }
    const size = Math.max(rect.width, rect.height);
    // Top and bottom vertices
    for (let k = 0; k < (opts.excludeBottom ? 1 : 2); k++) {
        for (let i = 0; i < topVertices.length; i += 2) {
            const x = topVertices[i];
            const y = topVertices[i + 1];
            const vtx3 = cursors.vertex * 3;
            const vtx2 = cursors.vertex * 2;
            out.position[vtx3] = x;
            out.position[vtx3 + 1] = y;
            out.position[vtx3 + 2] = (1 - k) * depth;

            out.uv[vtx2] = (x - rect.x) / size;
            out.uv[vtx2 + 1] = (y - rect.y) / size;
            cursors.vertex++;
        }
    }
    // Bottom indices
    if (!opts.excludeBottom) {
        const vertexCount = topVertices.length / 2;
        for (let i = 0; i < indicesLen; i += 3) {
            for (let k = 0; k < 3; k++) {
                out.indices[cursors.index++] = vertexOffset + vertexCount + indices[i + 2 - k];
            }
        }
    }
}

/**
 * Split vertices for sharp side.
 */
 function splitVertices(vertices, holes, smoothSide, smoothSideThreshold) {
    const isAutoSmooth = smoothSide == null || smoothSide === 'auto';
    if (smoothSide === true) {
        return {vertices, holes};
    }
    const newVertices = [];
    const newHoles = holes && [];
    const count = vertices.length / 2;
    const v1 = [];
    const v2 = [];

    // Map of splitted index to raw index
    const splittedMap = [];

    let start = 0;
    let end = 0;

    const polysCount = (holes ? holes.length : 0) + 1;
    for (let h = 0; h < polysCount; h++) {
        if (h === 0) {
            end = holes && holes.length ? holes[0] : count;
        }
        else {
            start = holes[h - 1];
            end = holes[h] || count;
        }

        for (let i = start; i < end; i++) {
            const x2 = vertices[i * 2];
            const y2 = vertices[i * 2 + 1];
            const nextIdx = i === end - 1 ? start : i + 1;
            const x3 = vertices[nextIdx * 2];
            const y3 = vertices[nextIdx * 2 + 1];

            if (isAutoSmooth) {
                const prevIdx = i === start ? end - 1 : i - 1;
                const x1 = vertices[prevIdx * 2];
                const y1 = vertices[prevIdx * 2 + 1];

                v1[0] = x1 - x2;
                v1[1] = y1 - y2;
                v2[0] = x3 - x2;
                v2[1] = y3 - y2;

                v2Normalize(v1, v1);
                v2Normalize(v2, v2);

                const angleCos = v2Dot(v1, v2) * 0.5 + 0.5;

                if ((1 - angleCos) > smoothSideThreshold) {
                    newVertices.push(x2, y2);
                    splittedMap.push(i);
                }
                else {
                    newVertices.push(x2, y2, x2, y2);
                    splittedMap.push(i, i);
                }
            }
            else {
                newVertices.push(x2, y2, x2, y2);
                splittedMap.push(i, i);
            }
        }

        if (h < polysCount - 1 && newHoles) {
            newHoles.push(newVertices.length / 2);
        }
    }

    return {
        vertices: new Float32Array(newVertices),
        splittedMap,
        holes: newHoles
    };
}

function innerExtrudeTriangulatedPolygon(preparedData, opts) {
    let indexCount = 0;
    let vertexCount = 0;

    for (let p = 0; p < preparedData.length; p++) {
        let {indices, vertices, splittedMap, topVertices, holes, depth} = preparedData[p];
        const bevelSize = Math.min(depth / 2, opts.bevelSize);
        const bevelSegments = !(bevelSize > 0) ? 0 : opts.bevelSegments;

        holes = holes || [];

        indexCount += indices.length * (opts.excludeBottom ? 1 : 2);
        vertexCount += topVertices.length / 2 * (opts.excludeBottom ? 1 : 2);
        const ringCount = 2 + bevelSegments * 2;

        let start = 0;
        let end = 0;
        for (let h = 0; h < holes.length + 1; h++) {
            if (h === 0) {
                end = holes.length ? holes[0] : vertices.length / 2;
            }
            else {
                start = holes[h - 1];
                end = holes[h] || vertices.length / 2;
            }

            const faceEnd = splittedMap ? splittedMap[end - 1] + 1 : end;
            const faceStart = splittedMap ? splittedMap[start] : start;
            indexCount += (faceEnd - faceStart) * 6 * (ringCount - 1);

            const sideRingVertexCount = end - start;
            vertexCount += sideRingVertexCount * ringCount
                // Double the bevel vertex number if not smooth
                + (!opts.smoothBevel ? bevelSegments * sideRingVertexCount * 2 : 0);
        }
    }

    const data = {
        position: new Float32Array(vertexCount * 3),
        indices: new (vertexCount > 0xffff ? Uint32Array : Uint16Array)(indexCount),
        uv: new Float32Array(vertexCount * 2)
    };

    const cursors = {
        vertex: 0, index: 0
    };

    for (let d = 0; d < preparedData.length; d++) {
        addTopAndBottom(preparedData[d], data, cursors, opts);
    }

    for (let d = 0; d < preparedData.length; d++) {
        const {holes, vertices} = preparedData[d];
        const vertexCount = vertices.length / 2;

        let start = 0;
        let end = (holes && holes.length) ? holes[0] : vertexCount;
        // Add exterior
        addExtrudeSide(data, preparedData[d], start, end, cursors, opts);
        // Add holes
        if (holes) {
            for (let h = 0; h < holes.length; h++) {
                start = holes[h];
                end = holes[h + 1] || vertexCount;
                addExtrudeSide(data, preparedData[d], start, end, cursors, opts);
            }
        }
    }

    // Wrap uv
    for (let i = 0; i < data.uv.length; i++) {
        const val = data.uv[i];
        if (val > 0 && Math.round(val) === val) {
            data.uv[i] = 1;
        }
        else {
            data.uv[i] = val % 1;
        }
    }

    data.normal = generateNormal(data.indices, data.position);
    // PENDING
    data.boundingRect = preparedData[0] && preparedData[0].rect;

    return data;
}

function convertPolylineToTriangulatedPolygon(polyline, polylineIdx, opts) {
    const lineWidth = opts.lineWidth;
    const pointCount = polyline.length;
    const points = new Float32Array(pointCount * 2);
    const translate = opts.translate || [0, 0];
    const scale = opts.scale || [1, 1];
    for (let i = 0, k = 0; i < pointCount; i++) {
        points[k++] = polyline[i][0] * scale[0] + translate[0];
        points[k++] = polyline[i][1] * scale[1] + translate[1];
    }

    if (area(points, 0, pointCount) < 0) {
        reversePoints(points, 2, 0, pointCount);
    }

    const insidePoints = [];
    const outsidePoints = [];
    const miterLimit = opts.miterLimit;
    const outsideIndicesMap = innerOffsetPolygon(
        points, outsidePoints, 0, pointCount, 0, -lineWidth / 2, miterLimit, false, true
    );
    reversePoints(points, 2, 0, pointCount);
    const insideIndicesMap = innerOffsetPolygon(
        points, insidePoints, 0, pointCount, 0, -lineWidth / 2, miterLimit, false, true
    );

    const polygonVertexCount = (insidePoints.length + outsidePoints.length) / 2;
    const polygonVertices = new Float32Array(polygonVertexCount * 2);

    let offset = 0;
    const outsidePointCount = outsidePoints.length / 2;
    for (let i = 0; i < outsidePoints.length; i++) {
        polygonVertices[offset++] = outsidePoints[i];
    }
    for (let i = 0; i < insidePoints.length; i++) {
        polygonVertices[offset++] = insidePoints[i];
    }

    // Built indices
    const indices = new (polygonVertexCount > 0xffff ? Uint32Array : Uint16Array)(
        ((pointCount - 1) * 2 + (polygonVertexCount - pointCount * 2)) * 3
    );
    let off = 0;
    for (let i = 0; i < pointCount - 1; i++) {
        const i2 = i + 1;
        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[i];
        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[i] - 1;
        indices[off++] = insideIndicesMap[i] + 1 + outsidePointCount;

        indices[off++] = outsidePointCount - 1 - outsideIndicesMap[i];
        indices[off++] = insideIndicesMap[i] + 1 + outsidePointCount;
        indices[off++] = insideIndicesMap[i] + outsidePointCount;

        if (insideIndicesMap[i2] - insideIndicesMap[i] === 2) {
            indices[off++] = insideIndicesMap[i] + 2 + outsidePointCount;
            indices[off++] = insideIndicesMap[i] + 1 + outsidePointCount;
            indices[off++] = outsidePointCount - outsideIndicesMap[i2] - 1;
        }
        else if (outsideIndicesMap[i2] - outsideIndicesMap[i] === 2) {
            indices[off++] = insideIndicesMap[i2] + outsidePointCount;
            indices[off++] = outsidePointCount - 1 - (outsideIndicesMap[i] + 1);
            indices[off++] = outsidePointCount - 1 - (outsideIndicesMap[i] + 2);
        }
    }

    const topVertices = opts.bevelSize > 0
        ? offsetPolygon(polygonVertices, [], opts.bevelSize, null, true) : polygonVertices;
    const boundingRect = opts.boundingRect;

    const res = splitVertices(polygonVertices, null, opts.smoothSide, opts.smoothSideThreshold);
    return {
        vertices: res.vertices,
        rawVertices: vertices,
        splittedMap: res.splittedMap,
        indices,
        topVertices,
        rect: {
            x: boundingRect.x * scale[0] + translate[0],
            y: boundingRect.y * scale[1] + translate[1],
            width: boundingRect.width * scale[0],
            height: boundingRect.height * scale[1],
        },
        depth: typeof opts.depth === 'function' ? opts.depth(polylineIdx) : opts.depth,
        holes: []
    };
}

function removeClosePointsOfPolygon(polygon, epsilon) {
    const newPolygon = [];
    for (let k  = 0; k < polygon.length; k++) {
        const points = polygon[k];
        const newPoints = [];
        const len = points.length;
        let x1 = points[len - 1][0];
        let y1 = points[len - 1][1];
        let dist = 0;
        for (let i = 0; i < len; i++) {
            let x2 = points[i][0];
            let y2 = points[i][1];
            const dx = x2 - x1;
            const dy = y2 - y1;
            dist += Math.sqrt(dx * dx + dy * dy);
            if (dist > epsilon) {
                newPoints.push(points[i]);
                dist = 0;
            }
            x1 = x2;
            y1 = y2;
        }
        if (newPoints.length >= 3) {
            newPolygon.push(newPoints);
        }
    }
    return newPolygon.length > 0 ? newPolygon : null;
}

function simplifyPolygon(polygon, tolerance) {
    const newPolygon = [];
    for (let k  = 0; k < polygon.length; k++) {
        let points = polygon[k];
        points = doSimplify(points, tolerance, true);
        if (points.length >= 3) {
            newPolygon.push(points);
        }
    }
    return newPolygon.length > 0 ? newPolygon : null;
}
/**
 *
 * @param {Array} polygons Polygons array that match GeoJSON MultiPolygon geometry.
 * @param {Object} [opts]
 * @param {number|Function} [opts.depth]
 * @param {number} [opts.bevelSize = 0]
 * @param {number} [opts.bevelSegments = 2]
 * @param {number} [opts.simplify = 0]
 * @param {boolean} [opts.smoothSide = 'auto']
 * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
 * @param {boolean} [opts.smoothBevel = false]
 * @param {boolean} [opts.excludeBottom = false]
 * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
 * @param {Array} [opts.translate]
 * @param {Array} [opts.scale]
 *
 * @return {Object} {indices, position, uv, normal, boundingRect}
 */
export function extrudePolygon(polygons, opts) {

    opts = Object.assign({}, opts);

    const min = [Infinity, Infinity];
    const max = [-Infinity, -Infinity];
    for (let i = 0; i < polygons.length; i++) {
        updateBoundingRect(polygons[i][0], min, max);
    }
    opts.boundingRect = opts.boundingRect || {
        x: min[0], y: min[1], width: max[0] - min[0], height: max[1] - min[1]
    };

    normalizeOpts(opts);

    const preparedData = [];
    const translate = opts.translate || [0, 0];
    const scale = opts.scale || [1, 1];
    const boundingRect = opts.boundingRect;
    const transformdRect = {
        x: boundingRect.x * scale[0] + translate[0],
        y: boundingRect.y * scale[1] + translate[1],
        width: boundingRect.width * scale[0],
        height: boundingRect.height * scale[1],
    };

    const epsilon = Math.min(
        boundingRect.width, boundingRect.height
    ) / 1e5;
    for (let i = 0; i < polygons.length; i++) {
        let newPolygon = removeClosePointsOfPolygon(polygons[i], epsilon);
        if (!newPolygon) {
            continue;
        }
        const simplifyTolerance = opts.simplify / Math.max(scale[0], scale[1]);
        if (simplifyTolerance > 0) {
            newPolygon = simplifyPolygon(newPolygon, simplifyTolerance);
        }
        if (!newPolygon) {
            continue;
        }

        const {vertices, holes, dimensions} = earcut.flatten(newPolygon);

        for (let k = 0; k < vertices.length;) {
            vertices[k] = vertices[k++] * scale[0] + translate[0];
            vertices[k] = vertices[k++] * scale[1] + translate[1];
        }

        convertToClockwise(vertices, holes);

        if (dimensions !== 2) {
            throw new Error('Only 2D polygon points are supported');
        }
        const topVertices = opts.bevelSize > 0
            ? offsetPolygon(vertices, holes, opts.bevelSize, null, true) : vertices;
        const indices = triangulate(topVertices, holes, dimensions);
        const res = splitVertices(vertices, holes, opts.smoothSide, opts.smoothSideThreshold)

        preparedData.push({
            indices,
            vertices: res.vertices,
            rawVertices: vertices,
            topVertices,
            holes: res.holes,
            splittedMap: res.splittedMap,
            rect: transformdRect,
            depth: typeof opts.depth === 'function' ? opts.depth(i) : opts.depth
        });
    }
    return innerExtrudeTriangulatedPolygon(preparedData, opts);
};

/**
 *
 * @param {Array} polylines Polylines array that match GeoJSON MultiLineString geometry.
 * @param {Object} [opts]
 * @param {number} [opts.depth]
 * @param {number} [opts.bevelSize = 0]
 * @param {number} [opts.bevelSegments = 2]
 * @param {number} [opts.simplify = 0]
 * @param {boolean} [opts.smoothSide = 'auto']
 * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
 * @param {boolean} [opts.smoothBevel = false]
 * @param {boolean} [opts.excludeBottom = false]
 * @param {boolean} [opts.lineWidth = 1]
 * @param {boolean} [opts.miterLimit = 2]
 * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
 * @param {Array} [opts.translate]
 * @param {Array} [opts.scale]
 * @param {Object} [opts.boundingRect]
 * @return {Object} {indices, position, uv, normal, boundingRect}
 */
export function extrudePolyline(polylines, opts) {

    opts = Object.assign({}, opts);

    const min = [Infinity, Infinity];
    const max = [-Infinity, -Infinity];
    for (let i = 0; i < polylines.length; i++) {
        updateBoundingRect(polylines[i], min, max);
    }
    opts.boundingRect = opts.boundingRect || {
        x: min[0], y: min[1], width: max[0] - min[0], height: max[1] - min[1]
    };

    normalizeOpts(opts);
    const scale = opts.scale || [1, 1];

    if (opts.lineWidth == null) {
        opts.lineWidth = 1;
    }
    if (opts.miterLimit == null) {
        opts.miterLimit = 2;
    }
    const preparedData = [];
    // Extrude polyline to polygon
    for (let i = 0; i < polylines.length; i++) {
        let newPolyline = polylines[i];
        const simplifyTolerance = opts.simplify / Math.max(scale[0], scale[1]);
        if (simplifyTolerance > 0) {
            newPolyline = doSimplify(newPolyline, simplifyTolerance, true);
        }
        preparedData.push(convertPolylineToTriangulatedPolygon(newPolyline, i, opts));
    }

    return innerExtrudeTriangulatedPolygon(preparedData, opts);
}

function updateBoundingRect(points, min, max) {
    for (let i = 0; i < points.length; i++) {
        min[0] = Math.min(points[i][0], min[0]);
        min[1] = Math.min(points[i][1], min[1]);
        max[0] = Math.max(points[i][0], max[0]);
        max[1] = Math.max(points[i][1], max[1]);
    }
}

/**
 *
 * @param {Object} geojson
 * @param {Object} [opts]
 * @param {number} opts.depth
 * @param {number} [opts.bevelSize = 0]
 * @param {number} [opts.bevelSegments = 2]
 * @param {number} [opts.simplify = 0]
 * @param {boolean} [opts.smoothSide = 'auto']
 * @param {boolean} [opts.smoothSideThreshold = 0.9]    // Will not smooth sharp side.
 * @param {boolean} [opts.smoothBevel = false]
 * @param {boolean} [opts.excludeBottom = false]
 * @param {boolean} [opts.lineWidth = 1]
 * @param {boolean} [opts.miterLimit = 2]
 * @param {Object} [opts.fitRect] translate and scale will be ignored if fitRect is set
 * @param {Array} [opts.translate]
 * @param {Array} [opts.scale]
 * @param {Object} [opts.boundingRect]
 * @return {Object} {polyline: {indices, position, uv, normal}, polygon: {indices, position, uv, normal}}
 */

 // TODO Not merge feature
export function extrudeGeoJSON(geojson, opts) {

    opts = Object.assign({}, opts);

    const polylines = [];
    const polygons = [];

    const polylineFeatureIndices = [];
    const polygonFeatureIndices = [];

    const min = [Infinity, Infinity];
    const max = [-Infinity, -Infinity];

    if (geojson.type === 'LineString' || geojson.type === 'MultiLineString' || geojson.type === 'Polygon' || geojson.type === 'MultiPolygon') {
        geojson = {
            features: [{
                geometry: geojson
            }]
        }
    }

    for (let i = 0; i < geojson.features.length; i++) {
        const feature = geojson.features[i];
        const geometry = feature.geometry;
        if (geometry && geometry.coordinates) {
            switch (geometry.type) {
                case 'LineString':
                    polylines.push(geometry.coordinates);
                    polylineFeatureIndices.push(i);
                    updateBoundingRect(geometry.coordinates, min, max);
                    break;
                case 'MultiLineString':
                    for (let k = 0; k < geometry.coordinates.length; k++) {
                        polylines.push(geometry.coordinates[k]);
                        polylineFeatureIndices.push(i);
                        updateBoundingRect(geometry.coordinates[k], min, max);
                    }
                    break;
                case 'Polygon':
                    polygons.push(geometry.coordinates);
                    polygonFeatureIndices.push(i);
                    updateBoundingRect(geometry.coordinates[0], min, max);
                    break;
                case 'MultiPolygon':
                    for (let k = 0; k < geometry.coordinates.length; k++) {
                        polygons.push(geometry.coordinates[k]);
                        polygonFeatureIndices.push(i);
                        updateBoundingRect(geometry.coordinates[k][0], min, max);
                    }
                    break;
            }
        }
    }

    opts.boundingRect = opts.boundingRect || {
        x: min[0], y: min[1], width: max[0] - min[0], height: max[1] - min[1]
    };

    const originalDepth = opts.depth;
    return {
        polyline: extrudePolyline(polylines, Object.assign(opts, {
            depth: function (idx) {
                if (typeof originalDepth === 'function') {
                    return originalDepth(
                        geojson.features[polylineFeatureIndices[idx]]
                    );
                }
                return originalDepth;
            }
        })),
        polygon: extrudePolygon(polygons, Object.assign(opts, {
            depth: function (idx) {
                if (typeof originalDepth === 'function') {
                    return originalDepth(
                        geojson.features[polygonFeatureIndices[idx]]
                    );
                }
                return originalDepth;
            }
        }))
    };
}

================================================
FILE: src/math.js
================================================
export function dot(v1, v2) {
    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];
}
export function v2Dot(v1, v2) {
    return v1[0] * v2[0] + v1[1] * v2[1];
}

export function normalize(out, v) {
    const x = v[0];
    const y = v[1];
    const z = v[2];
    const d = Math.sqrt(x * x + y * y + z * z);
    out[0] = x / d;
    out[1] = y / d;
    out[2] = z / d;
    return out;
}

export function v2Normalize(out, v) {
    const x = v[0];
    const y = v[1];
    const d = Math.sqrt(x * x + y * y);
    out[0] = x / d;
    out[1] = y / d;
    return out;
}

export function scale(out, v, s) {
    out[0] = v[0] * s;
    out[1] = v[1] * s;
    out[2] = v[2] * s;
    return out;
}

export function mul(out, v1, v2) {
    out[0] = v1[0] * v2[0];
    out[1] = v1[1] * v2[1];
    out[2] = v1[2] * v2[2];
    return out;
}

export function scaleAndAdd(out, v1, v2, s) {
    out[0] = v1[0] + v2[0] * s;
    out[1] = v1[1] + v2[1] * s;
    out[2] = v1[2] + v2[2] * s;
    return out;
}

export function add(out, v1, v2) {
    out[0] = v1[0] + v2[0];
    out[1] = v1[1] + v2[1];
    out[2] = v1[2] + v2[2];
    return out;
}

export function v2Add(out, v1, v2) {
    out[0] = v1[0] + v2[0];
    out[1] = v1[1] + v2[1];
    return out;
}

export function sub(out, v1, v2) {
    out[0] = v1[0] - v2[0];
    out[1] = v1[1] - v2[1];
    out[2] = v1[2] - v2[2];
    return out;
}

export function v2Sub(out, v1, v2) {
    out[0] = v1[0] - v2[0];
    out[1] = v1[1] - v2[1];
    return out;
}

export function v3Sub(out, v1, v2) {
    out[0] = v1[0] - v2[0];
    out[1] = v1[1] - v2[1];
    out[2] = v1[2] - v2[2];
    return out;
}

export function v3Normalize(out, v) {
    const x = v[0];
    const y = v[1];
    const z = v[2];
    const d = Math.sqrt(x * x + y * y + z * z);
    out[0] = x / d;
    out[1] = y / d;
    out[2] = z / d;
    return out;
}

export function v3Cross(out, v1, v2) {
    var ax = v1[0], ay = v1[1], az = v1[2],
        bx = v2[0], by = v2[1], bz = v2[2];

    out[0] = ay * bz - az * by;
    out[1] = az * bx - ax * bz;
    out[2] = ax * by - ay * bx;
    return out;
}

const rel = [];
// start and end must be normalized
export function slerp(out, start, end, t) {
    // https://keithmaggio.wordpress.com/2011/02/15/math-magician-lerp-slerp-and-nlerp/
    const cosT = dot(start, end);
    const theta = Math.acos(cosT) * t;

    scaleAndAdd(rel, end, start, -cosT);
    normalize(rel, rel);// start and rel Orthonormal basis

    scale(out, start, Math.cos(theta));
    scaleAndAdd(out, out, rel, Math.sin(theta));

    return out;
}

export function lineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, out, writeOffset) {
    const dx1 = x2 - x1;
    const dx2 = x4 - x3;
    const dy1 = y2 - y1;
    const dy2 = y4 - y3;

    const cross = dy2 * dx1 - dx2 * dy1;
    const tmp1 = y1 - y3;
    const tmp2 = x1 - x3;
    const t1 = (dx2 * tmp1 - dy2 * tmp2) / cross;
    // const t2 = (dx1 * tmp1 - dy1 * tmp2) / cross;

    if (out) {
        writeOffset = writeOffset || 0;
        out[writeOffset] = x1 + t1 * (x2 - x1);
        out[writeOffset + 1] = y1 + t1 * (y2 - y1);
    }

    return t1;
}

export function area(points, start, end) {
    // Signed polygon area
    const n = end - start;
    if (n < 3) {
        return 0;
    }
    let area = 0;
    for (let i = (end - 1) * 2, j = start * 2; j < end * 2;) {
        const x0 = points[i];
        const y0 = points[i + 1];
        const x1 = points[j];
        const y1 = points[j + 1];
        i = j;
        j += 2;
        area += x0 * y1 - x1 * y0;
    }

    return area;
}


export function triangleArea(x0, y0, x1, y1, x2, y2) {
    return (x1 - x0) * (y2 - y1) - (y1 - y0) * (x2 - x1);
}

================================================
FILE: src/simplify.js
================================================
/*
 (c) 2017, Vladimir Agafonkin
 Simplify.js, a high-performance JS polyline simplification library
 mourner.github.io/simplify-js
*/

// to suit your point format, run search/replace for '.x' and '.y';
// for 3D version, see 3d branch (configurability would draw significant performance overhead)

// square distance between 2 points
function getSqDist(p1, p2) {

    var dx = p1[0] - p2[0],
        dy = p1[1] - p2[1];

    return dx * dx + dy * dy;
}

// square distance from a point to a segment
function getSqSegDist(p, p1, p2) {

    var x = p1[0],
        y = p1[1],
        dx = p2[0] - x,
        dy = p2[1] - y;

    if (dx !== 0 || dy !== 0) {

        var t = ((p[0] - x) * dx + (p[1] - y) * dy) / (dx * dx + dy * dy);

        if (t > 1) {
            x = p2[0];
            y = p2[1];

        } else if (t > 0) {
            x += dx * t;
            y += dy * t;
        }
    }

    dx = p[0] - x;
    dy = p[1] - y;

    return dx * dx + dy * dy;
}
// rest of the code doesn't care about point format

// basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {

    var prevPoint = points[0],
        newPoints = [prevPoint],
        point;

    for (var i = 1, len = points.length; i < len; i++) {
        point = points[i];

        if (getSqDist(point, prevPoint) > sqTolerance) {
            newPoints.push(point);
            prevPoint = point;
        }
    }

    if (prevPoint !== point) newPoints.push(point);

    return newPoints;
}

function simplifyDPStep(points, first, last, sqTolerance, simplified) {
    var maxSqDist = sqTolerance,
        index;

    for (var i = first + 1; i < last; i++) {
        var sqDist = getSqSegDist(points[i], points[first], points[last]);

        if (sqDist > maxSqDist) {
            index = i;
            maxSqDist = sqDist;
        }
    }

    if (maxSqDist > sqTolerance) {
        if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
        simplified.push(points[index]);
        if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
    }
}

// simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
    var last = points.length - 1;

    var simplified = [points[0]];
    simplifyDPStep(points, 0, last, sqTolerance, simplified);
    simplified.push(points[last]);

    return simplified;
}

// both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {

    if (points.length <= 2) return points;

    var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;

    points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
    points = simplifyDouglasPeucker(points, sqTolerance);

    return points;
}
export default simplify;

================================================
FILE: test/asset/buildings-ny.geojson
================================================
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00942675, 40.70343591 ], [ -74.00930593, 40.70351579 ], [ -74.00937519, 40.70357599 ], [ -74.00890672, 40.70388592 ], [ -74.00867082, 40.70368087 ], [ -74.00853347, 40.70356156 ], [ -74.00812177, 40.70320389 ], [ -74.00819148, 40.70315779 ], [ -74.008209, 40.70317297 ], [ -74.0086109, 40.70290711 ], [ -74.00862959, 40.70292338 ], [ -74.0089033, 40.70274237 ], [ -74.0089298, 40.70276539 ], [ -74.00922849, 40.70256776 ], [ -74.01000401, 40.70324169 ], [ -74.00954937, 40.70354242 ], [ -74.00942675, 40.70343591 ] ] ] } },
{ "type": "Feature", "properties": { "height": 15.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01350187, 40.70067477 ], [ -74.01350843, 40.70071931 ], [ -74.01355101, 40.70100508 ], [ -74.01354481, 40.701052 ], [ -74.01352747, 40.70123676 ], [ -74.01336263, 40.70145258 ], [ -74.01284601, 40.70141158 ], [ -74.01252989, 40.70090728 ], [ -74.0124609, 40.70092342 ], [ -74.01243261, 40.7008532 ], [ -74.01239003, 40.70074792 ], [ -74.0123567, 40.70067716 ], [ -74.01239209, 40.70067062 ], [ -74.01262314, 40.70061852 ], [ -74.0128116, 40.70057459 ], [ -74.01289991, 40.70056056 ], [ -74.01313742, 40.70054299 ], [ -74.01326813, 40.70053196 ], [ -74.01350151, 40.70051568 ], [ -74.013544, 40.70051146 ], [ -74.01355343, 40.70057228 ], [ -74.01356844, 40.70066871 ], [ -74.01350187, 40.70067477 ] ] ] } },
{ "type": "Feature", "properties": { "height": 10.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00942675, 40.70343591 ], [ -74.00930593, 40.70351579 ], [ -74.00937519, 40.70357599 ], [ -74.00890672, 40.70388592 ], [ -74.00867082, 40.70368087 ], [ -74.00853347, 40.70356156 ], [ -74.00812177, 40.70320389 ], [ -74.00819148, 40.70315779 ], [ -74.008209, 40.70317297 ], [ -74.0086109, 40.70290711 ], [ -74.00862959, 40.70292338 ], [ -74.0089033, 40.70274237 ], [ -74.0089298, 40.70276539 ], [ -74.00877377, 40.70286849 ], [ -74.00942675, 40.70343591 ] ] ] } },
{ "type": "Feature", "properties": { "height": 29.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01426319, 40.70367031 ], [ -74.014234, 40.70380161 ], [ -74.01422124, 40.70380011 ], [ -74.01411749, 40.70432047 ], [ -74.01413231, 40.70432217 ], [ -74.01410644, 40.70445251 ], [ -74.01408092, 40.70444959 ], [ -74.01407841, 40.70446218 ], [ -74.013726, 40.70445646 ], [ -74.01339308, 40.70445531 ], [ -74.01339551, 40.70444339 ], [ -74.01336811, 40.70444359 ], [ -74.01334709, 40.70431366 ], [ -74.01336119, 40.70431189 ], [ -74.01327774, 40.7037854 ], [ -74.01325977, 40.70378561 ], [ -74.01324091, 40.70364988 ], [ -74.01355038, 40.70365642 ], [ -74.01354697, 40.70368829 ], [ -74.01395094, 40.70369388 ], [ -74.01395741, 40.70366378 ], [ -74.01426319, 40.70367031 ] ], [ [ -74.01369249, 40.70422097 ], [ -74.01369132, 40.70419911 ], [ -74.01367372, 40.70418549 ], [ -74.01365638, 40.70416929 ], [ -74.01364147, 40.70415178 ], [ -74.01362907, 40.70413319 ], [ -74.01362045, 40.7041163 ], [ -74.01361299, 40.70409621 ], [ -74.01360832, 40.70407558 ], [ -74.01360652, 40.70405481 ], [ -74.01359305, 40.7040559 ], [ -74.01361641, 40.7042205 ], [ -74.01369249, 40.70422097 ] ], [ [ -74.01391995, 40.70405801 ], [ -74.01390009, 40.7040576 ], [ -74.01389884, 40.70406979 ], [ -74.01389443, 40.70409056 ], [ -74.01388725, 40.70411086 ], [ -74.01387512, 40.70413408 ], [ -74.01386191, 40.7041526 ], [ -74.01384637, 40.70416997 ], [ -74.0138285, 40.70418611 ], [ -74.01380847, 40.70420061 ], [ -74.0138073, 40.70423418 ], [ -74.01388779, 40.70423541 ], [ -74.01391995, 40.70405801 ] ], [ [ -74.01364812, 40.70387857 ], [ -74.01364722, 40.70381762 ], [ -74.01354265, 40.70381571 ], [ -74.01356799, 40.70399318 ], [ -74.01359314, 40.70399331 ], [ -74.01359529, 40.70397629 ], [ -74.01360077, 40.7039545 ], [ -74.01360931, 40.70393332 ], [ -74.01361829, 40.7039167 ], [ -74.01363195, 40.70389709 ], [ -74.01364812, 40.70387857 ] ], [ [ -74.01395202, 40.70382688 ], [ -74.0138435, 40.70382048 ], [ -74.013847, 40.70388408 ], [ -74.01385769, 40.7038975 ], [ -74.01387144, 40.7039184 ], [ -74.01388213, 40.70394026 ], [ -74.01388743, 40.70395477 ], [ -74.013893, 40.70397772 ], [ -74.01389542, 40.70400101 ], [ -74.01392048, 40.70400169 ], [ -74.01395202, 40.70382688 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01289739, 40.70490238 ], [ -74.01268018, 40.70492199 ], [ -74.0126341, 40.70462937 ], [ -74.01216293, 40.70467207 ], [ -74.01215494, 40.70462181 ], [ -74.01214802, 40.70457332 ], [ -74.0121146, 40.7043614 ], [ -74.01219024, 40.70435459 ], [ -74.01236559, 40.70433872 ], [ -74.01235113, 40.70424631 ], [ -74.0123425, 40.70419026 ], [ -74.01233541, 40.70410718 ], [ -74.01287071, 40.70411406 ], [ -74.01287512, 40.70414266 ], [ -74.0130784, 40.70412461 ], [ -74.01320111, 40.70491416 ], [ -74.01290395, 40.70494079 ], [ -74.01289739, 40.70490238 ] ] ] } },
{ "type": "Feature", "properties": { "height": 195.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01156897, 40.70207436 ], [ -74.01150554, 40.70185609 ], [ -74.01235122, 40.70171471 ], [ -74.01241473, 40.70193311 ], [ -74.01249153, 40.70192017 ], [ -74.01260212, 40.70230039 ], [ -74.01159933, 40.70246799 ], [ -74.01148875, 40.70208777 ], [ -74.01156897, 40.70207436 ] ] ] } },
{ "type": "Feature", "properties": { "height": 29.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01137322, 40.70143249 ], [ -74.01122392, 40.70105336 ], [ -74.01114469, 40.70107127 ], [ -74.01108558, 40.70092138 ], [ -74.01112475, 40.70091259 ], [ -74.01112852, 40.70092226 ], [ -74.0114194, 40.70085641 ], [ -74.0114141, 40.70084278 ], [ -74.01146934, 40.70083032 ], [ -74.01147464, 40.70084367 ], [ -74.01178582, 40.70077332 ], [ -74.01178231, 40.7007644 ], [ -74.01183073, 40.70075336 ], [ -74.01183514, 40.7007644 ], [ -74.01215197, 40.70069268 ], [ -74.01214838, 40.70068342 ], [ -74.01218997, 40.70067396 ], [ -74.01220021, 40.7006999 ], [ -74.01219195, 40.70070181 ], [ -74.01224558, 40.70083822 ], [ -74.01222698, 40.70084278 ], [ -74.01224917, 40.7008989 ], [ -74.01217659, 40.70091538 ], [ -74.01216221, 40.70091872 ], [ -74.01225222, 40.70114727 ], [ -74.01227351, 40.70114251 ], [ -74.01227863, 40.70115552 ], [ -74.01230469, 40.70122158 ], [ -74.01137322, 40.70143249 ] ] ] } },
{ "type": "Feature", "properties": { "height": 209.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00942675, 40.70343591 ], [ -74.00877377, 40.70286849 ], [ -74.0089298, 40.70276539 ], [ -74.00922849, 40.70256776 ], [ -74.01000401, 40.70324169 ], [ -74.00954937, 40.70354242 ], [ -74.00942675, 40.70343591 ] ] ] } },
{ "type": "Feature", "properties": { "height": 65.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01289739, 40.70490238 ], [ -74.01289218, 40.70486949 ], [ -74.01275142, 40.70488222 ], [ -74.01271063, 40.70462297 ], [ -74.01270264, 40.70457216 ], [ -74.01215494, 40.70462181 ], [ -74.01214802, 40.70457332 ], [ -74.0121146, 40.7043614 ], [ -74.01219024, 40.70435459 ], [ -74.01236559, 40.70433872 ], [ -74.01235113, 40.70424631 ], [ -74.0123425, 40.70419026 ], [ -74.01287512, 40.70414266 ], [ -74.0130784, 40.70412461 ], [ -74.01320111, 40.70491416 ], [ -74.01290395, 40.70494079 ], [ -74.01289739, 40.70490238 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01450152, 40.70556042 ], [ -74.014431, 40.70572052 ], [ -74.01424038, 40.70615288 ], [ -74.01396289, 40.70605849 ], [ -74.0137605, 40.70598958 ], [ -74.01343135, 40.70587755 ], [ -74.01350367, 40.7057878 ], [ -74.01351687, 40.70579441 ], [ -74.01374073, 40.70551677 ], [ -74.01372699, 40.70551078 ], [ -74.01381502, 40.70540161 ], [ -74.01391905, 40.70542572 ], [ -74.0140776, 40.70546229 ], [ -74.01416357, 40.70548217 ], [ -74.01432212, 40.70551888 ], [ -74.01450152, 40.70556042 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01228914, 40.70555068 ], [ -74.0122409, 40.70564977 ], [ -74.01220147, 40.70564568 ], [ -74.0121323, 40.70563839 ], [ -74.01200132, 40.70562471 ], [ -74.01179318, 40.7056275 ], [ -74.01164748, 40.70562968 ], [ -74.01157723, 40.70562988 ], [ -74.01156861, 40.70563002 ], [ -74.01163975, 40.70504546 ], [ -74.01232014, 40.70507147 ], [ -74.01248408, 40.70507848 ], [ -74.01248049, 40.7051442 ], [ -74.01228914, 40.70555068 ] ] ] } },
{ "type": "Feature", "properties": { "height": 94.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01021547, 40.70258941 ], [ -74.01092442, 40.70246846 ], [ -74.0111384, 40.70243761 ], [ -74.01115367, 40.70253098 ], [ -74.01114694, 40.70253262 ], [ -74.01114469, 40.7025254 ], [ -74.01113095, 40.70252778 ], [ -74.01113328, 40.70253616 ], [ -74.0111216, 40.7025382 ], [ -74.01112852, 40.70256149 ], [ -74.01117532, 40.70255345 ], [ -74.01125743, 40.7028326 ], [ -74.01029084, 40.70299741 ], [ -74.01027153, 40.7029319 ], [ -74.01020892, 40.70271921 ], [ -74.0102462, 40.70271288 ], [ -74.01023622, 40.70267897 ], [ -74.01021853, 40.70268196 ], [ -74.01022077, 40.70269007 ], [ -74.01021359, 40.70269129 ], [ -74.01017352, 40.70259936 ], [ -74.01020883, 40.70259336 ], [ -74.01020802, 40.70259057 ], [ -74.01021547, 40.70258941 ] ] ] } },
{ "type": "Feature", "properties": { "height": 195.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01156897, 40.70207436 ], [ -74.01159717, 40.70206789 ], [ -74.0123858, 40.70193611 ], [ -74.01241473, 40.70193311 ], [ -74.01249153, 40.70192017 ], [ -74.01260212, 40.70230039 ], [ -74.01159933, 40.70246799 ], [ -74.01148875, 40.70208777 ], [ -74.01156897, 40.70207436 ] ] ] } },
{ "type": "Feature", "properties": { "height": 105.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00749609, 40.7042207 ], [ -74.00690967, 40.70462181 ], [ -74.00650328, 40.7042775 ], [ -74.0070852, 40.70387952 ], [ -74.0070897, 40.70387645 ], [ -74.00749609, 40.7042207 ] ] ] } },
{ "type": "Feature", "properties": { "height": 85.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01307445, 40.70478777 ], [ -74.01308451, 40.70485212 ], [ -74.01289218, 40.70486949 ], [ -74.01288212, 40.7048052 ], [ -74.01279561, 40.7048131 ], [ -74.01276489, 40.70461806 ], [ -74.01271063, 40.70462297 ], [ -74.01270264, 40.70457216 ], [ -74.01269491, 40.70452381 ], [ -74.01214802, 40.70457332 ], [ -74.0121146, 40.7043614 ], [ -74.01219024, 40.70435459 ], [ -74.01236559, 40.70433872 ], [ -74.01235113, 40.70424631 ], [ -74.01294357, 40.70419258 ], [ -74.01295273, 40.70425046 ], [ -74.01301507, 40.70424481 ], [ -74.01310032, 40.70478552 ], [ -74.01307445, 40.70478777 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00814441, 40.70383811 ], [ -74.0081056, 40.70386447 ], [ -74.00806679, 40.70389076 ], [ -74.0080279, 40.70391718 ], [ -74.00798963, 40.70394326 ], [ -74.00789459, 40.70400782 ], [ -74.00785668, 40.70403356 ], [ -74.00781787, 40.70405998 ], [ -74.00777897, 40.70408641 ], [ -74.00774008, 40.70411276 ], [ -74.00770181, 40.70413878 ], [ -74.00766596, 40.70410847 ], [ -74.0076312, 40.70407912 ], [ -74.00759644, 40.7040497 ], [ -74.00756158, 40.70402021 ], [ -74.00752673, 40.70399066 ], [ -74.00743501, 40.70391316 ], [ -74.00740024, 40.70388381 ], [ -74.00736539, 40.70385432 ], [ -74.00733062, 40.7038249 ], [ -74.00729568, 40.70379542 ], [ -74.00726118, 40.7037662 ], [ -74.00730098, 40.7037391 ], [ -74.00733988, 40.70371281 ], [ -74.00737868, 40.70368632 ], [ -74.00741749, 40.70365996 ], [ -74.00745648, 40.70363347 ], [ -74.00754972, 40.70357021 ], [ -74.00758853, 40.70354378 ], [ -74.00762743, 40.70351736 ], [ -74.00766632, 40.70349087 ], [ -74.00770513, 40.70346458 ], [ -74.0077434, 40.70343857 ], [ -74.00777951, 40.70346908 ], [ -74.00781419, 40.7034985 ], [ -74.00784913, 40.70352798 ], [ -74.00788381, 40.7035574 ], [ -74.00791875, 40.70358689 ], [ -74.0080102, 40.70366419 ], [ -74.00804496, 40.70369367 ], [ -74.00807982, 40.70372309 ], [ -74.00811458, 40.70375251 ], [ -74.00813201, 40.70376729 ], [ -74.00814953, 40.70378207 ], [ -74.00818402, 40.70381121 ], [ -74.00814441, 40.70383811 ] ] ] } },
{ "type": "Feature", "properties": { "height": 90.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01441761, 40.70571698 ], [ -74.01423175, 40.70613558 ], [ -74.01397151, 40.706048 ], [ -74.01401975, 40.7059893 ], [ -74.01399954, 40.70594586 ], [ -74.01387692, 40.7058939 ], [ -74.01382931, 40.70590309 ], [ -74.01376894, 40.70597936 ], [ -74.01353753, 40.70589887 ], [ -74.01358631, 40.70582907 ], [ -74.01351687, 40.70579441 ], [ -74.01374073, 40.70551677 ], [ -74.01382275, 40.70555266 ], [ -74.01391905, 40.70542572 ], [ -74.0140776, 40.70546229 ], [ -74.01416357, 40.70548217 ], [ -74.01400915, 40.70567871 ], [ -74.01402523, 40.70572032 ], [ -74.01414785, 40.70577411 ], [ -74.01419779, 40.70576546 ], [ -74.014264, 40.70567748 ], [ -74.01441761, 40.70571698 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00574447, 40.70538282 ], [ -74.00563047, 40.70546311 ], [ -74.00549438, 40.70555906 ], [ -74.00540338, 40.70555661 ], [ -74.00527537, 40.70545221 ], [ -74.00517215, 40.70536797 ], [ -74.00504585, 40.70526508 ], [ -74.0050489, 40.70519439 ], [ -74.00518392, 40.70509912 ], [ -74.00529783, 40.70501876 ], [ -74.00532289, 40.70501951 ], [ -74.00541191, 40.70495672 ], [ -74.00555394, 40.70495979 ], [ -74.0056346, 40.7050287 ], [ -74.00565284, 40.70502891 ], [ -74.00575615, 40.70511321 ], [ -74.00588245, 40.70521611 ], [ -74.00587931, 40.70528762 ], [ -74.00574447, 40.70538282 ] ] ] } },
{ "type": "Feature", "properties": { "height": 135.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01145542, 40.70416888 ], [ -74.01139882, 40.70421375 ], [ -74.01128644, 40.70421607 ], [ -74.01114631, 40.70424181 ], [ -74.01104543, 40.70428002 ], [ -74.01097033, 40.70426149 ], [ -74.01072392, 40.70388469 ], [ -74.01074934, 40.70382701 ], [ -74.01085013, 40.70378881 ], [ -74.01126812, 40.70371022 ], [ -74.01138059, 40.70370777 ], [ -74.01144024, 40.70374952 ], [ -74.01145542, 40.70416888 ] ] ] } },
{ "type": "Feature", "properties": { "height": 48.7 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00978114, 40.70583309 ], [ -74.00951784, 40.70626238 ], [ -74.00890905, 40.70597316 ], [ -74.00904344, 40.7057515 ], [ -74.00912231, 40.7056211 ], [ -74.00978114, 40.70583309 ] ] ] } },
{ "type": "Feature", "properties": { "height": 154.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01092442, 40.70246846 ], [ -74.01021547, 40.70258941 ], [ -74.01020029, 40.70253759 ], [ -74.01017047, 40.70254256 ], [ -74.01016535, 40.70252492 ], [ -74.01008962, 40.7022677 ], [ -74.01011944, 40.70226266 ], [ -74.01010381, 40.70220981 ], [ -74.01081582, 40.70208839 ], [ -74.01083127, 40.7021413 ], [ -74.0108637, 40.70213572 ], [ -74.01094473, 40.70241058 ], [ -74.01090915, 40.70241671 ], [ -74.01092442, 40.70246846 ] ] ] } },
{ "type": "Feature", "properties": { "height": 85.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00978293, 40.70420919 ], [ -74.00947607, 40.7044073 ], [ -74.00916992, 40.7041349 ], [ -74.00948299, 40.70393257 ], [ -74.00974485, 40.70376361 ], [ -74.00979102, 40.70380468 ], [ -74.01004048, 40.70402668 ], [ -74.01005099, 40.70403601 ], [ -74.00978293, 40.70420919 ] ] ] } },
{ "type": "Feature", "properties": { "height": 69.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01468342, 40.70514747 ], [ -74.01450152, 40.70556042 ], [ -74.01432212, 40.70551888 ], [ -74.01416357, 40.70548217 ], [ -74.0140776, 40.70546229 ], [ -74.01391905, 40.70542572 ], [ -74.01381502, 40.70540161 ], [ -74.01380101, 40.70539828 ], [ -74.0138806, 40.70529102 ], [ -74.0138983, 40.70529511 ], [ -74.0140202, 40.7051269 ], [ -74.01400205, 40.70512268 ], [ -74.01408542, 40.70500895 ], [ -74.01409458, 40.70501147 ], [ -74.01411048, 40.7050159 ], [ -74.01414974, 40.70502659 ], [ -74.0142534, 40.70505506 ], [ -74.01431691, 40.70507249 ], [ -74.01431961, 40.70507331 ], [ -74.01427128, 40.70513426 ], [ -74.01429113, 40.70514202 ], [ -74.01424972, 40.7052044 ], [ -74.01422088, 40.70519779 ], [ -74.01419447, 40.70523096 ], [ -74.01418261, 40.70522817 ], [ -74.01414857, 40.70527107 ], [ -74.01413042, 40.7053169 ], [ -74.01419241, 40.70533127 ], [ -74.01422385, 40.70525316 ], [ -74.01434772, 40.7052819 ], [ -74.01431556, 40.70536171 ], [ -74.01436892, 40.70537397 ], [ -74.01442139, 40.7052424 ], [ -74.01439506, 40.70523641 ], [ -74.01442031, 40.70517321 ], [ -74.01445022, 40.70518009 ], [ -74.01448184, 40.70510082 ], [ -74.01449109, 40.705103 ], [ -74.01454275, 40.70511492 ], [ -74.01462629, 40.70513419 ], [ -74.01466654, 40.70514352 ], [ -74.01468342, 40.70514747 ] ] ] } },
{ "type": "Feature", "properties": { "height": 132.7 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01104103, 40.70498771 ], [ -74.01101533, 40.70482427 ], [ -74.01100473, 40.70475678 ], [ -74.01108522, 40.7047495 ], [ -74.01105315, 40.70469352 ], [ -74.01094194, 40.70449991 ], [ -74.01136244, 40.70435356 ], [ -74.0114467, 40.7044028 ], [ -74.0114406, 40.70490837 ], [ -74.01136047, 40.70497947 ], [ -74.01104273, 40.70499881 ], [ -74.01104103, 40.70498771 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01122168, 40.70591828 ], [ -74.0110739, 40.70619728 ], [ -74.01030198, 40.70590799 ], [ -74.01039945, 40.7057575 ], [ -74.01044347, 40.70577398 ], [ -74.01050437, 40.70568 ], [ -74.01062987, 40.70572699 ], [ -74.01056995, 40.70581967 ], [ -74.01067047, 40.70585747 ], [ -74.01069895, 40.70581361 ], [ -74.01071467, 40.70581947 ], [ -74.01076857, 40.70575259 ], [ -74.01077027, 40.70571187 ], [ -74.01074593, 40.7057148 ], [ -74.01068664, 40.70542177 ], [ -74.01079812, 40.7054087 ], [ -74.0108063, 40.70544908 ], [ -74.01084124, 40.7056213 ], [ -74.01084528, 40.70564146 ], [ -74.0108575, 40.70570166 ], [ -74.0108478, 40.70578167 ], [ -74.01087241, 40.70579161 ], [ -74.01084133, 40.70586496 ], [ -74.01085831, 40.70587129 ], [ -74.01083549, 40.70590656 ], [ -74.01097428, 40.70595859 ], [ -74.01101938, 40.70588886 ], [ -74.01122168, 40.70591828 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01321118, 40.70555988 ], [ -74.01312224, 40.7055327 ], [ -74.01299019, 40.705493 ], [ -74.01277325, 40.70542647 ], [ -74.01265341, 40.70539079 ], [ -74.01258074, 40.70536716 ], [ -74.01254498, 40.7053566 ], [ -74.01266078, 40.7051156 ], [ -74.0126968, 40.70509619 ], [ -74.01290763, 40.70510409 ], [ -74.01302954, 40.70510722 ], [ -74.01324863, 40.70511437 ], [ -74.01325043, 40.70516586 ], [ -74.01325322, 40.70528006 ], [ -74.01325717, 40.7053455 ], [ -74.01325519, 40.70540638 ], [ -74.01324504, 40.70546529 ], [ -74.01321118, 40.70555988 ] ] ] } },
{ "type": "Feature", "properties": { "height": 55.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00992352, 40.7053472 ], [ -74.00983378, 40.70567176 ], [ -74.00976605, 40.70570758 ], [ -74.0091277, 40.70549988 ], [ -74.00911818, 40.70535701 ], [ -74.00987959, 40.7052633 ], [ -74.00992352, 40.7053472 ] ] ] } },
{ "type": "Feature", "properties": { "height": 20.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01350187, 40.70067477 ], [ -74.01350843, 40.70071931 ], [ -74.0131809, 40.70074792 ], [ -74.01283864, 40.7007725 ], [ -74.01275483, 40.70078108 ], [ -74.01243261, 40.7008532 ], [ -74.01239003, 40.70074792 ], [ -74.0123567, 40.70067716 ], [ -74.01239209, 40.70067062 ], [ -74.01262314, 40.70061852 ], [ -74.0128116, 40.70057459 ], [ -74.01289991, 40.70056056 ], [ -74.01313742, 40.70054299 ], [ -74.01326813, 40.70053196 ], [ -74.01350151, 40.70051568 ], [ -74.013544, 40.70051146 ], [ -74.01355343, 40.70057228 ], [ -74.01356844, 40.70066871 ], [ -74.01350187, 40.70067477 ] ] ] } },
{ "type": "Feature", "properties": { "height": 160.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00575615, 40.70511321 ], [ -74.00574447, 40.70538282 ], [ -74.00563047, 40.70546311 ], [ -74.00527537, 40.70545221 ], [ -74.00517215, 40.70536797 ], [ -74.00518392, 40.70509912 ], [ -74.00529783, 40.70501876 ], [ -74.00532289, 40.70501951 ], [ -74.0056346, 40.7050287 ], [ -74.00565284, 40.70502891 ], [ -74.00575615, 40.70511321 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0063608, 40.70466451 ], [ -74.00634607, 40.70467731 ], [ -74.00633565, 40.7046865 ], [ -74.00632559, 40.70469508 ], [ -74.00631427, 40.70470489 ], [ -74.00631113, 40.70470782 ], [ -74.00629064, 40.70472559 ], [ -74.00627699, 40.70473737 ], [ -74.00625453, 40.70475699 ], [ -74.00624474, 40.7047655 ], [ -74.00618572, 40.70481719 ], [ -74.0061965, 40.70482406 ], [ -74.00616138, 40.70485525 ], [ -74.00616757, 40.70485941 ], [ -74.00598414, 40.70500718 ], [ -74.00593392, 40.70497947 ], [ -74.00592817, 40.70498471 ], [ -74.00590805, 40.70500337 ], [ -74.00589251, 40.70501767 ], [ -74.00587401, 40.7050347 ], [ -74.00586035, 40.70504729 ], [ -74.005738, 40.70497232 ], [ -74.00571051, 40.7049555 ], [ -74.00553247, 40.70484627 ], [ -74.00567629, 40.70475072 ], [ -74.00573881, 40.70470979 ], [ -74.00587607, 40.7046199 ], [ -74.00593877, 40.70457877 ], [ -74.00608771, 40.70448146 ], [ -74.00621465, 40.70456576 ], [ -74.0063608, 40.70466451 ] ] ] } },
{ "type": "Feature", "properties": { "height": 151.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0141421, 40.70322671 ], [ -74.01413132, 40.70353248 ], [ -74.01340027, 40.70351756 ], [ -74.01341114, 40.70321438 ], [ -74.01341123, 40.70321166 ], [ -74.0141421, 40.70322671 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0124609, 40.70092342 ], [ -74.01252989, 40.70090728 ], [ -74.01284601, 40.70141158 ], [ -74.01336263, 40.70145258 ], [ -74.01352747, 40.70123676 ], [ -74.01354481, 40.701052 ], [ -74.01367021, 40.70127688 ], [ -74.01367363, 40.70130262 ], [ -74.01366482, 40.7013223 ], [ -74.01350043, 40.70149372 ], [ -74.01333218, 40.70166138 ], [ -74.01330694, 40.70168549 ], [ -74.01328924, 40.70171137 ], [ -74.0132816, 40.70173997 ], [ -74.01328268, 40.70178288 ], [ -74.01329813, 40.70181639 ], [ -74.01332221, 40.70184322 ], [ -74.01335167, 40.70186549 ], [ -74.01371585, 40.70210848 ], [ -74.01367524, 40.70214382 ], [ -74.01329571, 40.70188966 ], [ -74.01326678, 40.70186549 ], [ -74.01324621, 40.70184049 ], [ -74.01322968, 40.70180842 ], [ -74.01322204, 40.70177621 ], [ -74.01321971, 40.70174576 ], [ -74.01322618, 40.70171716 ], [ -74.01324154, 40.70168679 ], [ -74.01326274, 40.70166002 ], [ -74.0133134, 40.70161051 ], [ -74.01332517, 40.70159042 ], [ -74.01332517, 40.70157067 ], [ -74.01331403, 40.70154786 ], [ -74.01329274, 40.70153267 ], [ -74.01326274, 40.7015247 ], [ -74.01303699, 40.70150822 ], [ -74.01300115, 40.7015104 ], [ -74.01296755, 40.70152157 ], [ -74.01294509, 40.70153451 ], [ -74.01292623, 40.70154929 ], [ -74.01291087, 40.70156761 ], [ -74.01290153, 40.70158722 ], [ -74.01290862, 40.70161092 ], [ -74.01292515, 40.70162788 ], [ -74.01294797, 40.7016355 ], [ -74.01297285, 40.7016368 ], [ -74.01299809, 40.70163006 ], [ -74.01301525, 40.70161936 ], [ -74.01303699, 40.7016069 ], [ -74.01307472, 40.70163768 ], [ -74.01304885, 40.7016556 ], [ -74.01302289, 40.70167078 ], [ -74.01298929, 40.7016797 ], [ -74.01295102, 40.70168059 ], [ -74.01291859, 40.70167426 ], [ -74.01288742, 40.70166138 ], [ -74.01286613, 40.7016449 ], [ -74.01285023, 40.70162481 ], [ -74.01284323, 40.70159839 ], [ -74.01284367, 40.70156216 ], [ -74.012839, 40.70152961 ], [ -74.01282409, 40.7014946 ], [ -74.01259502, 40.70114571 ], [ -74.01257059, 40.70115211 ], [ -74.0124609, 40.70092342 ] ] ] } },
{ "type": "Feature", "properties": { "height": 94.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01361901, 40.70582301 ], [ -74.01354634, 40.70578869 ], [ -74.01375133, 40.70553951 ], [ -74.01382589, 40.70557472 ], [ -74.01392543, 40.7054486 ], [ -74.01397043, 40.7054706 ], [ -74.0139804, 40.70546461 ], [ -74.01399684, 40.7054642 ], [ -74.01401714, 40.70547087 ], [ -74.01403134, 40.70548129 ], [ -74.01403915, 40.70549212 ], [ -74.01403655, 40.70550301 ], [ -74.01408757, 40.70552801 ], [ -74.01405038, 40.7055769 ], [ -74.01402298, 40.70556512 ], [ -74.01390953, 40.70570002 ], [ -74.01413653, 40.70580306 ], [ -74.01421873, 40.7057861 ], [ -74.01425978, 40.70573189 ], [ -74.01427352, 40.70571861 ], [ -74.01428915, 40.70571337 ], [ -74.01431161, 40.70571262 ], [ -74.01433119, 40.70571711 ], [ -74.01434593, 40.70573421 ], [ -74.01434979, 40.70575198 ], [ -74.01419914, 40.70609309 ], [ -74.01405918, 40.70602928 ], [ -74.01411398, 40.70597078 ], [ -74.01384592, 40.70584991 ], [ -74.01376283, 40.70596111 ], [ -74.01356808, 40.70589506 ], [ -74.01361901, 40.70582301 ] ] ] } },
{ "type": "Feature", "properties": { "height": 82.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01321118, 40.70555988 ], [ -74.01312314, 40.70577765 ], [ -74.01312943, 40.7057799 ], [ -74.01307849, 40.70585617 ], [ -74.01242389, 40.70560891 ], [ -74.01254498, 40.7053566 ], [ -74.01258074, 40.70536716 ], [ -74.01265341, 40.70539079 ], [ -74.01277325, 40.70542647 ], [ -74.01299019, 40.705493 ], [ -74.01312224, 40.7055327 ], [ -74.01321118, 40.70555988 ] ] ] } },
{ "type": "Feature", "properties": { "height": 139.9 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01354292, 40.70285331 ], [ -74.0135431, 40.70300796 ], [ -74.01315494, 40.70302336 ], [ -74.01313581, 40.70274387 ], [ -74.0131208, 40.70252349 ], [ -74.01356044, 40.70250619 ], [ -74.01357769, 40.70275939 ], [ -74.01354274, 40.70276076 ], [ -74.01354292, 40.70285331 ] ] ] } },
{ "type": "Feature", "properties": { "height": 85.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00871502, 40.70486928 ], [ -74.00853913, 40.70470026 ], [ -74.00844382, 40.70460867 ], [ -74.00889091, 40.70431236 ], [ -74.00918924, 40.70460996 ], [ -74.00871502, 40.70486928 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01133747, 40.70545521 ], [ -74.01127692, 40.70545419 ], [ -74.01122482, 40.7054533 ], [ -74.01101174, 40.70544969 ], [ -74.01100608, 40.7053901 ], [ -74.01080863, 40.705401 ], [ -74.01080405, 40.70535326 ], [ -74.01078249, 40.70535449 ], [ -74.01069931, 40.70535898 ], [ -74.01066364, 40.70521346 ], [ -74.01066077, 40.70520127 ], [ -74.01064747, 40.7051476 ], [ -74.01141338, 40.70510538 ], [ -74.01133747, 40.70545521 ] ] ] } },
{ "type": "Feature", "properties": { "height": 95.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00725337, 40.70491212 ], [ -74.00726684, 40.70490258 ], [ -74.00728023, 40.70489291 ], [ -74.00729361, 40.70488318 ], [ -74.007307, 40.70487357 ], [ -74.00735838, 40.70483768 ], [ -74.00739863, 40.70486976 ], [ -74.00746124, 40.70482481 ], [ -74.00742099, 40.70479267 ], [ -74.00736413, 40.7047465 ], [ -74.00731948, 40.70471068 ], [ -74.00730691, 40.70470046 ], [ -74.00729424, 40.70469032 ], [ -74.00728158, 40.7046801 ], [ -74.00726891, 40.70466989 ], [ -74.00766147, 40.70438782 ], [ -74.00794759, 40.7046184 ], [ -74.00738794, 40.70502046 ], [ -74.00725337, 40.70491212 ] ] ] } },
{ "type": "Feature", "properties": { "height": 97.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00860929, 40.70416098 ], [ -74.00814351, 40.70447648 ], [ -74.00786674, 40.70423991 ], [ -74.0080111, 40.70414198 ], [ -74.00833242, 40.70392426 ], [ -74.00860929, 40.70416098 ] ] ] } },
{ "type": "Feature", "properties": { "height": 70.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00945604, 40.70559849 ], [ -74.00913588, 40.70549498 ], [ -74.00912734, 40.70538806 ], [ -74.00952772, 40.70533746 ], [ -74.00987843, 40.70529347 ], [ -74.00991014, 40.70535428 ], [ -74.00981914, 40.70567026 ], [ -74.00976784, 40.70569982 ], [ -74.00945604, 40.70559849 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01069931, 40.70535898 ], [ -74.01058172, 40.70536797 ], [ -74.0106313, 40.70554946 ], [ -74.0102921, 40.7055549 ], [ -74.0101755, 40.70556526 ], [ -74.01, 40.70558208 ], [ -74.01007677, 40.70536927 ], [ -74.0101075, 40.70529906 ], [ -74.01014217, 40.70521768 ], [ -74.01020317, 40.70525479 ], [ -74.01022113, 40.70526562 ], [ -74.01031312, 40.70525268 ], [ -74.01029614, 40.7052076 ], [ -74.0105562, 40.70518799 ], [ -74.01055881, 40.7052138 ], [ -74.01066364, 40.70521346 ], [ -74.01069931, 40.70535898 ] ] ] } },
{ "type": "Feature", "properties": { "height": 78.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01321118, 40.70555988 ], [ -74.01312224, 40.7055327 ], [ -74.01299019, 40.705493 ], [ -74.01277325, 40.70542647 ], [ -74.01265341, 40.70539079 ], [ -74.01258074, 40.70536716 ], [ -74.01268961, 40.70513419 ], [ -74.01270282, 40.70512588 ], [ -74.01289533, 40.70513528 ], [ -74.01289308, 40.70516388 ], [ -74.01289407, 40.70521257 ], [ -74.01306897, 40.70526392 ], [ -74.01304669, 40.70517158 ], [ -74.01304023, 40.70513971 ], [ -74.01324217, 40.70515632 ], [ -74.01325043, 40.70516586 ], [ -74.01325322, 40.70528006 ], [ -74.01325717, 40.7053455 ], [ -74.01325519, 40.70540638 ], [ -74.01324504, 40.70546529 ], [ -74.01321118, 40.70555988 ] ] ] } },
{ "type": "Feature", "properties": { "height": 175.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0079916, 40.70367808 ], [ -74.00797157, 40.70392801 ], [ -74.00787725, 40.70399297 ], [ -74.00754739, 40.70397847 ], [ -74.00745423, 40.70389927 ], [ -74.00747292, 40.70364879 ], [ -74.00756742, 40.7035856 ], [ -74.00789908, 40.70359922 ], [ -74.0079916, 40.70367808 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01267021, 40.70398596 ], [ -74.01254427, 40.70398746 ], [ -74.01254301, 40.70397806 ], [ -74.01254103, 40.70396301 ], [ -74.0125378, 40.70393849 ], [ -74.01253295, 40.70390186 ], [ -74.01253061, 40.7038849 ], [ -74.01251444, 40.70376559 ], [ -74.01243413, 40.70377192 ], [ -74.01242308, 40.7036902 ], [ -74.01241958, 40.70366439 ], [ -74.01241688, 40.7036441 ], [ -74.01258828, 40.70364682 ], [ -74.01300914, 40.70365349 ], [ -74.01305343, 40.70398126 ], [ -74.01267021, 40.70398596 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00830009, 40.7050966 ], [ -74.00827412, 40.7051156 ], [ -74.00826882, 40.70511948 ], [ -74.00826343, 40.70512336 ], [ -74.00823738, 40.70514236 ], [ -74.00823208, 40.70514631 ], [ -74.00822669, 40.70515019 ], [ -74.00820073, 40.70516919 ], [ -74.00819552, 40.70517307 ], [ -74.00819004, 40.70517696 ], [ -74.00816408, 40.70519609 ], [ -74.00815887, 40.70519997 ], [ -74.00815339, 40.70520392 ], [ -74.00812743, 40.70522292 ], [ -74.00812213, 40.70522667 ], [ -74.00811674, 40.70523069 ], [ -74.00809069, 40.70524948 ], [ -74.00808548, 40.70525336 ], [ -74.00808, 40.70525738 ], [ -74.00805404, 40.70527638 ], [ -74.00804883, 40.70528026 ], [ -74.00804344, 40.70528428 ], [ -74.0080173, 40.70530328 ], [ -74.00801209, 40.70530716 ], [ -74.0080067, 40.70531111 ], [ -74.00798064, 40.70532991 ], [ -74.00797543, 40.70533386 ], [ -74.00796986, 40.70533781 ], [ -74.00794399, 40.7053568 ], [ -74.00793869, 40.70536069 ], [ -74.0079333, 40.7053647 ], [ -74.00790734, 40.70538357 ], [ -74.00790213, 40.70538752 ], [ -74.00789665, 40.7053914 ], [ -74.00789036, 40.70538616 ], [ -74.00786234, 40.7053852 ], [ -74.00785659, 40.70538929 ], [ -74.00785147, 40.70538527 ], [ -74.00784644, 40.70538118 ], [ -74.00782299, 40.7053628 ], [ -74.00781805, 40.70535878 ], [ -74.00781275, 40.70535469 ], [ -74.00778957, 40.70533617 ], [ -74.00778463, 40.70533222 ], [ -74.00777933, 40.70532807 ], [ -74.00775607, 40.70530982 ], [ -74.00775113, 40.70530566 ], [ -74.00774583, 40.70530171 ], [ -74.00772247, 40.70528319 ], [ -74.00771753, 40.70527917 ], [ -74.00771223, 40.70527509 ], [ -74.00768914, 40.70525656 ], [ -74.00768411, 40.70525261 ], [ -74.00767881, 40.70524846 ], [ -74.00765563, 40.70523007 ], [ -74.00765051, 40.70522605 ], [ -74.0076453, 40.70522197 ], [ -74.00765105, 40.70521781 ], [ -74.00765078, 40.70519657 ], [ -74.00764539, 40.70519228 ], [ -74.00765069, 40.70518846 ], [ -74.00765608, 40.70518451 ], [ -74.00768231, 40.70516558 ], [ -74.00768752, 40.70516177 ], [ -74.007693, 40.70515782 ], [ -74.00771915, 40.70513889 ], [ -74.00772445, 40.70513507 ], [ -74.00772984, 40.70513106 ], [ -74.00775589, 40.70511206 ], [ -74.00776119, 40.70510831 ], [ -74.00776658, 40.70510429 ], [ -74.00779281, 40.70508529 ], [ -74.0077982, 40.70508162 ], [ -74.00780359, 40.70507746 ], [ -74.00782955, 40.70505867 ], [ -74.00783494, 40.70505499 ], [ -74.00784033, 40.7050509 ], [ -74.00786647, 40.70503197 ], [ -74.00787177, 40.70502816 ], [ -74.00787725, 40.70502421 ], [ -74.00790321, 40.70500521 ], [ -74.0079086, 40.7050014 ], [ -74.0079139, 40.70499738 ], [ -74.00794013, 40.70497838 ], [ -74.00794543, 40.7049747 ], [ -74.00795091, 40.70497068 ], [ -74.00797678, 40.70495182 ], [ -74.00798208, 40.70494801 ], [ -74.00798765, 40.70494399 ], [ -74.00801388, 40.70492492 ], [ -74.00801918, 40.70492131 ], [ -74.00802457, 40.70491716 ], [ -74.00805071, 40.70489816 ], [ -74.00805601, 40.70489448 ], [ -74.00806149, 40.70489039 ], [ -74.00806688, 40.70489468 ], [ -74.00809401, 40.70489441 ], [ -74.00809967, 40.70489026 ], [ -74.00810479, 40.70489428 ], [ -74.00810991, 40.70489836 ], [ -74.00813318, 40.70491702 ], [ -74.0081383, 40.70492097 ], [ -74.00814342, 40.70492519 ], [ -74.0081666, 40.70494378 ], [ -74.00817172, 40.7049478 ], [ -74.00817693, 40.70495189 ], [ -74.0082001, 40.70497048 ], [ -74.00820522, 40.7049745 ], [ -74.00821043, 40.70497872 ], [ -74.00823361, 40.70499731 ], [ -74.00823873, 40.70500126 ], [ -74.00824394, 40.70500541 ], [ -74.00826712, 40.70502387 ], [ -74.00827224, 40.70502789 ], [ -74.00827727, 40.70503211 ], [ -74.00830071, 40.7050507 ], [ -74.00830583, 40.70505479 ], [ -74.00831087, 40.70505887 ], [ -74.00830395, 40.70506357 ], [ -74.0083053, 40.70508448 ], [ -74.00831078, 40.70508877 ], [ -74.00830548, 40.70509272 ], [ -74.00830009, 40.7050966 ] ] ] } },
{ "type": "Feature", "properties": { "height": 55.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00867082, 40.70368087 ], [ -74.00913929, 40.70337108 ], [ -74.00930593, 40.70351579 ], [ -74.00937519, 40.70357599 ], [ -74.00890672, 40.70388592 ], [ -74.00867082, 40.70368087 ] ] ] } },
{ "type": "Feature", "properties": { "height": 128.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01307445, 40.70478777 ], [ -74.01288212, 40.7048052 ], [ -74.01279561, 40.7048131 ], [ -74.01276489, 40.70461806 ], [ -74.01271054, 40.70427239 ], [ -74.01301507, 40.70424481 ], [ -74.01310032, 40.70478552 ], [ -74.01307445, 40.70478777 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00626513, 40.70516129 ], [ -74.00623225, 40.70514331 ], [ -74.00598414, 40.70500718 ], [ -74.00616757, 40.70485941 ], [ -74.00616138, 40.70485525 ], [ -74.0061965, 40.70482406 ], [ -74.00618572, 40.70481719 ], [ -74.00624474, 40.7047655 ], [ -74.00625453, 40.70475699 ], [ -74.00627699, 40.70473737 ], [ -74.00629064, 40.70472559 ], [ -74.00631113, 40.70470782 ], [ -74.00631427, 40.70470489 ], [ -74.00632559, 40.70469508 ], [ -74.00633565, 40.7046865 ], [ -74.00634607, 40.70467731 ], [ -74.0063608, 40.70466451 ], [ -74.00668411, 40.70487242 ], [ -74.00626513, 40.70516129 ] ] ] } },
{ "type": "Feature", "properties": { "height": 136.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00644704, 40.7055897 ], [ -74.00592269, 40.70595246 ], [ -74.00571644, 40.70577977 ], [ -74.00601064, 40.70557629 ], [ -74.0062407, 40.70541707 ], [ -74.00644704, 40.7055897 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00899978, 40.70554517 ], [ -74.0088919, 40.70576397 ], [ -74.00882794, 40.7058939 ], [ -74.00878554, 40.7059052 ], [ -74.00848011, 40.70575906 ], [ -74.00849601, 40.70573748 ], [ -74.00852898, 40.70569301 ], [ -74.0085367, 40.70568252 ], [ -74.00854542, 40.70567081 ], [ -74.00858054, 40.70562328 ], [ -74.00863615, 40.70554809 ], [ -74.00868358, 40.70556839 ], [ -74.00870199, 40.70553468 ], [ -74.00869894, 40.70551922 ], [ -74.00869633, 40.70550601 ], [ -74.00869103, 40.70547952 ], [ -74.00868223, 40.70543498 ], [ -74.0086754, 40.7054008 ], [ -74.00898047, 40.70537199 ], [ -74.00899978, 40.70554517 ] ] ] } },
{ "type": "Feature", "properties": { "height": 62.3 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0121968, 40.70366677 ], [ -74.01219671, 40.70368257 ], [ -74.01219491, 40.70393196 ], [ -74.01219482, 40.70394762 ], [ -74.01219464, 40.7039799 ], [ -74.01205657, 40.70397629 ], [ -74.01198839, 40.70397452 ], [ -74.01192012, 40.70397282 ], [ -74.01185193, 40.70397098 ], [ -74.0117144, 40.70396737 ], [ -74.01172051, 40.703874 ], [ -74.01172383, 40.70382231 ], [ -74.01172725, 40.70377056 ], [ -74.01173048, 40.70371887 ], [ -74.01173659, 40.7036255 ], [ -74.01186442, 40.70362789 ], [ -74.0119326, 40.70362918 ], [ -74.01200088, 40.70363048 ], [ -74.01206915, 40.70363177 ], [ -74.01219698, 40.70363422 ], [ -74.0121968, 40.70366677 ] ] ] } },
{ "type": "Feature", "properties": { "height": 21.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01224917, 40.7008989 ], [ -74.01217659, 40.70091538 ], [ -74.01216221, 40.70091872 ], [ -74.01214541, 40.70092247 ], [ -74.01212367, 40.7008673 ], [ -74.01129219, 40.70105561 ], [ -74.01128285, 40.70103157 ], [ -74.01119572, 40.70105132 ], [ -74.0111508, 40.70093752 ], [ -74.01217227, 40.7007061 ], [ -74.01224917, 40.7008989 ] ] ] } },
{ "type": "Feature", "properties": { "height": 89.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00978293, 40.70420919 ], [ -74.00960085, 40.70416248 ], [ -74.00949952, 40.70407122 ], [ -74.00948299, 40.70393257 ], [ -74.00974485, 40.70376361 ], [ -74.00979102, 40.70380468 ], [ -74.01004048, 40.70402668 ], [ -74.01005099, 40.70403601 ], [ -74.00978293, 40.70420919 ] ] ] } },
{ "type": "Feature", "properties": { "height": 130.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00824394, 40.70500541 ], [ -74.00823693, 40.70501011 ], [ -74.00823622, 40.70502761 ], [ -74.00823271, 40.7051173 ], [ -74.0082319, 40.70513807 ], [ -74.00823738, 40.70514236 ], [ -74.00823208, 40.70514631 ], [ -74.00822669, 40.70515019 ], [ -74.00820073, 40.70516919 ], [ -74.00819552, 40.70517307 ], [ -74.00819004, 40.70517696 ], [ -74.00816408, 40.70519609 ], [ -74.00815887, 40.70519997 ], [ -74.00815339, 40.70520392 ], [ -74.00812743, 40.70522292 ], [ -74.00812213, 40.70522667 ], [ -74.00811674, 40.70523069 ], [ -74.00809069, 40.70524948 ], [ -74.00808548, 40.70525336 ], [ -74.00808, 40.70525738 ], [ -74.00805404, 40.70527638 ], [ -74.00804883, 40.70528026 ], [ -74.00804344, 40.70528428 ], [ -74.0080173, 40.70530328 ], [ -74.00801209, 40.70530716 ], [ -74.0080067, 40.70531111 ], [ -74.00798064, 40.70532991 ], [ -74.00797543, 40.70533386 ], [ -74.00796986, 40.70533781 ], [ -74.00796367, 40.70533249 ], [ -74.00793905, 40.70533236 ], [ -74.00782039, 40.70533222 ], [ -74.00779532, 40.70533208 ], [ -74.00778957, 40.70533617 ], [ -74.00778463, 40.70533222 ], [ -74.00777933, 40.70532807 ], [ -74.00775607, 40.70530982 ], [ -74.00775113, 40.70530566 ], [ -74.00774583, 40.70530171 ], [ -74.00772247, 40.70528319 ], [ -74.00771753, 40.70527917 ], [ -74.00771223, 40.70527509 ], [ -74.00771789, 40.705271 ], [ -74.00771879, 40.70525282 ], [ -74.00772373, 40.70516191 ], [ -74.00772453, 40.70514318 ], [ -74.00771915, 40.70513889 ], [ -74.00772445, 40.70513507 ], [ -74.00772984, 40.70513106 ], [ -74.00775589, 40.70511206 ], [ -74.00776119, 40.70510831 ], [ -74.00776658, 40.70510429 ], [ -74.00779281, 40.70508529 ], [ -74.0077982, 40.70508162 ], [ -74.00780359, 40.70507746 ], [ -74.00782955, 40.70505867 ], [ -74.00783494, 40.70505499 ], [ -74.00784033, 40.7050509 ], [ -74.00786647, 40.70503197 ], [ -74.00787177, 40.70502816 ], [ -74.00787725, 40.70502421 ], [ -74.00790321, 40.70500521 ], [ -74.0079086, 40.7050014 ], [ -74.0079139, 40.70499738 ], [ -74.00794013, 40.70497838 ], [ -74.00794543, 40.7049747 ], [ -74.00795091, 40.70497068 ], [ -74.00797678, 40.70495182 ], [ -74.00798208, 40.70494801 ], [ -74.00798765, 40.70494399 ], [ -74.00799304, 40.70494828 ], [ -74.00801873, 40.70494821 ], [ -74.00813524, 40.70494787 ], [ -74.00816094, 40.70494787 ], [ -74.0081666, 40.70494378 ], [ -74.00817172, 40.7049478 ], [ -74.00817693, 40.70495189 ], [ -74.0082001, 40.70497048 ], [ -74.00820522, 40.7049745 ], [ -74.00821043, 40.70497872 ], [ -74.00823361, 40.70499731 ], [ -74.00823873, 40.70500126 ], [ -74.00824394, 40.70500541 ] ] ] } },
{ "type": "Feature", "properties": { "height": 44.3 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01468342, 40.70514747 ], [ -74.01466654, 40.70514352 ], [ -74.01462629, 40.70513419 ], [ -74.01454275, 40.70511492 ], [ -74.01449109, 40.705103 ], [ -74.01451328, 40.70504736 ], [ -74.01453269, 40.70505186 ], [ -74.01454122, 40.70503061 ], [ -74.01443702, 40.7050065 ], [ -74.01445642, 40.70495802 ], [ -74.0143311, 40.70492901 ], [ -74.01430739, 40.70498818 ], [ -74.01434692, 40.70499738 ], [ -74.01431691, 40.70507249 ], [ -74.0142534, 40.70505506 ], [ -74.01414974, 40.70502659 ], [ -74.01411048, 40.7050159 ], [ -74.01409458, 40.70501147 ], [ -74.01418522, 40.7047847 ], [ -74.01424208, 40.70477027 ], [ -74.01475879, 40.70487657 ], [ -74.01478673, 40.70491307 ], [ -74.01468342, 40.70514747 ] ] ] } },
{ "type": "Feature", "properties": { "height": 111.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00711179, 40.70519936 ], [ -74.00674932, 40.70545582 ], [ -74.0064872, 40.70524117 ], [ -74.00675939, 40.70504866 ], [ -74.00684967, 40.70498478 ], [ -74.00711179, 40.70519936 ] ] ] } },
{ "type": "Feature", "properties": { "height": 70.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0123858, 40.70193611 ], [ -74.01159717, 40.70206789 ], [ -74.01154767, 40.70189777 ], [ -74.0123364, 40.70176599 ], [ -74.0123858, 40.70193611 ] ] ] } },
{ "type": "Feature", "properties": { "height": 141.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00824394, 40.70500541 ], [ -74.00823693, 40.70501011 ], [ -74.00823622, 40.70502761 ], [ -74.00823271, 40.7051173 ], [ -74.0082319, 40.70513807 ], [ -74.00823738, 40.70514236 ], [ -74.00823208, 40.70514631 ], [ -74.00822669, 40.70515019 ], [ -74.00822049, 40.70514488 ], [ -74.0081957, 40.70514406 ], [ -74.00819525, 40.7051649 ], [ -74.00820073, 40.70516919 ], [ -74.00819552, 40.70517307 ], [ -74.00819004, 40.70517696 ], [ -74.00818384, 40.70517178 ], [ -74.00815905, 40.70517089 ], [ -74.0081586, 40.7051918 ], [ -74.00816408, 40.70519609 ], [ -74.00815887, 40.70519997 ], [ -74.00815339, 40.70520392 ], [ -74.00814719, 40.70519861 ], [ -74.00812231, 40.70519779 ], [ -74.00812195, 40.70521856 ], [ -74.00812743, 40.70522292 ], [ -74.00812213, 40.70522667 ], [ -74.00811674, 40.70523069 ], [ -74.00811045, 40.70522537 ], [ -74.00808557, 40.70522456 ], [ -74.00808521, 40.70524519 ], [ -74.00809069, 40.70524948 ], [ -74.00808548, 40.70525336 ], [ -74.00808, 40.70525738 ], [ -74.0080738, 40.70525207 ], [ -74.00804901, 40.70525132 ], [ -74.00804865, 40.70527209 ], [ -74.00805404, 40.70527638 ], [ -74.00804883, 40.70528026 ], [ -74.00804344, 40.70528428 ], [ -74.00803715, 40.70527897 ], [ -74.00801227, 40.70527822 ], [ -74.00801191, 40.70529899 ], [ -74.0080173, 40.70530328 ], [ -74.00801209, 40.70530716 ], [ -74.0080067, 40.70531111 ], [ -74.00800041, 40.7053058 ], [ -74.00797552, 40.70530498 ], [ -74.00797508, 40.70532568 ], [ -74.00798064, 40.70532991 ], [ -74.00797543, 40.70533386 ], [ -74.00796986, 40.70533781 ], [ -74.00796367, 40.70533249 ], [ -74.00793905, 40.70533236 ], [ -74.00782039, 40.70533222 ], [ -74.00779532, 40.70533208 ], [ -74.00778957, 40.70533617 ], [ -74.00778463, 40.70533222 ], [ -74.00777933, 40.70532807 ], [ -74.00778499, 40.70532398 ], [ -74.00778697, 40.705306 ], [ -74.00776173, 40.70530559 ], [ -74.00775607, 40.70530982 ], [ -74.00775113, 40.70530566 ], [ -74.00774583, 40.70530171 ], [ -74.00775139, 40.70529749 ], [ -74.00775355, 40.70527938 ], [ -74.00772822, 40.7052791 ], [ -74.00772247, 40.70528319 ], [ -74.00771753, 40.70527917 ], [ -74.00771223, 40.70527509 ], [ -74.00771789, 40.705271 ], [ -74.00771879, 40.70525282 ], [ -74.00772373, 40.70516191 ], [ -74.00772453, 40.70514318 ], [ -74.00771915, 40.70513889 ], [ -74.00772445, 40.70513507 ], [ -74.00772984, 40.70513106 ], [ -74.00773531, 40.70513541 ], [ -74.00776083, 40.70513712 ], [ -74.00776137, 40.70511642 ], [ -74.00775589, 40.70511206 ], [ -74.00776119, 40.70510831 ], [ -74.00776658, 40.70510429 ], [ -74.00777215, 40.70510858 ], [ -74.00779775, 40.70511029 ], [ -74.00779829, 40.70508958 ], [ -74.00779281, 40.70508529 ], [ -74.0077982, 40.70508162 ], [ -74.00780359, 40.70507746 ], [ -74.00780898, 40.70508182 ], [ -74.0078344, 40.70508346 ], [ -74.00783503, 40.70506296 ], [ -74.00782955, 40.70505867 ], [ -74.00783494, 40.70505499 ], [ -74.00784033, 40.7050509 ], [ -74.00784572, 40.70505526 ], [ -74.00787141, 40.70505696 ], [ -74.00787195, 40.70503626 ], [ -74.00786647, 40.70503197 ], [ -74.00787177, 40.70502816 ], [ -74.00787725, 40.70502421 ], [ -74.00788273, 40.7050285 ], [ -74.00790815, 40.7050302 ], [ -74.0079086, 40.7050095 ], [ -74.00790321, 40.70500521 ], [ -74.0079086, 40.7050014 ], [ -74.0079139, 40.70499738 ], [ -74.00791938, 40.70500167 ], [ -74.00794498, 40.70500337 ], [ -74.00794561, 40.70498267 ], [ -74.00794013, 40.70497838 ], [ -74.00794543, 40.7049747 ], [ -74.00795091, 40.70497068 ], [ -74.0079563, 40.70497497 ], [ -74.00798172, 40.70497661 ], [ -74.00798235, 40.70495611 ], [ -74.00797678, 40.70495182 ], [ -74.00798208, 40.70494801 ], [ -74.00798765, 40.70494399 ], [ -74.00799304, 40.70494828 ], [ -74.00801873, 40.70494821 ], [ -74.00813524, 40.70494787 ], [ -74.00816094, 40.70494787 ], [ -74.0081666, 40.70494378 ], [ -74.00817172, 40.7049478 ], [ -74.00817693, 40.70495189 ], [ -74.00816992, 40.70495659 ], [ -74.00816857, 40.70497409 ], [ -74.00819444, 40.70497456 ], [ -74.0082001, 40.70497048 ], [ -74.00820522, 40.7049745 ], [ -74.00821043, 40.70497872 ], [ -74.00820343, 40.70498328 ], [ -74.00820208, 40.70500092 ], [ -74.00822795, 40.7050014 ], [ -74.00823361, 40.70499731 ], [ -74.00823873, 40.70500126 ], [ -74.00824394, 40.70500541 ] ] ] } },
{ "type": "Feature", "properties": { "height": 130.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01288149, 40.70342658 ], [ -74.01275357, 40.7034558 ], [ -74.01276974, 40.70350401 ], [ -74.01274612, 40.70353616 ], [ -74.0124229, 40.70353691 ], [ -74.012377, 40.7035177 ], [ -74.01232041, 40.70337591 ], [ -74.01234484, 40.70333996 ], [ -74.0128602, 40.70322248 ], [ -74.01290512, 40.70324012 ], [ -74.01296028, 40.70338027 ], [ -74.01293665, 40.70341398 ], [ -74.01288149, 40.70342658 ] ] ] } },
{ "type": "Feature", "properties": { "height": 90.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00945604, 40.70559849 ], [ -74.00943187, 40.70556069 ], [ -74.00917109, 40.70547618 ], [ -74.00916588, 40.70541142 ], [ -74.00947311, 40.70537267 ], [ -74.00952772, 40.70533746 ], [ -74.00982237, 40.70538411 ], [ -74.00984797, 40.70541489 ], [ -74.00979039, 40.70562471 ], [ -74.00975329, 40.70564541 ], [ -74.00945604, 40.70559849 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01421441, 40.7030034 ], [ -74.01420965, 40.7030604 ], [ -74.01376939, 40.70304467 ], [ -74.01377855, 40.70271227 ], [ -74.01387647, 40.70271411 ], [ -74.0138771, 40.70270961 ], [ -74.01397672, 40.7027233 ], [ -74.01406125, 40.7027579 ], [ -74.0141262, 40.70280189 ], [ -74.0141774, 40.70286168 ], [ -74.01421253, 40.7029385 ], [ -74.01422034, 40.7030034 ], [ -74.01421441, 40.7030034 ] ] ] } },
{ "type": "Feature", "properties": { "height": 157.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01421441, 40.7030034 ], [ -74.01420965, 40.7030604 ], [ -74.01376939, 40.70304467 ], [ -74.01377855, 40.70271227 ], [ -74.01387647, 40.70271411 ], [ -74.01397483, 40.70272759 ], [ -74.01405784, 40.70276157 ], [ -74.01412171, 40.70280489 ], [ -74.0141721, 40.70286366 ], [ -74.01420669, 40.70293945 ], [ -74.01421441, 40.7030034 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00949134, 40.70517736 ], [ -74.00906815, 40.70524798 ], [ -74.00906195, 40.70522776 ], [ -74.0089829, 40.70497061 ], [ -74.00938813, 40.70488052 ], [ -74.00939091, 40.70488849 ], [ -74.0094025, 40.70492186 ], [ -74.00942702, 40.70499227 ], [ -74.00945155, 40.70506296 ], [ -74.00948002, 40.70514488 ], [ -74.00949134, 40.70517736 ] ] ] } },
{ "type": "Feature", "properties": { "height": 90.9 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.012203, 40.7032278 ], [ -74.01172815, 40.7033497 ], [ -74.01166832, 40.70304678 ], [ -74.0120704, 40.70294361 ], [ -74.01212888, 40.70306892 ], [ -74.012203, 40.7032278 ] ], [ [ -74.01202522, 40.70315288 ], [ -74.01199378, 40.70308049 ], [ -74.0120148, 40.70307518 ], [ -74.01200689, 40.70305707 ], [ -74.01186478, 40.70309289 ], [ -74.01191203, 40.70320151 ], [ -74.01199665, 40.70318019 ], [ -74.01198875, 40.70316208 ], [ -74.01202522, 40.70315288 ] ] ] } },
{ "type": "Feature", "properties": { "height": 82.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01039945, 40.7057575 ], [ -74.01030198, 40.70590799 ], [ -74.00994301, 40.70577336 ], [ -74.01, 40.70558208 ], [ -74.0101755, 40.70556526 ], [ -74.0102921, 40.7055549 ], [ -74.0106313, 40.70554946 ], [ -74.01065053, 40.70562859 ], [ -74.01019517, 40.70563458 ], [ -74.01018313, 40.70567197 ], [ -74.01026695, 40.70570336 ], [ -74.01028312, 40.7056783 ], [ -74.01040151, 40.7057227 ], [ -74.01038301, 40.7057513 ], [ -74.01039945, 40.7057575 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01195794, 40.70446246 ], [ -74.01195587, 40.7044835 ], [ -74.01195461, 40.70450298 ], [ -74.01181834, 40.70450427 ], [ -74.01178402, 40.70450461 ], [ -74.01175231, 40.70450488 ], [ -74.01171934, 40.70450515 ], [ -74.01167937, 40.70450556 ], [ -74.01168107, 40.70447826 ], [ -74.01168521, 40.70441519 ], [ -74.01168691, 40.70438768 ], [ -74.01169464, 40.70426667 ], [ -74.01169653, 40.70423698 ], [ -74.01170012, 40.70417916 ], [ -74.0117021, 40.7041479 ], [ -74.01174189, 40.70414858 ], [ -74.01177495, 40.7041492 ], [ -74.01180648, 40.70414981 ], [ -74.0118408, 40.70415049 ], [ -74.01204714, 40.70415417 ], [ -74.01204768, 40.7041859 ], [ -74.01204831, 40.70421559 ], [ -74.0120492, 40.7042619 ], [ -74.01205019, 40.7043159 ], [ -74.0120519, 40.70441356 ], [ -74.01205352, 40.70446607 ], [ -74.01195794, 40.70446246 ] ] ] } },
{ "type": "Feature", "properties": { "height": 153.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01179552, 40.70559188 ], [ -74.01171261, 40.70558848 ], [ -74.01173551, 40.70527148 ], [ -74.01183154, 40.70527549 ], [ -74.01212044, 40.70528762 ], [ -74.01209744, 40.70560448 ], [ -74.01179552, 40.70559188 ] ] ] } },
{ "type": "Feature", "properties": { "height": 81.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0121968, 40.70366677 ], [ -74.01219671, 40.70368257 ], [ -74.01219491, 40.70393196 ], [ -74.01219482, 40.70394762 ], [ -74.01205801, 40.70394442 ], [ -74.01198983, 40.70394278 ], [ -74.01192164, 40.70394101 ], [ -74.01185328, 40.70393897 ], [ -74.01175752, 40.70393631 ], [ -74.01176228, 40.7038755 ], [ -74.0117657, 40.70382381 ], [ -74.01176911, 40.70377206 ], [ -74.01177243, 40.70372037 ], [ -74.01177701, 40.70365772 ], [ -74.01186334, 40.70365969 ], [ -74.01193161, 40.70366098 ], [ -74.0119998, 40.70366221 ], [ -74.01206807, 40.7036635 ], [ -74.0121968, 40.70366677 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0122409, 40.70564977 ], [ -74.01219473, 40.70574721 ], [ -74.01199647, 40.70572651 ], [ -74.01193287, 40.70571977 ], [ -74.01194177, 40.70586836 ], [ -74.01171647, 40.7058766 ], [ -74.01161819, 40.70586046 ], [ -74.0115863, 40.70585522 ], [ -74.01154462, 40.70584841 ], [ -74.0115545, 40.70578167 ], [ -74.01156537, 40.70570908 ], [ -74.01157723, 40.70562988 ], [ -74.01164748, 40.70562968 ], [ -74.01179318, 40.7056275 ], [ -74.01200132, 40.70562471 ], [ -74.0121323, 40.70563839 ], [ -74.01220147, 40.70564568 ], [ -74.0122409, 40.70564977 ] ] ] } },
{ "type": "Feature", "properties": { "height": 86.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01312224, 40.7055327 ], [ -74.01299019, 40.705493 ], [ -74.01277325, 40.70542647 ], [ -74.01265341, 40.70539079 ], [ -74.01276301, 40.70517437 ], [ -74.01288482, 40.70521019 ], [ -74.01289407, 40.70521257 ], [ -74.01306897, 40.70526392 ], [ -74.0131032, 40.705274 ], [ -74.01323372, 40.705312 ], [ -74.01312224, 40.7055327 ] ] ] } },
{ "type": "Feature", "properties": { "height": 41.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00755359, 40.70585161 ], [ -74.00741821, 40.70598672 ], [ -74.00708952, 40.70580081 ], [ -74.0070976, 40.70579236 ], [ -74.00707631, 40.70578038 ], [ -74.00692647, 40.70569621 ], [ -74.00706679, 40.70559052 ], [ -74.0070738, 40.7055959 ], [ -74.00709544, 40.70557956 ], [ -74.00711305, 40.70558997 ], [ -74.00712374, 40.70559651 ], [ -74.00716174, 40.70561946 ], [ -74.00726657, 40.70568116 ], [ -74.00731248, 40.7057086 ], [ -74.00731922, 40.70571262 ], [ -74.00735623, 40.70573462 ], [ -74.00747274, 40.70580367 ], [ -74.00753544, 40.70584078 ], [ -74.00755359, 40.70585161 ] ] ] } },
{ "type": "Feature", "properties": { "height": 96.4 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00949134, 40.70517736 ], [ -74.00948002, 40.70514488 ], [ -74.00945155, 40.70506296 ], [ -74.00942702, 40.70499227 ], [ -74.0094025, 40.70492186 ], [ -74.00939091, 40.70488849 ], [ -74.0096861, 40.7048227 ], [ -74.00985453, 40.70509278 ], [ -74.00949134, 40.70517736 ] ] ] } },
{ "type": "Feature", "properties": { "height": 20.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01205513, 40.70114258 ], [ -74.01151327, 40.70126441 ], [ -74.01144599, 40.70109238 ], [ -74.01198785, 40.70097048 ], [ -74.01205513, 40.70114258 ] ] ] } },
{ "type": "Feature", "properties": { "height": 11.6 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01522744, 40.70139762 ], [ -74.01519573, 40.70142541 ], [ -74.01517893, 40.701447 ], [ -74.0151536, 40.70149256 ], [ -74.0151307, 40.70153989 ], [ -74.01511983, 40.70155201 ], [ -74.01510734, 40.70155998 ], [ -74.015098, 40.70156407 ], [ -74.01507491, 40.70156917 ], [ -74.01505703, 40.70157026 ], [ -74.01504518, 40.70156958 ], [ -74.01503763, 40.70156849 ], [ -74.01502433, 40.7015625 ], [ -74.01501149, 40.70157891 ], [ -74.01494322, 40.70154806 ], [ -74.01493639, 40.70155691 ], [ -74.01488537, 40.7015339 ], [ -74.01489219, 40.70152518 ], [ -74.01483749, 40.70150059 ], [ -74.01484809, 40.7014869 ], [ -74.01480281, 40.70146647 ], [ -74.01490549, 40.70133442 ], [ -74.01488222, 40.70132387 ], [ -74.01495013, 40.70123656 ], [ -74.01494214, 40.70123302 ], [ -74.01499191, 40.70116886 ], [ -74.01505614, 40.70119788 ], [ -74.01510545, 40.70113447 ], [ -74.01515648, 40.70115749 ], [ -74.01507725, 40.70125951 ], [ -74.01502173, 40.70123452 ], [ -74.0150034, 40.70125801 ], [ -74.01507239, 40.70128907 ], [ -74.01507653, 40.70128389 ], [ -74.01510842, 40.70128239 ], [ -74.01513393, 40.70128852 ], [ -74.01515369, 40.70130296 ], [ -74.01516232, 40.7013225 ], [ -74.01515818, 40.70132782 ], [ -74.01519421, 40.70134389 ], [ -74.01517193, 40.70137256 ], [ -74.01522744, 40.70139762 ] ] ] } },
{ "type": "Feature", "properties": { "height": 93.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01219671, 40.70368257 ], [ -74.01219491, 40.70393196 ], [ -74.01205873, 40.70392842 ], [ -74.011854, 40.7039231 ], [ -74.01178995, 40.70392126 ], [ -74.01180055, 40.70367447 ], [ -74.0118628, 40.70367556 ], [ -74.01206762, 40.70367951 ], [ -74.01219671, 40.70368257 ] ] ] } },
{ "type": "Feature", "properties": { "height": 55.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00913929, 40.70337108 ], [ -74.00867082, 40.70368087 ], [ -74.00853347, 40.70356156 ], [ -74.00900194, 40.70325177 ], [ -74.00913929, 40.70337108 ] ] ] } },
{ "type": "Feature", "properties": { "height": 46.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01130809, 40.7056292 ], [ -74.01130118, 40.70567156 ], [ -74.01128959, 40.70571296 ], [ -74.01127872, 40.70575532 ], [ -74.01126947, 40.7057861 ], [ -74.01125976, 40.7058181 ], [ -74.01124539, 40.70585678 ], [ -74.01123111, 40.70589526 ], [ -74.01122168, 40.70591828 ], [ -74.01101938, 40.70588886 ], [ -74.0109556, 40.7058796 ], [ -74.01095748, 40.70584419 ], [ -74.01092011, 40.70583949 ], [ -74.01092371, 40.70570969 ], [ -74.01092559, 40.70564289 ], [ -74.01084528, 40.70564146 ], [ -74.01084124, 40.7056213 ], [ -74.01130809, 40.7056292 ] ] ] } },
{ "type": "Feature", "properties": { "height": 18.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01105315, 40.70469352 ], [ -74.01099395, 40.70471837 ], [ -74.01100473, 40.70475678 ], [ -74.01072563, 40.70480227 ], [ -74.01069679, 40.70475208 ], [ -74.01056402, 40.70479628 ], [ -74.01050392, 40.70471177 ], [ -74.01067649, 40.7046088 ], [ -74.01094194, 40.70449991 ], [ -74.01105315, 40.70469352 ] ] ] } },
{ "type": "Feature", "properties": { "height": 73.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0128691, 40.70304998 ], [ -74.01260445, 40.70312251 ], [ -74.01257885, 40.70307069 ], [ -74.01257535, 40.70307171 ], [ -74.01248381, 40.70289648 ], [ -74.01248659, 40.70289546 ], [ -74.01246063, 40.70284268 ], [ -74.01278555, 40.70275899 ], [ -74.0128691, 40.70304998 ] ] ] } },
{ "type": "Feature", "properties": { "height": 122.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01122482, 40.7054533 ], [ -74.01101174, 40.70544969 ], [ -74.01100608, 40.7053901 ], [ -74.01080863, 40.705401 ], [ -74.01080405, 40.70535326 ], [ -74.01078249, 40.70535449 ], [ -74.01077144, 40.70523389 ], [ -74.01127037, 40.70521121 ], [ -74.01122482, 40.7054533 ] ] ] } },
{ "type": "Feature", "properties": { "height": 83.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01307849, 40.70585617 ], [ -74.01297294, 40.70597977 ], [ -74.01237188, 40.70571711 ], [ -74.01242389, 40.70560891 ], [ -74.01307849, 40.70585617 ] ] ] } },
{ "type": "Feature", "properties": { "height": 105.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0098044, 40.70543498 ], [ -74.00975994, 40.70560537 ], [ -74.00972948, 40.70562239 ], [ -74.00949341, 40.70558507 ], [ -74.0094811, 40.7055831 ], [ -74.00945891, 40.70554837 ], [ -74.00920864, 40.70546828 ], [ -74.0092055, 40.70543525 ], [ -74.00948954, 40.7053995 ], [ -74.00953814, 40.70536818 ], [ -74.00954982, 40.70537008 ], [ -74.00978374, 40.70540761 ], [ -74.0098044, 40.70543498 ] ] ] } },
{ "type": "Feature", "properties": { "height": 37.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01232014, 40.70507147 ], [ -74.01231951, 40.70510388 ], [ -74.01188787, 40.70508597 ], [ -74.01188427, 40.70515339 ], [ -74.01184097, 40.70515196 ], [ -74.01183154, 40.70527549 ], [ -74.01173551, 40.70527148 ], [ -74.01167542, 40.70526868 ], [ -74.01164748, 40.70562968 ], [ -74.01157723, 40.70562988 ], [ -74.01156861, 40.70563002 ], [ -74.01163975, 40.70504546 ], [ -74.01232014, 40.70507147 ] ] ] } },
{ "type": "Feature", "properties": { "height": 54.4 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01472151, 40.70491811 ], [ -74.01464596, 40.70508938 ], [ -74.01462629, 40.70513419 ], [ -74.01454275, 40.70511492 ], [ -74.01449109, 40.705103 ], [ -74.01451328, 40.70504736 ], [ -74.01453269, 40.70505186 ], [ -74.01454122, 40.70503061 ], [ -74.01443702, 40.7050065 ], [ -74.01445642, 40.70495802 ], [ -74.0143311, 40.70492901 ], [ -74.01430739, 40.70498818 ], [ -74.01434692, 40.70499738 ], [ -74.01431691, 40.70507249 ], [ -74.0142534, 40.70505506 ], [ -74.01414974, 40.70502659 ], [ -74.01417174, 40.70497232 ], [ -74.01423328, 40.70482018 ], [ -74.01423912, 40.70481807 ], [ -74.01471945, 40.70491539 ], [ -74.01472151, 40.70491811 ] ] ] } },
{ "type": "Feature", "properties": { "height": 105.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00616757, 40.70485941 ], [ -74.00616138, 40.70485525 ], [ -74.0061965, 40.70482406 ], [ -74.00618572, 40.70481719 ], [ -74.00624474, 40.7047655 ], [ -74.00625453, 40.70475699 ], [ -74.00627699, 40.70473737 ], [ -74.00629064, 40.70472559 ], [ -74.00631113, 40.70470782 ], [ -74.00631427, 40.70470489 ], [ -74.00656598, 40.70486622 ], [ -74.00623414, 40.70509701 ], [ -74.00607765, 40.70500262 ], [ -74.00621321, 40.70489142 ], [ -74.00616757, 40.70485941 ] ] ] } },
{ "type": "Feature", "properties": { "height": 104.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01303178, 40.70547959 ], [ -74.01275591, 40.70539228 ], [ -74.01267317, 40.70536879 ], [ -74.01276274, 40.70519997 ], [ -74.01284152, 40.7052249 ], [ -74.01312116, 40.70530696 ], [ -74.01320965, 40.70533426 ], [ -74.01312098, 40.70550628 ], [ -74.01303178, 40.70547959 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01133747, 40.70545521 ], [ -74.01130809, 40.7056292 ], [ -74.01084124, 40.7056213 ], [ -74.0108063, 40.70544908 ], [ -74.01101174, 40.70544969 ], [ -74.01122482, 40.7054533 ], [ -74.01127692, 40.70545419 ], [ -74.01133747, 40.70545521 ] ] ] } },
{ "type": "Feature", "properties": { "height": 13.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01485662, 40.70109048 ], [ -74.01482464, 40.70119202 ], [ -74.01403035, 40.70104716 ], [ -74.01406224, 40.70094576 ], [ -74.01485662, 40.70109048 ] ] ] } },
{ "type": "Feature", "properties": { "height": 17.1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01450152, 40.70556042 ], [ -74.014431, 40.70572052 ], [ -74.01441761, 40.70571698 ], [ -74.014264, 40.70567748 ], [ -74.01419779, 40.70576546 ], [ -74.01414785, 40.70577411 ], [ -74.01402523, 40.70572032 ], [ -74.01400915, 40.70567871 ], [ -74.01416357, 40.70548217 ], [ -74.01432212, 40.70551888 ], [ -74.01450152, 40.70556042 ] ] ] } },
{ "type": "Feature", "properties": { "height": 81.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00948002, 40.70514488 ], [ -74.00912213, 40.70521536 ], [ -74.00905907, 40.70500357 ], [ -74.0094025, 40.70492186 ], [ -74.00942702, 40.70499227 ], [ -74.00945155, 40.70506296 ], [ -74.00948002, 40.70514488 ] ] ] } },
{ "type": "Feature", "properties": { "height": 78.3 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01200132, 40.70562471 ], [ -74.01199647, 40.70572651 ], [ -74.01193287, 40.70571977 ], [ -74.01194177, 40.70586836 ], [ -74.01171647, 40.7058766 ], [ -74.01161819, 40.70586046 ], [ -74.0115863, 40.70585522 ], [ -74.01154462, 40.70584841 ], [ -74.0115545, 40.70578167 ], [ -74.01175959, 40.70578086 ], [ -74.01177719, 40.70575648 ], [ -74.01177719, 40.70572978 ], [ -74.01175887, 40.70570697 ], [ -74.01156537, 40.70570908 ], [ -74.01157723, 40.70562988 ], [ -74.01164748, 40.70562968 ], [ -74.01179318, 40.7056275 ], [ -74.01200132, 40.70562471 ] ] ] } },
{ "type": "Feature", "properties": { "height": 19.1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00470341, 40.70563792 ], [ -74.00455788, 40.70573979 ], [ -74.0045524, 40.7057353 ], [ -74.00453498, 40.70574749 ], [ -74.00464008, 40.70583438 ], [ -74.00459615, 40.70586516 ], [ -74.0044791, 40.70576846 ], [ -74.00442933, 40.7058034 ], [ -74.00424877, 40.70565406 ], [ -74.00434822, 40.70559127 ], [ -74.00451755, 40.70548422 ], [ -74.00470341, 40.70563792 ] ] ] } },
{ "type": "Feature", "properties": { "height": 143.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00820001, 40.70509919 ], [ -74.00791174, 40.70530982 ], [ -74.00784168, 40.70531009 ], [ -74.00775382, 40.7052409 ], [ -74.00775777, 40.70518887 ], [ -74.00804631, 40.70497811 ], [ -74.00811153, 40.70497926 ], [ -74.00819992, 40.70504879 ], [ -74.00820001, 40.70509919 ] ] ] } },
{ "type": "Feature", "properties": { "height": 54.4 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01104103, 40.70498771 ], [ -74.01067739, 40.7050208 ], [ -74.0105995, 40.7048795 ], [ -74.01056591, 40.70489707 ], [ -74.01043942, 40.70475467 ], [ -74.01050392, 40.70471177 ], [ -74.01056402, 40.70479628 ], [ -74.01060417, 40.7048526 ], [ -74.01062987, 40.70485028 ], [ -74.01062897, 40.70484477 ], [ -74.01072042, 40.70483646 ], [ -74.01072275, 40.7048509 ], [ -74.01101533, 40.70482427 ], [ -74.01104103, 40.70498771 ] ] ] } },
{ "type": "Feature", "properties": { "height": 15.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01069931, 40.70535898 ], [ -74.01058172, 40.70536797 ], [ -74.0106313, 40.70554946 ], [ -74.0102921, 40.7055549 ], [ -74.01029363, 40.70549899 ], [ -74.01026551, 40.7054787 ], [ -74.01028132, 40.70540992 ], [ -74.01032067, 40.7054104 ], [ -74.01043763, 40.70538697 ], [ -74.01044922, 40.70535789 ], [ -74.01056698, 40.7053517 ], [ -74.01056483, 40.70533208 ], [ -74.01061603, 40.7053344 ], [ -74.0106092, 40.70528721 ], [ -74.01059124, 40.70522728 ], [ -74.01054524, 40.70522748 ], [ -74.01040834, 40.70523321 ], [ -74.01031312, 40.70525268 ], [ -74.01029614, 40.7052076 ], [ -74.0105562, 40.70518799 ], [ -74.01055881, 40.7052138 ], [ -74.01066364, 40.70521346 ], [ -74.01069931, 40.70535898 ] ] ] } },
{ "type": "Feature", "properties": { "height": 9.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0124229, 40.70353691 ], [ -74.01174512, 40.70353908 ], [ -74.0117462, 40.70347882 ], [ -74.01234484, 40.70333996 ], [ -74.01232041, 40.70337591 ], [ -74.012377, 40.7035177 ], [ -74.0124229, 40.70353691 ] ] ] } },
{ "type": "Feature", "properties": { "height": 48.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01023649, 40.70474241 ], [ -74.01002701, 40.70497967 ], [ -74.00997311, 40.70498458 ], [ -74.00983845, 40.70476856 ], [ -74.01005243, 40.70463236 ], [ -74.01005063, 40.70462651 ], [ -74.01005189, 40.70462058 ], [ -74.01005369, 40.70461786 ], [ -74.01005917, 40.7046135 ], [ -74.01006653, 40.70461119 ], [ -74.01007049, 40.70461091 ], [ -74.01007821, 40.704612 ], [ -74.01008477, 40.70461541 ], [ -74.01008908, 40.70462052 ], [ -74.01009025, 40.70462338 ], [ -74.01009007, 40.70462937 ], [ -74.01008881, 40.7046323 ], [ -74.01023649, 40.70474241 ] ] ] } },
{ "type": "Feature", "properties": { "height": 23.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0135475, 40.70056996 ], [ -74.01333272, 40.70059148 ], [ -74.01294321, 40.70063071 ], [ -74.01284143, 40.70064072 ], [ -74.01239254, 40.70074397 ], [ -74.01236137, 40.70067968 ], [ -74.01281223, 40.70057902 ], [ -74.01289982, 40.70056431 ], [ -74.01353951, 40.70051541 ], [ -74.0135475, 40.70056996 ] ] ] } },
{ "type": "Feature", "properties": { "height": 150.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01044922, 40.70535789 ], [ -74.01043763, 40.70538697 ], [ -74.01032067, 40.7054104 ], [ -74.01028132, 40.70540992 ], [ -74.01026551, 40.7054787 ], [ -74.01024242, 40.70553161 ], [ -74.0101755, 40.70556526 ], [ -74.01012663, 40.70552801 ], [ -74.01012358, 40.7054644 ], [ -74.01016068, 40.70532487 ], [ -74.01020317, 40.70525479 ], [ -74.01022113, 40.70526562 ], [ -74.01031312, 40.70525268 ], [ -74.01040834, 40.70523321 ], [ -74.01054524, 40.70522748 ], [ -74.01056483, 40.70533208 ], [ -74.01056698, 40.7053517 ], [ -74.01044922, 40.70535789 ] ] ] } },
{ "type": "Feature", "properties": { "height": 104.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00889684, 40.70556321 ], [ -74.00881437, 40.70573938 ], [ -74.00878814, 40.70578596 ], [ -74.00874547, 40.70579577 ], [ -74.00852898, 40.70569301 ], [ -74.0085367, 40.70568252 ], [ -74.00854542, 40.70567081 ], [ -74.00858054, 40.70562328 ], [ -74.00863615, 40.70554809 ], [ -74.00868358, 40.70556839 ], [ -74.00870199, 40.70553468 ], [ -74.00869894, 40.70551922 ], [ -74.00869633, 40.70550601 ], [ -74.00869103, 40.70547952 ], [ -74.00888264, 40.70546277 ], [ -74.00889684, 40.70556321 ] ] ] } },
{ "type": "Feature", "properties": { "height": 21.1 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01095452, 40.70353711 ], [ -74.01070658, 40.70359207 ], [ -74.01061927, 40.70332089 ], [ -74.01086244, 40.70325946 ], [ -74.01089155, 40.70334697 ], [ -74.01090358, 40.70338368 ], [ -74.01095452, 40.70353711 ] ] ] } },
{ "type": "Feature", "properties": { "height": 41.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01014783, 40.70395722 ], [ -74.00985552, 40.70367202 ], [ -74.00999557, 40.70358117 ], [ -74.01011423, 40.70368536 ], [ -74.01031393, 40.70386086 ], [ -74.01028491, 40.70387966 ], [ -74.01028015, 40.70387509 ], [ -74.01015484, 40.70396396 ], [ -74.01014783, 40.70395722 ] ] ] } },
{ "type": "Feature", "properties": { "height": 35.9 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00753544, 40.70584078 ], [ -74.00741102, 40.70596057 ], [ -74.00711224, 40.70578406 ], [ -74.00713398, 40.70576111 ], [ -74.00704298, 40.70570806 ], [ -74.00712221, 40.705629 ], [ -74.00713973, 40.70564098 ], [ -74.00716174, 40.70561946 ], [ -74.00726657, 40.70568116 ], [ -74.00731248, 40.7057086 ], [ -74.00731922, 40.70571262 ], [ -74.00735623, 40.70573462 ], [ -74.00747274, 40.70580367 ], [ -74.00753544, 40.70584078 ] ] ] } },
{ "type": "Feature", "properties": { "height": 98.7 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00870199, 40.70553468 ], [ -74.00860569, 40.70553441 ], [ -74.00861423, 40.70554476 ], [ -74.00855233, 40.70560707 ], [ -74.00858054, 40.70562328 ], [ -74.00854542, 40.70567081 ], [ -74.0085367, 40.70568252 ], [ -74.00852898, 40.70569301 ], [ -74.00849601, 40.70573748 ], [ -74.00848011, 40.70575906 ], [ -74.0084758, 40.70576492 ], [ -74.00831617, 40.70567292 ], [ -74.00847274, 40.70542409 ], [ -74.0086754, 40.7054008 ], [ -74.00868223, 40.70543498 ], [ -74.00869103, 40.70547952 ], [ -74.00869633, 40.70550601 ], [ -74.00869894, 40.70551922 ], [ -74.00870199, 40.70553468 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00505735, 40.70561217 ], [ -74.00491155, 40.70571269 ], [ -74.00457819, 40.70544751 ], [ -74.00472497, 40.70535776 ], [ -74.00505735, 40.70561217 ] ] ] } },
{ "type": "Feature", "properties": { "height": 120.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01312116, 40.70530696 ], [ -74.01303178, 40.70547959 ], [ -74.01299019, 40.705493 ], [ -74.01277325, 40.70542647 ], [ -74.01275591, 40.70539228 ], [ -74.01284152, 40.7052249 ], [ -74.01288482, 40.70521019 ], [ -74.01289407, 40.70521257 ], [ -74.01306897, 40.70526392 ], [ -74.0131032, 40.705274 ], [ -74.01312116, 40.70530696 ] ] ] } },
{ "type": "Feature", "properties": { "height": 65.2 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00875778, 40.70528462 ], [ -74.00834671, 40.70532916 ], [ -74.0083344, 40.70532807 ], [ -74.00832281, 40.70532487 ], [ -74.00831751, 40.70532248 ], [ -74.00830808, 40.70531649 ], [ -74.00830413, 40.70531288 ], [ -74.00829811, 40.70530471 ], [ -74.00829604, 40.70530021 ], [ -74.00829398, 40.70529102 ], [ -74.00829497, 40.70528156 ], [ -74.00829883, 40.7052727 ], [ -74.00830521, 40.7052646 ], [ -74.00830934, 40.70526106 ], [ -74.00846115, 40.70516068 ], [ -74.00865519, 40.70506139 ], [ -74.00875778, 40.70528462 ] ] ] } },
{ "type": "Feature", "properties": { "height": 18.4 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01354292, 40.70285331 ], [ -74.01354274, 40.70276076 ], [ -74.01357769, 40.70275939 ], [ -74.01356044, 40.70250619 ], [ -74.01356044, 40.70246131 ], [ -74.01363401, 40.70246118 ], [ -74.01367363, 40.70246622 ], [ -74.01373822, 40.70249577 ], [ -74.01372429, 40.70252676 ], [ -74.01384395, 40.7025683 ], [ -74.01376023, 40.70272248 ], [ -74.01364821, 40.70270907 ], [ -74.01364839, 40.70285869 ], [ -74.01363545, 40.70285869 ], [ -74.01354292, 40.70285331 ] ] ] } },
{ "type": "Feature", "properties": { "height": 18.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01137322, 40.70143249 ], [ -74.01143592, 40.70134641 ], [ -74.01227863, 40.70115552 ], [ -74.01230469, 40.70122158 ], [ -74.01137322, 40.70143249 ] ] ] } },
{ "type": "Feature", "properties": { "height": 96.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01214802, 40.70541517 ], [ -74.0121323, 40.70563839 ], [ -74.01200132, 40.70562471 ], [ -74.01179318, 40.7056275 ], [ -74.01179552, 40.70559188 ], [ -74.01209744, 40.70560448 ], [ -74.01212044, 40.70528762 ], [ -74.01183154, 40.70527549 ], [ -74.01184097, 40.70515196 ], [ -74.01188427, 40.70515339 ], [ -74.01193027, 40.70515496 ], [ -74.01216608, 40.70516265 ], [ -74.01214802, 40.70541517 ] ] ] } },
{ "type": "Feature", "properties": { "height": 28.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01133747, 40.70545521 ], [ -74.01127692, 40.70545419 ], [ -74.01134358, 40.70517266 ], [ -74.01066077, 40.70520127 ], [ -74.01064747, 40.7051476 ], [ -74.01141338, 40.70510538 ], [ -74.01133747, 40.70545521 ] ] ] } },
{ "type": "Feature", "properties": { "height": 20.8 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00838093, 40.70363116 ], [ -74.00828437, 40.7036949 ], [ -74.00788165, 40.70334159 ], [ -74.00797813, 40.70327792 ], [ -74.00838093, 40.70363116 ] ] ] } },
{ "type": "Feature", "properties": { "height": 120.6 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01195794, 40.70446246 ], [ -74.01195587, 40.7044835 ], [ -74.0118363, 40.70448118 ], [ -74.01184133, 40.70440151 ], [ -74.0118505, 40.70425748 ], [ -74.01185535, 40.70418209 ], [ -74.01204768, 40.7041859 ], [ -74.01204831, 40.70421559 ], [ -74.0120492, 40.7042619 ], [ -74.01205019, 40.7043159 ], [ -74.0120519, 40.70441356 ], [ -74.01205352, 40.70446607 ], [ -74.01195794, 40.70446246 ] ] ] } },
{ "type": "Feature", "properties": { "height": 5.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00491155, 40.70571269 ], [ -74.00505735, 40.70561217 ], [ -74.00532639, 40.70581817 ], [ -74.00517601, 40.70592318 ], [ -74.00491155, 40.70571269 ] ] ] } },
{ "type": "Feature", "properties": { "height": 139.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01310625, 40.7053092 ], [ -74.01302325, 40.70546699 ], [ -74.01298201, 40.70547829 ], [ -74.01278771, 40.70541939 ], [ -74.01277064, 40.70538908 ], [ -74.01285014, 40.70523368 ], [ -74.01289039, 40.7052219 ], [ -74.01309152, 40.70528176 ], [ -74.01310625, 40.7053092 ] ] ] } },
{ "type": "Feature", "properties": { "height": 110.5 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.0085367, 40.70568252 ], [ -74.00854542, 40.70567081 ], [ -74.00858054, 40.70562328 ], [ -74.00863615, 40.70554809 ], [ -74.00868358, 40.70556839 ], [ -74.00870199, 40.70553468 ], [ -74.00869894, 40.70551922 ], [ -74.00869633, 40.70550601 ], [ -74.00886513, 40.70548851 ], [ -74.00887501, 40.70555572 ], [ -74.00877251, 40.7057626 ], [ -74.00873011, 40.70577248 ], [ -74.0085367, 40.70568252 ] ] ] } },
{ "type": "Feature", "properties": { "height": 158.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01068152, 40.7024067 ], [ -74.01041391, 40.70245157 ], [ -74.01035525, 40.70225047 ], [ -74.01062268, 40.70220559 ], [ -74.01068152, 40.7024067 ] ] ] } },
{ "type": "Feature", "properties": { "height": 214.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.00979497, 40.70547707 ], [ -74.00979075, 40.70550308 ], [ -74.0097841, 40.70550662 ], [ -74.00977835, 40.70552739 ], [ -74.00978249, 40.70553257 ], [ -74.00977269, 40.7055577 ], [ -74.00976353, 40.70556049 ], [ -74.00974161, 40.70558337 ], [ -74.00969804, 40.7056068 ], [ -74.00966849, 40.70560829 ], [ -74.00966238, 40.70561388 ], [ -74.00962232, 40.70561129 ], [ -74.00959339, 40.70560727 ], [ -74.00955521, 40.70559706 ], [ -74.0095527, 40.70559011 ], [ -74.00952682, 40.70557956 ], [ -74.00949979, 40.70554428 ], [ -74.00949449, 40.70551929 ], [ -74.00948793, 40.70551432 ], [ -74.00949215, 40.70548817 ], [ -74.00950023, 40.70545882 ], [ -74.00950994, 40.70543362 ], [ -74.0095191, 40.70543076 ], [ -74.00954165, 40.7054074 ], [ -74.00958522, 40.70538411 ], [ -74.00961477, 40.70538261 ], [ -74.00962088, 40.70537696 ], [ -74.00966112, 40.70537969 ], [ -74.00969005, 40.70538391 ], [ -74.00972814, 40.70539405 ], [ -74.00973065, 40.70540107 ], [ -74.0097567, 40.70541176 ], [ -74.00978329, 40.70544697 ], [ -74.0097885, 40.70547196 ], [ -74.00979497, 40.70547707 ] ] ] } },
{ "type": "Feature", "properties": { "height": 30.0 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01289739, 40.70490238 ], [ -74.01268018, 40.70492199 ], [ -74.0126341, 40.70462937 ], [ -74.01216293, 40.70467207 ], [ -74.01215494, 40.70462181 ], [ -74.01270264, 40.70457216 ], [ -74.01271063, 40.70462297 ], [ -74.01275142, 40.70488222 ], [ -74.01289218, 40.70486949 ], [ -74.01289739, 40.70490238 ] ] ] } },
{ "type": "Feature", "properties": { "height": 102.6 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -74.01361901, 40.70582301 ], [ -74.01354634, 40.70578869 ], [ -74.01375133, 40.70553951 ], [ -74.01382589, 40.70557472 ], [ -74.01391123, 40.70561497 ], [ -74.01370624, 40.70586428 ], [ -74.01361901, 40.70582301 ] ] ] } },
{ "type":
Download .txt
gitextract_sfii8sv8/

├── .babelrc
├── .gitattributes
├── .gitignore
├── .npmignore
├── LICENSE
├── README.md
├── dist/
│   └── geometry-extrude.js
├── index.d.ts
├── package.json
├── rollup.config.js
├── src/
│   ├── main.js
│   ├── math.js
│   └── simplify.js
└── test/
    ├── asset/
    │   ├── buildings-ny.geojson
    │   ├── pisa.hdr
    │   └── street.geojson
    ├── distortion.html
    ├── extrude-bevel.html
    ├── extrude-dude.html
    ├── extrude-exclude-bottom.html
    ├── extrude-hole.html
    ├── extrude-multipolygon.html
    ├── extrude-normal.html
    ├── extrude-polyline.html
    ├── extrude-simple.html
    ├── extrude-simplify.html
    ├── extrude-star.html
    ├── extrude-uv.html
    ├── geojson.html
    ├── lib/
    │   ├── claygl-advanced-renderer.js
    │   ├── claygl.js
    │   └── dat.gui.js
    ├── polygon-offset.html
    ├── polyline-offset.html
    ├── slerp.html
    └── street.html
Download .txt
SYMBOL INDEX (321 symbols across 8 files)

FILE: dist/geometry-extrude.js
  function earcut (line 12) | function earcut(data, holeIndices, dim) {
  function linkedList (line 52) | function linkedList(data, start, end, dim, clockwise) {
  function filterPoints (line 70) | function filterPoints(start, end) {
  function earcutLinked (line 94) | function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
  function isEar (line 147) | function isEar(ear) {
  function isEarHashed (line 166) | function isEarHashed(ear, minX, minY, invSize) {
  function cureLocalIntersections (line 219) | function cureLocalIntersections(start, triangles, dim) {
  function splitEarcut (line 244) | function splitEarcut(start, triangles, dim, minX, minY, invSize) {
  function eliminateHoles (line 270) | function eliminateHoles(data, holeIndices, outerNode, dim) {
  function compareX (line 293) | function compareX(a, b) {
  function eliminateHole (line 298) | function eliminateHole(hole, outerNode) {
  function findHoleBridge (line 315) | function findHoleBridge(hole, outerNode) {
  function sectorContainsSector (line 375) | function sectorContainsSector(m, p) {
  function indexCurve (line 380) | function indexCurve(start, minX, minY, invSize) {
  function sortLinked (line 397) | function sortLinked(list) {
  function zOrder (line 449) | function zOrder(x, y, minX, minY, invSize) {
  function getLeftmost (line 468) | function getLeftmost(start) {
  function pointInTriangle (line 480) | function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
  function isValidDiagonal (line 487) | function isValidDiagonal(a, b) {
  function area$1 (line 495) | function area$1(p, q, r) {
  function equals (line 500) | function equals(p1, p2) {
  function intersects (line 505) | function intersects(p1, q1, p2, q2) {
  function onSegment (line 522) | function onSegment(p, q, r) {
  function sign (line 526) | function sign(num) {
  function intersectsPolygon (line 531) | function intersectsPolygon(a, b) {
  function locallyInside (line 543) | function locallyInside(a, b) {
  function middleInside (line 550) | function middleInside(a, b) {
  function splitPolygon (line 567) | function splitPolygon(a, b) {
  function insertNode (line 589) | function insertNode(i, x, y, last) {
  function removeNode (line 605) | function removeNode(p) {
  function Node (line 613) | function Node(i, x, y) {
  function signedArea (line 665) | function signedArea(data, start, end, dim) {
  function getSqDist (line 702) | function getSqDist(p1, p2) {
  function getSqSegDist (line 709) | function getSqSegDist(p, p1, p2) {
  function simplifyRadialDist (line 734) | function simplifyRadialDist(points, sqTolerance) {
  function simplifyDPStep (line 752) | function simplifyDPStep(points, first, last, sqTolerance, simplified) {
  function simplifyDouglasPeucker (line 773) | function simplifyDouglasPeucker(points, sqTolerance) {
  function simplify (line 782) | function simplify(points, tolerance, highestQuality) {
  function dot (line 790) | function dot(v1, v2) {
  function v2Dot (line 793) | function v2Dot(v1, v2) {
  function normalize (line 796) | function normalize(out, v) {
  function v2Normalize (line 806) | function v2Normalize(out, v) {
  function scale (line 814) | function scale(out, v, s) {
  function scaleAndAdd (line 820) | function scaleAndAdd(out, v1, v2, s) {
  function v2Add (line 826) | function v2Add(out, v1, v2) {
  function v3Sub (line 831) | function v3Sub(out, v1, v2) {
  function v3Normalize (line 837) | function v3Normalize(out, v) {
  function v3Cross (line 847) | function v3Cross(out, v1, v2) {
  function slerp (line 861) | function slerp(out, start, end, t) {
  function lineIntersection (line 872) | function lineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, out, writeOffs...
  function area (line 890) | function area(points, start, end) {
  function triangulate (line 914) | function triangulate(vertices, holes) {
  function flatten (line 918) | function flatten(data) {
  function innerOffsetPolygon (line 925) | function innerOffsetPolygon(vertices, out, start, end, outStart, offset,...
  function offsetPolygon (line 1034) | function offsetPolygon(vertices, holes, offset, miterLimit, close) {
  function reversePoints (line 1072) | function reversePoints(points, stride, start, end) {
  function convertToClockwise (line 1086) | function convertToClockwise(vertices, holes) {
  function normalizeOpts (line 1105) | function normalizeOpts(opts) {
  function generateNormal (line 1156) | function generateNormal(indices, position) {
  function addExtrudeSide (line 1205) | function addExtrudeSide(out, _ref, start, end, cursors, opts) {
  function addTopAndBottom (line 1350) | function addTopAndBottom(_ref2, out, cursors, opts) {
  function splitVertices (line 1401) | function splitVertices(vertices, holes, smoothSide, smoothSideThreshold) {
  function innerExtrudeTriangulatedPolygon (line 1474) | function innerExtrudeTriangulatedPolygon(preparedData, opts) {
  function convertPolylineToTriangulatedPolygon (line 1566) | function convertPolylineToTriangulatedPolygon(polyline, polylineIdx, opt...
  function removeClosePointsOfPolygon (line 1645) | function removeClosePointsOfPolygon(polygon, epsilon) {
  function simplifyPolygon (line 1680) | function simplifyPolygon(polygon, tolerance) {
  function extrudePolygon (line 1714) | function extrudePolygon(polygons, opts) {
  function extrudePolyline (line 1813) | function extrudePolyline(polylines, opts) {
  function updateBoundingRect (line 1855) | function updateBoundingRect(points, min, max) {
  function extrudeGeoJSON (line 1886) | function extrudeGeoJSON(geojson, opts) {

FILE: index.d.ts
  type Rect (line 1) | type Rect = {
  type BasicExtrudeOpt (line 8) | type BasicExtrudeOpt = {
  type ExtrudeResult (line 21) | type ExtrudeResult = {
  type Polygon (line 29) | type Polygon = ArrayLike<ArrayLike<ArrayLike<number>>>;
  type Polyline (line 30) | type Polyline = ArrayLike<ArrayLike<number>>;
  type GeoJSONPolygonGeometry (line 32) | type GeoJSONPolygonGeometry = {
  type GeoJSONLineStringGeometry (line 37) | type GeoJSONLineStringGeometry = {
  type GeoJSONMultiPolygonGeometry (line 41) | type GeoJSONMultiPolygonGeometry = {
  type GeoJSONMultiLineStringGeometry (line 45) | type GeoJSONMultiLineStringGeometry = {
  type GeoJSONFeature (line 50) | type GeoJSONFeature = {
  type GeoJSON (line 58) | type GeoJSON = {
  type GeometryExtrudeStatic (line 62) | interface GeometryExtrudeStatic {

FILE: src/main.js
  function triangulate (line 13) | function triangulate(vertices, holes, dimensions=2) {
  function flatten (line 17) | function flatten(data) {
  function innerOffsetPolygon (line 25) | function innerOffsetPolygon(
  function offsetPolygon (line 153) | function offsetPolygon(vertices, holes, offset, miterLimit, close) {
  function reversePoints (line 201) | function reversePoints(points, stride, start, end) {
  function convertToClockwise (line 215) | function convertToClockwise(vertices, holes) {
  function normalizeOpts (line 231) | function normalizeOpts(opts) {
  function generateNormal (line 290) | function generateNormal(indices, position) {
  function addExtrudeSide (line 347) | function addExtrudeSide(
  function addTopAndBottom (line 490) | function addTopAndBottom({indices, topVertices, rect, depth}, out, curso...
  function splitVertices (line 532) | function splitVertices(vertices, holes, smoothSide, smoothSideThreshold) {
  function innerExtrudeTriangulatedPolygon (line 608) | function innerExtrudeTriangulatedPolygon(preparedData, opts) {
  function convertPolylineToTriangulatedPolygon (line 695) | function convertPolylineToTriangulatedPolygon(polyline, polylineIdx, opt...
  function removeClosePointsOfPolygon (line 782) | function removeClosePointsOfPolygon(polygon, epsilon) {
  function simplifyPolygon (line 811) | function simplifyPolygon(polygon, tolerance) {
  function extrudePolygon (line 840) | function extrudePolygon(polygons, opts) {
  function extrudePolyline (line 933) | function extrudePolyline(polylines, opts) {
  function updateBoundingRect (line 969) | function updateBoundingRect(points, min, max) {
  function extrudeGeoJSON (line 1000) | function extrudeGeoJSON(geojson, opts) {

FILE: src/math.js
  function dot (line 1) | function dot(v1, v2) {
  function v2Dot (line 4) | function v2Dot(v1, v2) {
  function normalize (line 8) | function normalize(out, v) {
  function v2Normalize (line 19) | function v2Normalize(out, v) {
  function scale (line 28) | function scale(out, v, s) {
  function mul (line 35) | function mul(out, v1, v2) {
  function scaleAndAdd (line 42) | function scaleAndAdd(out, v1, v2, s) {
  function add (line 49) | function add(out, v1, v2) {
  function v2Add (line 56) | function v2Add(out, v1, v2) {
  function sub (line 62) | function sub(out, v1, v2) {
  function v2Sub (line 69) | function v2Sub(out, v1, v2) {
  function v3Sub (line 75) | function v3Sub(out, v1, v2) {
  function v3Normalize (line 82) | function v3Normalize(out, v) {
  function v3Cross (line 93) | function v3Cross(out, v1, v2) {
  function slerp (line 105) | function slerp(out, start, end, t) {
  function lineIntersection (line 119) | function lineIntersection(x1, y1, x2, y2, x3, y3, x4, y4, out, writeOffs...
  function area (line 140) | function area(points, start, end) {
  function triangleArea (line 161) | function triangleArea(x0, y0, x1, y1, x2, y2) {

FILE: src/simplify.js
  function getSqDist (line 11) | function getSqDist(p1, p2) {
  function getSqSegDist (line 20) | function getSqSegDist(p, p1, p2) {
  function simplifyRadialDist (line 49) | function simplifyRadialDist(points, sqTolerance) {
  function simplifyDPStep (line 69) | function simplifyDPStep(points, first, last, sqTolerance, simplified) {
  function simplifyDouglasPeucker (line 90) | function simplifyDouglasPeucker(points, sqTolerance) {
  function simplify (line 101) | function simplify(points, tolerance, highestQuality) {

FILE: test/lib/claygl-advanced-renderer.js
  function halton (line 9) | function halton(index, base) {
  function generateNoiseData (line 27) | function generateNoiseData(size) {
  function generateNoiseTexture (line 44) | function generateNoiseTexture(size) {
  function generateKernel (line 54) | function generateKernel(size, offset, hemisphere) {
  function SSAOPass (line 72) | function SSAOPass(opt) {
  function SSRPass (line 296) | function SSRPass(opt) {
  function EffectCompositor (line 1359) | function EffectCompositor() {
  function TemporalSuperSampling (line 1813) | function TemporalSuperSampling (opt) {
  function RenderMain (line 1980) | function RenderMain(renderer, scene, enableShadow) {
  function updateQueue (line 2212) | function updateQueue(queue) {
  function updateQueue (line 2227) | function updateQueue(queue) {
  function clone (line 2472) | function clone(source) {
  function merge (line 2520) | function merge(target, source, overwrite) {
  function isArray (line 2681) | function isArray(value) {
  function isObject (line 2704) | function isObject(value) {
  function isBuiltInObject (line 2716) | function isBuiltInObject(value) {
  function isDom (line 2732) | function isDom(value) {
  function isPrimitive (line 2797) | function isPrimitive(obj) {
  function ClayAdvancedRenderer (line 2806) | function ClayAdvancedRenderer(renderer, scene, timeline, graphicOpts) {
  function accumulate (line 2882) | function accumulate(id) {

FILE: test/lib/claygl.js
  function noop (line 351) | function noop () {}
  function defaultGetter (line 604) | function defaultGetter(target, key) {
  function defaultSetter (line 607) | function defaultSetter(target, key, value) {
  function interpolateNumber (line 611) | function interpolateNumber(p0, p1, percent) {
  function interpolateArray (line 615) | function interpolateArray(p0, p1, percent, out, arrDim) {
  function isArrayLike (line 634) | function isArrayLike(data) {
  function cloneValue (line 644) | function cloneValue(value) {
  function catmullRomInterpolateArray (line 661) | function catmullRomInterpolateArray(
  function catmullRomInterpolate (line 684) | function catmullRomInterpolate(p0, p1, p2, p3, t, t2, t3) {
  function fillArr (line 694) | function fillArr(arr0, arr1, arrDim) {
  function isArraySame (line 731) | function isArraySame(arr0, arr1, arrDim) {
  function createTrackClip (line 759) | function createTrackClip(animator, globalEasing, oneTrackDone, keyframes...
  function Animator (line 960) | function Animator(target, loop, getter, setter, interpolater) {
  function noopEasing (line 984) | function noopEasing(w) {
  function supertriangle (line 1356) | function supertriangle(vertices) {
  function circumcircle (line 1383) | function circumcircle(vertices, i, j, k) {
  function dedup (line 1433) | function dedup(edges) {
  function vec3lerp (line 5174) | function vec3lerp(out, a, b, t, oa, ob) {
  function quatSlerp (line 5185) | function quatSlerp(out, a, b, t, oa, ob) {
  function derive (line 5562) | function derive(makeDefaultOpt, initialize/*optional*/, proto/*optional*...
  function extend (line 5631) | function extend(target, source) {
  function extendWithPropList (line 5642) | function extendWithPropList(target, source, propList) {
  function Handler (line 5661) | function Handler(action, context) {
  function wrapper (line 5756) | function wrapper() {
  function get (line 6087) | function get(options) {
  function step (line 6346) | function step() {
  function GLInfo (line 6660) | function GLInfo(_gl) {
  function clampCssByte (line 7540) | function clampCssByte(i) {  // Clamp to integer 0 .. 255.
  function clampCssAngle (line 7545) | function clampCssAngle(i) {  // Clamp to integer 0 .. 360.
  function clampCssFloat (line 7550) | function clampCssFloat(f) {  // Clamp to float 0.0 .. 1.0.
  function parseCssInt (line 7554) | function parseCssInt(str) {  // int or percentage.
  function parseCssFloat (line 7561) | function parseCssFloat(str) {  // float or percentage.
  function cssHueToRgb (line 7568) | function cssHueToRgb(m1, m2, h) {
  function lerpNumber (line 7588) | function lerpNumber(a, b, p) {
  function setRgba (line 7592) | function setRgba(out, r, g, b, a) {
  function copyRgba (line 7596) | function copyRgba(out, a) {
  function putToCache (line 7604) | function putToCache(colorStr, rgbaArr) {
  function hsla2rgba (line 7745) | function hsla2rgba(hsla, rgba) {
  function rgba2hsla (line 7774) | function rgba2hsla(rgba) {
  function getDefineCode (line 8003) | function getDefineCode(defines) {
  function getProgramKey (line 8021) | function getProgramKey(vertexDefines, fragmentDefines, enabledTextures) {
  function addLineNumbers (line 8504) | function addLineNumbers(string) {
  function checkShaderErrorMsg (line 8515) | function checkShaderErrorMsg(_gl, shader, shaderString) {
  function unrollLoop (line 8817) | function unrollLoop(shaderStr, defines, lightsNumbers) {
  function getDefineCode$1 (line 8867) | function getDefineCode$1(defines, lightsNumbers, enabledTextures) {
  function getExtensionCode (line 8896) | function getExtensionCode(exts) {
  function getPrecisionCode (line 8906) | function getPrecisionCode(precision) {
  function ProgramManager (line 8913) | function ProgramManager(renderer) {
  function createZeroArray (line 9021) | function createZeroArray(len) {
  function getShaderID (line 9119) | function getShaderID(vertex, fragment) {
  function removeComment (line 9135) | function removeComment(code) {
  function logSyntaxError (line 9140) | function logSyntaxError() {
  function parseDeclarations (line 9144) | function parseDeclarations(type, line) {
  function Shader (line 9311) | function Shader(vertex, fragment) {
  function makeDefaultValueFunc (line 9443) | function makeDefaultValueFunc(value) {
  function _uniformParser (line 9447) | function _uniformParser(str, type, content) {
  function _attributeParser (line 9499) | function _attributeParser(str, type, content) {
  function _defineParser (line 9540) | function _defineParser(str, symbol, value) {
  function defaultGetMaterial (line 10619) | function defaultGetMaterial(renderable) {
  function defaultGetUniform (line 10622) | function defaultGetUniform(renderable, material, symbol) {
  function defaultIsMaterialChanged (line 10625) | function defaultIsMaterialChanged(renderabled, prevRenderable, material,...
  function defaultIfRender (line 10628) | function defaultIfRender(renderable) {
  function noop$1 (line 10632) | function noop$1() {}
  function VertexArrayObject (line 10642) | function VertexArrayObject(availableAttributes, availableAttributeSymbol...
  function PlaceHolderTexture (line 10650) | function PlaceHolderTexture(renderer) {
  function clamp (line 12697) | function clamp(val, min, max) {
  function defaultFilter (line 15252) | function defaultFilter (el) {
  function getProgramKey$1 (line 16260) | function getProgramKey$1(lightNumbers) {
  function RenderList (line 16279) | function RenderList() {
  function buildNodesMap (line 16477) | function buildNodesMap(sNode, tNode) {
  function setUniforms (line 16749) | function setUniforms(uniforms, program, renderer) {
  function lightSortFunc (line 16796) | function lightSortFunc(a, b) {
  function getArrayCtorByType (line 16910) | function getArrayCtorByType (type) {
  function makeAttrKey (line 16919) | function makeAttrKey(attrName) {
  function Attribute$1 (line 16927) | function Attribute$1(name, type, size, semantic) {
  function AttributeBuffer (line 17139) | function AttributeBuffer(name, type, buffer, size, semantic) {
  function IndicesBuffer (line 17154) | function IndicesBuffer(buffer) {
  function createPlane (line 18297) | function createPlane(pos, widthSegments, heightSegments) {
  function nearestPowerOfTwo (line 19001) | function nearestPowerOfTwo(val) {
  function convertTextureToPowerOfTwo (line 19004) | function convertTextureToPowerOfTwo(texture, canvas) {
  function isImageRenderable (line 19508) | function isImageRenderable(image) {
  function ShaderLibrary (line 20199) | function ShaderLibrary () {
  function template (line 20225) | function template(name, vertex, fragment) {
  function getAccessorData (line 20760) | function getAccessorData(json, lib, accessorIdx, isIndices) {
  function checkLoad (line 21011) | function checkLoad() {
  function getResult (line 21035) | function getResult() {
  function afterLoadBuffer (line 21050) | function afterLoadBuffer(immediately) {
  function enableSkinningForMesh (line 21197) | function enableSkinningForMesh(mesh, skeleton, jointIndices) {
  function getJointIndex (line 21206) | function getJointIndex(joint) {
  function instanceMesh (line 21755) | function instanceMesh(mesh) {
  function checkChannelPath (line 21831) | function checkChannelPath(channelInfo) {
  function getChannelHash (line 21839) | function getChannelHash(channelInfo, animationInfo) {
  function fourCCToInt32 (line 23018) | function fourCCToInt32(value) {
  function rgbe2float (line 23127) | function rgbe2float(rgbe, buffer, offset, exposure) {
  function uint82string (line 23143) | function uint82string(array, offset, size) {
  function copyrgbe (line 23151) | function copyrgbe(s, t) {
  function oldReadColors (line 23159) | function oldReadColors(scan, buffer, offset, xmax) {
  function readColors (line 23183) | function readColors(scan, buffer, offset, xmax) {
  function generateKey (line 24079) | function generateKey(parameters) {
  function fallBack (line 24092) | function fallBack(target) {
  function isPowerOfTwo$2 (line 24117) | function isPowerOfTwo$2(width, height) {
  function getDepthMaterialUniform (line 24128) | function getDepthMaterialUniform(renderable, depthMaterial, symbol) {
  function isDepthMaterialChanged (line 24146) | function isDepthMaterialChanged(renderable, prevRenderable) {
  function getSize (line 24448) | function getSize(texture) {
  function harmonics (line 25254) | function harmonics(normal, index){
  function projectEnvironmentMapCPU (line 25298) | function projectEnvironmentMapCPU(renderer, cubePixels, width, height) {
  function App3D (line 25520) | function App3D(dom, appNS) {
  function isImageLikeElement (line 25708) | function isImageLikeElement(val) {
  function getKeyFromImageLike (line 25714) | function getKeyFromImageLike(val) {
  function makeHandlerName (line 25719) | function makeHandlerName(eveType) {
  function packageEvent (line 25723) | function packageEvent(eventType, pickResult, offsetX, offsetY, wheelDelt...
  function bubblingEvent (line 25734) | function bubblingEvent(target, event) {
  function markUnused (line 25903) | function markUnused(resourceList) {
  function checkAndDispose (line 25909) | function checkAndDispose(renderer, resourceList) {
  function updateUsed (line 25917) | function updateUsed(resource, list) {
  function collectResources (line 25925) | function collectResources(scene, textureResourceList, geometryResourceLi...
  function makeTextureSetter (line 26151) | function makeTextureSetter(key) {
  function afterLoad (line 26647) | function afterLoad(result) {
  function Task (line 26749) | function Task() {
  function makeRequestTask (line 26795) | function makeRequestTask(url, responseType) {
  function register (line 27994) | function register(Shader) {
  function createCompositor (line 28024) | function createCompositor(json, opts) {
  function createNode (line 28055) | function createNode(nodeInfo, lib, opts) {
  function defaultWidthFunc (line 28168) | function defaultWidthFunc(width, height) {
  function defaultHeightFunc (line 28171) | function defaultHeightFunc(width, height) {
  function convertParameter (line 28175) | function convertParameter(paramInfo) {
  function loadTextures (line 28221) | function loadTextures(json, lib, opts, callback) {
  function createSizeSetHandler (line 28270) | function createSizeSetHandler(name, exprFunc) {
  function createSizeParser (line 28282) | function createSizeParser(name, exprFunc, scale) {
  function tryConvertExpr (line 28292) | function tryConvertExpr(string) {
  function createFillCanvas (line 28318) | function createFillCanvas(color) {
  function getGetUniformHook1 (line 28330) | function getGetUniformHook1(defaultNormalMap, defaultRoughnessMap, defau...
  function getGetUniformHook2 (line 28375) | function getGetUniformHook2(defaultDiffuseMap, defaultMetalnessMap) {
  function clearViewport (line 28649) | function clearViewport() {
  function isMaterialChanged (line 28662) | function isMaterialChanged(renderable, prevRenderable) {
  function getPreZMaterial (line 29678) | function getPreZMaterial() {
  function packID (line 32946) | function packID(id){
  function unpackID (line 32953) | function unpackID(r, g, b){
  function dist (line 33586) | function dist(pointPair) {
  function center (line 33593) | function center(pointPair) {
  function convertToArray (line 33765) | function convertToArray(val) {
  function copyAttribute (line 34929) | function copyAttribute(attr, shallow) {
  function copyIfNecessary (line 34939) | function copyIfNecessary(arr, shallow) {
  function lerp (line 34964) | function lerp (a, b, t) {

FILE: test/lib/dat.gui.js
  function a (line 19) | function a(b){if(b==="0"||e.isUndefined(b))return 0;b=b.match(d);return!...
  function h (line 29) | function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}
  function j (line 29) | function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedSte...
  function m (line 29) | function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}
  function o (line 32) | function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.g...
  function y (line 32) | function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y)...
  function q (line 45) | function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no pr...
  function s (line 46) | function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b)...
  function p (line 46) | function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(...
  function t (line 50) | function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.obje...
  function I (line 50) | function I(a){var b=a.__save_row=document.createElement("li");g.addClass...
  function J (line 54) | function J(a){function b(f){f.preventDefault();e=f.clientX;g.addClass(a....
  function D (line 55) | function D(a,b){a.domElement.style.width=b+"px";if(a.__save_row&&a.autoP...
  function z (line 56) | function z(a,b){var c={};i.each(a.__rememberedObjects,function(d,e){var ...
  function C (line 56) | function C(a,b,c){var d=document.createElement("option");d.innerHTML=b;d...
  function B (line 56) | function B(a,b){var c=a.__preset_select[a.__preset_select.selectedIndex]...
  function E (line 56) | function E(a){a.length!=
  function b (line 57) | function b(){localStorage.setItem(document.location.href+".gui",JSON.str...
  function c (line 57) | function c(){var a=d.getRoot();a.width+=1;i.defer(function(){a.width-=1})}
  function e (line 73) | function e(){h.setValue(h.__input.value)}
  function b (line 75) | function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.c...
  function n (line 75) | function n(a){a.style.background="";a.style.cssText+="background: -moz-l...
  function o (line 76) | function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window,
  function j (line 77) | function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}
  function g (line 77) | function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setVal...
  function i (line 77) | function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}
  function q (line 77) | function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),...
  function s (line 78) | function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.ge...
  function f (line 85) | function f(a,
  function b (line 86) | function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__stat...
  function n (line 86) | function n(b,c,e){if(b.__state.space===
  function h (line 87) | function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,...
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,213K chars).
[
  {
    "path": ".babelrc",
    "chars": 120,
    "preview": "{\n    \"presets\": [\n      [\n        \"@babel/preset-env\",\n        {\n          \"modules\": false\n        }\n      ]\n    ]\n  }"
  },
  {
    "path": ".gitattributes",
    "chars": 66,
    "preview": "# Auto detect text files and perform LF normalization\n* text=auto\n"
  },
  {
    "path": ".gitignore",
    "chars": 30,
    "preview": "node_modules\npackage-lock.json"
  },
  {
    "path": ".npmignore",
    "chars": 15,
    "preview": "test\nscreenshot"
  },
  {
    "path": "LICENSE",
    "chars": 1063,
    "preview": "MIT License\n\nCopyright (c) 2018 pissang\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof"
  },
  {
    "path": "README.md",
    "chars": 5112,
    "preview": "# Geometry Extrude\n\nA small and fast JavaScript library for extruding 2D polygons and polylines to 3D meshes. It depends"
  },
  {
    "path": "dist/geometry-extrude.js",
    "chars": 66526,
    "preview": "(function (global, factory) {\n    typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n    "
  },
  {
    "path": "index.d.ts",
    "chars": 2181,
    "preview": "type Rect = {\n    x?: number,\n    y?: number,\n    width?: number,\n    height?: number\n}\n\ntype BasicExtrudeOpt = {\n    be"
  },
  {
    "path": "package.json",
    "chars": 669,
    "preview": "{\n  \"name\": \"geometry-extrude\",\n  \"version\": \"0.2.1\",\n  \"main\": \"dist/geometry-extrude.js\",\n  \"scripts\": {\n    \"dev\": \"r"
  },
  {
    "path": "rollup.config.js",
    "chars": 539,
    "preview": "import nodeResolve from '@rollup/plugin-node-resolve';\nimport commonjs from '@rollup/plugin-commonjs';\nimport babel from"
  },
  {
    "path": "src/main.js",
    "chars": 37311,
    "preview": "// TODO fitRect x, y are negative?\n// TODO Extrude dimensions\n// TODO bevel=\"top\"|\"bottom\"\n// TODO Not add top and botto"
  },
  {
    "path": "src/math.js",
    "chars": 3683,
    "preview": "export function dot(v1, v2) {\n    return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2];\n}\nexport function v2Dot(v1, v2) "
  },
  {
    "path": "src/simplify.js",
    "chars": 2828,
    "preview": "/*\n (c) 2017, Vladimir Agafonkin\n Simplify.js, a high-performance JS polyline simplification library\n mourner.github.io/"
  },
  {
    "path": "test/asset/buildings-ny.geojson",
    "chars": 1190927,
    "preview": "{\n\"type\": \"FeatureCollection\",\n\"crs\": { \"type\": \"name\", \"properties\": { \"name\": \"urn:ogc:def:crs:OGC:1.3:CRS84\" } },\n\"fe"
  },
  {
    "path": "test/asset/street.geojson",
    "chars": 465454,
    "preview": "{\"type\":\"FeatureCollection\",\"crs\":{\"type\":\"name\",\"properties\":{\"name\":\"urn:ogc:def:crs:OGC:1.3:CRS84\"}},\"features\":[{\"ty"
  },
  {
    "path": "test/distortion.html",
    "chars": 7285,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-bevel.html",
    "chars": 3302,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-dude.html",
    "chars": 5030,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-exclude-bottom.html",
    "chars": 3347,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-hole.html",
    "chars": 2257,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-multipolygon.html",
    "chars": 2255,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-normal.html",
    "chars": 3083,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-polyline.html",
    "chars": 4146,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-simple.html",
    "chars": 2030,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-simplify.html",
    "chars": 3510,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-star.html",
    "chars": 4197,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/extrude-uv.html",
    "chars": 3187,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/geojson.html",
    "chars": 8076,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/lib/claygl-advanced-renderer.js",
    "chars": 112145,
    "preview": "(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(r"
  },
  {
    "path": "test/lib/claygl.js",
    "chars": 1090546,
    "preview": "(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n\ttypeof"
  },
  {
    "path": "test/lib/dat.gui.js",
    "chars": 45406,
    "preview": "/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Go"
  },
  {
    "path": "test/polygon-offset.html",
    "chars": 1433,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/polyline-offset.html",
    "chars": 1407,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/slerp.html",
    "chars": 1338,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  },
  {
    "path": "test/street.html",
    "chars": 9228,
    "preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width"
  }
]

// ... and 1 more files (download for full content)

About this extraction

This page contains the full source code of the pissang/geometry-extrude GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (2.9 MB), approximately 773.9k tokens, and a symbol index with 321 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!