├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── debug ├── .gitignore ├── .vscode │ └── extensions.json ├── README.md ├── dist │ ├── assets │ │ ├── graphCreation.worker.02f291a7.js │ │ ├── index.065c788d.css │ │ ├── index.628061df.js │ │ └── vendor.6c2250e5.js │ ├── favicon.ico │ └── index.html ├── gj.js ├── index.html ├── public │ └── favicon.ico ├── src │ ├── App.vue │ ├── graphCreation.worker.js │ ├── graphHelper.js │ ├── main.js │ └── mapHelpers.js └── vite.config.js ├── dist └── visibilityGraph.js ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── Contour.js ├── Edge.js ├── EdgeKey.js ├── EdgeKeys.js ├── Point.js ├── VisibilityGraph.js ├── createGraphFromGeoJson.js ├── debug.js ├── setupStructure.js └── utils.js └── test ├── Edge.spec.js ├── EdgeKey.spec.js ├── EdgeKeys.spec.js ├── Point.spec.js ├── bench.js ├── harness ├── asia.geojson ├── australia.geojson ├── bayarea2.geojson ├── bayarea3.geojson ├── continents2.geojson ├── multipolygon.geojson ├── polygon.geojson └── polygonCollection.geojson ├── setStructures.spec.js ├── sort.spec.js └── utils.spec.js /.editorconfig: -------------------------------------------------------------------------------- 1 | indent_style = space 2 | indent_size = 2 -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["plugin:vue-libs/recommended"], 3 | "rules": { 4 | "padded-blocks": "off", 5 | "no-console": "off", 6 | "indent": ["error", 2] 7 | }, 8 | "globals": { 9 | "L": true, 10 | "turf": true 11 | } 12 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | .nyc_output/ 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | debug/ 2 | test/ 3 | .nyc_output/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Rowan Winsemius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # visibility-graph.js 2 | Visibility graph implementation to support shortest path calculations such as [dijkstra](https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm) or [a-star](https://en.wikipedia.org/wiki/A*_search_algorithm). 3 | 4 | ### [Demo](https://rowanwins.github.io/visibility-graph/debug/dist/index.html) 5 | 6 | ## Documentation 7 | 8 | This library exposes a `VisibilityGraph` class 9 | 10 | #### API 11 | `new VisibilityGraph(geojson, ?existingGraph)` - creates a new instance using a [Polygon](http://geojson.win/#appendix-A.3) or [MultiPolygon](http://geojson.win/#appendix-A.6) feature or geometry. Optionally, if you have an existing graph that you've previously generated using the `.saveGraphToJson()` you can pass it in as a argument. 12 | 13 | `.saveGraphToJson()` - Returns a json representation of the visibility graph which can be saved to disk and then restored by passing a second argument to the class constructor. 14 | 15 | `.addStartAndEndPointsToGraph(origin, destination)` - Takes 2 geojson point features, one for the origin, and one for the destination, and returns the newly added nodes in an object `{startNode: ngraphNode, endNode: ngraphNode}`. Each time this is called any previously added start and end points are removed from the graph. 16 | 17 | `.getNodeIdByLatLon([lat, lon])` - Returns a graph node ID that matches the lat lon. 18 | 19 | 20 | 21 | ## Example 22 | 23 | ````js 24 | import VisibilityGraph from 'visibility-graph.js' 25 | import path from 'ngraph.path' 26 | 27 | // Create the visibility graph from the geojson data 28 | const vg = new VisibilityGraph(geojson) 29 | 30 | // Use the 'ngraph.path' library to find a way 31 | //through the newly created visibility graph 32 | const pathFinder = path.nba(vg.graph, { 33 | distance (fromNode, toNode) { 34 | const dx = fromNode.data.x - toNode.data.x 35 | const dy = fromNode.data.y - toNode.data.y 36 | return Math.sqrt(dx * dx + dy * dy) 37 | } 38 | }) 39 | 40 | // Add the start and endpoints to the graph 41 | const startEndNodes = vg.addStartAndEndPointsToGraph( 42 | {type: 'Feature', geometry: {type: 'Point', coordinates: [0, 0]}}, 43 | {type: 'Feature', geometry: {type: 'Point', coordinates: [10, 10]}} 44 | ) 45 | 46 | // And finally retrive the optimal path 47 | const optimalPath = pathFinder.find( 48 | startEndNodes.startNode.nodeId, 49 | startEndNodes.endNode.nodeId 50 | ) 51 | 52 | ```` 53 | 54 | **NOTE:** If you get occassional issues with how your edges are being linked try reducing the precision of your coordinates (eg 8 decimal places). 55 | 56 | ## Using with other packages 57 | - Path finding can be achieved with the [ngraph.path](https://github.com/anvaka/ngraph.path) package. 58 | 59 | 60 | ## Performance 61 | The process of creating a visibility graph can be slow depending on the number of vertices in your input geometry. 62 | 63 | | Scenario | Nodes/Vertices | Create Graph | Time Reload Graph / Graph Size | 64 | | --------- | --------------- | ------------- | ------------------------------- | 65 | | Australia | 250 | 1 second | 300kb | 66 | | Asia | 1400 | 4 seconds | 100ms / 5.2MB | 67 | | World | 4400 | 20 seconds | | 68 | 69 | 70 | Depending on your requirements you may also be able to convert your input data if it has concave polygons, to only having convex polygons, this may reduce redundant nodes in the graph. 71 | 72 | 73 | ## References & Credits 74 | * Based on [pyvisgraph](https://github.com/TaipanRex/pyvisgraph) and associated blog posts 75 | * [Distance Tables Part 1: Defining the Problem](https://taipanrex.github.io/2016/09/17/Distance-Tables-Part-1-Defining-the-Problem.html) 76 | * [Distance Tables Part 2: Lee's Visibility Graph Algorithm](https://taipanrex.github.io/2016/10/19/Distance-Tables-Part-2-Lees-Visibility-Graph-Algorithm.html) 77 | * [Lee's o(n2 log n) Visibility Graph Algorithm](https://github.com/davetcoleman/visibility_graph/blob/master/Visibility_Graph_Algorithm.pdf) paper by Dave Coleman 78 | * [Intro to path finding](https://www.redblobgames.com/pathfinding/) 79 | * And specifically a bit about [visibility graphs](https://www.redblobgames.com/pathfinding/visibility-graphs/) 80 | -------------------------------------------------------------------------------- /debug/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | *.local 12 | 13 | # Editor directories and files 14 | .vscode/* 15 | !.vscode/extensions.json 16 | .idea 17 | .DS_Store 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | -------------------------------------------------------------------------------- /debug/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["johnsoncodehk.volar"] 3 | } 4 | -------------------------------------------------------------------------------- /debug/README.md: -------------------------------------------------------------------------------- 1 | # Vue 3 + Vite 2 | 3 | This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 ` 9 | 10 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /debug/gj.js: -------------------------------------------------------------------------------- 1 | import {createFilter, dataToEsm} from '@rollup/pluginutils' 2 | 3 | export default function json(options = { 4 | include: ['**/*.json', '**/*.geojson'] 5 | }) { 6 | const filter = createFilter(options.include, options.exclude) 7 | const indent = 'indent' in options ? options.indent : '\t' 8 | 9 | return { 10 | name: 'geojson', 11 | 12 | // eslint-disable-next-line no-shadow 13 | transform(json, id) { 14 | if (!filter(id)) return null 15 | try { 16 | const parsed = JSON.parse(json) 17 | return { 18 | code: dataToEsm(parsed, { 19 | preferConst: options.preferConst, 20 | compact: options.compact, 21 | namedExports: options.namedExports, 22 | indent 23 | }), 24 | map: {mappings: ''} 25 | } 26 | } catch (err) { 27 | const message = 'Could not parse GeoJSON file' 28 | const position = parseInt(/[\d]/.exec(err.message)[0], 10) 29 | this.warn({message, id, position}) 30 | return null 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /debug/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite App 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /debug/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rowanwins/visibility-graph/143ca2eb72e58ff5e83eab545c73b08b50fe6d9d/debug/public/favicon.ico -------------------------------------------------------------------------------- /debug/src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 59 | -------------------------------------------------------------------------------- /debug/src/graphCreation.worker.js: -------------------------------------------------------------------------------- 1 | import VisibilityGraph from '../../src/VisibilityGraph' 2 | 3 | self.addEventListener('message', function (e) { //eslint-disable-line 4 | const vg = new VisibilityGraph(e.data.data) 5 | self.postMessage(vg.saveGraphToJson()) //eslint-disable-line 6 | }, false) 7 | -------------------------------------------------------------------------------- /debug/src/graphHelper.js: -------------------------------------------------------------------------------- 1 | import fromjson from 'ngraph.fromjson' 2 | import path from 'ngraph.path' 3 | import VisibilityGraph from '../../src/VisibilityGraph' 4 | 5 | import Worker from './graphCreation.worker.js?worker' 6 | 7 | import { setPathFinder, setGraph, clearGraphRelatedData } from './mapHelpers' 8 | 9 | export function loadGraphFromFile (filename) { 10 | function reqListener () { 11 | const startLoad = window.performance.now() 12 | const graph = fromjson(this.responseText) 13 | console.log(graph.getNodesCount()) 14 | const endLoad = window.performance.now() 15 | const timeTakenToLoad = parseInt(endLoad - startLoad) 16 | console.log('Time to load: ', timeTakenToLoad) 17 | 18 | setGraph(graph) 19 | findRouteThroughGraph(graph) 20 | } 21 | clearGraphRelatedData() 22 | var oReq = new XMLHttpRequest() //eslint-disable-line 23 | oReq.addEventListener('load', reqListener) 24 | oReq.open('GET', filename) 25 | oReq.send() 26 | } 27 | 28 | export function createGraphFromData (data) { 29 | clearGraphRelatedData() 30 | 31 | const startCreation = window.performance.now() 32 | const vg = new VisibilityGraph(data) 33 | const endCreation = window.performance.now() 34 | const timeTakenToCreate = parseInt(endCreation - startCreation) 35 | console.log('Time to construct: ', timeTakenToCreate) 36 | setGraph(vg) 37 | findRouteThroughGraph(vg.graph) 38 | } 39 | 40 | export function createGraphFromDataUsingWorker (data) { 41 | clearGraphRelatedData() 42 | 43 | const startCreation = window.performance.now() 44 | const worker = new Worker() //eslint-disable-line 45 | worker.postMessage({ data: data }) 46 | worker.onmessage = function (e) { 47 | const endCreation = window.performance.now() 48 | const timeTakenToCreate = parseInt(endCreation - startCreation) 49 | console.log('Time to construct: ', timeTakenToCreate) 50 | const vg = new VisibilityGraph(data, e.data) 51 | setGraph(vg) 52 | findRouteThroughGraph(vg.graph) 53 | } 54 | } 55 | 56 | export function findRouteThroughGraph (graph) { 57 | const pathFinder = path.nba(graph, { 58 | distance (fromNode, toNode) { 59 | const dx = fromNode.data.x - toNode.data.x 60 | const dy = fromNode.data.y - toNode.data.y 61 | return Math.sqrt(dx * dx + dy * dy) 62 | } 63 | }) 64 | setPathFinder(pathFinder) 65 | } 66 | -------------------------------------------------------------------------------- /debug/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | 4 | createApp(App).mount('#app') 5 | -------------------------------------------------------------------------------- /debug/src/mapHelpers.js: -------------------------------------------------------------------------------- 1 | import { Map } from 'leaflet' 2 | import 'leaflet/dist/leaflet.css' 3 | import { featureCollection, point, coordEach } from '@turf/turf' 4 | 5 | let map = null 6 | let startMarker = null 7 | let endMarker = null 8 | let selectionLayer = null 9 | let polyLayer = null 10 | let points = null 11 | let pointsLyr = null 12 | let vg = null 13 | let foundPath = null 14 | let pathFinder = null 15 | let routeLayer = null 16 | 17 | export function setupMap () { 18 | map = new Map('map', { 19 | minZoom: 1, 20 | maxZoom: 20, 21 | center: [0, 0], 22 | zoom: 2, 23 | crs: L.CRS.Simple 24 | }) 25 | 26 | L.NumberedDivIcon = createNumberDiv() 27 | 28 | startMarker = L.marker([-17.9614, 122.2359], { 29 | draggable: true, 30 | icon: new L.NumberedDivIcon() 31 | }).addTo(map) 32 | 33 | endMarker = L.marker([-42.8821, 147.3272], { 34 | draggable: true, 35 | icon: new L.NumberedDivIcon() 36 | }).addTo(map) 37 | 38 | startMarker.on('drag', updatePathMarkers) 39 | endMarker.on('drag', updatePathMarkers) 40 | selectionLayer = L.layerGroup([]).addTo(map) 41 | } 42 | 43 | export function setData (data) { 44 | polyLayer = L.geoJson(data, { 45 | noClip: true, 46 | stroke: false, 47 | fillColor: '#8B99AE' 48 | }).addTo(map) 49 | 50 | map.fitBounds(polyLayer.getBounds(), { 51 | padding: [20, 20] 52 | }) 53 | 54 | points = featureCollection([]) 55 | 56 | pointsLyr = L.layerGroup([], { 57 | pane: 'popupPane' 58 | }).addTo(map) 59 | 60 | coordEach(data, function (currentCoord) { 61 | points.features.push(point([currentCoord[0], currentCoord[1]])) 62 | 63 | var layer = L.circleMarker([currentCoord[1], currentCoord[0]], { 64 | radius: 3, 65 | opacity: 0, 66 | fillOpacity: 0.5, 67 | origPoint: [currentCoord[0], currentCoord[1]] 68 | }).addTo(pointsLyr) 69 | layer.on('mouseover', highlightFeature) 70 | layer.on('mouseout', unhighlightFeature) 71 | 72 | }, true) 73 | } 74 | 75 | export function clearGraphRelatedData () { 76 | if (routeLayer !== null) routeLayer.setLatLngs([]) 77 | if (selectionLayer !== null) selectionLayer.clearLayers() 78 | } 79 | 80 | export function setupRouteLayer () { 81 | routeLayer = L.polyline([], { 82 | color: '#EB3223' 83 | }).addTo(map) 84 | } 85 | 86 | export function setPathFinder (pathGraph) { 87 | pathFinder = pathGraph 88 | updatePathMarkers() 89 | } 90 | 91 | function updatePathMarkers () { 92 | if (vg === null) return 93 | routeLayer.setLatLngs([]) 94 | const s = startMarker.toGeoJSON() 95 | const d = endMarker.toGeoJSON() 96 | const nodes = vg.addStartAndEndPointsToGraph(s, d) 97 | foundPath = pathFinder.find(nodes.startNode.nodeId, nodes.endNode.nodeId) 98 | drawPath() 99 | } 100 | 101 | function drawPath () { 102 | const pathLatLngs = foundPath.map(function (node) { 103 | return [node.data.y, node.data.x] 104 | }) 105 | routeLayer.setLatLngs(pathLatLngs) 106 | } 107 | 108 | function unhighlightFeature () { 109 | selectionLayer.clearLayers() 110 | } 111 | 112 | export function setGraph (completedGraph) { 113 | vg = completedGraph 114 | } 115 | 116 | function highlightFeature (e) { 117 | if (vg === null) return 118 | 119 | selectionLayer.clearLayers() 120 | const nodeId = vg.getNodeIdByLatLon([e.target._latlng.lng, e.target._latlng.lat]) 121 | const node = vg.graph.getNode(nodeId) 122 | 123 | vg.graph.forEachLinkedNode(nodeId, function (linkedNode, link) { 124 | L.polyline([[linkedNode.data.y, linkedNode.data.x], [node.data.y, node.data.x]], { 125 | weight: 0.5, 126 | opacity: 0.8, 127 | pane: 'shadowPane', 128 | interactive: false 129 | }).addTo(selectionLayer) 130 | }) 131 | } 132 | 133 | function createNumberDiv () { 134 | return L.Icon.extend({ 135 | options: { 136 | iconSize: new L.Point(15, 15), 137 | className: 'leaflet-div-icon' 138 | }, 139 | createIcon: function () { 140 | var div = document.createElement('div') 141 | var numdiv = document.createElement('div') 142 | numdiv.setAttribute('class', 'number') 143 | numdiv.innerHTML = this.options['number'] || '' 144 | div.appendChild(numdiv) 145 | this._setIconStyles(div, 'icon') 146 | return div 147 | } 148 | }) 149 | } 150 | -------------------------------------------------------------------------------- /debug/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | import gj from './gj' 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | base: '/visibility-graph/debug/dist/', 8 | plugins: [ 9 | vue(), gj() 10 | ] 11 | }) 12 | -------------------------------------------------------------------------------- /dist/visibilityGraph.js: -------------------------------------------------------------------------------- 1 | !function(n,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(n="undefined"!=typeof globalThis?globalThis:n||self).visibilityGraph=t()}(this,(function(){"use strict";var n=function(n){!function(n){if(!n)throw new Error("Eventify cannot use falsy object as events subject");for(var t=["on","fire","off"],e=0;e1&&(i=Array.prototype.splice.call(arguments,1));for(var r=0;r=0&&e.links.splice(t,1),o&&(t=i(n,o.links))>=0&&o.links.splice(t,1),g(n,"remove"),k(),!0}function O(n,t){var e,i=P(n);if(!i||!i.links)return null;for(e=0;e0&&(x.fire("changed",p),p.length=0)}function N(n){if("function"==typeof n)for(var e=Object.keys(t),i=0;i=0&&i.links.splice(t,1),o&&(t=h(n,o.links))>=0&&o.links.splice(t,1),g(n,"remove"),k(),!0}function O(n,t){var e,i=P(n);if(!i||!i.links)return null;for(e=0;e0&&(x.fire("changed",u),u.length=0)}function N(n){if("function"==typeof n)for(var e=Object.keys(t),i=0;if==h>-f?(r=f,f=t[++l]):(r=h,h=i[++c]);let d=0;if(lf==h>-f?(s=f+r,a=r-(s-f),f=t[++l]):(s=h+r,a=r-(s-h),h=i[++c]),r=s,0!==a&&(o[d++]=a);lf==h>-f?(s=r+f,u=s-r,a=r-(s-u)+(f-u),f=t[++l]):(s=r+h,u=s-r,a=r-(s-u)+(h-u),h=i[++c]),r=s,0!==a&&(o[d++]=a);for(;l0!=a>0)return u;const f=Math.abs(s+a);return Math.abs(u)>=33306690738754716e-32*f?u:-function(n,t,e,i,o,r,s){let a,u,f,h,l,c,d,p,g,y,v,k,x,w,I,b,N,q;const j=n-o,T=e-o,D=t-r,K=i-r;w=j*K,c=m*j,d=c-(c-j),p=j-d,c=m*K,g=c-(c-K),y=K-g,I=p*y-(w-d*g-p*g-d*y),b=D*T,c=m*D,d=c-(c-D),p=D-d,c=m*T,g=c-(c-T),y=T-g,N=p*y-(b-d*g-p*g-d*y),v=I-N,l=I-v,E[0]=I-(v+l)+(l-N),k=w+v,l=k-w,x=w-(k-l)+(v-l),v=x-b,l=x-v,E[1]=x-(v+l)+(l-b),q=k+v,l=q-k,E[2]=k-(q-l)+(v-l),E[3]=q;let S=function(n,t){let e=t[0];for(let i=1;i=U||-S>=U)return S;if(l=n-j,a=n-(j+l)+(l-o),l=e-T,f=e-(T+l)+(l-o),l=t-D,u=t-(D+l)+(l-r),l=i-K,h=i-(K+l)+(l-r),0===a&&0===u&&0===f&&0===h)return S;if(U=11093356479670487e-47*s+33306690738754706e-32*Math.abs(S),S+=j*h+K*a-(D*f+T*u),S>=U||-S>=U)return S;w=a*K,c=m*a,d=c-(c-a),p=a-d,c=m*K,g=c-(c-K),y=K-g,I=p*y-(w-d*g-p*g-d*y),b=u*T,c=m*u,d=c-(c-u),p=u-d,c=m*T,g=c-(c-T),y=T-g,N=p*y-(b-d*g-p*g-d*y),v=I-N,l=I-v,L[0]=I-(v+l)+(l-N),k=w+v,l=k-w,x=w-(k-l)+(v-l),v=x-b,l=x-v,L[1]=x-(v+l)+(l-b),q=k+v,l=q-k,L[2]=k-(q-l)+(v-l),L[3]=q;const C=P(4,E,4,L,O);w=j*h,c=m*j,d=c-(c-j),p=j-d,c=m*h,g=c-(c-h),y=h-g,I=p*y-(w-d*g-p*g-d*y),b=D*f,c=m*D,d=c-(c-D),p=D-d,c=m*f,g=c-(c-f),y=f-g,N=p*y-(b-d*g-p*g-d*y),v=I-N,l=I-v,L[0]=I-(v+l)+(l-N),k=w+v,l=k-w,x=w-(k-l)+(v-l),v=x-b,l=x-v,L[1]=x-(v+l)+(l-b),q=k+v,l=q-k,L[2]=k-(q-l)+(v-l),L[3]=q;const G=P(C,O,4,L,_);w=a*h,c=m*a,d=c-(c-a),p=a-d,c=m*h,g=c-(c-h),y=h-g,I=p*y-(w-d*g-p*g-d*y),b=u*f,c=m*u,d=c-(c-u),p=u-d,c=m*f,g=c-(c-f),y=f-g,N=p*y-(b-d*g-p*g-d*y),v=I-N,l=I-v,L[0]=I-(v+l)+(l-N),k=w+v,l=k-w,x=w-(k-l)+(v-l),v=x-b,l=x-v,L[1]=x-(v+l)+(l-b),q=k+v,l=q-k,L[2]=k-(q-l)+(v-l),L[3]=q;const F=P(G,_,4,L,M);return M[F-1]}(n,t,e,i,o,r,f)}const q=3*Math.PI/2,j=Math.PI/2;function T(n,t,e){const i=e.p1,o=e.p2,r=D(n,t,i),s=D(n,t,o),a=D(i,o,n),u=D(i,o,t);if(r!==s&&a!==u)return!0;if(0===r){if(K(n,i,t))return!0;if(K(n,o,t))return!0;if(K(i,n,o))return!0;if(K(i,t,o))return!0}return!1}function D(n,t,e){const i=N(e.x,e.y,t.x,t.y,n.x,n.y);return i>0?1:i<0?-1:0}function K(n,t,e){return t.x<=Math.max(n.x,e.x)&&t.x>=Math.min(n.x,e.x)&&t.y<=Math.max(n.y,e.y)&&t.y>=Math.min(n.y,e.y)}const S=Math.pow(10,10),U=Math.pow(10,10);function C(n,t,e){const i=Math.pow(e.x-t.x,2)+Math.pow(e.y-t.y,2),o=Math.pow(e.x-n.x,2)+Math.pow(e.y-n.y,2),r=Math.pow(t.x-n.x,2)+Math.pow(t.y-n.y,2),s=(i+r-o)/(2*Math.sqrt(i)*Math.sqrt(r));return Math.acos(parseInt(s*S)/U)}function G(n,t,e){const i=function(n,t,e){if(e.containsPoint(n))return n;if(e.containsPoint(t))return t;if(e.p1.x===e.p2.x){if(n.x===t.x)return null;const i=(n.y-t.y)/(n.x-t.x),o=e.p1.x,r=i*(o-n.x)+n.y;return new I([o,r],-1)}if(n.x===t.x){const t=(e.p1.y-e.p2.y)/(e.p1.x-e.p2.x),i=n.x,o=t*(i-e.p1.x)+e.p1.y;return new I([i,o],-1)}const i=(n.y-t.y)/(n.x-t.x),o=(e.p1.y-e.p2.y)/(e.p1.x-e.p2.x);if(i===o)return null;const r=(o*e.p1.x-i*n.x+n.y-e.p1.y)/(o-i),s=o*(r-e.p1.x)+e.p1.y;return new I([r,s],-1)}(n,t,e);return null!==i?F(n,i):0}function F(n,t){return Math.sqrt(Math.pow(t.x-n.x,2)+Math.pow(t.y-n.y,2))}class A{constructor(n,t,e){this.p1=n,this.p2=t,this.edge=e}isLessThanOtherEdgeKey(n){if(this.matchesOtherKey(n))return!1;if(!T(this.p1,this.p2,n.edge))return!0;const t=G(this.p1,this.p2,this.edge),e=G(this.p1,this.p2,n.edge);if(t>e)return!1;if(t{const i=n.angleToPoint(t),o=n.angleToPoint(e);if(io)return 1;const r=F(n,t),s=F(n,e);return rs?1:0}))}(n,o);const h=new x,l=new I([1/0,n.y],-1);for(let e=0;e100&&console.log(h.keys.length);const c=[];let d=null,p=null;for(let i=0;iMath.PI)break;if(h.keys.length>0)for(let e=0;e100&&console.log(h.keys.length);let r=!1;if(null!==d&&0===D(n,d,t)&&K(n,d,t))if(p){r=!0;for(let n=0;na.p1.y&&n.y>a.p2.y)continue;const u=0===D(n,a.p1,e)&&a.p1.x>n.x,f=0===D(n,a.p2,e)&&a.p2.x>n.x,h=u?a.p1:a.p2;u||f?(r=a.getOtherPointInEdge(h).y>n.y?r++:r--,o?(0===r&&i++,o=!1,r=0):o=!0):T(n,e,a)&&i++}return i%2!=0}(new I([(n.x+t.x)/2,(n.y+t.y)/2],-1),e[n.polygonID].edges)}class Q{constructor(){this.edges=[],this.bbox=[1/0,1/0,-1/0,-1/0]}}class R{constructor(n,t){this.p1=n,this.p2=t,n.edges.push(this),t.edges.push(this)}getOtherPointInEdge(n){return this.p1.isPointEqual(n)?this.p2:this.p1}areEdgesEqual(n){return!(!this.p1.isPointEqual(n.p1)||!this.p2.isPointEqual(n.p2))||!(!this.p1.isPointEqual(n.p2)||!this.p2.isPointEqual(n.p1))}containsPoint(n){return this.p1.isPointEqual(n)||this.p2.isPointEqual(n)}}function V(n,t){t[0]=Math.min(t[0],n.x),t[1]=Math.min(t[1],n.y),t[2]=Math.max(t[2],n.x),t[3]=Math.max(t[3],n.y)}function W(n,t,e){t.prevPoint=n,t.nextPoint=e}return class{constructor(n,e){this._geojson=n,this.graph=null,this._points=[],this._clonedPoints=[],this._edges=[],this._polygons=[],this._lastOrigin=null,this._lastDestination=null,function(n){const t="Feature"===n._geojson.type?n._geojson.geometry:n._geojson;let e=t.coordinates;"Polygon"===t.type&&(e=[e]);for(let t=0;t otherDistance) return false 17 | if (selfDistance < otherDistance) return true 18 | if (selfDistance === otherDistance) { 19 | let samePoint = null 20 | if (otherEdgeKey.edge.containsPoint(this.edge.p1)) samePoint = this.edge.p1 21 | else samePoint = this.edge.p2 22 | const aslf = angle2(this.p1, this.p2, this.edge.getOtherPointInEdge(samePoint)) 23 | const aot = angle2(this.p1, this.p2, otherEdgeKey.edge.getOtherPointInEdge(samePoint)) 24 | if (aslf < aot) return true 25 | return false 26 | } 27 | } 28 | 29 | matchesOtherKey (otherKey) { 30 | return this.edge.areEdgesEqual(otherKey.edge) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/EdgeKeys.js: -------------------------------------------------------------------------------- 1 | export default class EdgeKeys { 2 | 3 | constructor () { 4 | this.keys = [] 5 | } 6 | 7 | findKeyPosition (edgekey, p) { 8 | let lo = 0 9 | let hi = this.keys.length 10 | while (lo < hi) { 11 | const mid = Math.floor((lo + hi) / 2) 12 | if (edgekey.isLessThanOtherEdgeKey(this.keys[mid])) hi = mid 13 | else lo = mid + 1 14 | } 15 | return lo 16 | } 17 | 18 | addKey (edgekey, p) { 19 | const lo = this.findKeyPosition(edgekey) 20 | this.keys.splice(lo, 0, edgekey) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Point.js: -------------------------------------------------------------------------------- 1 | import { pi1, pi2 } from './utils' 2 | 3 | let nodeId = 0 4 | 5 | export default class Point { 6 | 7 | constructor (coords, polygonID) { 8 | this.x = coords[0] 9 | this.y = coords[1] 10 | this.nodeId = nodeId 11 | this.polygonID = polygonID 12 | this.edges = [] 13 | this.prevPoint = null 14 | this.nextPoint = null 15 | nodeId++ 16 | } 17 | 18 | isPointEqual (otherPoint) { 19 | if (otherPoint === null) return false 20 | return this.x === otherPoint.x && this.y === otherPoint.y 21 | } 22 | 23 | angleToPoint (otherPoint) { 24 | if (this.isPointEqual(otherPoint)) return 0 25 | const dx = otherPoint.x - this.x 26 | const dy = otherPoint.y - this.y 27 | if (dx === 0) return dy < 0 ? pi1 : pi2 28 | if (dy === 0) return dx < 0 ? Math.PI : 0 29 | if (dx < 0) return Math.PI + Math.atan(dy / dx) 30 | if (dy < 0) return 2 * Math.PI + Math.atan(dy / dx) 31 | return Math.atan(dy / dx) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/VisibilityGraph.js: -------------------------------------------------------------------------------- 1 | import createGraph from 'ngraph.graph' 2 | import fromJSON from 'ngraph.fromjson' 3 | import toJSON from 'ngraph.tojson' 4 | 5 | import { createGraphFromGeoJson, addSinglePoint } from './createGraphFromGeoJson' 6 | import { setupStructure } from './setupStructure' 7 | import Point from './Point' 8 | 9 | export default class VisibilityGraph { 10 | constructor (geojson, jsonGraph) { 11 | this._geojson = geojson 12 | this.graph = null 13 | 14 | this._points = [] 15 | this._clonedPoints = [] 16 | this._edges = [] 17 | this._polygons = [] 18 | 19 | this._lastOrigin = null 20 | this._lastDestination = null 21 | setupStructure(this) 22 | 23 | if (jsonGraph) { 24 | this.graph = fromJSON(jsonGraph) 25 | } else { 26 | this.graph = createGraph() 27 | createGraphFromGeoJson(this) 28 | } 29 | } 30 | 31 | getNodeIdByLatLon (latLon) { 32 | for (var i = 0; i < this._points.length; i++) { 33 | if (this._points[i].x === latLon[0] && this._points[i].y === latLon[1]) return this._points[i].nodeId 34 | } 35 | return null 36 | } 37 | 38 | saveGraphToJson () { 39 | return toJSON(this.graph) 40 | } 41 | 42 | addStartAndEndPointsToGraph (origin, destination) { 43 | if (this._lastOrigin !== null) { 44 | this.graph.removeNode(this._lastOrigin.nodeId) 45 | this.graph.removeNode(this._lastDestination.nodeId) 46 | } 47 | 48 | this._lastOrigin = new Point(origin.geometry.coordinates, -1) 49 | this._lastDestination = new Point(destination.geometry.coordinates, -1) 50 | 51 | addSinglePoint(this, this._lastOrigin) 52 | addSinglePoint(this, this._lastDestination) 53 | return { 54 | startNode: this._lastOrigin, 55 | endNode: this._lastDestination 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/createGraphFromGeoJson.js: -------------------------------------------------------------------------------- 1 | import EdgeKeys from './EdgeKeys' 2 | import EdgeKey from './EdgeKey' 3 | import Point from './Point' 4 | 5 | import { edgeIntersect, onSegment, ccw, calcEdgeDistance } from './utils' 6 | import { _renderSortedPoints, _renderOpenEdges } from './debug' //eslint-disable-line 7 | 8 | export const FULL_PROCESS = 0 9 | export const HALF_PROCESS = 1 10 | 11 | export function createGraphFromGeoJson (visibilityGraph) { 12 | processGraph(visibilityGraph) 13 | } 14 | 15 | export function addSinglePoint (visibilityGraph, p) { 16 | processPoint(p, visibilityGraph._points.length, FULL_PROCESS, visibilityGraph) 17 | } 18 | 19 | function processGraph (visibilityGraph) { 20 | const points = visibilityGraph._points 21 | const pointsLen = points.length 22 | const scan = HALF_PROCESS 23 | for (var i = 0; i < pointsLen; i++) { 24 | const p = points[i] 25 | processPoint(p, pointsLen, scan, visibilityGraph) 26 | } 27 | } 28 | 29 | export function processPoint (p, pointsLen, scan, visibilityGraph) { 30 | const clonedPoints = visibilityGraph._clonedPoints 31 | const edges = visibilityGraph._edges 32 | const polygons = visibilityGraph._polygons 33 | const g = visibilityGraph.graph 34 | const prevPoint = p.prevPoint 35 | const nextPoint = p.nextPoint 36 | 37 | sortPoints(p, clonedPoints) 38 | // _renderSortedPoints(p, clonedPoints) 39 | 40 | const openEdges = new EdgeKeys() 41 | const pointInf = new Point([Infinity, p.y], -1) 42 | for (let ii = 0; ii < pointsLen; ii++) { 43 | const e = edges[ii] 44 | if (e.containsPoint(p)) continue 45 | if (edgeIntersect(p, pointInf, e)) { 46 | if (onSegment(p, e.p1, pointInf) || onSegment(p, e.p2, pointInf)) continue 47 | openEdges.addKey(new EdgeKey(p, pointInf, e)) 48 | } 49 | } 50 | if (openEdges.keys.length > 100) console.log(openEdges.keys.length) 51 | // _renderOpenEdges(p, openEdges.keys) 52 | 53 | const visible = [] 54 | let prev = null 55 | let prevVisible = null 56 | 57 | for (let ii = 0; ii < pointsLen; ii++) { 58 | const p2 = clonedPoints[ii] 59 | if (p2.isPointEqual(p)) continue 60 | if (scan === HALF_PROCESS && p.angleToPoint(p2) > Math.PI) { 61 | break 62 | } 63 | 64 | if (openEdges.keys.length > 0) { 65 | for (let iii = 0; iii < p2.edges.length; iii++) { 66 | const e = p2.edges[iii] 67 | if (ccw(p, p2, e.getOtherPointInEdge(p2)) === -1) { 68 | const k = new EdgeKey(p, p2, e) 69 | const index = openEdges.findKeyPosition(k) - 1 70 | if (index !== -1 && openEdges.keys[index].matchesOtherKey(k)) { 71 | openEdges.keys.splice(index, 1) 72 | } 73 | } 74 | } 75 | } 76 | if (openEdges.keys.length > 100) console.log(openEdges.keys.length) 77 | 78 | let isVisible = false 79 | if (prev === null || ccw(p, prev, p2) !== 0 || !onSegment(p, prev, p2)) { 80 | if (openEdges.keys.length === 0) { 81 | isVisible = true 82 | } else if (!edgeIntersect(p, p2, openEdges.keys[0].edge)) { 83 | isVisible = true 84 | } 85 | } else if (!prevVisible) { 86 | isVisible = false 87 | } else { 88 | isVisible = true 89 | for (let iii = 0; iii < openEdges.keys.length; iii++) { 90 | const e = openEdges.keys[iii] 91 | if (!e.edge.containsPoint(prev) && edgeIntersect(prev, p2, e.edge)) { 92 | isVisible = false 93 | break 94 | } 95 | } 96 | if (isVisible && edgeInPolygon(prev, p2, polygons)) isVisible = false 97 | } 98 | 99 | const isInAdjacentPoints = p2.isPointEqual(prevPoint) || p2.isPointEqual(nextPoint) 100 | if (isVisible && !isInAdjacentPoints) isVisible = !edgeInPolygon(p, p2, polygons) 101 | 102 | if (isVisible) visible.push(p2) 103 | 104 | for (let iii = 0; iii < p2.edges.length; iii++) { 105 | const e = p2.edges[iii] 106 | if (!e.containsPoint(p) && ccw(p, p2, e.getOtherPointInEdge(p2)) === 1) { 107 | const k = new EdgeKey(p, p2, e) 108 | openEdges.addKey(k) 109 | } 110 | } 111 | 112 | prev = p2 113 | prevVisible = isVisible 114 | } 115 | const nodeId = p.nodeId 116 | g.addNode(nodeId, { x: p.x, y: p.y }) 117 | 118 | for (var ii = 0; ii < visible.length; ii++) { 119 | const otherNodeId = visible[ii].nodeId 120 | g.addNode(otherNodeId, { x: visible[ii].x, y: visible[ii].y }) 121 | g.addLink(nodeId, otherNodeId) 122 | } 123 | } 124 | 125 | export function sortPoints (point, clonedPoints) { 126 | clonedPoints.sort((a, b) => { 127 | const angle1 = point.angleToPoint(a) 128 | const angle2 = point.angleToPoint(b) 129 | if (angle1 < angle2) return -1 130 | if (angle1 > angle2) return 1 131 | const dist1 = calcEdgeDistance(point, a) 132 | const dist2 = calcEdgeDistance(point, b) 133 | if (dist1 < dist2) return -1 134 | if (dist1 > dist2) return 1 135 | return 0 136 | }) 137 | } 138 | 139 | function edgeInPolygon (p1, p2, polygons) { 140 | if (p1.polygonID !== p2.polygonID) return false 141 | if (p1.polygonID === -1 || p2.polygonID === -1) return false 142 | const midPoint = new Point([(p1.x + p2.x) / 2, (p1.y + p2.y) / 2], -1) 143 | return polygonCrossing(midPoint, polygons[p1.polygonID].edges) 144 | } 145 | 146 | function polygonCrossing (p1, polyEdges) { 147 | const p2 = new Point([Infinity, p1.y], -1) 148 | let intersectCount = 0 149 | let coFlag = false 150 | let coDir = 0 151 | 152 | for (let i = 0; i < polyEdges.length; i++) { 153 | const e = polyEdges[i] 154 | if (p1.y < e.p1.y && p1.y < e.p2.y) continue 155 | if (p1.y > e.p1.y && p1.y > e.p2.y) continue 156 | const co0 = (ccw(p1, e.p1, p2) === 0) && (e.p1.x > p1.x) 157 | const co1 = (ccw(p1, e.p2, p2) === 0) && (e.p2.x > p1.x) 158 | const coPoint = co0 ? e.p1 : e.p2 159 | if (co0 || co1) { 160 | coDir = e.getOtherPointInEdge(coPoint).y > p1.y ? coDir++ : coDir-- 161 | if (coFlag) { 162 | if (coDir === 0) intersectCount++ 163 | coFlag = false 164 | coDir = 0 165 | } else { 166 | coFlag = true 167 | } 168 | } else if (edgeIntersect(p1, p2, e)) { 169 | intersectCount++ 170 | } 171 | } 172 | if (intersectCount % 2 === 0) return false 173 | return true 174 | } 175 | -------------------------------------------------------------------------------- /src/debug.js: -------------------------------------------------------------------------------- 1 | 2 | export function _renderSortedPoints (point, sortedPoints) { 3 | const map = window.map 4 | var sortedPointsLg = null 5 | 6 | L.NumberedDivIcon = createNumberDiv() 7 | 8 | setCurrentPoint(point) 9 | 10 | if (sortedPointsLg === null) sortedPointsLg = L.layerGroup([]).addTo(map) 11 | else sortedPointsLg.clearLayers() 12 | sortedPoints.forEach((p, index) => { 13 | return L.marker([p.y, p.x], { 14 | color: 'black', 15 | icon: new L.NumberedDivIcon({ number: index.toString() }) 16 | }).addTo(sortedPointsLg) 17 | }) 18 | 19 | debugger 20 | } 21 | 22 | export function _renderOpenEdges (point, edges) { 23 | const map = window.map 24 | const currentPoint = L.circleMarker([point.y, point.x], { 25 | radius: 20, 26 | color: 'green' 27 | }).addTo(map) 28 | var openEdges = L.layerGroup([]).addTo(map) 29 | 30 | edges.forEach((e, index) => { 31 | L.polyline([[e.edge.p1.y, e.edge.p1.x], [e.edge.p2.y, e.edge.p2.x]], { 32 | color: 'red' 33 | }).addTo(openEdges) 34 | }) 35 | 36 | // debugger 37 | openEdges.clearLayers() 38 | map.removeLayer(currentPoint) 39 | } 40 | 41 | function setCurrentPoint (point) { 42 | const map = window.map 43 | let currentPoint = null 44 | 45 | if (currentPoint !== null) map.removeLayer(currentPoint) 46 | 47 | currentPoint = L.circleMarker([point.y, point.x], { 48 | radius: 20, 49 | color: 'green' 50 | }).addTo(map) 51 | 52 | } 53 | 54 | function createNumberDiv () { 55 | return L.Icon.extend({ 56 | options: { 57 | number: '', 58 | iconSize: new L.Point(25, 25), 59 | className: 'leaflet-div-icon' 60 | }, 61 | createIcon: function () { 62 | var div = document.createElement('div') 63 | var numdiv = document.createElement('div') 64 | numdiv.setAttribute('class', 'number') 65 | numdiv.innerHTML = this.options['number'] || '' 66 | div.appendChild(numdiv) 67 | this._setIconStyles(div, 'icon') 68 | return div 69 | } 70 | }) 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/setupStructure.js: -------------------------------------------------------------------------------- 1 | import Contour from './Contour' 2 | import Point from './Point' 3 | import Edge from './Edge' 4 | 5 | export function setupStructure (vg) { 6 | const geom = vg._geojson.type === 'Feature' ? vg._geojson.geometry : vg._geojson 7 | 8 | let coords = geom.coordinates 9 | 10 | // standardise the input 11 | if (geom.type === 'Polygon') coords = [coords] 12 | 13 | for (let i = 0; i < coords.length; i++) { 14 | const contour = new Contour() 15 | const edges = contour.edges 16 | const bbox = contour.bbox 17 | vg._polygons.push(contour) 18 | 19 | for (let ii = 0; ii < coords[i].length; ii++) { 20 | let prevPoint = new Point(coords[i][ii][0], i) 21 | let currentPoint = new Point(coords[i][ii][1], i) 22 | checkPointAgainstBbox(prevPoint, bbox) 23 | checkPointAgainstBbox(currentPoint, bbox) 24 | 25 | prevPoint.nextPoint = currentPoint 26 | let nextPoint = new Point(coords[i][ii][2], i) 27 | linkPoints(prevPoint, currentPoint, nextPoint) 28 | 29 | vg._points.push(prevPoint) 30 | 31 | let prevEdge = new Edge(prevPoint, currentPoint) 32 | vg._edges.push(prevEdge) 33 | edges.push(prevEdge) 34 | 35 | // Save me for later 36 | const firstPoint = prevPoint 37 | 38 | prevPoint = currentPoint 39 | currentPoint = nextPoint 40 | 41 | for (let iii = 2; iii < coords[i][ii].length - 2; iii++) { 42 | vg._points.push(prevPoint) 43 | 44 | nextPoint = new Point(coords[i][ii][iii + 1], i) 45 | checkPointAgainstBbox(nextPoint, bbox) 46 | 47 | linkPoints(prevPoint, currentPoint, nextPoint) 48 | 49 | const e = new Edge(prevPoint, currentPoint) // eslint-disable-line 50 | 51 | vg._edges.push(e) 52 | edges.push(e) 53 | 54 | prevPoint = currentPoint 55 | currentPoint = nextPoint 56 | prevEdge = e 57 | } 58 | 59 | linkPoints(prevPoint, currentPoint, firstPoint) 60 | 61 | const secondLastEdge = new Edge(prevEdge.p2, currentPoint) 62 | 63 | vg._edges.push(secondLastEdge) 64 | edges.push(secondLastEdge) 65 | 66 | const lastEdge = new Edge(currentPoint, firstPoint) // eslint-disable-line 67 | linkPoints(currentPoint, firstPoint, firstPoint.nextPoint) 68 | 69 | vg._edges.push(lastEdge) 70 | edges.push(lastEdge) 71 | 72 | vg._points.push(prevPoint) 73 | vg._points.push(nextPoint) 74 | } 75 | } 76 | 77 | vg._clonedPoints = clonePoints(vg._points) 78 | } 79 | 80 | function clonePoints (points) { 81 | return points.slice(0) 82 | } 83 | 84 | function checkPointAgainstBbox (point, bbox) { 85 | bbox[0] = Math.min(bbox[0], point.x) 86 | bbox[1] = Math.min(bbox[1], point.y) 87 | bbox[2] = Math.max(bbox[2], point.x) 88 | bbox[3] = Math.max(bbox[3], point.y) 89 | } 90 | function linkPoints (prevPoint, currentPoint, nextPoint) { 91 | currentPoint.prevPoint = prevPoint 92 | currentPoint.nextPoint = nextPoint 93 | } 94 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | import Point from './Point' 2 | import { orient2d } from 'robust-predicates' 3 | 4 | export const pi1 = Math.PI * 3 / 2 5 | export const pi2 = Math.PI / 2 6 | 7 | export function edgeIntersect (p1, q1, edge) { 8 | const p2 = edge.p1 9 | const q2 = edge.p2 10 | const o1 = ccw(p1, q1, p2) 11 | const o2 = ccw(p1, q1, q2) 12 | const o3 = ccw(p2, q2, p1) 13 | const o4 = ccw(p2, q2, q1) 14 | if (o1 !== o2 && o3 !== o4) return true 15 | if (o1 === 0) { 16 | if (onSegment(p1, p2, q1)) return true 17 | if (onSegment(p1, q2, q1)) return true 18 | if (onSegment(p2, p1, q2)) return true 19 | if (onSegment(p2, q1, q2)) return true 20 | } 21 | return false 22 | } 23 | 24 | export function ccw (a, b, c) { 25 | const r = orient2d(c.x, c.y, b.x, b.y, a.x, a.y) 26 | if (r > 0) return 1 27 | if (r < 0) return -1 28 | return 0 29 | } 30 | 31 | export function onSegment (p, q, r) { 32 | if (q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x)) { 33 | if (q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y)) return true 34 | } 35 | return false 36 | } 37 | 38 | const COLIN_TOLERANCE = 10 39 | const T = Math.pow(10, COLIN_TOLERANCE) 40 | const T2 = Math.pow(10, COLIN_TOLERANCE) 41 | 42 | export function angle2 (p1, p2, p3) { 43 | const a = Math.pow((p3.x - p2.x), 2) + Math.pow((p3.y - p2.y), 2) 44 | const b = Math.pow((p3.x - p1.x), 2) + Math.pow((p3.y - p1.y), 2) 45 | const c = Math.pow((p2.x - p1.x), 2) + Math.pow((p2.y - p1.y), 2) 46 | const cos = (a + c - b) / (2 * Math.sqrt(a) * Math.sqrt(c)) 47 | return Math.acos(parseInt(cos * T) / T2) 48 | // const d = (a + c - b) 49 | // const e = (2 * Math.sqrt(a) * Math.sqrt(c)) 50 | // const f = Math.acos(d / e) 51 | // if (isNaN(f)) { 52 | // return 0 53 | // // return Math.PI 54 | // } 55 | // return f 56 | } 57 | 58 | export function pointEdgeDistance (p1, p2, edge) { 59 | const ip = intersectPoint(p1, p2, edge) 60 | return ip !== null ? calcEdgeDistance(p1, ip) : 0 61 | } 62 | 63 | export function intersectPoint (p1, p2, edge) { 64 | if (edge.containsPoint(p1)) return p1 65 | if (edge.containsPoint(p2)) return p2 66 | if (edge.p1.x === edge.p2.x) { 67 | if (p1.x === p2.x) return null 68 | const pslope = (p1.y - p2.y) / (p1.x - p2.x) 69 | const intersectX = edge.p1.x 70 | const intersectY = pslope * (intersectX - p1.x) + p1.y 71 | return new Point([intersectX, intersectY], -1) 72 | } 73 | if (p1.x === p2.x) { 74 | const eslope = (edge.p1.y - edge.p2.y) / (edge.p1.x - edge.p2.x) 75 | const intersectX = p1.x 76 | const intersectY = eslope * (intersectX - edge.p1.x) + edge.p1.y 77 | return new Point([intersectX, intersectY], -1) 78 | } 79 | 80 | const pslope = (p1.y - p2.y) / (p1.x - p2.x) 81 | const eslope = (edge.p1.y - edge.p2.y) / (edge.p1.x - edge.p2.x) 82 | 83 | if (pslope === eslope) return null 84 | const intersectX = (eslope * edge.p1.x - pslope * p1.x + p1.y - edge.p1.y) / (eslope - pslope) 85 | const intersectY = eslope * (intersectX - edge.p1.x) + edge.p1.y 86 | return new Point([intersectX, intersectY], -1) 87 | } 88 | 89 | export function calcEdgeDistance (p1, p2) { 90 | return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2)) 91 | } 92 | -------------------------------------------------------------------------------- /test/Edge.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Edge from '../src/Edge' 3 | import Point from '../src/Point' 4 | 5 | test('Edge test', t => { 6 | 7 | var p1 = new Point([0, 0], -1) 8 | var p2 = new Point([0, 1], -1) 9 | var p3 = new Point([0, 2], -1) 10 | var p4 = new Point([0, 0], -1) 11 | var p5 = new Point([0, 1], -1) 12 | 13 | var e1 = new Edge(p1, p2) 14 | var e1a = new Edge(p2, p1) 15 | var e2 = new Edge(p3, p4) 16 | var e2a = new Edge(p5, p4) 17 | 18 | t.is(e1.areEdgesEqual(e1a), true) 19 | t.is(e2a.areEdgesEqual(e1), true) 20 | t.is(e1.areEdgesEqual(e1), true) 21 | t.is(e1.areEdgesEqual(e2), false) 22 | 23 | t.is(e1.containsPoint(p1), true) 24 | t.is(e1.containsPoint(p2), true) 25 | t.is(e1.containsPoint(p3), false) 26 | 27 | t.is(e1.getOtherPointInEdge(p1), p2) 28 | t.is(e1.getOtherPointInEdge(p2), p1) 29 | 30 | }) 31 | -------------------------------------------------------------------------------- /test/EdgeKey.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Edge from '../src/Edge' 3 | import Point from '../src/Point' 4 | import EdgeKey from '../src/EdgeKey' 5 | 6 | test('EdgeKey test', t => { 7 | 8 | var p1 = new Point([0, 0], -1) 9 | var p2 = new Point([0, 1], -1) 10 | var p3 = new Point([0, 2], -1) 11 | var p4 = new Point([0, 0], -1) 12 | 13 | var e1 = new Edge(p1, p2) 14 | var e2 = new Edge(p3, p4) 15 | 16 | var ek1 = new EdgeKey(p1, p2, e1) 17 | var ek1a = new EdgeKey(p1, p2, e1) 18 | var ek1b = new EdgeKey(p2, p1, e1) 19 | 20 | var ek2 = new EdgeKey(p1, p2, e2) 21 | 22 | t.is(ek1.matchesOtherKey(ek1), true) 23 | t.is(ek1.matchesOtherKey(ek1a), true) 24 | t.is(ek1.matchesOtherKey(ek1b), true) 25 | t.is(ek1.matchesOtherKey(ek2), false) 26 | }) 27 | 28 | test('EdgeKey less than test', t => { 29 | 30 | var p1 = new Point([3, 1], -1) 31 | var p2 = new Point([3, 5], -1) 32 | var p3 = new Point([2, 2], -1) 33 | var p4 = new Point([4, 4], -1) 34 | 35 | var e1 = new Edge(p1, p2) 36 | var e2 = new Edge(p3, p4) 37 | 38 | var ek1 = new EdgeKey(p1, p2, e1) 39 | var ek2 = new EdgeKey(p1, p2, e2) 40 | 41 | t.is(ek1.isLessThanOtherEdgeKey(ek2), true) 42 | t.is(ek2.isLessThanOtherEdgeKey(ek1), false) 43 | 44 | }) 45 | -------------------------------------------------------------------------------- /test/EdgeKeys.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Edge from '../src/Edge' 3 | import Point from '../src/Point' 4 | import EdgeKey from '../src/EdgeKey' 5 | import EdgeKeys from '../src/EdgeKeys' 6 | 7 | test('EdgeKey test', t => { 8 | 9 | var p1 = new Point([0, 0], -1) 10 | var p2 = new Point([0, 1], -1) 11 | var p3 = new Point([0, 2], -1) 12 | var p4 = new Point([0, 0], -1) 13 | 14 | var e1 = new Edge(p1, p2) 15 | var e2 = new Edge(p3, p4) 16 | 17 | var ek1 = new EdgeKey(p1, p2, e1) 18 | var ek2 = new EdgeKey(p1, p2, e2) 19 | 20 | var eks = new EdgeKeys() 21 | t.is(eks.keys.length, 0) 22 | 23 | eks.addKey(ek1) 24 | eks.addKey(ek1) 25 | eks.addKey(ek1) 26 | eks.addKey(ek2) 27 | t.is(eks.keys.length, 4) 28 | 29 | }) 30 | -------------------------------------------------------------------------------- /test/Point.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Point from '../src/Point' 3 | 4 | test('Point constructor test', t => { 5 | 6 | var p1 = new Point([0, 0], -1) 7 | var p1a = new Point([0, 0], -1) 8 | var p2 = new Point([0, 1], -1) 9 | 10 | t.is(p1.isPointEqual(p1a), true) 11 | t.is(p1.isPointEqual(p2), false) 12 | 13 | }) 14 | 15 | test('Point - angle to point tests', t => { 16 | 17 | var center = new Point([1, 1], -1) 18 | var p1 = new Point([3, 1], -1) 19 | var p2 = new Point([1, 0], -1) 20 | 21 | t.is(center.angleToPoint(p1), 0) 22 | // t.is(center.angleToPoint(p2), Math.PI * 3 / 2) 23 | 24 | }) 25 | -------------------------------------------------------------------------------- /test/bench.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const Benchmark = require('benchmark') 3 | const VisibilityGraph = require('./../dist/visibilityGraph') 4 | const loadJsonFile = require('load-json-file') 5 | 6 | const asia = loadJsonFile.sync(path.join(__dirname, 'harness', 'asia.geojson')) 7 | 8 | const options = { 9 | onStart () { console.log(this.name) }, 10 | onError (event) { console.log(event.target.error) }, 11 | onCycle (event) { console.log(String(event.target)) } 12 | } 13 | 14 | // Asia Test 15 | // VisibilityGraph x 0.27 ops/sec ±7.56% (5 runs sampled) 16 | // with half scan x 0.40 ops/sec ±2.32% (5 runs sampled) 17 | const suite = new Benchmark.Suite('Asia Test', options) 18 | suite 19 | .add('VisibilityGraph', function () { 20 | const vg = new VisibilityGraph() 21 | vg.createGraphFromGeoJson(asia) 22 | }) 23 | .run() 24 | -------------------------------------------------------------------------------- /test/harness/asia.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", "properties": { }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 173.02037479, -40.91905242 ], [ 173.24723433, -41.33199879 ], [ 173.95840539, -40.92670053 ], [ 174.2475867, -41.34915537 ], [ 174.24851688, -41.77000823 ], [ 173.87644657, -42.2331841 ], [ 173.2227397, -42.97003834 ], [ 172.71124637, -43.37228769 ], [ 173.08011275, -43.8533436 ], [ 172.30858361, -43.86569427 ], [ 171.45292525, -44.24251881 ], [ 171.18513797, -44.89710418 ], [ 170.61669722, -45.90892872 ], [ 169.83142215, -46.35577483 ], [ 169.33233117, -46.64123545 ], [ 168.41135379, -46.61994476 ], [ 167.76374475, -46.29019744 ], [ 166.67688602, -46.21991749 ], [ 166.50914432, -45.85270477 ], [ 167.04642419, -45.11094126 ], [ 168.30376346, -44.12397308 ], [ 168.94940881, -43.93581919 ], [ 169.66781457, -43.55532562 ], [ 170.52491988, -43.03168833 ], [ 171.12508996, -42.51275359 ], [ 171.56971398, -41.76742441 ], [ 171.94870894, -41.5144166 ], [ 172.097227, -40.95610442 ], [ 172.79857954, -40.49396209 ], [ 173.02037479, -40.91905242 ] ] ], [ [ [ 145.39797814, -40.79254852 ], [ 146.36412072, -41.13769541 ], [ 146.90858361, -41.00054616 ], [ 147.68925947, -40.80825815 ], [ 148.28906782, -40.87543751 ], [ 148.35986454, -42.06244516 ], [ 148.01730147, -42.40702361 ], [ 147.91405196, -43.21152231 ], [ 147.56456424, -42.9376889 ], [ 146.87034305, -43.63459726 ], [ 146.66332726, -43.58085377 ], [ 146.04837772, -43.54974456 ], [ 145.43192956, -42.69377614 ], [ 145.29509037, -42.03360971 ], [ 144.71807132, -41.16255177 ], [ 144.74375451, -40.70397511 ], [ 145.39797814, -40.79254852 ] ] ], [ [ [ 174.61200891, -36.15639739 ], [ 175.33661584, -37.209098 ], [ 175.35759647, -36.52619394 ], [ 175.80888675, -36.79894215 ], [ 175.95849003, -37.55538177 ], [ 176.76319543, -37.88125335 ], [ 177.4388131, -37.96124847 ], [ 178.01035445, -37.57982472 ], [ 178.51709354, -37.69537322 ], [ 178.27473107, -38.5828126 ], [ 177.97046024, -39.16634287 ], [ 177.20699263, -39.14577565 ], [ 176.9399805, -39.44973642 ], [ 177.03294641, -39.87994272 ], [ 176.8858236, -40.06597788 ], [ 176.50801721, -40.60480804 ], [ 176.01244022, -41.28962412 ], [ 175.2395675, -41.68830779 ], [ 175.06789839, -41.42589487 ], [ 174.65097294, -41.28182098 ], [ 175.22763024, -40.45923553 ], [ 174.90015669, -39.9089332 ], [ 173.82404667, -39.50885426 ], [ 173.852262, -39.14660247 ], [ 174.57480187, -38.7976832 ], [ 174.74347375, -38.02780771 ], [ 174.69701664, -37.38112884 ], [ 174.29202844, -36.71109222 ], [ 174.31900353, -36.53482391 ], [ 173.84099654, -36.12198089 ], [ 173.05417118, -35.23712534 ], [ 172.63600549, -34.52910654 ], [ 173.00704227, -34.45066172 ], [ 173.55129846, -35.00618336 ], [ 174.3293905, -35.2654957 ], [ 174.61200891, -36.15639739 ] ] ], [ [ [ 179.36414266, -16.80135408 ], [ 178.72505936, -17.01204167 ], [ 178.5968386, -16.63915 ], [ 179.09660936, -16.43398428 ], [ 179.41350936, -16.37905428 ], [ 180.0, -16.06713266 ], [ 180.0, -16.55521657 ], [ 179.36414266, -16.80135408 ] ] ], [ [ [ 165.77998986, -21.08000498 ], [ 166.74003462, -22.39997609 ], [ 166.18973229, -22.12970835 ], [ 165.47437544, -21.67960662 ], [ 164.8298153, -21.14981984 ], [ 164.16799523, -20.4447466 ], [ 164.02960575, -20.10564585 ], [ 164.45996708, -20.1200119 ], [ 165.02003625, -20.45999114 ], [ 165.46000939, -20.80002207 ], [ 165.77998986, -21.08000498 ] ] ], [ [ [ 178.3736, -17.33992 ], [ 178.71806, -17.62846 ], [ 178.55271, -18.15059 ], [ 177.93266, -18.28799 ], [ 177.38146, -18.16432 ], [ 177.28504, -17.72465 ], [ 177.67087, -17.38114 ], [ 178.12557, -17.50481 ], [ 178.3736, -17.33992 ] ] ], [ [ [ 161.67998172, -9.59998219 ], [ 161.5293966, -9.78431203 ], [ 160.78825321, -8.91754323 ], [ 160.57999719, -8.32000864 ], [ 160.92002811, -8.32000864 ], [ 161.28000614, -9.12001149 ], [ 161.67998172, -9.59998219 ] ] ], [ [ [ 143.56181115, -13.76365569 ], [ 143.92209924, -14.54831064 ], [ 144.56371382, -14.17117604 ], [ 144.89490808, -14.5944577 ], [ 145.37472375, -14.9849765 ], [ 145.271991, -15.42820525 ], [ 145.48525964, -16.2856723 ], [ 145.63703332, -16.78491831 ], [ 145.88890425, -16.90692636 ], [ 146.16030887, -17.76165455 ], [ 146.06367394, -18.28007252 ], [ 146.38747847, -18.95827402 ], [ 147.47108158, -19.48072275 ], [ 148.17760176, -19.95593922 ], [ 148.84841353, -20.39120981 ], [ 148.71746545, -20.63346893 ], [ 149.2894202, -21.26051076 ], [ 149.67833703, -22.3425119 ], [ 150.07738244, -22.12278371 ], [ 150.48293908, -22.55614227 ], [ 150.72726525, -22.40240488 ], [ 150.89955448, -23.46223683 ], [ 151.60917525, -24.0762562 ], [ 152.07353967, -24.45788665 ], [ 152.85519738, -25.26750132 ], [ 153.13616214, -26.07117319 ], [ 153.16194868, -26.64131927 ], [ 153.09290897, -27.26029957 ], [ 153.56946903, -28.11006683 ], [ 153.51210819, -28.99507741 ], [ 153.33909549, -29.45820159 ], [ 153.06924116, -30.35024017 ], [ 153.08960168, -30.92364186 ], [ 152.89157759, -31.64044565 ], [ 152.45000248, -32.55000254 ], [ 151.70911747, -33.04134205 ], [ 151.3439718, -33.81602345 ], [ 151.01055545, -34.3103602 ], [ 150.71413944, -35.17345997 ], [ 150.32821984, -35.67187916 ], [ 150.07521203, -36.42020558 ], [ 149.9461243, -37.10905242 ], [ 149.99728397, -37.42526051 ], [ 149.42388228, -37.77268117 ], [ 148.30462243, -37.80906137 ], [ 147.38173303, -38.21921722 ], [ 146.92212284, -38.60653208 ], [ 146.31792199, -39.03575652 ], [ 145.48965213, -38.593768 ], [ 144.87697635, -38.41744801 ], [ 145.03221236, -37.89618784 ], [ 144.48568241, -38.08532358 ], [ 143.60997359, -38.80946543 ], [ 142.74542687, -38.53826751 ], [ 142.17832971, -38.38003428 ], [ 141.60658166, -38.30851409 ], [ 140.63857873, -38.01933278 ], [ 139.99215824, -37.40293629 ], [ 139.80658817, -36.6436028 ], [ 139.57414758, -36.13836232 ], [ 139.08280806, -35.732754 ], [ 138.12074792, -35.61229624 ], [ 138.4494617, -35.12726124 ], [ 138.20756433, -34.38472259 ], [ 137.71917036, -35.07682505 ], [ 136.82940555, -35.26053476 ], [ 137.35237105, -34.70733856 ], [ 137.50388635, -34.13026784 ], [ 137.890116, -33.64047861 ], [ 137.81032759, -32.90000701 ], [ 136.99683719, -33.7527715 ], [ 136.37206913, -34.09476613 ], [ 135.98904341, -34.8901181 ], [ 135.20821252, -34.47867034 ], [ 135.23921838, -33.94795338 ], [ 134.61341678, -33.22277801 ], [ 134.08590376, -32.8480722 ], [ 134.27390262, -32.61723358 ], [ 132.99077681, -32.01122405 ], [ 132.28808068, -31.98264699 ], [ 131.3263306, -31.49580332 ], [ 129.5357939, -31.59042287 ], [ 128.24093753, -31.94848886 ], [ 127.10286747, -32.28226694 ], [ 126.14871382, -32.21596608 ], [ 125.08862349, -32.72875132 ], [ 124.22164798, -32.95948659 ], [ 124.02894657, -33.48384734 ], [ 123.65966678, -33.89017913 ], [ 122.81103641, -33.91446705 ], [ 122.18306441, -34.00340219 ], [ 121.29919071, -33.82103607 ], [ 120.58026818, -33.93017669 ], [ 119.8936951, -33.97606536 ], [ 119.29889937, -34.50936614 ], [ 119.00734094, -34.46414927 ], [ 118.50571781, -34.74681935 ], [ 118.02497196, -35.06473276 ], [ 117.29550744, -35.02545867 ], [ 116.62510908, -35.02509694 ], [ 115.56434696, -34.38642791 ], [ 115.02680871, -34.19651702 ], [ 115.04861616, -33.62342539 ], [ 115.54512333, -33.48725799 ], [ 115.7146737, -33.25957163 ], [ 115.6793787, -32.90036875 ], [ 115.80164514, -32.20506235 ], [ 115.68961063, -31.61243703 ], [ 115.16090905, -30.60159433 ], [ 114.99704308, -30.03072479 ], [ 115.04003788, -29.46109547 ], [ 114.64197432, -28.81023081 ], [ 114.61649784, -28.51639861 ], [ 114.17357914, -28.11807667 ], [ 114.04888391, -27.33476531 ], [ 113.47749759, -26.54313405 ], [ 113.33895308, -26.1165451 ], [ 113.77835778, -26.54902516 ], [ 113.44096236, -25.62127817 ], [ 113.93690108, -25.91123463 ], [ 114.232852, -26.29844614 ], [ 114.21616052, -25.78628102 ], [ 113.72125532, -24.9989389 ], [ 113.62534387, -24.68397104 ], [ 113.39352339, -24.3847645 ], [ 113.5020439, -23.80635019 ], [ 113.70699263, -23.56021535 ], [ 113.84341841, -23.05998748 ], [ 113.73655155, -22.47547536 ], [ 114.1497563, -21.75588104 ], [ 114.22530724, -22.5174883 ], [ 114.64776208, -21.82951995 ], [ 115.46016727, -21.49517344 ], [ 115.94737267, -21.06868784 ], [ 116.71161543, -20.70168182 ], [ 117.16631636, -20.62359873 ], [ 117.44154504, -20.7468987 ], [ 118.22955895, -20.37420827 ], [ 118.83608524, -20.26331064 ], [ 118.98780724, -20.04420257 ], [ 119.25249393, -19.95294199 ], [ 119.80522505, -19.97650644 ], [ 120.85622033, -19.68370778 ], [ 121.3998564, -19.23975555 ], [ 121.65513797, -18.70531789 ], [ 122.24166548, -18.19764861 ], [ 122.28662398, -17.7986032 ], [ 122.31277225, -17.25496714 ], [ 123.0125745, -16.40519988 ], [ 123.4337891, -17.26855804 ], [ 123.85934452, -17.06903533 ], [ 123.50324222, -16.59650604 ], [ 123.8170732, -16.11131601 ], [ 124.25828657, -16.32794362 ], [ 124.37972619, -15.56705983 ], [ 124.92615279, -15.07510019 ], [ 125.16727502, -14.6803956 ], [ 125.6700867, -14.51007008 ], [ 125.68579634, -14.23065561 ], [ 126.12514937, -14.347341 ], [ 126.14282271, -14.09598683 ], [ 126.58258915, -13.95279144 ], [ 127.06586714, -13.81796762 ], [ 127.80463342, -14.27690602 ], [ 128.35968998, -14.86916961 ], [ 128.98554325, -14.8759909 ], [ 129.62147342, -14.96978362 ], [ 129.40960005, -14.42066985 ], [ 129.88864058, -13.6187033 ], [ 130.33946577, -13.35737558 ], [ 130.1835063, -13.10752003 ], [ 130.61779504, -12.5363921 ], [ 131.2234945, -12.18364878 ], [ 131.73509118, -12.30245289 ], [ 132.57529829, -12.11404062 ], [ 132.55721154, -11.60301238 ], [ 131.82469811, -11.27378183 ], [ 132.35722375, -11.12851938 ], [ 133.01956058, -11.37641123 ], [ 133.55084598, -11.78651539 ], [ 134.39306848, -12.04236541 ], [ 134.67863244, -11.94118296 ], [ 135.29849125, -12.24860605 ], [ 135.88269331, -11.96226694 ], [ 136.25838098, -12.04934173 ], [ 136.49247521, -11.85720875 ], [ 136.95162031, -12.35195892 ], [ 136.68512495, -12.8872234 ], [ 136.30540653, -13.29122975 ], [ 135.96175825, -13.32450937 ], [ 136.07761682, -13.72427825 ], [ 135.7838363, -14.22398935 ], [ 135.42866418, -14.71543222 ], [ 135.50018436, -14.99774057 ], [ 136.2951746, -15.55026499 ], [ 137.06536014, -15.87076222 ], [ 137.58047082, -16.21508229 ], [ 138.3032174, -16.80760426 ], [ 138.58516402, -16.80662241 ], [ 139.10854292, -17.06267913 ], [ 139.26057499, -17.37160084 ], [ 140.2152454, -17.71080495 ], [ 140.8754635, -17.3690687 ], [ 141.07111047, -16.83204721 ], [ 141.27409549, -16.38887013 ], [ 141.39822228, -15.84053151 ], [ 141.70218306, -15.04492116 ], [ 141.56338016, -14.5613331 ], [ 141.63552046, -14.27039479 ], [ 141.51986861, -13.6980783 ], [ 141.65092004, -12.9446876 ], [ 141.84269128, -12.74154754 ], [ 141.68699019, -12.40761443 ], [ 141.92862919, -11.87746592 ], [ 142.1184884, -11.32804209 ], [ 142.1437065, -11.0427365 ], [ 142.51526004, -10.66818572 ], [ 142.79731001, -11.15735483 ], [ 142.86676314, -11.78470672 ], [ 143.11594689, -11.90562957 ], [ 143.15863163, -12.32565561 ], [ 143.52212365, -12.83435841 ], [ 143.59715783, -13.40042205 ], [ 143.56181115, -13.76365569 ] ] ], [ [ [ 167.84487674, -16.4663331 ], [ 167.51518111, -16.59784962 ], [ 167.18000777, -16.15999521 ], [ 167.21680139, -15.89184621 ], [ 167.84487674, -16.4663331 ] ] ], [ [ [ 167.10771244, -14.93392018 ], [ 167.27002811, -15.74002085 ], [ 167.00120731, -15.61460215 ], [ 166.79315799, -15.66881072 ], [ 166.64985925, -15.39270355 ], [ 166.629137, -14.62649708 ], [ 167.10771244, -14.93392018 ] ] ], [ [ [ 162.11902469, -10.48271901 ], [ 162.39864587, -10.82636728 ], [ 161.70003218, -10.82001108 ], [ 161.31979699, -10.20475148 ], [ 161.91738325, -10.44670053 ], [ 162.11902469, -10.48271901 ] ] ], [ [ [ 157.53842573, -7.34781992 ], [ 157.33941979, -7.40476735 ], [ 156.90203047, -7.17687428 ], [ 156.49135786, -6.76594329 ], [ 156.54282759, -6.59933847 ], [ 157.14000044, -7.02163828 ], [ 157.53842573, -7.34781992 ] ] ], [ [ [ 134.72462447, -6.21440073 ], [ 134.21013391, -6.89523773 ], [ 134.11277551, -6.14246714 ], [ 134.29033573, -5.78305755 ], [ 134.49962528, -5.44504201 ], [ 134.72700158, -5.73758229 ], [ 134.72462447, -6.21440073 ] ] ], [ [ [ 155.88002567, -6.81999684 ], [ 155.59999108, -6.91999074 ], [ 155.16699426, -6.53593149 ], [ 154.72919152, -5.90082814 ], [ 154.51411421, -5.13911753 ], [ 154.6525037, -5.04243092 ], [ 154.75999068, -5.33998382 ], [ 155.06291792, -5.56679168 ], [ 155.54774621, -6.2006548 ], [ 156.01996545, -6.54001393 ], [ 155.88002567, -6.81999684 ] ] ], [ [ [ 151.98279585, -5.47806325 ], [ 151.45910689, -5.56028045 ], [ 151.30139042, -5.84072845 ], [ 150.75444706, -6.08376271 ], [ 150.24119673, -6.31775359 ], [ 149.70996301, -6.31651336 ], [ 148.89006473, -6.02604013 ], [ 148.3189368, -5.74714243 ], [ 148.4018258, -5.43775563 ], [ 149.2984119, -5.58374155 ], [ 149.84556197, -5.50550343 ], [ 149.99625044, -5.02610117 ], [ 150.13975589, -5.00134816 ], [ 150.23690759, -5.53222015 ], [ 150.80746708, -5.45584238 ], [ 151.08967207, -5.11369272 ], [ 151.64788089, -4.75707366 ], [ 151.53786177, -4.16780731 ], [ 152.13679162, -4.14879038 ], [ 152.33874312, -4.3129664 ], [ 152.31869266, -4.86766123 ], [ 151.98279585, -5.47806325 ] ] ], [ [ [ 153.14003788, -4.49998341 ], [ 152.82729211, -4.7664271 ], [ 152.63867313, -4.17612721 ], [ 152.40602583, -3.78974253 ], [ 151.95323693, -3.46206227 ], [ 151.38427941, -3.03542164 ], [ 150.6620496, -2.7414861 ], [ 150.93996545, -2.50000213 ], [ 151.47998417, -2.77998504 ], [ 151.82001509, -2.99997161 ], [ 152.23998946, -3.24000864 ], [ 152.64001672, -3.65998301 ], [ 153.01999352, -3.98001515 ], [ 153.14003788, -4.49998341 ] ] ], [ [ [ 127.24921512, -3.45906504 ], [ 126.87492272, -3.79098276 ], [ 126.18380212, -3.6073764 ], [ 125.98903364, -3.17727345 ], [ 127.00065148, -3.12931772 ], [ 127.24921512, -3.45906504 ] ] ], [ [ [ 125.24050052, 1.41983613 ], [ 124.43703535, 0.42788117 ], [ 123.685505, 0.23559317 ], [ 122.72308312, 0.43113679 ], [ 121.05672489, 0.38121735 ], [ 120.18308312, 0.23724681 ], [ 120.04086958, -0.51965789 ], [ 120.93590539, -1.40890594 ], [ 121.47582075, -0.95596201 ], [ 123.34056481, -0.6156727 ], [ 123.25839929, -1.07621307 ], [ 122.82271529, -0.93095062 ], [ 122.3885299, -1.51685801 ], [ 121.50827355, -1.90448292 ], [ 122.45457238, -3.18605844 ], [ 122.27189619, -3.52950001 ], [ 123.17096276, -4.68369313 ], [ 123.1623328, -5.34060394 ], [ 122.62851525, -5.63459116 ], [ 122.23639448, -5.28293304 ], [ 122.71956913, -4.46417164 ], [ 121.73823368, -4.85133148 ], [ 121.48946333, -4.5745525 ], [ 121.61917118, -4.18847788 ], [ 120.89818159, -3.6021054 ], [ 120.97238895, -2.62764292 ], [ 120.30545292, -2.93160369 ], [ 120.39004724, -4.09757903 ], [ 120.43071659, -5.52824106 ], [ 119.79654341, -5.67340016 ], [ 119.36690555, -5.37987802 ], [ 119.6536064, -4.45941741 ], [ 119.49883548, -3.49441172 ], [ 119.07834435, -3.48702199 ], [ 118.767769, -2.8019992 ], [ 119.18097375, -2.14710377 ], [ 119.323394, -1.35314707 ], [ 119.82599898, 0.15425446 ], [ 120.03570194, 0.56647736 ], [ 120.88577925, 1.30922272 ], [ 121.66681685, 1.01394359 ], [ 122.92756677, 0.87519237 ], [ 124.07752241, 0.91710196 ], [ 125.06598921, 1.64325918 ], [ 125.24050052, 1.41983613 ] ] ], [ [ [ 105.81765506, -5.85235565 ], [ 104.71038415, -5.8732846 ], [ 103.86821333, -5.03731496 ], [ 102.5842607, -4.22025888 ], [ 102.15617313, -3.61414601 ], [ 101.3991134, -2.79977711 ], [ 100.90250288, -2.05026214 ], [ 100.14198083, -0.65034759 ], [ 99.26373986, 0.18314159 ], [ 98.97001102, 1.04288239 ], [ 98.60135135, 1.82350658 ], [ 97.69959761, 2.45318391 ], [ 97.17694217, 3.30879059 ], [ 96.42401655, 3.86885977 ], [ 95.38087609, 4.97078217 ], [ 95.29302616, 5.47982087 ], [ 95.93686283, 5.43951325 ], [ 97.48488203, 5.24632091 ], [ 98.36916914, 4.26837027 ], [ 99.14255863, 3.59034964 ], [ 99.69399784, 3.17432852 ], [ 100.64143355, 2.09938121 ], [ 101.65801232, 2.08369741 ], [ 102.49827111, 1.39870047 ], [ 103.07684045, 0.5613614 ], [ 103.83839603, 0.10454173 ], [ 103.4376453, -0.7119459 ], [ 104.01078861, -1.05921152 ], [ 104.36999149, -1.08484303 ], [ 104.53949019, -1.78237151 ], [ 104.88789269, -2.34042531 ], [ 105.62211144, -2.42884368 ], [ 106.10859338, -3.06177663 ], [ 105.85744592, -4.305525 ], [ 105.81765506, -5.85235565 ] ] ], [ [ [ 130.47134403, -3.09376434 ], [ 130.83483605, -3.85847218 ], [ 129.9905465, -3.44630096 ], [ 129.15524865, -3.36263681 ], [ 128.59068363, -3.42867929 ], [ 127.89889123, -3.39343597 ], [ 128.13587935, -2.8436504 ], [ 129.37099776, -2.80215423 ], [ 130.47134403, -3.09376434 ] ] ], [ [ [ 128.68824873, 1.13238597 ], [ 128.63595218, 0.25848583 ], [ 128.12016971, 0.35641267 ], [ 127.9680343, -0.25207733 ], [ 128.37999881, -0.78000376 ], [ 128.1000159, -0.89999643 ], [ 127.69647464, -0.2665984 ], [ 127.39949019, 1.0117215 ], [ 127.60051151, 1.81069082 ], [ 127.93237756, 2.17459626 ], [ 128.00415612, 1.6285314 ], [ 128.59455936, 1.54081066 ], [ 128.68824873, 1.13238597 ] ] ], [ [ [ 117.87562707, 1.82764069 ], [ 118.99674727, 0.90221914 ], [ 117.81185835, 0.78424185 ], [ 117.47833866, 0.10247468 ], [ 117.52164351, -0.80372324 ], [ 116.56004846, -1.48766082 ], [ 116.53379683, -2.48351735 ], [ 116.14808394, -4.01272633 ], [ 116.00085778, -3.65703745 ], [ 114.86480309, -4.10698414 ], [ 114.46865156, -3.49570363 ], [ 113.75567183, -3.43916961 ], [ 113.25699426, -3.11877573 ], [ 112.06812626, -3.47839202 ], [ 111.70329064, -2.99444223 ], [ 111.04824019, -3.04942596 ], [ 110.22384606, -2.93403248 ], [ 110.0709355, -1.59287404 ], [ 109.57194787, -1.31490651 ], [ 109.09187381, -0.45950652 ], [ 108.95265751, 0.41537547 ], [ 109.06913618, 1.34193391 ], [ 109.66326013, 2.00646699 ], [ 110.39613529, 1.66377473 ], [ 111.16885298, 1.8506367 ], [ 111.37008101, 2.69730337 ], [ 111.79692834, 2.88589651 ], [ 112.99561486, 3.10239492 ], [ 113.71293542, 3.89350943 ], [ 114.20401655, 4.52587393 ], [ 114.59996138, 4.9000113 ], [ 115.45071048, 5.4477298 ], [ 116.220741, 6.14319123 ], [ 116.72510298, 6.92477143 ], [ 117.12962609, 6.92805288 ], [ 117.64339318, 6.42216645 ], [ 117.68907515, 5.98749014 ], [ 118.34769128, 5.70869579 ], [ 119.18190392, 5.4078356 ], [ 119.1106938, 5.01612824 ], [ 118.439727, 4.96651887 ], [ 118.61832075, 4.47820242 ], [ 117.88203495, 4.13755138 ], [ 117.31323246, 3.23442821 ], [ 118.04832971, 2.28769013 ], [ 117.87562707, 1.82764069 ] ] ], [ [ [ 120.71560876, -10.23958139 ], [ 120.29501428, -10.25865 ], [ 118.96780847, -9.55796925 ], [ 119.90030969, -9.36134043 ], [ 120.42575565, -9.66592132 ], [ 120.77550174, -9.96967539 ], [ 120.71560876, -10.23958139 ] ] ], [ [ [ 124.43595015, -10.14000091 ], [ 123.57998172, -10.35998748 ], [ 123.45998905, -10.23999481 ], [ 123.55000939, -9.90001556 ], [ 123.98000899, -9.29002695 ], [ 124.96868249, -8.89279022 ], [ 124.96868249, -8.89279022 ], [ 125.08624637, -8.6568873 ], [ 125.94707238, -8.43209482 ], [ 126.64470422, -8.39824676 ], [ 126.95724328, -8.27334482 ], [ 127.33592818, -8.39731658 ], [ 126.96799198, -8.66825612 ], [ 125.92588504, -9.10600718 ], [ 125.08852014, -9.39317311 ], [ 124.43595015, -10.14000091 ] ] ], [ [ [ 160.85222863, -9.87293711 ], [ 160.46258833, -9.89520965 ], [ 159.84944746, -9.79402719 ], [ 159.64000288, -9.63997975 ], [ 159.70294478, -9.24294972 ], [ 160.36295617, -9.40030446 ], [ 160.68851769, -9.61016245 ], [ 160.85222863, -9.87293711 ] ] ], [ [ [ 159.8750273, -8.33732024 ], [ 159.91740197, -8.53828989 ], [ 159.1336772, -8.11418141 ], [ 158.58611372, -7.7548235 ], [ 158.21114953, -7.42187225 ], [ 158.35997766, -7.320018 ], [ 158.82000126, -7.56000335 ], [ 159.64000288, -8.02002695 ], [ 159.8750273, -8.33732024 ] ] ], [ [ [ 108.62347863, -6.77767384 ], [ 110.53922733, -6.87735768 ], [ 110.75957564, -6.46518646 ], [ 112.61481123, -6.94603566 ], [ 112.97876835, -7.59421315 ], [ 114.47893517, -7.7765276 ], [ 115.70552697, -8.37080657 ], [ 114.56451135, -8.75181691 ], [ 113.46473351, -8.34894744 ], [ 112.55967248, -8.37618092 ], [ 111.5220614, -8.30212859 ], [ 110.58614953, -8.12260467 ], [ 109.42766727, -7.74066416 ], [ 108.69365523, -7.64160044 ], [ 108.2777633, -7.7666574 ], [ 106.454102, -7.35489959 ], [ 106.28062422, -6.9249 ], [ 105.36548628, -6.85141611 ], [ 106.05164595, -5.89591888 ], [ 107.26500858, -5.95498504 ], [ 108.0720911, -6.34576222 ], [ 108.48684614, -6.42198496 ], [ 108.62347863, -6.77767384 ] ] ], [ [ [ 117.90001835, -8.09568125 ], [ 118.26061649, -8.36238331 ], [ 118.87845991, -8.28068288 ], [ 119.12650679, -8.70582488 ], [ 117.97040165, -8.9066395 ], [ 117.27773075, -9.04089487 ], [ 116.74014082, -9.0329367 ], [ 117.08373742, -8.45715789 ], [ 117.63202437, -8.44930307 ], [ 117.90001835, -8.09568125 ] ] ], [ [ [ 122.90353723, -8.09423431 ], [ 122.75698286, -8.64980763 ], [ 121.25449059, -8.93366627 ], [ 119.9243909, -8.81041798 ], [ 119.92092858, -8.4448589 ], [ 120.71509199, -8.23696461 ], [ 121.34166874, -8.5367396 ], [ 122.00736454, -8.46062021 ], [ 122.90353723, -8.09423431 ] ] ], [ [ [ 147.19187381, -7.38802418 ], [ 148.08463586, -8.04410817 ], [ 148.73410526, -9.10466359 ], [ 149.30683516, -9.07143564 ], [ 149.26663089, -9.51440602 ], [ 150.03872847, -9.68431813 ], [ 149.73879846, -9.87293711 ], [ 150.80162764, -10.29368662 ], [ 150.69057499, -10.5827129 ], [ 150.02839318, -10.65247609 ], [ 149.78231001, -10.3932671 ], [ 148.92313765, -10.28092254 ], [ 147.91301843, -10.13044077 ], [ 147.13544315, -9.49244354 ], [ 146.56788089, -8.94255462 ], [ 146.04848107, -8.06741424 ], [ 144.74416792, -7.63012827 ], [ 143.89708784, -7.9153305 ], [ 143.28637577, -8.24549122 ], [ 143.4139132, -8.98306894 ], [ 142.62843143, -9.32682057 ], [ 142.06825891, -9.15959564 ], [ 141.03385176, -9.11789275 ], [ 140.14341516, -8.29716766 ], [ 139.12776655, -8.09604298 ], [ 138.88147668, -8.38093515 ], [ 137.61447391, -8.41168263 ], [ 138.03909916, -7.59788218 ], [ 138.66862145, -7.3202247 ], [ 138.40791385, -6.23284922 ], [ 137.9278398, -5.39336557 ], [ 135.98925012, -4.54654388 ], [ 135.16459761, -4.46293141 ], [ 133.66288049, -3.53885345 ], [ 133.36770471, -4.02481862 ], [ 132.98395552, -4.11297861 ], [ 132.75694095, -3.74628265 ], [ 132.75378869, -3.3117872 ], [ 131.98980432, -2.82055104 ], [ 133.06684452, -2.46041798 ], [ 133.78003096, -2.47984832 ], [ 133.69621179, -2.21454152 ], [ 132.23237349, -2.21252614 ], [ 131.83622196, -1.61716196 ], [ 130.9428398, -1.43252207 ], [ 130.51955814, -0.93772023 ], [ 131.86753788, -0.69546111 ], [ 132.38011641, -0.36953786 ], [ 133.98554813, -0.78021046 ], [ 134.14336795, -1.15186736 ], [ 134.42262739, -2.76918467 ], [ 135.45760298, -3.36775278 ], [ 136.29331424, -2.30704233 ], [ 137.44073775, -1.70351328 ], [ 138.32972741, -1.70268646 ], [ 139.18492069, -2.05129567 ], [ 139.9266842, -2.40905161 ], [ 141.0002104, -2.60015106 ], [ 142.73524662, -3.28915293 ], [ 144.58397098, -3.86141774 ], [ 145.27317956, -4.37373789 ], [ 145.82978641, -4.8764979 ], [ 145.98192183, -5.46560923 ], [ 147.64807336, -6.08365936 ], [ 147.89110762, -6.61401458 ], [ 146.97090539, -6.72165659 ], [ 147.19187381, -7.38802418 ] ] ], [ [ [ 143.91016198, 44.17409984 ], [ 144.61342655, 43.96088288 ], [ 145.32082523, 44.38473298 ], [ 145.54313724, 43.26208832 ], [ 144.0596619, 42.98835826 ], [ 143.18384973, 41.99521475 ], [ 141.61149092, 42.6787906 ], [ 141.06728641, 41.58459382 ], [ 139.95510624, 41.56955598 ], [ 139.81754357, 42.56375886 ], [ 140.31208703, 43.33327261 ], [ 141.38054894, 43.38882477 ], [ 141.67195235, 44.77212535 ], [ 141.96764489, 45.55148347 ], [ 143.14287031, 44.51035838 ], [ 143.91016198, 44.17409984 ] ] ], [ [ [ 27.19237674, 40.6905657 ], [ 26.35800907, 40.15199392 ], [ 26.04335127, 40.61775361 ], [ 26.05694217, 40.82412344 ], [ 26.29460209, 40.9362613 ], [ 26.60419559, 41.56211457 ], [ 26.11704186, 41.82690461 ], [ 27.13573937, 42.14148489 ], [ 27.99672041, 42.00735871 ], [ 28.11552453, 41.62288605 ], [ 28.98844282, 41.29993419 ], [ 28.80643843, 41.05496206 ], [ 27.61901737, 40.99982331 ], [ 27.19237674, 40.6905657 ] ] ], [ [ [ 134.63842818, 34.14923371 ], [ 134.76637902, 33.80633474 ], [ 134.20341597, 33.20117788 ], [ 133.79295007, 33.52198518 ], [ 133.28026818, 33.28957042 ], [ 133.01485803, 32.70456737 ], [ 132.36311486, 32.98938203 ], [ 132.37117639, 33.46364248 ], [ 132.92437259, 34.06029857 ], [ 133.49296838, 33.94462088 ], [ 133.90410607, 34.36493114 ], [ 134.63842818, 34.14923371 ] ] ], [ [ [ 140.97638757, 37.14207429 ], [ 140.59976973, 36.34398347 ], [ 140.77407433, 35.8428771 ], [ 140.25327925, 35.13811392 ], [ 138.97552779, 34.6676 ], [ 137.21759891, 34.60628592 ], [ 135.79298303, 33.4648052 ], [ 135.1209827, 33.84907115 ], [ 135.07943485, 34.59654491 ], [ 133.3403162, 34.37593822 ], [ 132.15677087, 33.90493338 ], [ 130.98614465, 33.88576142 ], [ 132.00003625, 33.14999238 ], [ 131.33279016, 31.45035452 ], [ 130.68631799, 31.02957917 ], [ 130.20241988, 31.41823762 ], [ 130.44767622, 32.3194746 ], [ 129.8146916, 32.61030956 ], [ 129.40846317, 33.29605581 ], [ 130.35393517, 33.6041507 ], [ 130.87845096, 34.23274282 ], [ 131.88422936, 34.74971385 ], [ 132.61767297, 35.43339305 ], [ 134.60830082, 35.73161774 ], [ 135.67753788, 35.5271341 ], [ 136.7238306, 37.30498424 ], [ 137.39061161, 36.82739065 ], [ 138.85760217, 37.82748465 ], [ 139.42640466, 38.21596223 ], [ 140.05479007, 39.43880748 ], [ 139.88337935, 40.56331249 ], [ 140.30578251, 41.19500519 ], [ 141.36897342, 41.37855988 ], [ 141.91426314, 39.99161612 ], [ 141.88460086, 39.18086457 ], [ 140.95948937, 38.17400096 ], [ 140.97638757, 37.14207429 ] ] ], [ [ [ 33.97361657, 35.05850637 ], [ 34.00488081, 34.97809785 ], [ 32.9798271, 34.57186941 ], [ 32.49029626, 34.70165477 ], [ 32.25666711, 35.10323233 ], [ 32.73178023, 35.14002595 ], [ 32.80247359, 35.14550365 ], [ 32.94696089, 35.3867034 ], [ 33.667227, 35.37321585 ], [ 34.57647383, 35.67159557 ], [ 33.90080448, 35.24575593 ], [ 33.97361657, 35.05850637 ] ] ], [ [ [ 121.77781782, 24.39427359 ], [ 121.17563236, 22.79085725 ], [ 120.74707971, 21.9705714 ], [ 120.22008345, 22.81486095 ], [ 120.10618859, 23.55626272 ], [ 120.6946798, 24.53845083 ], [ 121.49504439, 25.29545889 ], [ 121.95124393, 24.99759593 ], [ 121.77781782, 24.39427359 ] ] ], [ [ [ 81.78795902, 7.52305532 ], [ 81.63732222, 6.48177521 ], [ 81.21801965, 6.19714142 ], [ 80.34835697, 5.96836986 ], [ 79.8724687, 6.76346345 ], [ 79.69516686, 8.20084341 ], [ 80.14780073, 9.82407766 ], [ 80.83881799, 9.26842683 ], [ 81.30431929, 8.56420624 ], [ 81.78795902, 7.52305532 ] ] ], [ [ [ 125.50255171, 12.16269461 ], [ 125.7834648, 11.04612193 ], [ 125.01188399, 11.31145458 ], [ 125.03276127, 10.97581615 ], [ 125.27744917, 10.35872203 ], [ 124.80181929, 10.13467886 ], [ 124.76016808, 10.8379951 ], [ 124.45910119, 10.88992992 ], [ 124.3025216, 11.495371 ], [ 124.89101281, 11.41558259 ], [ 124.87799035, 11.79418997 ], [ 124.26676151, 12.55776093 ], [ 125.22711633, 12.53572093 ], [ 125.50255171, 12.16269461 ] ] ], [ [ [ 121.8835478, 11.89175507 ], [ 122.48382124, 11.5821874 ], [ 123.12021651, 11.58366018 ], [ 123.10083784, 11.16593374 ], [ 122.63771366, 10.7413085 ], [ 122.0026103, 10.44101675 ], [ 121.96736698, 10.90569123 ], [ 122.0383704, 11.41584097 ], [ 121.8835478, 11.89175507 ] ] ], [ [ [ 121.52739383, 13.06959016 ], [ 121.26219038, 12.20556021 ], [ 120.83389611, 12.70449616 ], [ 120.32343631, 13.46641348 ], [ 121.18012821, 13.42969737 ], [ 121.52739383, 13.06959016 ] ] ], [ [ [ 121.32130822, 18.50406464 ], [ 121.93760135, 18.21855235 ], [ 122.2460063, 18.4789499 ], [ 122.33695682, 18.22488272 ], [ 122.17427941, 17.8102827 ], [ 122.51565392, 17.09350475 ], [ 122.25231083, 16.26244436 ], [ 121.66278609, 15.93101756 ], [ 121.50506961, 15.12481354 ], [ 121.72882857, 14.32837637 ], [ 122.25892541, 14.21820222 ], [ 122.70127567, 14.33654125 ], [ 123.95029504, 13.78213064 ], [ 123.85510705, 13.2377711 ], [ 124.18128869, 12.99752737 ], [ 124.07741906, 12.53667695 ], [ 123.29803511, 13.02752554 ], [ 122.92865197, 13.55291983 ], [ 122.67135502, 13.18583629 ], [ 122.03464969, 13.78448192 ], [ 121.12638472, 13.63668732 ], [ 120.62863732, 13.85765575 ], [ 120.67938358, 14.27101553 ], [ 120.99181929, 14.52539277 ], [ 120.69333622, 14.75667064 ], [ 120.56414514, 14.3962792 ], [ 120.0704285, 14.97086945 ], [ 119.92092858, 15.40634675 ], [ 119.88377323, 16.36370433 ], [ 120.28648766, 16.03462881 ], [ 120.39004724, 17.59908112 ], [ 120.71586714, 18.50522736 ], [ 121.32130822, 18.50406464 ] ] ], [ [ [ 126.37681359, 8.41470633 ], [ 126.47851281, 7.75035411 ], [ 126.53742394, 7.1893806 ], [ 126.1967729, 6.27429434 ], [ 125.83142053, 7.29371532 ], [ 125.36385217, 6.7864853 ], [ 125.68316084, 6.04965689 ], [ 125.39651167, 5.58100332 ], [ 124.21978763, 6.1613555 ], [ 123.93871952, 6.88513561 ], [ 124.24366214, 7.36061046 ], [ 123.61021244, 7.83352733 ], [ 123.29607141, 7.41887564 ], [ 122.82550581, 7.45737458 ], [ 122.0854993, 6.89942414 ], [ 121.91992801, 7.19211945 ], [ 122.31235884, 8.03496206 ], [ 122.9423979, 8.31623688 ], [ 123.48768762, 8.69300975 ], [ 123.84115441, 8.2403242 ], [ 124.60146976, 8.51415762 ], [ 124.76461226, 8.96040945 ], [ 125.47139082, 8.98699698 ], [ 125.41211795, 9.76033478 ], [ 126.22271447, 9.28607433 ], [ 126.306637, 8.78248749 ], [ 126.37681359, 8.41470633 ] ] ], [ [ [ 118.50458093, 9.31638255 ], [ 117.17427453, 8.3674999 ], [ 117.66447717, 9.06688874 ], [ 118.38691369, 9.68449962 ], [ 118.98734216, 10.37629202 ], [ 119.51149621, 11.36966808 ], [ 119.68967655, 10.55429149 ], [ 119.02945845, 10.00365327 ], [ 118.50458093, 9.31638255 ] ] ], [ [ [ 123.98243778, 10.27877859 ], [ 123.62318322, 9.95009064 ], [ 123.30992069, 9.31826874 ], [ 122.99588301, 9.02218863 ], [ 122.38005497, 9.71336091 ], [ 122.5860889, 9.98104483 ], [ 122.83708133, 10.26115693 ], [ 122.94741052, 10.88186839 ], [ 123.49884973, 10.9406245 ], [ 123.33777429, 10.26738394 ], [ 124.07793583, 11.23272553 ], [ 123.98243778, 10.27877859 ] ] ], [ [ [ 102.14118696, 6.22163605 ], [ 102.14118696, 6.22163605 ], [ 102.37114709, 6.12820506 ], [ 102.96170536, 5.52449514 ], [ 103.38121463, 4.85500113 ], [ 103.43857547, 4.18160554 ], [ 103.33212202, 3.7266979 ], [ 103.42942875, 3.38286876 ], [ 103.50244754, 2.79101858 ], [ 103.85467411, 2.51545401 ], [ 104.24793176, 1.63114106 ], [ 104.22881148, 1.293048 ], [ 103.51970747, 1.22633373 ], [ 102.57361535, 1.96711538 ], [ 101.39063846, 2.76081371 ], [ 101.27353967, 3.27029165 ], [ 100.69543542, 3.93913972 ], [ 100.55740767, 4.76728038 ], [ 100.19670617, 5.31249258 ], [ 100.30626021, 6.04056184 ], [ 100.08575687, 6.46448945 ], [ 100.08575687, 6.46448945 ], [ 99.69069055, 6.8482128 ], [ 99.51964155, 7.34345388 ], [ 98.9882528, 7.90799307 ], [ 98.50378625, 8.3823052 ], [ 98.3396619, 7.79451162 ], [ 98.15000939, 8.35000743 ], [ 98.25915002, 8.97392284 ], [ 98.55355065, 9.93295991 ], [ 98.45717411, 10.67526602 ], [ 98.76454553, 11.44129161 ], [ 98.42833866, 12.03298676 ], [ 98.50957401, 13.12237763 ], [ 98.10360396, 13.6404597 ], [ 97.77773238, 14.83728587 ], [ 97.59707157, 16.10056794 ], [ 97.16453983, 16.92873444 ], [ 96.50576867, 16.42724051 ], [ 95.36935225, 15.71438996 ], [ 94.80840458, 15.80345429 ], [ 94.18880415, 16.0379361 ], [ 94.53348596, 17.2772403 ], [ 94.32481652, 18.2135139 ], [ 93.5409884, 19.36649262 ], [ 93.66325484, 19.72696157 ], [ 93.07827762, 19.85514497 ], [ 92.3685535, 20.67088329 ], [ 92.08288618, 21.19219514 ], [ 92.02521529, 21.70156973 ], [ 91.83489099, 22.1829357 ], [ 91.41708703, 22.76501903 ], [ 90.4960063, 22.80501659 ], [ 90.58695682, 22.39279369 ], [ 90.27297082, 21.8363677 ], [ 89.84746708, 22.03914602 ], [ 89.7020496, 21.85711579 ], [ 89.41886275, 21.9661789 ], [ 89.0319613, 22.05570832 ], [ 88.8887659, 21.69058849 ], [ 88.20849735, 21.7031717 ], [ 86.97570438, 21.49556163 ], [ 87.03316857, 20.74330781 ], [ 86.49935103, 20.1516385 ], [ 85.06026574, 19.4785788 ], [ 83.94100589, 18.30200979 ], [ 83.18921716, 17.67122142 ], [ 82.19279219, 17.01663605 ], [ 82.1912419, 16.55666413 ], [ 81.69271935, 16.31021922 ], [ 80.79199914, 15.95197236 ], [ 80.32489587, 15.89918488 ], [ 80.02506921, 15.1364149 ], [ 80.23327355, 13.83577078 ], [ 80.28629357, 13.00626069 ], [ 79.86254683, 12.05621532 ], [ 79.8579993, 10.35727509 ], [ 79.34051151, 10.30885427 ], [ 78.88534549, 9.54613597 ], [ 79.18971968, 9.21654369 ], [ 78.27794071, 8.93304678 ], [ 77.9411654, 8.25295909 ], [ 77.5398979, 7.96553478 ], [ 76.59297896, 8.89927623 ], [ 76.13006148, 10.29963003 ], [ 75.74646732, 11.30825064 ], [ 75.39610111, 11.78124502 ], [ 74.86481571, 12.74193574 ], [ 74.61671716, 13.99258291 ], [ 74.44385949, 14.61722179 ], [ 73.53419925, 15.99065217 ], [ 73.1199093, 17.92857005 ], [ 72.82090946, 19.20823355 ], [ 72.82447513, 20.41950328 ], [ 72.63053348, 21.35600943 ], [ 71.17527347, 20.75744131 ], [ 70.47045861, 20.87733063 ], [ 69.16413008, 22.089298 ], [ 69.64492761, 22.45077464 ], [ 69.3495968, 22.84317963 ], [ 68.17664514, 23.69196503 ], [ 67.44366662, 23.94484365 ], [ 67.14544193, 24.66361115 ], [ 66.37282759, 25.4251409 ], [ 64.53040775, 25.23703868 ], [ 62.90570072, 25.21840933 ], [ 61.49736291, 25.07823701 ], [ 59.61613407, 25.38015656 ], [ 58.52576135, 25.60996166 ], [ 57.39725142, 25.73990205 ], [ 56.97076582, 26.96610627 ], [ 56.49213871, 27.14330476 ], [ 55.72371016, 26.96463349 ], [ 54.71508955, 26.48065786 ], [ 53.49309696, 26.81236888 ], [ 52.48359785, 27.58084911 ], [ 51.52076257, 27.8656896 ], [ 50.85294803, 28.81452058 ], [ 50.11500858, 30.14777253 ], [ 49.57685021, 29.98571524 ], [ 48.94133345, 30.31709036 ], [ 48.56797123, 29.92677827 ], [ 47.97451908, 29.9758192 ], [ 48.18318851, 29.53447663 ], [ 48.09394331, 29.30629934 ], [ 48.41609419, 28.5520043 ], [ 48.80759484, 27.689628 ], [ 49.29955448, 27.46121817 ], [ 49.47091353, 27.10999929 ], [ 50.15242232, 26.68966319 ], [ 50.21293542, 26.27702688 ], [ 50.11330326, 25.94397228 ], [ 50.23985884, 25.60804963 ], [ 50.52738651, 25.32780834 ], [ 50.66055668, 24.99989553 ], [ 50.81010827, 24.75474254 ], [ 50.74391076, 25.48242422 ], [ 51.01335168, 26.00699169 ], [ 51.28646162, 26.11458202 ], [ 51.58907881, 25.80111278 ], [ 51.60670047, 25.21567048 ], [ 51.38960778, 24.62738597 ], [ 51.57951867, 24.24549714 ], [ 51.75744063, 24.29407298 ], [ 51.79438928, 24.01982616 ], [ 52.57708052, 24.17743928 ], [ 53.40400679, 24.15131684 ], [ 54.00800093, 24.12175792 ], [ 54.69302372, 24.79789236 ], [ 55.43902469, 25.43914521 ], [ 56.07082075, 26.05546418 ], [ 56.07082075, 26.05546418 ], [ 56.36201745, 26.39593435 ], [ 56.48567915, 26.30911795 ], [ 56.39142134, 25.89599071 ], [ 56.2610417, 25.71460643 ], [ 56.2610417, 25.71460643 ], [ 56.39684737, 24.92473216 ], [ 56.84514042, 24.24167308 ], [ 57.40345259, 23.87859447 ], [ 58.13694787, 23.74793061 ], [ 58.72921146, 23.56566783 ], [ 59.18050174, 22.99239533 ], [ 59.45009769, 22.6602709 ], [ 59.80806034, 22.53361197 ], [ 59.80614831, 22.31052481 ], [ 59.4421912, 21.71454051 ], [ 59.28240767, 21.43388581 ], [ 58.86114139, 21.11403453 ], [ 58.48798587, 20.42898591 ], [ 58.03431848, 20.48143749 ], [ 57.82637251, 20.24300243 ], [ 57.66576216, 19.73600495 ], [ 57.78870039, 19.0675703 ], [ 57.6943909, 18.94470958 ], [ 57.23426395, 18.94799103 ], [ 56.60965091, 18.57426708 ], [ 56.51218916, 18.08711335 ], [ 56.28352095, 17.8760668 ], [ 55.66149173, 17.88412832 ], [ 55.26993941, 17.63230907 ], [ 55.27490034, 17.2283544 ], [ 54.79100223, 16.95069693 ], [ 54.23925296, 17.04498058 ], [ 53.57050825, 16.70766267 ], [ 53.10857263, 16.65105113 ], [ 53.10857263, 16.65105113 ], [ 52.38520593, 16.3824112 ], [ 52.19172936, 15.93843313 ], [ 52.16816491, 15.59742036 ], [ 51.17251509, 15.17524974 ], [ 49.57457645, 14.70876659 ], [ 48.67923058, 14.00320242 ], [ 48.23894738, 13.9480895 ], [ 47.93891402, 14.00723318 ], [ 47.35445357, 13.59221975 ], [ 46.71707645, 13.3996992 ], [ 45.87759281, 13.34776439 ], [ 45.62505008, 13.29094615 ], [ 45.40645877, 13.02690542 ], [ 45.14435591, 12.9539383 ], [ 44.98953332, 12.6995869 ], [ 44.49457645, 12.72165274 ], [ 44.17511275, 12.58595043 ], [ 43.48295861, 12.63680004 ], [ 43.22287113, 13.22095043 ], [ 43.2514482, 13.76758373 ], [ 43.08794396, 14.06263032 ], [ 42.89224531, 14.80224925 ], [ 42.60487267, 15.21333527 ], [ 42.8050155, 15.2619628 ], [ 42.70243778, 15.71888581 ], [ 42.82367069, 15.91174226 ], [ 42.77933231, 16.34789134 ], [ 42.64957279, 16.77463532 ], [ 42.34798913, 17.07580557 ], [ 42.27088789, 17.47472179 ], [ 41.75438195, 17.83304617 ], [ 41.22139123, 18.67159964 ], [ 40.93934126, 19.4864853 ], [ 40.24765222, 20.17463451 ], [ 39.8016846, 20.33886221 ], [ 39.13939945, 21.29190481 ], [ 39.02369592, 21.98687531 ], [ 39.06632897, 22.57965567 ], [ 38.49277225, 23.68845104 ], [ 38.0238603, 24.07868561 ], [ 37.48363488, 24.2854947 ], [ 37.15481774, 24.85848298 ], [ 37.20949141, 25.08454153 ], [ 36.93162723, 25.6029595 ], [ 36.63960371, 25.82622753 ], [ 36.24913659, 26.57013561 ], [ 35.64018151, 27.37652049 ], [ 35.1301868, 28.06335196 ], [ 34.63233605, 28.05854605 ], [ 34.78777876, 28.60742727 ], [ 34.83222049, 28.95748343 ], [ 34.95603723, 29.35655467 ], [ 34.92260257, 29.5013262 ], [ 34.26543338, 31.21936087 ], [ 34.5563717, 31.54882396 ], [ 34.48810713, 31.60553885 ], [ 34.75258711, 32.07292634 ], [ 34.95541711, 32.82737641 ], [ 35.09845747, 33.08053925 ], [ 35.12605269, 33.09090038 ], [ 35.48220666, 33.90545014 ], [ 35.97959232, 34.6100583 ], [ 35.99840254, 34.64491405 ], [ 35.90502323, 35.41000947 ], [ 36.14976281, 35.82153474 ], [ 36.14976281, 35.82153474 ], [ 35.782085, 36.27499543 ], [ 36.16082157, 36.65060558 ], [ 35.55093631, 36.56544282 ], [ 34.71455326, 36.79553213 ], [ 34.02689497, 36.21996003 ], [ 32.50915816, 36.10756379 ], [ 31.69959517, 36.64427521 ], [ 30.62162479, 36.6778649 ], [ 30.39109623, 36.26298066 ], [ 29.69997562, 36.14435741 ], [ 28.73290287, 36.67683137 ], [ 27.64118656, 36.65882213 ], [ 27.04876794, 37.65336091 ], [ 26.31821821, 38.20813325 ], [ 26.80470015, 38.9857602 ], [ 26.17078535, 39.46361217 ], [ 27.28001997, 40.42001374 ], [ 28.81997765, 40.4600113 ], [ 29.2400037, 41.21999075 ], [ 31.14593387, 41.08762157 ], [ 32.34797936, 41.73626415 ], [ 33.51328291, 42.01896007 ], [ 35.16770389, 42.04022492 ], [ 36.91312707, 41.33535838 ], [ 38.34766483, 40.94858613 ], [ 39.51260664, 41.10276276 ], [ 40.37343265, 41.01367259 ], [ 41.5540841, 41.53565624 ], [ 41.70317061, 41.96294282 ], [ 41.45347009, 42.6451234 ], [ 40.87546919, 43.01362804 ], [ 40.32139448, 43.12863394 ], [ 39.95500858, 43.43499767 ], [ 40.07696496, 43.55310415 ], [ 40.92218469, 43.38215851 ], [ 42.39439457, 43.22030793 ], [ 43.75601688, 42.74082815 ], [ 43.93119999, 42.55497386 ], [ 44.53762292, 42.7119927 ], [ 45.47027917, 42.50278067 ], [ 45.77641035, 42.09244396 ], [ 46.4049508, 41.86067516 ], [ 46.4049508, 41.86067516 ], [ 46.68607059, 41.82713715 ], [ 47.37331546, 41.21973237 ], [ 47.81566572, 41.15141612 ], [ 47.98728316, 41.4058192 ], [ 48.58435265, 41.80886953 ], [ 49.11026371, 41.28228669 ], [ 49.61891483, 40.5729243 ], [ 50.08482954, 40.52615713 ], [ 50.39282108, 40.25656118 ], [ 49.5692021, 40.17610098 ], [ 49.39525923, 39.39948172 ], [ 49.22322839, 39.04921886 ], [ 48.85653242, 38.81548636 ], [ 48.88324914, 38.32024527 ], [ 48.8289189, 38.30935891 ], [ 48.88324914, 38.32024527 ], [ 49.19961226, 37.58287425 ], [ 50.14777144, 37.37456656 ], [ 50.84235436, 36.87281424 ], [ 52.26402469, 36.70042166 ], [ 53.82578983, 36.96503083 ], [ 53.92159793, 37.19891836 ], [ 53.7355111, 37.90613618 ], [ 53.88092858, 38.952093 ], [ 53.10102787, 39.29057364 ], [ 53.35780806, 39.97528636 ], [ 52.69397261, 40.03362906 ], [ 52.91525109, 40.87652334 ], [ 53.85813928, 40.63103445 ], [ 54.73684533, 40.95101492 ], [ 54.00831099, 41.55121084 ], [ 53.72171349, 42.12319143 ], [ 52.91674971, 41.86811656 ], [ 52.81468876, 41.13537059 ], [ 52.50245975, 41.78331554 ], [ 52.5196365, 41.79625033 ], [ 52.50245975, 41.78331554 ], [ 52.44633915, 42.02715078 ], [ 52.69211226, 42.44389537 ], [ 52.50142622, 42.79229788 ], [ 51.3424272, 43.13297476 ], [ 50.89129195, 44.03103364 ], [ 50.33912927, 44.28401561 ], [ 50.30564294, 44.60983552 ], [ 51.27850345, 44.51485423 ], [ 51.31689904, 45.24599824 ], [ 52.16738976, 45.40839143 ], [ 53.0408765, 45.25904654 ], [ 53.22086551, 46.2346459 ], [ 53.04273685, 46.85300609 ], [ 52.04202274, 46.80463695 ], [ 51.19194543, 47.04870474 ], [ 50.03408329, 46.60898998 ], [ 49.10116, 46.39933 ], [ 48.593241, 46.56103425 ], [ 48.69473351, 47.07562816 ], [ 48.05725305, 47.74375275 ], [ 47.31523115, 47.71584748 ], [ 46.46644575, 48.39415233 ], [ 47.0436715, 49.15203889 ], [ 46.75159631, 49.35600576 ], [ 47.54948042, 50.45469839 ], [ 48.57784142, 49.87475963 ], [ 48.70238163, 50.60512849 ], [ 50.76664839, 51.69276236 ], [ 52.32872359, 51.71865225 ], [ 54.53287845, 51.02623973 ], [ 55.71694055, 50.62171662 ], [ 56.77796105, 51.04355134 ], [ 58.36329064, 51.06365347 ], [ 59.64228234, 50.54544221 ], [ 59.93280724, 50.84219412 ], [ 61.33742435, 50.79907014 ], [ 61.58800337, 51.2726588 ], [ 59.96753381, 51.96042044 ], [ 60.92726851, 52.44754833 ], [ 60.73999312, 52.71998648 ], [ 61.6999862, 52.97999645 ], [ 60.97806644, 53.66499339 ], [ 61.43659142, 54.00626455 ], [ 65.17853356, 54.35422781 ], [ 65.66687585, 54.60126699 ], [ 68.16910038, 54.97039175 ], [ 69.06816695, 55.38525015 ], [ 70.86526655, 55.16973359 ], [ 71.18013106, 54.13328522 ], [ 72.22415002, 54.37665538 ], [ 73.50851607, 54.03561677 ], [ 73.42567875, 53.48981029 ], [ 74.38484501, 53.54686107 ], [ 76.89110029, 54.4905244 ], [ 76.52517948, 54.17700349 ], [ 77.80091556, 53.40441498 ], [ 80.03555952, 50.86475088 ], [ 80.56844689, 51.38833649 ], [ 81.94598555, 50.81219595 ], [ 83.38300378, 51.06918285 ], [ 83.93511478, 50.88924551 ], [ 84.41637739, 50.31139964 ], [ 85.11555952, 50.11730296 ], [ 85.54126997, 49.69285859 ], [ 86.82935672, 49.82667471 ], [ 87.35997033, 49.21498078 ], [ 87.75126428, 49.29719798 ], [ 87.75126428, 49.29719798 ], [ 88.80556685, 49.47052074 ], [ 90.71366743, 50.33181184 ], [ 92.23471154, 50.80217072 ], [ 93.10421919, 50.49529023 ], [ 94.14756636, 50.48053661 ], [ 94.81594933, 50.01343334 ], [ 95.81402795, 49.97746654 ], [ 97.25972782, 49.7260607 ], [ 98.23176151, 50.42240062 ], [ 97.82573978, 51.01099518 ], [ 98.86149051, 52.04736603 ], [ 99.98173221, 51.63400625 ], [ 100.88948042, 51.51685578 ], [ 102.06522261, 51.25992056 ], [ 102.25590864, 50.51056061 ], [ 103.67654544, 50.08996613 ], [ 104.62155236, 50.27532949 ], [ 105.88659142, 50.40601919 ], [ 106.88880415, 50.27429597 ], [ 107.8681759, 49.79370515 ], [ 108.47516727, 49.28254772 ], [ 109.40244917, 49.29296052 ], [ 110.66201053, 49.13012808 ], [ 111.58123091, 49.37796825 ], [ 112.8977397, 49.54356538 ], [ 114.3624565, 50.24830272 ], [ 114.96210982, 50.1402473 ], [ 115.48569543, 49.80517731 ], [ 116.6788009, 49.8885314 ], [ 116.6788009, 49.8885314 ], [ 117.87924442, 49.51098338 ], [ 119.28846073, 50.1428828 ], [ 119.27936568, 50.58290762 ], [ 120.1820496, 51.64356639 ], [ 120.73819136, 51.9641153 ], [ 120.72578902, 52.5162263 ], [ 120.17708866, 52.75388622 ], [ 121.00308475, 53.25140107 ], [ 122.24574792, 53.43172598 ], [ 123.57150679, 53.45880443 ], [ 125.0682113, 53.16104483 ], [ 125.94634891, 52.79279857 ], [ 126.56439904, 51.78425548 ], [ 126.93915653, 51.35389415 ], [ 127.28745568, 50.73979727 ], [ 127.65740726, 49.76027049 ], [ 129.39781782, 49.44060008 ], [ 130.58229333, 48.7296874 ], [ 130.98728153, 47.79013235 ], [ 132.50667199, 47.78896963 ], [ 133.37359582, 48.18344168 ], [ 135.02631148, 48.47822989 ], [ 134.50081384, 47.57843985 ], [ 134.1123621, 47.21246735 ], [ 133.769644, 46.11692699 ], [ 133.09712691, 45.14406647 ], [ 131.88345422, 45.32116161 ], [ 131.02521203, 44.96795319 ], [ 131.28855513, 44.11151968 ], [ 131.14468794, 42.92998973 ], [ 130.63386641, 42.90301463 ], [ 130.6400159, 42.39500947 ], [ 130.6400159, 42.39500947 ], [ 130.78000736, 42.22000723 ], [ 130.40003055, 42.28000357 ], [ 129.96594852, 41.94136791 ], [ 129.6673621, 41.60110444 ], [ 129.70518924, 40.88282787 ], [ 129.18811486, 40.66180777 ], [ 129.01039961, 40.4854361 ], [ 128.63336836, 40.18984691 ], [ 127.96741418, 40.0254125 ], [ 127.5334355, 39.75685008 ], [ 127.50211958, 39.32393077 ], [ 127.3854342, 39.2134724 ], [ 127.78334273, 39.05089834 ], [ 128.34971642, 38.61224295 ], [ 129.21291955, 37.43239248 ], [ 129.46044966, 36.78418915 ], [ 129.46830448, 35.63214061 ], [ 129.09137658, 35.08248424 ], [ 128.18585046, 34.8903771 ], [ 127.3865194, 34.47567373 ], [ 126.48574751, 34.39004588 ], [ 126.37391971, 34.93456045 ], [ 126.5592314, 35.68454051 ], [ 126.1173979, 36.72548473 ], [ 126.86014326, 36.89392406 ], [ 126.17475874, 37.74968578 ], [ 125.68910363, 37.94001008 ], [ 125.56843916, 37.75208873 ], [ 125.27533044, 37.66907054 ], [ 125.24008711, 37.85722443 ], [ 124.98103316, 37.94882091 ], [ 124.71216068, 38.10834606 ], [ 124.98599409, 38.54847423 ], [ 125.22194868, 38.66585725 ], [ 125.13285851, 38.84855927 ], [ 125.3865898, 39.38795787 ], [ 125.32111576, 39.55138459 ], [ 124.73748213, 39.66034435 ], [ 124.26562463, 39.92849335 ], [ 122.86757043, 39.63778758 ], [ 122.13138797, 39.17045177 ], [ 121.05455448, 38.89747101 ], [ 121.58599491, 39.36085358 ], [ 121.37675703, 39.75026134 ], [ 122.16859501, 40.42244253 ], [ 121.64035851, 40.94638988 ], [ 120.76862878, 40.59338817 ], [ 119.63960209, 39.89805594 ], [ 119.02346398, 39.25233308 ], [ 118.04274865, 39.20427399 ], [ 117.53270226, 38.73763581 ], [ 118.05969852, 38.06147553 ], [ 118.87814986, 37.89732534 ], [ 118.91163618, 37.44846385 ], [ 119.70280236, 37.15638866 ], [ 120.82345747, 37.87042776 ], [ 121.71125858, 37.48112336 ], [ 122.35793745, 37.45448416 ], [ 122.51999474, 36.93061433 ], [ 121.10416385, 36.65132905 ], [ 120.63700891, 36.11143952 ], [ 119.6645618, 35.60979055 ], [ 119.15120812, 34.90985912 ], [ 120.22752486, 34.36033194 ], [ 120.62036909, 33.37672272 ], [ 121.22901411, 32.46031871 ], [ 121.90814579, 31.69217438 ], [ 121.89191939, 30.94935151 ], [ 121.26425744, 30.6762674 ], [ 121.50351932, 30.14291494 ], [ 122.09211389, 29.83252045 ], [ 121.93842818, 29.01802237 ], [ 121.68443851, 28.2255126 ], [ 121.12566125, 28.13567312 ], [ 120.39547326, 27.0532069 ], [ 119.58549686, 25.74078054 ], [ 118.65687137, 24.54739086 ], [ 117.28160648, 23.62450145 ], [ 115.8907353, 22.78287324 ], [ 114.76382735, 22.66807404 ], [ 114.15254683, 22.22376008 ], [ 113.80677982, 22.54833975 ], [ 113.24107792, 22.0513675 ], [ 111.84359216, 21.55049368 ], [ 110.78546553, 21.39714387 ], [ 110.44403934, 20.34103262 ], [ 109.88986128, 20.28245738 ], [ 109.62765506, 21.00822704 ], [ 109.86448815, 21.39505097 ], [ 108.52281294, 21.71521231 ], [ 108.05018029, 21.55237987 ], [ 106.71506799, 20.69685069 ], [ 105.88168216, 19.75205048 ], [ 105.66200565, 19.05816519 ], [ 106.42681685, 18.004121 ], [ 107.36195357, 16.69745657 ], [ 108.26949507, 16.07974234 ], [ 108.87710656, 15.27669058 ], [ 109.33526981, 13.42602835 ], [ 109.20013594, 11.66685924 ], [ 108.36613, 11.00832062 ], [ 107.22092858, 10.36448395 ], [ 106.40511275, 9.53083975 ], [ 105.15826379, 8.59975963 ], [ 104.79518517, 9.24103832 ], [ 105.07620161, 9.91849051 ], [ 104.33433475, 10.48654369 ], [ 103.4972799, 10.63255545 ], [ 103.09068973, 11.15366059 ], [ 102.58493249, 12.18659496 ], [ 101.68715783, 12.64574006 ], [ 100.83180952, 12.62708487 ], [ 100.97846724, 13.41272167 ], [ 100.09779748, 13.40685639 ], [ 100.01873254, 12.30700104 ], [ 99.47892053, 10.84636669 ], [ 99.15377241, 9.96306143 ], [ 99.22239872, 9.23925548 ], [ 99.87383182, 9.20786205 ], [ 100.27964684, 8.2951529 ], [ 100.45927412, 7.42957266 ], [ 101.01732792, 6.8568686 ], [ 101.62307905, 6.74062246 ], [ 102.14118696, 6.22163605 ] ] ], [ [ [ 110.33918786, 18.67839509 ], [ 109.47520959, 18.19770091 ], [ 108.65520796, 18.50768199 ], [ 108.62621748, 19.36788789 ], [ 109.11905562, 19.82103852 ], [ 110.21159875, 20.10125397 ], [ 110.78655073, 20.07753449 ], [ 111.0100513, 19.69592988 ], [ 110.5706466, 19.25587922 ], [ 110.33918786, 18.67839509 ] ] ] ] } } -------------------------------------------------------------------------------- /test/harness/australia.geojson: -------------------------------------------------------------------------------- 1 | { "type": "Feature", "properties": { "scalerank": 1, "featurecla": "Admin-0 country", "labelrank": 2, "sovereignt": "Australia", "sov_a3": "AU1", "adm0_dif": 1, "level": 2, "type": "Country", "admin": "Australia", "adm0_a3": "AUS", "geou_dif": 0, "geounit": "Australia", "gu_a3": "AUS", "su_dif": 0, "subunit": "Australia", "su_a3": "AUS", "brk_diff": 0, "name": "Australia", "name_long": "Australia", "brk_a3": "AUS", "brk_name": "Australia", "brk_group": null, "abbrev": "Auz.", "postal": "AU", "formal_en": "Commonwealth of Australia", "formal_fr": null, "note_adm0": null, "note_brk": null, "name_sort": "Australia", "name_alt": null, "mapcolor7": 1, "mapcolor8": 2, "mapcolor9": 2, "mapcolor13": 7, "pop_est": 21262641, "gdp_md_est": 800200.0, "pop_year": -99, "lastcensus": 2006, "gdp_year": -99, "economy": "2. Developed region: nonG7", "income_grp": "1. High income: OECD", "wikipedia": -99, "fips_10": null, "iso_a2": "AU", "iso_a3": "AUS", "iso_n3": "036", "un_a3": "036", "wb_a2": "AU", "wb_a3": "AUS", "woe_id": -99, "adm0_a3_is": "AUS", "adm0_a3_us": "AUS", "adm0_a3_un": -99, "adm0_a3_wb": -99, "continent": "Oceania", "region_un": "Oceania", "subregion": "Australia and New Zealand", "region_wb": "East Asia & Pacific", "name_len": 9, "long_len": 9, "abbrev_len": 4, "tiny": -99, "homepart": 1, "filename": "AUS.geojson" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 145.397978143494839, -40.792548516605891 ], [ 146.364120721623721, -41.137695407883342 ], [ 146.908583612250851, -41.000546156580683 ], [ 147.689259474884153, -40.808258152022688 ], [ 148.289067824496016, -40.87543751400213 ], [ 148.359864536735842, -42.062445163746439 ], [ 148.017301467073111, -42.407023614268624 ], [ 147.914051955353813, -43.211522312188485 ], [ 147.564564243764011, -42.937688897473862 ], [ 146.87034305235494, -43.634597263362096 ], [ 146.663327264593676, -43.580853773778557 ], [ 146.048377720320417, -43.549744561538887 ], [ 145.431929559510564, -42.693776137056268 ], [ 145.295090366801702, -42.033609714527557 ], [ 144.718071323830628, -41.162551771815714 ], [ 144.743754510679679, -40.703975111657712 ], [ 145.397978143494839, -40.792548516605891 ] ] ], [ [ [ 143.561811151299963, -13.763655694232213 ], [ 143.922099237238939, -14.548310642152003 ], [ 144.563713820574861, -14.171176039285882 ], [ 144.894908075133543, -14.594457696188625 ], [ 145.374723748963476, -14.984976495018286 ], [ 145.271991001567272, -15.428205254785695 ], [ 145.485259637635778, -16.285672295804773 ], [ 145.637033319276952, -16.784918308176614 ], [ 145.888904250267672, -16.90692636481765 ], [ 146.160308872664501, -17.761654554925244 ], [ 146.06367394427869, -18.280072523677319 ], [ 146.387478469019612, -18.958274021075908 ], [ 147.471081577747924, -19.480722751546679 ], [ 148.177601760042506, -19.955939222902771 ], [ 148.848413527623222, -20.391209812097259 ], [ 148.717465448195611, -20.633468926681516 ], [ 149.289420200802056, -21.260510756111103 ], [ 149.678337030230665, -22.342511895438392 ], [ 150.077382440388618, -22.122783705333319 ], [ 150.482939081015161, -22.556142266533012 ], [ 150.727265252891215, -22.402404880464658 ], [ 150.899554478152282, -23.462236830338682 ], [ 151.60917524638424, -24.076256198830762 ], [ 152.073539666959078, -24.457886651306197 ], [ 152.855197381805937, -25.267501316023015 ], [ 153.13616214417678, -26.07117319102619 ], [ 153.161948683890415, -26.641319268502443 ], [ 153.092908970348589, -27.26029957449451 ], [ 153.569469028944212, -28.110066827102099 ], [ 153.512108189100218, -28.995077406532758 ], [ 153.339095493787056, -29.458201592732447 ], [ 153.069241164358885, -30.350240166954816 ], [ 153.089601678681788, -30.923641859665448 ], [ 152.891577590139406, -31.640445651985956 ], [ 152.450002476205356, -32.550002536755244 ], [ 151.709117466436822, -33.041342054986345 ], [ 151.343971795862416, -33.816023451473853 ], [ 151.01055545471516, -34.310360202777886 ], [ 150.714139439089053, -35.17345997491681 ], [ 150.328219842733262, -35.67187916437193 ], [ 150.075212030232279, -36.420205580390508 ], [ 149.946124302367167, -37.109052422841231 ], [ 149.997283970336156, -37.425260512035138 ], [ 149.423882277625552, -37.772681166333463 ], [ 148.304622430615922, -37.809061374666882 ], [ 147.381733026315288, -38.21921721776755 ], [ 146.922122837511353, -38.606532077795123 ], [ 146.317921991154805, -39.03575652441144 ], [ 145.489652134380577, -38.59376799901905 ], [ 144.876976353128157, -38.417448012039117 ], [ 145.03221235573298, -37.896187839510986 ], [ 144.485682407814039, -38.085323581699271 ], [ 143.609973586196105, -38.809465427405328 ], [ 142.745426873952994, -38.538267510737526 ], [ 142.178329705982009, -38.380034275059842 ], [ 141.606581659104705, -38.308514092767879 ], [ 140.63857872941324, -38.019332777662555 ], [ 139.992158237874349, -37.402936293285109 ], [ 139.806588169514072, -36.643602797188279 ], [ 139.574147577065247, -36.138362318670673 ], [ 139.082808058834104, -35.732754001611781 ], [ 138.120747918856324, -35.612296237939404 ], [ 138.449461704665026, -35.127261244447894 ], [ 138.207564325106688, -34.384722588845932 ], [ 137.719170363516156, -35.076825046531027 ], [ 136.829405552314739, -35.260534763328621 ], [ 137.352371047108505, -34.707338555644093 ], [ 137.503886346588359, -34.130267836240776 ], [ 137.890116001537677, -33.640478610978334 ], [ 137.81032759007914, -32.900007012668112 ], [ 136.996837192940376, -33.752771498348636 ], [ 136.37206912653167, -34.094766127256193 ], [ 135.989043410384369, -34.890118096660487 ], [ 135.208212518454133, -34.478670342752608 ], [ 135.239218377829161, -33.947953383114978 ], [ 134.613416782774607, -33.222778008763143 ], [ 134.085903761939136, -32.848072198214766 ], [ 134.273902622617044, -32.617233575166964 ], [ 132.990776808809841, -32.011224053680195 ], [ 132.288080682504898, -31.982646986622768 ], [ 131.326330601120929, -31.495803318001048 ], [ 129.535793898639696, -31.590422865527483 ], [ 128.240937534702226, -31.948488864877856 ], [ 127.10286746633831, -32.282266941051049 ], [ 126.148713820501158, -32.215966078420607 ], [ 125.088623488465615, -32.728751316052836 ], [ 124.221647983904944, -32.959486586236068 ], [ 124.028946567888539, -33.483847344701715 ], [ 123.659666782730724, -33.890179131812729 ], [ 122.811036411633637, -33.914467054989842 ], [ 122.183064406422858, -34.003402194964224 ], [ 121.299190708502607, -33.821036065406133 ], [ 120.580268182458141, -33.930176690406626 ], [ 119.893695103028236, -33.976065362281815 ], [ 119.298899367348795, -34.509366143533967 ], [ 119.007340936358005, -34.464149265278536 ], [ 118.505717808100798, -34.7468193499151 ], [ 118.02497195848953, -35.064732761374714 ], [ 117.295507440257467, -35.025458672832869 ], [ 116.625109084134948, -35.025096937806829 ], [ 115.564346958479717, -34.386427911111554 ], [ 115.026808709779544, -34.196517022438925 ], [ 115.048616164206791, -33.623425388322033 ], [ 115.545123325667106, -33.487257989232958 ], [ 115.714673700016675, -33.259571628554951 ], [ 115.679378696761404, -32.900368747694131 ], [ 115.801645135563973, -32.205062351207033 ], [ 115.689610630355133, -31.612437025683789 ], [ 115.160909051576965, -30.601594333622462 ], [ 114.997043084779449, -30.030724786094165 ], [ 115.040037876446277, -29.461095472940798 ], [ 114.641974318502008, -28.810230808224713 ], [ 114.61649783738217, -28.516398614213042 ], [ 114.173579136208474, -28.118076674107328 ], [ 114.048883905088161, -27.334765313427127 ], [ 113.477497593236905, -26.543134047147902 ], [ 113.338953078262506, -26.116545098578484 ], [ 113.77835778204026, -26.549025160429181 ], [ 113.440962355606615, -25.621278171493156 ], [ 113.936901076311671, -25.911234633082884 ], [ 114.232852004047317, -26.298446140245872 ], [ 114.216160516417034, -25.786281019801105 ], [ 113.721255324357713, -24.998938897402127 ], [ 113.625343866024053, -24.683971042583153 ], [ 113.393523390762667, -24.384764499613269 ], [ 113.502043898575636, -23.806350192970257 ], [ 113.706992629045175, -23.560215345964068 ], [ 113.843418410295698, -23.059987481378737 ], [ 113.7365515483161, -22.475475355725379 ], [ 114.149756300921894, -21.755881036061012 ], [ 114.225307244932679, -22.517488295178634 ], [ 114.647762078918689, -21.829519952076904 ], [ 115.460167270979326, -21.495173435148544 ], [ 115.947372674627019, -21.068687839443712 ], [ 116.711615431791557, -20.70168181730682 ], [ 117.166316359527713, -20.623598728113805 ], [ 117.441545037914267, -20.746898695562162 ], [ 118.22955895393298, -20.374208265873236 ], [ 118.836085239742729, -20.263310642174829 ], [ 118.987807244951767, -20.044202569257322 ], [ 119.252493931150653, -19.952941989829839 ], [ 119.805225050944571, -19.976506442954985 ], [ 120.856220330896662, -19.683707777589191 ], [ 121.399856398607227, -19.239755547769732 ], [ 121.65513797412909, -18.705317885007133 ], [ 122.241665480641771, -18.197648614171769 ], [ 122.286623976735669, -17.798603204013915 ], [ 122.312772251475437, -17.254967136303449 ], [ 123.012574497571933, -16.405199883695857 ], [ 123.433789097183038, -17.268558037996225 ], [ 123.859344517106621, -17.069035332917252 ], [ 123.50324222218326, -16.596506036040367 ], [ 123.817073195491929, -16.111316013251994 ], [ 124.258286574399875, -16.327943617419564 ], [ 124.379726190285822, -15.567059828353976 ], [ 124.926152785340051, -15.075100192935324 ], [ 125.167275018413889, -14.680395603090004 ], [ 125.670086704613851, -14.510070082256021 ], [ 125.685796340030507, -14.230655612853838 ], [ 126.12514936737611, -14.347340996968953 ], [ 126.142822707219892, -14.095986830301213 ], [ 126.582589146023764, -13.95279143642041 ], [ 127.065867140817346, -13.817967624570926 ], [ 127.804633416861947, -14.276906019755046 ], [ 128.359689976108967, -14.869169610252257 ], [ 128.985543247595928, -14.875990899314742 ], [ 129.621473423379626, -14.969783623924556 ], [ 129.409600050982988, -14.420669854391035 ], [ 129.888640578328619, -13.618703301653483 ], [ 130.339465773642956, -13.357375583553477 ], [ 130.183506300986011, -13.107520033422304 ], [ 130.617795037966999, -12.536392103732467 ], [ 131.223494500860028, -12.183648776908115 ], [ 131.735091180549517, -12.302452894747162 ], [ 132.575298293183124, -12.114040622611014 ], [ 132.55721154188106, -11.603012383676685 ], [ 131.824698114143672, -11.273781833545101 ], [ 132.357223748911423, -11.128519382372644 ], [ 133.019560581596437, -11.376411228076847 ], [ 133.550845981989056, -11.786515394745138 ], [ 134.393068475482011, -12.042365411022175 ], [ 134.67863244032705, -11.9411829565947 ], [ 135.298491245668032, -12.248606052299051 ], [ 135.882693312727639, -11.962266940969798 ], [ 136.258380975489473, -12.049341729381609 ], [ 136.492475213771655, -11.857208754120393 ], [ 136.951620314685016, -12.351958916882737 ], [ 136.685124953355768, -12.887223402562057 ], [ 136.305406528875125, -13.291229750219898 ], [ 135.961758254134139, -13.324509372615893 ], [ 136.077616815332561, -13.724278252825783 ], [ 135.783836297753254, -14.223989353088214 ], [ 135.428664178611228, -14.7154322241839 ], [ 135.500184360903177, -14.997740573794429 ], [ 136.295174595281367, -15.550264987859123 ], [ 137.065360142159506, -15.870762220933356 ], [ 137.580470819244823, -16.215082289294084 ], [ 138.303217401278999, -16.807604261952658 ], [ 138.585164015863398, -16.806622409739177 ], [ 139.108542922115504, -17.062679131745369 ], [ 139.260574985918225, -17.371600843986187 ], [ 140.215245396078302, -17.710804945550066 ], [ 140.87546349503927, -17.369068698803943 ], [ 141.071110467696286, -16.832047214426723 ], [ 141.274095493738827, -16.388870131091608 ], [ 141.398222284103809, -15.840531508042588 ], [ 141.702183058844668, -15.04492115647693 ], [ 141.563380161708693, -14.56133310308951 ], [ 141.635520461188122, -14.270394789286284 ], [ 141.519868605718983, -13.698078301653808 ], [ 141.650920038011009, -12.944687595270565 ], [ 141.842691278246235, -12.74154753993119 ], [ 141.686990187750808, -12.407614434461138 ], [ 141.928629185147571, -11.87746591557878 ], [ 142.118488397388006, -11.328042087451619 ], [ 142.143706496346368, -11.042736504768143 ], [ 142.515260044524979, -10.668185723516643 ], [ 142.797310011974076, -11.157354831591519 ], [ 142.866763136974299, -11.784706719614931 ], [ 143.115946893485699, -11.905629571177911 ], [ 143.158631626558787, -12.325655612846191 ], [ 143.522123651299893, -12.834358412327433 ], [ 143.597157830987697, -13.400422051652598 ], [ 143.561811151299963, -13.763655694232213 ] ] ] ] } } 2 | 3 | -------------------------------------------------------------------------------- /test/harness/multipolygon.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": { 5 | "type": "MultiPolygon", 6 | "coordinates": [ 7 | [ 8 | [ 9 | [102.0, 2.0], [103.0, 2.0], [103.0, 3.0], [102.0, 3.0], [102.0, 2.0] 10 | ] 11 | ], 12 | [ 13 | [ 14 | [100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0] 15 | ] 16 | ] 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/harness/polygon.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": { 5 | "type": "Polygon", 6 | "coordinates": [ 7 | [ 8 | [ 9 | 14.414062499999998, 10 | 12.897489183755892 11 | ], 12 | [ 13 | 3.515625, 14 | 8.754794702435618 15 | ], 16 | [ 17 | 7.734374999999999, 18 | -1.4061088354351594 19 | ], 20 | [ 21 | 11.6015625, 22 | 4.915832801313164 23 | ], 24 | [ 25 | 10.8984375, 26 | -9.79567758282973 27 | ], 28 | [ 29 | 14.414062499999998, 30 | -3.162455530237848 31 | ], 32 | [ 33 | 18.6328125, 34 | 2.1088986592431382 35 | ], 36 | [ 37 | 16.5234375, 38 | 5.61598581915534 39 | ], 40 | [ 41 | 19.335937499999996, 42 | 8.754794702435618 43 | ], 44 | [ 45 | 28.4765625, 46 | 13.239945499286312 47 | ], 48 | [ 49 | 24.960937499999996, 50 | 16.636191878397664 51 | ], 52 | [ 53 | 22.148437499999996, 54 | 13.239945499286312 55 | ], 56 | [ 57 | 20.390625, 58 | 19.973348786110602 59 | ], 60 | [ 61 | 18.28125, 62 | 10.833305983642491 63 | ], 64 | [ 65 | 14.414062499999998, 66 | 12.897489183755892 67 | ] 68 | ] 69 | ] 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /test/harness/polygonCollection.geojson: -------------------------------------------------------------------------------- 1 | { 2 | "type": "Feature", 3 | "properties": {}, 4 | "geometry": { 5 | "type": "MultiPolygon", 6 | "coordinates": [ 7 | [ 8 | [ 9 | [ 10 | 14.414062499999998, 11 | 12.897489183755892 12 | ], 13 | [ 14 | 3.515625, 15 | 8.754794702435618 16 | ], 17 | [ 18 | 7.734374999999999, 19 | -1.4061088354351594 20 | ], 21 | [ 22 | 11.6015625, 23 | 4.915832801313164 24 | ], 25 | [ 26 | 10.8984375, 27 | -9.79567758282973 28 | ], 29 | [ 30 | 14.414062499999998, 31 | -3.162455530237848 32 | ], 33 | [ 34 | 18.6328125, 35 | 2.1088986592431382 36 | ], 37 | [ 38 | 16.5234375, 39 | 5.61598581915534 40 | ], 41 | [ 42 | 19.335937499999996, 43 | 8.754794702435618 44 | ], 45 | [ 46 | 28.4765625, 47 | 13.239945499286312 48 | ], 49 | [ 50 | 24.960937499999996, 51 | 16.636191878397664 52 | ], 53 | [ 54 | 22.148437499999996, 55 | 13.239945499286312 56 | ], 57 | [ 58 | 20.390625, 59 | 19.973348786110602 60 | ], 61 | [ 62 | 18.28125, 63 | 10.833305983642491 64 | ], 65 | [ 66 | 14.414062499999998, 67 | 12.897489183755892 68 | ] 69 | ] 70 | ], 71 | [ 72 | [ 73 | [ 74 | 24.2578125, 75 | 4.565473550710278 76 | ], 77 | [ 78 | 26.015625, 79 | -2.811371193331128 80 | ], 81 | [ 82 | 21.09375, 83 | -9.102096738726443 84 | ], 85 | [ 86 | 22.5, 87 | -14.944784875088372 88 | ], 89 | [ 90 | 25.3125, 91 | -8.754794702435618 92 | ], 93 | [ 94 | 30.585937499999996, 95 | -10.141931686131018 96 | ], 97 | [ 98 | 42.5390625, 99 | -5.266007882805485 100 | ], 101 | [ 102 | 48.1640625, 103 | 21.94304553343818 104 | ], 105 | [ 106 | 24.2578125, 107 | 4.565473550710278 108 | ] 109 | ] 110 | ] 111 | ] 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/setStructures.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import VisibilityGraph from '../src/VisibilityGraph' 3 | import { setupStructure } from '../src/setupStructure' 4 | import loadJsonFile from 'load-json-file' 5 | import path from 'path' 6 | 7 | const poly = loadJsonFile.sync(path.join(__dirname, 'harness', 'polygon.geojson')) 8 | const lengthUniqueCoords = poly.geometry.coordinates[0].length - 1 9 | const vg = new VisibilityGraph() 10 | 11 | setupStructure(vg, poly) 12 | 13 | test('Polygon has right edges & points length', t => { 14 | t.is(vg._edges.length, 14) 15 | t.is(vg._points.length, lengthUniqueCoords) 16 | t.is(vg._polygons[0].edges.length, 14) 17 | }) 18 | 19 | test('Points each have edges', t => { 20 | for (var i = 0; i < vg._points.length; i++) { 21 | t.not(vg._points[i].prevPoint, null) 22 | t.not(vg._points[i].nextPoint, null) 23 | t.is(vg._points[i].edges.length, 2) 24 | } 25 | }) 26 | 27 | const poly2 = loadJsonFile.sync(path.join(__dirname, 'harness', 'multipolygon.geojson')) 28 | const vg2 = new VisibilityGraph() 29 | 30 | setupStructure(vg2, poly2) 31 | 32 | test('MultiPolygon has right edges & points length', t => { 33 | t.is(vg2._edges.length, 12) 34 | t.is(vg2._points.length, 12) 35 | t.is(vg2._polygons[0].edges.length, 4) 36 | t.is(vg2._polygons[1].edges.length, 8) 37 | }) 38 | 39 | test('MultiPoly - Points each have edges and next and prev points', t => { 40 | for (var i = 0; i < vg2._points.length; i++) { 41 | t.not(vg2._points[i].prevPoint, null) 42 | t.not(vg2._points[i].nextPoint, null) 43 | t.is(vg2._points[i].edges.length, 2) 44 | } 45 | }) 46 | 47 | test('MultiPoly - edges all have points', t => { 48 | for (var i = 0; i < vg2._edges.length; i++) { 49 | t.not(vg2._edges[i].p1, null) 50 | t.not(vg2._edges[i].p2, null) 51 | } 52 | }) 53 | -------------------------------------------------------------------------------- /test/sort.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Point from '../src/Point' 3 | import { sortPoints } from '../src/createGraphFromGeoJson' 4 | 5 | 6 | // sort the vertices of the obstacle polygons according to the 7 | // counter clock wise angle the half line from v to each vertex makes 8 | // with the x-axis. In the case of ties, vertices closer to v should 9 | // come first. 10 | test('Edge test', t => { 11 | var p1 = new Point([0, 0], -1) 12 | var p2 = new Point([-10, -10], -1) 13 | var p3 = new Point([10, -10], -1) 14 | var p4 = new Point([10, 10], -1) 15 | 16 | const points = [p1, p2, p3, p4] 17 | const clonedPoints = points.slice(0) 18 | sortPoints(p1, clonedPoints) 19 | console.log(clonedPoints.map(p => p.nodeId)) 20 | t.deepEqual([p2, p3, p4], clonedPoints) 21 | }) -------------------------------------------------------------------------------- /test/utils.spec.js: -------------------------------------------------------------------------------- 1 | import test from 'ava' 2 | import Edge from '../src/Edge' 3 | import Point from '../src/Point' 4 | import { edgeIntersect, pointEdgeDistance, intersectPoint } from '../src/utils' 5 | 6 | test('edgeIntersect test', t => { 7 | var p1 = new Point([0, 0], -1) 8 | var p2 = new Point([0, 1], -1) 9 | var p3 = new Point([0, 2], -1) 10 | var p4 = new Point([2, 2], -1) 11 | 12 | var e1 = new Edge(p1, p2) 13 | 14 | t.is(edgeIntersect(p1, p2, e1), true) 15 | t.is(edgeIntersect(p2, p1, e1), true) 16 | t.is(edgeIntersect(p3, p4, e1), false) 17 | }) 18 | 19 | test('pointEdgeDistance test', t => { 20 | var p1 = new Point([3, 1], -1) 21 | var p2 = new Point([3, 5], -1) 22 | var p3 = new Point([2, 2], -1) 23 | var p4 = new Point([4, 4], -1) 24 | var p5 = new Point([1, 1], -1) 25 | var p6 = new Point([1, 2], -1) 26 | var p7 = new Point([3, 4], -1) 27 | var p8 = new Point([2, 5], -1) 28 | 29 | var e1 = new Edge(p1, p2) 30 | var e2 = new Edge(p3, p4) 31 | var e3 = new Edge(p5, p2) 32 | const i = intersectPoint(p3, p4, e1) 33 | const ip = new Point([3, 3], -1) 34 | t.is(i.x, ip.x) 35 | t.is(i.y, ip.y) 36 | 37 | const i2 = intersectPoint(p3, p4, e1) 38 | const ip2 = new Point([3, 3], -1) 39 | t.is(i2.x, ip2.x) 40 | t.is(i2.y, ip2.y) 41 | 42 | t.is(pointEdgeDistance(p3, p4, e1), 1.4142135623730951) 43 | t.is(pointEdgeDistance(p1, p2, e2), 2) 44 | t.is(pointEdgeDistance(p6, p7, e3), 1.4142135623730951) 45 | t.is(pointEdgeDistance(p8, p7, e3), 0.9428090415820635) 46 | 47 | }) 48 | --------------------------------------------------------------------------------