├── make_exe.bat
├── exeico.ico
├── .gitignore
├── images
├── bank.png
├── shop.png
├── tent.png
├── castle.png
├── cavern.png
├── cross.png
├── house.png
├── houses.png
├── layers.png
├── building.png
├── layers-2x.png
├── loghouse.png
├── marker-icon.png
├── marker-icon-2x.png
└── marker-shadow.png
├── css
├── L.Control.MousePosition.css
├── toggle.css
└── leaflet.css
├── simple_server.py
├── js
├── L.Control.MousePosition.js
└── leaflet-omnivore.min.js
├── README.md
├── index.html
├── map_reader.py
└── LICENCE
/make_exe.bat:
--------------------------------------------------------------------------------
1 | pyinstaller -F --icon=exeico.ico map_reader.py
2 | pause
--------------------------------------------------------------------------------
/exeico.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/exeico.ico
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | tiles/
2 | .idea
3 | *.pyc
4 | *.pyd
5 | *.spec
6 | dist/
7 | build/
8 | *.exe
--------------------------------------------------------------------------------
/images/bank.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/bank.png
--------------------------------------------------------------------------------
/images/shop.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/shop.png
--------------------------------------------------------------------------------
/images/tent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/tent.png
--------------------------------------------------------------------------------
/images/castle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/castle.png
--------------------------------------------------------------------------------
/images/cavern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/cavern.png
--------------------------------------------------------------------------------
/images/cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/cross.png
--------------------------------------------------------------------------------
/images/house.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/house.png
--------------------------------------------------------------------------------
/images/houses.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/houses.png
--------------------------------------------------------------------------------
/images/layers.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/layers.png
--------------------------------------------------------------------------------
/images/building.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/building.png
--------------------------------------------------------------------------------
/images/layers-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/layers-2x.png
--------------------------------------------------------------------------------
/images/loghouse.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/loghouse.png
--------------------------------------------------------------------------------
/images/marker-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/marker-icon.png
--------------------------------------------------------------------------------
/images/marker-icon-2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/marker-icon-2x.png
--------------------------------------------------------------------------------
/images/marker-shadow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/nicolas-f/7DTD-leaflet/HEAD/images/marker-shadow.png
--------------------------------------------------------------------------------
/css/L.Control.MousePosition.css:
--------------------------------------------------------------------------------
1 | .leaflet-container .leaflet-control-mouseposition {
2 | background-color: rgba(255, 255, 255, 0.7);
3 | box-shadow: 0 0 5px #bbb;
4 | padding: 0 5px;
5 | margin:0;
6 | color: #333;
7 | font: 11px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/simple_server.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import BaseHTTPServer
3 | from SimpleHTTPServer import SimpleHTTPRequestHandler
4 |
5 |
6 | HandlerClass = SimpleHTTPRequestHandler
7 | ServerClass = BaseHTTPServer.HTTPServer
8 | Protocol = "HTTP/1.0"
9 |
10 | if sys.argv[1:]:
11 | port = int(sys.argv[1])
12 | else:
13 | port = 8000
14 | server_address = ('127.0.0.1', port)
15 |
16 | HandlerClass.protocol_version = Protocol
17 | httpd = ServerClass(server_address, HandlerClass)
18 |
19 | sa = httpd.socket.getsockname()
20 | print "Serving HTTP on", sa[0], "port", sa[1], "..."
21 | httpd.serve_forever()
--------------------------------------------------------------------------------
/css/toggle.css:
--------------------------------------------------------------------------------
1 | .menu-ui {
2 | background:#fff;
3 | position:absolute;
4 | bottom:10px;right:10px;
5 | z-index:1;
6 | border-radius:3px;
7 | width:120px;
8 | border:1px solid rgba(0,0,0,0.4);
9 | }
10 | .menu-ui a {
11 | font-size:13px;
12 | color:#404040;
13 | display:block;
14 | margin:0;padding:0;
15 | padding:10px;
16 | text-decoration:none;
17 | border-bottom:1px solid rgba(0,0,0,0.25);
18 | text-align:center;
19 | }
20 | .menu-ui a:first-child {
21 | border-radius:3px 3px 0 0;
22 | }
23 | .menu-ui a:last-child {
24 | border:none;
25 | border-radius:0 0 3px 3px;
26 | }
27 | .menu-ui a:hover {
28 | background:#f8f8f8;
29 | color:#404040;
30 | }
31 | .menu-ui a.active {
32 | background:#3887BE;
33 | color:#FFF;
34 | }
35 | .menu-ui a.active:hover {
36 | background:#3074a4;
37 | }
--------------------------------------------------------------------------------
/js/L.Control.MousePosition.js:
--------------------------------------------------------------------------------
1 | L.Control.MousePosition = L.Control.extend({
2 | options: {
3 | position: 'bottomleft',
4 | separator: ' : ',
5 | emptyString: 'Unavailable',
6 | lngFirst: false,
7 | numDigits: 5,
8 | lngFormatter: undefined,
9 | latFormatter: undefined,
10 | prefix: ""
11 | },
12 |
13 | onAdd: function (map) {
14 | this._container = L.DomUtil.create('div', 'leaflet-control-mouseposition');
15 | L.DomEvent.disableClickPropagation(this._container);
16 | map.on('mousemove', this._onMouseMove, this);
17 | this._container.innerHTML=this.options.emptyString;
18 | return this._container;
19 | },
20 |
21 | onRemove: function (map) {
22 | map.off('mousemove', this._onMouseMove)
23 | },
24 |
25 | _onMouseMove: function (e) {
26 | var lng = this.options.lngFormatter ? this.options.lngFormatter(e.latlng.lng) : L.Util.formatNum(e.latlng.lng, this.options.numDigits);
27 | var lat = this.options.latFormatter ? this.options.latFormatter(e.latlng.lat) : L.Util.formatNum(e.latlng.lat, this.options.numDigits);
28 | var value = this.options.lngFirst ? lng + this.options.separator + lat : lat + this.options.separator + lng;
29 | var prefixAndValue = this.options.prefix + ' ' + value;
30 | this._container.innerHTML = prefixAndValue;
31 | },
32 |
33 | });
34 |
35 | L.Map.mergeOptions({
36 | positionControl: false
37 | });
38 |
39 | L.Map.addInitHook(function () {
40 | if (this.options.positionControl) {
41 | this.positionControl = new L.Control.MousePosition();
42 | this.addControl(this.positionControl);
43 | }
44 | });
45 |
46 | L.control.mousePosition = function (options) {
47 | return new L.Control.MousePosition(options);
48 | };
49 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 7DTD-leaflet
2 |
3 | Merge 7DTD discovered map in html
4 |
5 | The python script map_reader.py will extract dans merge all .map files of a random world.
6 | The result is then saved into png files.
7 |
8 | A javascript code (Leaflet) merge all this png files while browsing the big map.
9 |
10 | 7DTD forum post https://7daystodie.com/forums/showthread.php?14947-Export-discovered-map-to-png
11 |
12 | ## How to use
13 |
14 | ### From source
15 |
16 | Install:
17 |
18 | * Python 2.7 https://www.python.org/downloads/
19 | * Pillow https://pillow.readthedocs.org/en/latest/
20 |
21 | Run map_reader.py by double clicking on this file. A gui will ask you the path of the .map folder. (for me this is in C:\Users\cumu\AppData\Roaming\7DaysToDie\Saves\Random Gen\testalpha\Player)
22 |
23 | ### Using prebuilt binary
24 |
25 | You will find windows binary in the releases. Place the .exe file into the extracted source file (along index.html file)
26 | https://github.com/nicolas-f/7DTD-leaflet/releases
27 |
28 | Then double click on the exe. A gui will ask you the path of the .map folder. (for me this is in C:\Users\cumu\AppData\Roaming\7DaysToDie\Saves\Random Gen\testalpha\Player)
29 |
30 | ### How to view the result
31 |
32 | A sub directory named tiles will be created.
33 |
34 | Open index.html in your browser (Firefox or Chrome).
35 |
36 | Enjoy !
37 |
38 | ## Command line
39 |
40 | You can also use it in command line.
41 |
42 | ```bash
43 | python map_reader.py -g "C:\Users\CUMU\Documents\7 Days To Die\Saves\Random Gen\ver91\Player"
44 | ```
45 |
46 | ### Available parameters:
47 |
48 | ```
49 | -g "C:\\Users..\" The folder that contain .map files
50 | -t "tiles" The folder that will contain tiles (Optional)
51 | -z 8 Zoom level 4-n. Number of tiles to extract around position 0,0 of map. It is in the form of 4^n tiles.It will extract a grid of 2^n*16 tiles on each side.(Optional)
52 | -n Keep track of updates and write the last version of tiles. This will show players bases on map.
53 | ```
54 |
55 | ## Additonnal content
56 |
57 | You can run simple_server.py with python to give access on http://localhost:8000 .
58 |
59 | Remember that python files are under GPLv3 license and then you need to redistribute your modifications.
60 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Ketchu Free Party map
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 | Fork me on GitHub
19 |
20 |
21 |
22 |
23 |
24 |
25 |
142 |
143 |
144 |
--------------------------------------------------------------------------------
/css/leaflet.css:
--------------------------------------------------------------------------------
1 | /* required styles */
2 |
3 | .leaflet-map-pane,
4 | .leaflet-tile,
5 | .leaflet-marker-icon,
6 | .leaflet-marker-shadow,
7 | .leaflet-tile-pane,
8 | .leaflet-tile-container,
9 | .leaflet-overlay-pane,
10 | .leaflet-shadow-pane,
11 | .leaflet-marker-pane,
12 | .leaflet-popup-pane,
13 | .leaflet-overlay-pane svg,
14 | .leaflet-zoom-box,
15 | .leaflet-image-layer,
16 | .leaflet-layer {
17 | position: absolute;
18 | left: 0;
19 | top: 0;
20 | }
21 | .leaflet-container {
22 | overflow: hidden;
23 | -ms-touch-action: none;
24 | }
25 | .leaflet-tile,
26 | .leaflet-marker-icon,
27 | .leaflet-marker-shadow {
28 | -webkit-user-select: none;
29 | -moz-user-select: none;
30 | user-select: none;
31 | -webkit-user-drag: none;
32 | }
33 | .leaflet-marker-icon,
34 | .leaflet-marker-shadow {
35 | display: block;
36 | }
37 | /* map is broken in FF if you have max-width: 100% on tiles */
38 | .leaflet-container img {
39 | max-width: none !important;
40 | }
41 | /* stupid Android 2 doesn't understand "max-width: none" properly */
42 | .leaflet-container img.leaflet-image-layer {
43 | max-width: 15000px !important;
44 | }
45 | .leaflet-tile {
46 | filter: inherit;
47 | visibility: hidden;
48 | }
49 | .leaflet-tile-loaded {
50 | visibility: inherit;
51 | }
52 | .leaflet-zoom-box {
53 | width: 0;
54 | height: 0;
55 | }
56 | /* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
57 | .leaflet-overlay-pane svg {
58 | -moz-user-select: none;
59 | }
60 |
61 | .leaflet-tile-pane { z-index: 2; }
62 | .leaflet-objects-pane { z-index: 3; }
63 | .leaflet-overlay-pane { z-index: 4; }
64 | .leaflet-shadow-pane { z-index: 5; }
65 | .leaflet-marker-pane { z-index: 6; }
66 | .leaflet-popup-pane { z-index: 7; }
67 |
68 | .leaflet-vml-shape {
69 | width: 1px;
70 | height: 1px;
71 | }
72 | .lvml {
73 | behavior: url(#default#VML);
74 | display: inline-block;
75 | position: absolute;
76 | }
77 |
78 |
79 | /* control positioning */
80 |
81 | .leaflet-control {
82 | position: relative;
83 | z-index: 7;
84 | pointer-events: auto;
85 | }
86 | .leaflet-top,
87 | .leaflet-bottom {
88 | position: absolute;
89 | z-index: 1000;
90 | pointer-events: none;
91 | }
92 | .leaflet-top {
93 | top: 0;
94 | }
95 | .leaflet-right {
96 | right: 0;
97 | }
98 | .leaflet-bottom {
99 | bottom: 0;
100 | }
101 | .leaflet-left {
102 | left: 0;
103 | }
104 | .leaflet-control {
105 | float: left;
106 | clear: both;
107 | }
108 | .leaflet-right .leaflet-control {
109 | float: right;
110 | }
111 | .leaflet-top .leaflet-control {
112 | margin-top: 10px;
113 | }
114 | .leaflet-bottom .leaflet-control {
115 | margin-bottom: 10px;
116 | }
117 | .leaflet-left .leaflet-control {
118 | margin-left: 10px;
119 | }
120 | .leaflet-right .leaflet-control {
121 | margin-right: 10px;
122 | }
123 |
124 |
125 | /* zoom and fade animations */
126 |
127 | .leaflet-fade-anim .leaflet-tile,
128 | .leaflet-fade-anim .leaflet-popup {
129 | opacity: 0;
130 | -webkit-transition: opacity 0.2s linear;
131 | -moz-transition: opacity 0.2s linear;
132 | -o-transition: opacity 0.2s linear;
133 | transition: opacity 0.2s linear;
134 | }
135 | .leaflet-fade-anim .leaflet-tile-loaded,
136 | .leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
137 | opacity: 1;
138 | }
139 |
140 | .leaflet-zoom-anim .leaflet-zoom-animated {
141 | -webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
142 | -moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
143 | -o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
144 | transition: transform 0.25s cubic-bezier(0,0,0.25,1);
145 | }
146 | .leaflet-zoom-anim .leaflet-tile,
147 | .leaflet-pan-anim .leaflet-tile,
148 | .leaflet-touching .leaflet-zoom-animated {
149 | -webkit-transition: none;
150 | -moz-transition: none;
151 | -o-transition: none;
152 | transition: none;
153 | }
154 |
155 | .leaflet-zoom-anim .leaflet-zoom-hide {
156 | visibility: hidden;
157 | }
158 |
159 |
160 | /* cursors */
161 |
162 | .leaflet-clickable {
163 | cursor: pointer;
164 | }
165 | .leaflet-container {
166 | cursor: -webkit-grab;
167 | cursor: -moz-grab;
168 | }
169 | .leaflet-popup-pane,
170 | .leaflet-control {
171 | cursor: auto;
172 | }
173 | .leaflet-dragging .leaflet-container,
174 | .leaflet-dragging .leaflet-clickable {
175 | cursor: move;
176 | cursor: -webkit-grabbing;
177 | cursor: -moz-grabbing;
178 | }
179 |
180 |
181 | /* visual tweaks */
182 |
183 | .leaflet-container {
184 | background: #000;
185 | outline: 0;
186 | }
187 | .leaflet-container a {
188 | color: #0078A8;
189 | }
190 | .leaflet-container a.leaflet-active {
191 | outline: 2px solid orange;
192 | }
193 | .leaflet-zoom-box {
194 | border: 2px dotted #38f;
195 | background: rgba(255,255,255,0.5);
196 | }
197 |
198 |
199 | /* general typography */
200 | .leaflet-container {
201 | font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
202 | }
203 |
204 |
205 | /* general toolbar styles */
206 |
207 | .leaflet-bar {
208 | box-shadow: 0 1px 5px rgba(0,0,0,0.65);
209 | border-radius: 4px;
210 | }
211 | .leaflet-bar a,
212 | .leaflet-bar a:hover {
213 | background-color: #fff;
214 | border-bottom: 1px solid #ccc;
215 | width: 26px;
216 | height: 26px;
217 | line-height: 26px;
218 | display: block;
219 | text-align: center;
220 | text-decoration: none;
221 | color: black;
222 | }
223 | .leaflet-bar a,
224 | .leaflet-control-layers-toggle {
225 | background-position: 50% 50%;
226 | background-repeat: no-repeat;
227 | display: block;
228 | }
229 | .leaflet-bar a:hover {
230 | background-color: #f4f4f4;
231 | }
232 | .leaflet-bar a:first-child {
233 | border-top-left-radius: 4px;
234 | border-top-right-radius: 4px;
235 | }
236 | .leaflet-bar a:last-child {
237 | border-bottom-left-radius: 4px;
238 | border-bottom-right-radius: 4px;
239 | border-bottom: none;
240 | }
241 | .leaflet-bar a.leaflet-disabled {
242 | cursor: default;
243 | background-color: #f4f4f4;
244 | color: #bbb;
245 | }
246 |
247 | .leaflet-touch .leaflet-bar a {
248 | width: 30px;
249 | height: 30px;
250 | line-height: 30px;
251 | }
252 |
253 |
254 | /* zoom control */
255 |
256 | .leaflet-control-zoom-in,
257 | .leaflet-control-zoom-out {
258 | font: bold 18px 'Lucida Console', Monaco, monospace;
259 | text-indent: 1px;
260 | }
261 | .leaflet-control-zoom-out {
262 | font-size: 20px;
263 | }
264 |
265 | .leaflet-touch .leaflet-control-zoom-in {
266 | font-size: 22px;
267 | }
268 | .leaflet-touch .leaflet-control-zoom-out {
269 | font-size: 24px;
270 | }
271 |
272 |
273 | /* layers control */
274 |
275 | .leaflet-control-layers {
276 | box-shadow: 0 1px 5px rgba(0,0,0,0.4);
277 | background: #fff;
278 | border-radius: 5px;
279 | }
280 | .leaflet-control-layers-toggle {
281 | background-image: url(images/layers.png);
282 | width: 36px;
283 | height: 36px;
284 | }
285 | .leaflet-retina .leaflet-control-layers-toggle {
286 | background-image: url(images/layers-2x.png);
287 | background-size: 26px 26px;
288 | }
289 | .leaflet-touch .leaflet-control-layers-toggle {
290 | width: 44px;
291 | height: 44px;
292 | }
293 | .leaflet-control-layers .leaflet-control-layers-list,
294 | .leaflet-control-layers-expanded .leaflet-control-layers-toggle {
295 | display: none;
296 | }
297 | .leaflet-control-layers-expanded .leaflet-control-layers-list {
298 | display: block;
299 | position: relative;
300 | }
301 | .leaflet-control-layers-expanded {
302 | padding: 6px 10px 6px 6px;
303 | color: #333;
304 | background: #fff;
305 | }
306 | .leaflet-control-layers-selector {
307 | margin-top: 2px;
308 | position: relative;
309 | top: 1px;
310 | }
311 | .leaflet-control-layers label {
312 | display: block;
313 | }
314 | .leaflet-control-layers-separator {
315 | height: 0;
316 | border-top: 1px solid #ddd;
317 | margin: 5px -10px 5px -6px;
318 | }
319 |
320 |
321 | /* attribution and scale controls */
322 |
323 | .leaflet-container .leaflet-control-attribution {
324 | background: #fff;
325 | background: rgba(255, 255, 255, 0.7);
326 | margin: 0;
327 | }
328 | .leaflet-control-attribution,
329 | .leaflet-control-scale-line {
330 | padding: 0 5px;
331 | color: #333;
332 | }
333 | .leaflet-control-attribution a {
334 | text-decoration: none;
335 | }
336 | .leaflet-control-attribution a:hover {
337 | text-decoration: underline;
338 | }
339 | .leaflet-container .leaflet-control-attribution,
340 | .leaflet-container .leaflet-control-scale {
341 | font-size: 11px;
342 | }
343 | .leaflet-left .leaflet-control-scale {
344 | margin-left: 5px;
345 | }
346 | .leaflet-bottom .leaflet-control-scale {
347 | margin-bottom: 5px;
348 | }
349 | .leaflet-control-scale-line {
350 | border: 2px solid #777;
351 | border-top: none;
352 | line-height: 1.1;
353 | padding: 2px 5px 1px;
354 | font-size: 11px;
355 | white-space: nowrap;
356 | overflow: hidden;
357 | -moz-box-sizing: content-box;
358 | box-sizing: content-box;
359 |
360 | background: #fff;
361 | background: rgba(255, 255, 255, 0.5);
362 | }
363 | .leaflet-control-scale-line:not(:first-child) {
364 | border-top: 2px solid #777;
365 | border-bottom: none;
366 | margin-top: -2px;
367 | }
368 | .leaflet-control-scale-line:not(:first-child):not(:last-child) {
369 | border-bottom: 2px solid #777;
370 | }
371 |
372 | .leaflet-touch .leaflet-control-attribution,
373 | .leaflet-touch .leaflet-control-layers,
374 | .leaflet-touch .leaflet-bar {
375 | box-shadow: none;
376 | }
377 | .leaflet-touch .leaflet-control-layers,
378 | .leaflet-touch .leaflet-bar {
379 | border: 2px solid rgba(0,0,0,0.2);
380 | background-clip: padding-box;
381 | }
382 |
383 |
384 | /* popup */
385 |
386 | .leaflet-popup {
387 | position: absolute;
388 | text-align: center;
389 | }
390 | .leaflet-popup-content-wrapper {
391 | padding: 1px;
392 | text-align: left;
393 | border-radius: 12px;
394 | }
395 | .leaflet-popup-content {
396 | margin: 13px 19px;
397 | line-height: 1.4;
398 | }
399 | .leaflet-popup-content p {
400 | margin: 18px 0;
401 | }
402 | .leaflet-popup-tip-container {
403 | margin: 0 auto;
404 | width: 40px;
405 | height: 20px;
406 | position: relative;
407 | overflow: hidden;
408 | }
409 | .leaflet-popup-tip {
410 | width: 17px;
411 | height: 17px;
412 | padding: 1px;
413 |
414 | margin: -10px auto 0;
415 |
416 | -webkit-transform: rotate(45deg);
417 | -moz-transform: rotate(45deg);
418 | -ms-transform: rotate(45deg);
419 | -o-transform: rotate(45deg);
420 | transform: rotate(45deg);
421 | }
422 | .leaflet-popup-content-wrapper,
423 | .leaflet-popup-tip {
424 | background: white;
425 |
426 | box-shadow: 0 3px 14px rgba(0,0,0,0.4);
427 | }
428 | .leaflet-container a.leaflet-popup-close-button {
429 | position: absolute;
430 | top: 0;
431 | right: 0;
432 | padding: 4px 4px 0 0;
433 | text-align: center;
434 | width: 18px;
435 | height: 14px;
436 | font: 16px/14px Tahoma, Verdana, sans-serif;
437 | color: #c3c3c3;
438 | text-decoration: none;
439 | font-weight: bold;
440 | background: transparent;
441 | }
442 | .leaflet-container a.leaflet-popup-close-button:hover {
443 | color: #999;
444 | }
445 | .leaflet-popup-scrolled {
446 | overflow: auto;
447 | border-bottom: 1px solid #ddd;
448 | border-top: 1px solid #ddd;
449 | }
450 |
451 | .leaflet-oldie .leaflet-popup-content-wrapper {
452 | zoom: 1;
453 | }
454 | .leaflet-oldie .leaflet-popup-tip {
455 | width: 24px;
456 | margin: 0 auto;
457 |
458 | -ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
459 | filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
460 | }
461 | .leaflet-oldie .leaflet-popup-tip-container {
462 | margin-top: -1px;
463 | }
464 |
465 | .leaflet-oldie .leaflet-control-zoom,
466 | .leaflet-oldie .leaflet-control-layers,
467 | .leaflet-oldie .leaflet-popup-content-wrapper,
468 | .leaflet-oldie .leaflet-popup-tip {
469 | border: 1px solid #999;
470 | }
471 |
472 |
473 | /* div icon */
474 |
475 | .leaflet-div-icon {
476 | background: #fff;
477 | border: 1px solid #666;
478 | }
479 |
--------------------------------------------------------------------------------
/map_reader.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8 -*-
3 | # This file is part of 7dtd-prefabs.
4 | #
5 | # 7dtd-prefabs is free software: you can redistribute it and/or modify
6 | # it under the terms of the GNU General Public License as published by
7 | # the Free Software Foundation, either version 3 of the License, or
8 | # (at your option) any later version.
9 | #
10 | # 7dtd-prefabs is distributed in the hope that it will be useful,
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | # GNU General Public License for more details.
14 | #
15 | # You should have received a copy of the GNU General Public License
16 | # along with 7dtd-prefabs. If not, see .
17 | # Source code hosted at https://github.com/nicolas-f/7dtd-prefabs
18 | # @author Nicolas Fortin github@nettrader.fr https://github.com/nicolas-f
19 | # @author Nicolas Grimaud ketchu13@hotmail.com
20 | from __future__ import print_function
21 |
22 | import struct
23 | import itertools
24 | import getopt
25 | import sys
26 | import os
27 | import time
28 | import sqlite3
29 | __version__ = "1.3.4-dev"
30 |
31 | print("Welcome to 7DTD leaflet builder version " + __version__)
32 |
33 | try:
34 | from PIL import Image, ImageOps
35 | except ImportError:
36 | print("This program require:")
37 | print("Pillow https://pillow.readthedocs.org/en/latest/")
38 | exit(-1)
39 |
40 | ##
41 | # Convert X Y position to MAP file index
42 |
43 |
44 | def index_from_xy(x, y):
45 | return (y - 16) << 16 | (x & 65535)
46 |
47 |
48 | class MapReader:
49 | db = None
50 | store_history = False
51 | tiles_file_path = {}
52 | known_tiles = set()
53 | new_tiles = 0
54 |
55 | def __init__(self, database_directory, store_history):
56 | self.db = sqlite3.connect(os.path.join(database_directory, 'tile_history.db'))
57 | self.db.text_factory = str
58 | self.store_history = store_history
59 | self.db.execute("CREATE TABLE IF NOT EXISTS TILES(POS int,HASH int, T TIMESTAMP, data CHAR(512),"
60 | " PRIMARY KEY(POS,HASH))")
61 | self.db.execute("CREATE TABLE IF NOT EXISTS VERSION as select 1 version")
62 | # Read already known index
63 | for record in self.db.execute("SELECT DISTINCT POS FROM TILES"):
64 | self.known_tiles.add(record[0])
65 |
66 | def is_tile_stored(self, index):
67 | return index in self.known_tiles
68 |
69 | def do_insert_tile(self, index, tile_hash):
70 | if self.store_history:
71 | # Check if the tile is not already in the db
72 | rs = self.db.execute("SELECT COUNT(*) CPT FROM TILES WHERE POS=? AND HASH=?", [index, tile_hash])
73 | if rs.fetchone()[0] == 0:
74 | return True
75 | else:
76 | return False
77 | else:
78 | return True
79 |
80 | def insert_tile(self, index, data, file_date):
81 | tile_hash = hash(data)
82 | if self.do_insert_tile(index, tile_hash):
83 | self.db.execute("INSERT INTO TILES VALUES (?,?,?,?)", [index, tile_hash, file_date, data])
84 | self.known_tiles.add(index)
85 | return True
86 | else:
87 | return False
88 |
89 | def fetch_tile(self, index):
90 | if not self.is_tile_stored(index):
91 | return None
92 | if self.store_history:
93 | data = self.db.execute("SELECT data FROM TILES WHERE POS=? ORDER BY T DESC LIMIT 1", [index]).fetchone()
94 | if not data is None:
95 | return data[0]
96 | else:
97 | return None
98 | else:
99 | data = self.db.execute("SELECT data FROM TILES WHERE POS=? LIMIT 1", [index]).fetchone()
100 | if not data is None:
101 | return data[0]
102 | else:
103 | return None
104 |
105 | def import_file(self, map_file, index_only):
106 | file_date = os.stat(map_file).st_mtime
107 | with open(map_file, "rb") as curs:
108 | # Check beginning of file
109 | header_magic = curs.read(4).decode('ascii')
110 | if not header_magic.startswith("map"):
111 | print("Skip " + os.path.basename(map_file) + " wrong file header")
112 | return
113 | ## Read version
114 | version = struct.unpack("I", curs.read(4))[0]
115 |
116 | tiles_pos = 524297
117 | if version == 2:
118 | tiles_pos = 524300
119 | elif version == 3:
120 | # Credits to DorHans & Seraphin for support of version 3
121 | max_tiles_count = struct.unpack("I", curs.read(4))[0]
122 | tiles_pos = max_tiles_count * 4 + 16
123 | else:
124 | print("Warning old map version or unsupported: ", version)
125 | curs.seek(5)
126 |
127 | #######################
128 | # read index
129 | num = struct.unpack("I", curs.read(4))[0]
130 |
131 | # read tiles position
132 | tiles_index = [struct.unpack("i", curs.read(4))[0] for i in range(num)]
133 | #######################
134 | # read tiles pixels
135 | if not index_only:
136 | curs.seek(tiles_pos)
137 | for i in range(num):
138 | if self.store_history or not self.is_tile_stored(tiles_index[i]):
139 | # extract 16-bytes pixel 16*16 tile
140 | tile_data = curs.read(512)
141 | if len(tile_data) == 512:
142 | if self.insert_tile(tiles_index[i], tile_data, file_date):
143 | self.tiles_file_path[tiles_index[i]] = map_file
144 | self.new_tiles += 1
145 | else:
146 | # Corrupted file
147 | print("Skip " + os.path.basename(map_file) + " may be already used by another process")
148 | break
149 | else:
150 | curs.seek(curs.tell() + 512)
151 | else:
152 | self.tiles = dict.fromkeys(tiles_index + self.tiles.keys())
153 | self.db.commit()
154 |
155 |
156 | def create_tiles(player_map_path, tile_output_path, tile_level, store_history):
157 | """
158 | Call base tile and intermediate zoom tiles
159 | """
160 | if not os.path.exists(tile_output_path):
161 | os.mkdir(tile_output_path)
162 | create_base_tiles(player_map_path, tile_output_path, tile_level, store_history)
163 | create_low_zoom_tiles(tile_output_path, tile_level)
164 |
165 |
166 | def create_base_tiles(player_map_path, tile_output_path, tile_level, store_history):
167 | """
168 | Read all .map files and create a leaflet tile folder
169 | @param player_map_path array of folder name where are stored map
170 | @param tile_level number of tiles to extract around position 0,0 of map. It is in the form of 4^n tiles.It will
171 | extract a grid of 2**n tiles on each side. n=8 will give you an extraction of -128 +128 in X and Y tiles index.
172 | """
173 | reader = MapReader(tile_output_path, store_history)
174 | # Read and merge all tiles in .map files
175 | lastprint = 0
176 | for i, map_file in enumerate(player_map_path):
177 | if time.time() - lastprint > 1:
178 | print("Read map file ", os.path.basename(map_file), i + 1, "/", len(player_map_path))
179 | lastprint = time.time()
180 | try:
181 | reader.import_file(map_file, False)
182 | except struct.error as e:
183 | print("Skip " + os.path.basename(map_file) + " may be already used by another process", e)
184 | except OSError as e:
185 | print("Skip " + os.path.basename(map_file) + " may be already used by another process", e)
186 |
187 | # make zoom folder
188 | z_path = os.path.join(tile_output_path, str(tile_level))
189 | if not os.path.exists(z_path):
190 | os.mkdir(z_path)
191 | # compute min-max X Y
192 | big_tile_range = 2**tile_level
193 | tile_range = big_tile_range*16
194 | # iterate on x
195 | minmax_tile = [(tile_range, tile_range),(-tile_range, -tile_range)]
196 | used_tiles = 0
197 | for x in range(2**tile_level):
198 | if time.time() - lastprint > 1:
199 | print("Write tile X:", x + 1, " of ", 2 ** tile_level)
200 | lastprint = time.time()
201 | x_dir_make = False
202 | x_path = os.path.join(z_path, str(x - big_tile_range // 2))
203 | for y in range(2**tile_level):
204 | # Fetch 256 tiles
205 | big_tile = None
206 | # Combine two for loop into one
207 | for tx, ty in itertools.product(range(16), range(16)):
208 | world_txy = (x * 16 + tx - tile_range // 2, y * 16 + ty - tile_range // 2)
209 | tile_data = reader.fetch_tile(index_from_xy(world_txy[0], world_txy[1]))
210 | if not tile_data is None:
211 | used_tiles += 1
212 | minmax_tile = [(min(minmax_tile[0][0], world_txy[0]), min(minmax_tile[0][1], world_txy[1])),
213 | (max(minmax_tile[1][0], world_txy[0]), max(minmax_tile[1][1], world_txy[1]))]
214 | # Add this tile to big tile
215 | # Create empty big tile if not exists
216 | if big_tile is None:
217 | big_tile = Image.new("RGBA", (256, 256))
218 | # convert image string into pil image
219 | try:
220 | tile_im = Image.frombuffer('RGB', (16, 16), tile_data, 'raw', 'BGR;15', 0, 1)
221 | # Push this tile into the big one
222 | big_tile.paste(tile_im, (tx * 16, ty * 16))
223 | except ValueError:
224 | print("The following file is corrupted, skip it:\n" +
225 | reader.tiles_file_path.get(index_from_xy(world_txy[0], world_txy[1])))
226 | # All 16pix tiles of this big tile has been copied into big tile
227 | # Time to save big tile
228 | if not big_tile is None:
229 | # Create Dirs if not exists
230 | if not x_dir_make:
231 | if not os.path.exists(x_path):
232 | os.mkdir(x_path)
233 | x_dir_make = True
234 | png_path = os.path.join(x_path, str((big_tile_range - y) - big_tile_range // 2)+".png")
235 | big_tile = ImageOps.flip(big_tile)
236 | big_tile.save(png_path, "png")
237 | print("Min max tiles minx:", minmax_tile[0][0], " maxx:", minmax_tile[1][0],
238 | "miny:", minmax_tile[0][1], " maxy: ", minmax_tile[1][1])
239 | print("Tiles used / total read", used_tiles, " / ", reader.new_tiles)
240 |
241 |
242 | def create_low_zoom_tiles(tile_output_path, tile_level_native):
243 | """
244 | Merge 4 tiles of 256x256 into a big 512x512 tile then resize to 256x256
245 | """
246 | lastprint = 0
247 | for tile_level in range(tile_level_native, 0, -1):
248 | z_path = os.path.join(tile_output_path, str(tile_level))
249 | z_lower_path = os.path.join(tile_output_path, str(tile_level - 1))
250 | if not os.path.exists(z_lower_path):
251 | os.mkdir(z_lower_path)
252 | # list all X folders, convert to int then sort ascending
253 | tiles_to_process = set()
254 | x_paths = map(lambda x: int(x), os.listdir(z_path))
255 | for x_path in sorted(x_paths):
256 | for y_path in map(lambda y: int(y[:-4]), os.listdir(os.path.join(z_path, str(x_path)))):
257 | tiles_to_process.add((x_path, y_path))
258 | while len(tiles_to_process) > 0:
259 | if time.time() - lastprint > 1:
260 | print("Zoom level ", tile_level - 1, ", ", len(tiles_to_process), " tiles left")
261 | lastprint = time.time()
262 | tile_to_process = next(iter(tiles_to_process))
263 | # compute id of origin tile
264 | orig_tile = (tile_to_process[0] - tile_to_process[0] % 2, tile_to_process[1] - tile_to_process[1] % 2)
265 | # compute the index of the 4 tiles
266 | tiles = [orig_tile, #bottom left
267 | (orig_tile[0] + 1, orig_tile[1]), #bottom right
268 | (orig_tile[0], orig_tile[1] + 1), #top left
269 | (orig_tile[0] + 1, orig_tile[1] + 1)] #top right
270 | tiles_paste_pos = [(0, 0), (256, 0), (0, 256), (256, 256)]
271 | # Remove tiles from processing
272 | missing_tiles = set()
273 | for tile_index in tiles:
274 | if tile_index in tiles_to_process:
275 | tiles_to_process.remove(tile_index)
276 | else:
277 | missing_tiles.add(tile_index)
278 | lower_zoom_image = Image.new("RGBA", (512, 512))
279 | for tile_index, paste_pos in zip(*[tiles, tiles_paste_pos]):
280 | if tile_index not in missing_tiles:
281 | # Compute path
282 | tile_index_path = os.path.join(z_path, str(tile_index[0]), str(tile_index[1])+".png")
283 | tile_im = Image.open(tile_index_path)
284 | # Paste in big image
285 | lower_zoom_image.paste(tile_im, paste_pos)
286 | # Dezoom the big tile
287 | lower_zoom_image = lower_zoom_image.resize((256, 256), Image.BICUBIC)
288 | # Save in lower zoom folder
289 | x_lower_path = os.path.join(z_lower_path, str(((orig_tile[0] + (2 ** tile_level) // 2) // 2)
290 | - (2 ** (tile_level - 1)) // 2))
291 | if not os.path.exists(x_lower_path):
292 | os.mkdir(x_lower_path)
293 | lower_zoom_image.save(os.path.join(x_lower_path, str(((orig_tile[1] + (2 ** tile_level) // 2) // 2)
294 | - (2 ** (tile_level - 1)) // 2) + ".png"))
295 |
296 |
297 | def read_folder(path):
298 | map_files = [os.path.join(path, file_name) for file_name in os.listdir(path) if file_name.endswith(".map")]
299 | map_files.sort(key=lambda file_path: -os.stat(file_path).st_mtime)
300 | return map_files
301 |
302 |
303 | def usage():
304 | print("This program extract and merge map tiles of all players.Then write it in a folder with verious zoom"
305 | " levels. In order to hide player bases, this program keep only the oldest version of each tile by default.")
306 | print("Usage:")
307 | print(" -g \"C:\\Users..\":\t The folder that contain .map files")
308 | print(" -t \"tiles\":\t\t The folder that will contain tiles (Optional)")
309 | print(" -z 8:\t\t\t\t Zoom level 4-n. Number of tiles to extract around position 0,0 of map."
310 | " It is in the form of 4^n tiles.It will extract a grid of 2^n*16 tiles on each side.(Optional)")
311 | print(
312 | "-n :\t\t\t\t Keep track of updates and write the last version of tiles. This will show players bases on "
313 | "map.(Optional)")
314 |
315 |
316 | def main():
317 | game_player_path = None
318 | tile_path = "tiles"
319 | tile_zoom = 8
320 | store_history = False
321 | # parse command line options
322 | try:
323 | for opt, value in getopt.getopt(sys.argv[1:], "g:t:z:n")[0]:
324 | if opt == "-g":
325 | game_player_path = value
326 | elif opt == "-t":
327 | tile_path = value
328 | elif opt == "-z":
329 | tile_zoom = int(value)
330 | elif opt == "-n":
331 | store_history = True
332 | print("Store all version of tiles, may take huge disk space")
333 | except getopt.error as msg:
334 | usage()
335 | exit(-1)
336 | if game_player_path is None:
337 | # Show gui to select tile folder
338 | try:
339 | import tkFileDialog
340 | from Tkinter import Tk
341 | root = Tk()
342 | root.withdraw()
343 | opts = {"initialdir": os.path.join(os.getenv("appdata"), "7DaysToDie", "Saves"),
344 | "title": "Choose player path that contain .map files"}
345 | game_player_path = tkFileDialog.askdirectory(**opts)
346 | except ImportError:
347 | # Headless environment
348 | usage()
349 | exit(-1)
350 | if len(game_player_path) == 0:
351 | print("You must define the .map game path")
352 | exit(-1)
353 | map_files = read_folder(game_player_path)
354 | if len(map_files) == 0:
355 | print("No .map files found in ", game_player_path)
356 | exit(-1)
357 | create_tiles(map_files, tile_path, tile_zoom, store_history)
358 |
359 | if __name__ == "__main__":
360 | main()
361 |
--------------------------------------------------------------------------------
/LICENCE:
--------------------------------------------------------------------------------
1 | GNU AFFERO GENERAL PUBLIC LICENSE
2 | Version 3, 19 November 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU Affero General Public License is a free, copyleft license for
11 | software and other kinds of works, specifically designed to ensure
12 | cooperation with the community in the case of network server software.
13 |
14 | The licenses for most software and other practical works are designed
15 | to take away your freedom to share and change the works. By contrast,
16 | our General Public Licenses are intended to guarantee your freedom to
17 | share and change all versions of a program--to make sure it remains free
18 | software for all its users.
19 |
20 | When we speak of free software, we are referring to freedom, not
21 | price. Our General Public Licenses are designed to make sure that you
22 | have the freedom to distribute copies of free software (and charge for
23 | them if you wish), that you receive source code or can get it if you
24 | want it, that you can change the software or use pieces of it in new
25 | free programs, and that you know you can do these things.
26 |
27 | Developers that use our General Public Licenses protect your rights
28 | with two steps: (1) assert copyright on the software, and (2) offer
29 | you this License which gives you legal permission to copy, distribute
30 | and/or modify the software.
31 |
32 | A secondary benefit of defending all users' freedom is that
33 | improvements made in alternate versions of the program, if they
34 | receive widespread use, become available for other developers to
35 | incorporate. Many developers of free software are heartened and
36 | encouraged by the resulting cooperation. However, in the case of
37 | software used on network servers, this result may fail to come about.
38 | The GNU General Public License permits making a modified version and
39 | letting the public access it on a server without ever releasing its
40 | source code to the public.
41 |
42 | The GNU Affero General Public License is designed specifically to
43 | ensure that, in such cases, the modified source code becomes available
44 | to the community. It requires the operator of a network server to
45 | provide the source code of the modified version running there to the
46 | users of that server. Therefore, public use of a modified version, on
47 | a publicly accessible server, gives the public access to the source
48 | code of the modified version.
49 |
50 | An older license, called the Affero General Public License and
51 | published by Affero, was designed to accomplish similar goals. This is
52 | a different license, not a version of the Affero GPL, but Affero has
53 | released a new version of the Affero GPL which permits relicensing under
54 | this license.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | TERMS AND CONDITIONS
60 |
61 | 0. Definitions.
62 |
63 | "This License" refers to version 3 of the GNU Affero General Public License.
64 |
65 | "Copyright" also means copyright-like laws that apply to other kinds of
66 | works, such as semiconductor masks.
67 |
68 | "The Program" refers to any copyrightable work licensed under this
69 | License. Each licensee is addressed as "you". "Licensees" and
70 | "recipients" may be individuals or organizations.
71 |
72 | To "modify" a work means to copy from or adapt all or part of the work
73 | in a fashion requiring copyright permission, other than the making of an
74 | exact copy. The resulting work is called a "modified version" of the
75 | earlier work or a work "based on" the earlier work.
76 |
77 | A "covered work" means either the unmodified Program or a work based
78 | on the Program.
79 |
80 | To "propagate" a work means to do anything with it that, without
81 | permission, would make you directly or secondarily liable for
82 | infringement under applicable copyright law, except executing it on a
83 | computer or modifying a private copy. Propagation includes copying,
84 | distribution (with or without modification), making available to the
85 | public, and in some countries other activities as well.
86 |
87 | To "convey" a work means any kind of propagation that enables other
88 | parties to make or receive copies. Mere interaction with a user through
89 | a computer network, with no transfer of a copy, is not conveying.
90 |
91 | An interactive user interface displays "Appropriate Legal Notices"
92 | to the extent that it includes a convenient and prominently visible
93 | feature that (1) displays an appropriate copyright notice, and (2)
94 | tells the user that there is no warranty for the work (except to the
95 | extent that warranties are provided), that licensees may convey the
96 | work under this License, and how to view a copy of this License. If
97 | the interface presents a list of user commands or options, such as a
98 | menu, a prominent item in the list meets this criterion.
99 |
100 | 1. Source Code.
101 |
102 | The "source code" for a work means the preferred form of the work
103 | for making modifications to it. "Object code" means any non-source
104 | form of a work.
105 |
106 | A "Standard Interface" means an interface that either is an official
107 | standard defined by a recognized standards body, or, in the case of
108 | interfaces specified for a particular programming language, one that
109 | is widely used among developers working in that language.
110 |
111 | The "System Libraries" of an executable work include anything, other
112 | than the work as a whole, that (a) is included in the normal form of
113 | packaging a Major Component, but which is not part of that Major
114 | Component, and (b) serves only to enable use of the work with that
115 | Major Component, or to implement a Standard Interface for which an
116 | implementation is available to the public in source code form. A
117 | "Major Component", in this context, means a major essential component
118 | (kernel, window system, and so on) of the specific operating system
119 | (if any) on which the executable work runs, or a compiler used to
120 | produce the work, or an object code interpreter used to run it.
121 |
122 | The "Corresponding Source" for a work in object code form means all
123 | the source code needed to generate, install, and (for an executable
124 | work) run the object code and to modify the work, including scripts to
125 | control those activities. However, it does not include the work's
126 | System Libraries, or general-purpose tools or generally available free
127 | programs which are used unmodified in performing those activities but
128 | which are not part of the work. For example, Corresponding Source
129 | includes interface definition files associated with source files for
130 | the work, and the source code for shared libraries and dynamically
131 | linked subprograms that the work is specifically designed to require,
132 | such as by intimate data communication or control flow between those
133 | subprograms and other parts of the work.
134 |
135 | The Corresponding Source need not include anything that users
136 | can regenerate automatically from other parts of the Corresponding
137 | Source.
138 |
139 | The Corresponding Source for a work in source code form is that
140 | same work.
141 |
142 | 2. Basic Permissions.
143 |
144 | All rights granted under this License are granted for the term of
145 | copyright on the Program, and are irrevocable provided the stated
146 | conditions are met. This License explicitly affirms your unlimited
147 | permission to run the unmodified Program. The output from running a
148 | covered work is covered by this License only if the output, given its
149 | content, constitutes a covered work. This License acknowledges your
150 | rights of fair use or other equivalent, as provided by copyright law.
151 |
152 | You may make, run and propagate covered works that you do not
153 | convey, without conditions so long as your license otherwise remains
154 | in force. You may convey covered works to others for the sole purpose
155 | of having them make modifications exclusively for you, or provide you
156 | with facilities for running those works, provided that you comply with
157 | the terms of this License in conveying all material for which you do
158 | not control copyright. Those thus making or running the covered works
159 | for you must do so exclusively on your behalf, under your direction
160 | and control, on terms that prohibit them from making any copies of
161 | your copyrighted material outside their relationship with you.
162 |
163 | Conveying under any other circumstances is permitted solely under
164 | the conditions stated below. Sublicensing is not allowed; section 10
165 | makes it unnecessary.
166 |
167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
168 |
169 | No covered work shall be deemed part of an effective technological
170 | measure under any applicable law fulfilling obligations under article
171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
172 | similar laws prohibiting or restricting circumvention of such
173 | measures.
174 |
175 | When you convey a covered work, you waive any legal power to forbid
176 | circumvention of technological measures to the extent such circumvention
177 | is effected by exercising rights under this License with respect to
178 | the covered work, and you disclaim any intention to limit operation or
179 | modification of the work as a means of enforcing, against the work's
180 | users, your or third parties' legal rights to forbid circumvention of
181 | technological measures.
182 |
183 | 4. Conveying Verbatim Copies.
184 |
185 | You may convey verbatim copies of the Program's source code as you
186 | receive it, in any medium, provided that you conspicuously and
187 | appropriately publish on each copy an appropriate copyright notice;
188 | keep intact all notices stating that this License and any
189 | non-permissive terms added in accord with section 7 apply to the code;
190 | keep intact all notices of the absence of any warranty; and give all
191 | recipients a copy of this License along with the Program.
192 |
193 | You may charge any price or no price for each copy that you convey,
194 | and you may offer support or warranty protection for a fee.
195 |
196 | 5. Conveying Modified Source Versions.
197 |
198 | You may convey a work based on the Program, or the modifications to
199 | produce it from the Program, in the form of source code under the
200 | terms of section 4, provided that you also meet all of these conditions:
201 |
202 | a) The work must carry prominent notices stating that you modified
203 | it, and giving a relevant date.
204 |
205 | b) The work must carry prominent notices stating that it is
206 | released under this License and any conditions added under section
207 | 7. This requirement modifies the requirement in section 4 to
208 | "keep intact all notices".
209 |
210 | c) You must license the entire work, as a whole, under this
211 | License to anyone who comes into possession of a copy. This
212 | License will therefore apply, along with any applicable section 7
213 | additional terms, to the whole of the work, and all its parts,
214 | regardless of how they are packaged. This License gives no
215 | permission to license the work in any other way, but it does not
216 | invalidate such permission if you have separately received it.
217 |
218 | d) If the work has interactive user interfaces, each must display
219 | Appropriate Legal Notices; however, if the Program has interactive
220 | interfaces that do not display Appropriate Legal Notices, your
221 | work need not make them do so.
222 |
223 | A compilation of a covered work with other separate and independent
224 | works, which are not by their nature extensions of the covered work,
225 | and which are not combined with it such as to form a larger program,
226 | in or on a volume of a storage or distribution medium, is called an
227 | "aggregate" if the compilation and its resulting copyright are not
228 | used to limit the access or legal rights of the compilation's users
229 | beyond what the individual works permit. Inclusion of a covered work
230 | in an aggregate does not cause this License to apply to the other
231 | parts of the aggregate.
232 |
233 | 6. Conveying Non-Source Forms.
234 |
235 | You may convey a covered work in object code form under the terms
236 | of sections 4 and 5, provided that you also convey the
237 | machine-readable Corresponding Source under the terms of this License,
238 | in one of these ways:
239 |
240 | a) Convey the object code in, or embodied in, a physical product
241 | (including a physical distribution medium), accompanied by the
242 | Corresponding Source fixed on a durable physical medium
243 | customarily used for software interchange.
244 |
245 | b) Convey the object code in, or embodied in, a physical product
246 | (including a physical distribution medium), accompanied by a
247 | written offer, valid for at least three years and valid for as
248 | long as you offer spare parts or customer support for that product
249 | model, to give anyone who possesses the object code either (1) a
250 | copy of the Corresponding Source for all the software in the
251 | product that is covered by this License, on a durable physical
252 | medium customarily used for software interchange, for a price no
253 | more than your reasonable cost of physically performing this
254 | conveying of source, or (2) access to copy the
255 | Corresponding Source from a network server at no charge.
256 |
257 | c) Convey individual copies of the object code with a copy of the
258 | written offer to provide the Corresponding Source. This
259 | alternative is allowed only occasionally and noncommercially, and
260 | only if you received the object code with such an offer, in accord
261 | with subsection 6b.
262 |
263 | d) Convey the object code by offering access from a designated
264 | place (gratis or for a charge), and offer equivalent access to the
265 | Corresponding Source in the same way through the same place at no
266 | further charge. You need not require recipients to copy the
267 | Corresponding Source along with the object code. If the place to
268 | copy the object code is a network server, the Corresponding Source
269 | may be on a different server (operated by you or a third party)
270 | that supports equivalent copying facilities, provided you maintain
271 | clear directions next to the object code saying where to find the
272 | Corresponding Source. Regardless of what server hosts the
273 | Corresponding Source, you remain obligated to ensure that it is
274 | available for as long as needed to satisfy these requirements.
275 |
276 | e) Convey the object code using peer-to-peer transmission, provided
277 | you inform other peers where the object code and Corresponding
278 | Source of the work are being offered to the general public at no
279 | charge under subsection 6d.
280 |
281 | A separable portion of the object code, whose source code is excluded
282 | from the Corresponding Source as a System Library, need not be
283 | included in conveying the object code work.
284 |
285 | A "User Product" is either (1) a "consumer product", which means any
286 | tangible personal property which is normally used for personal, family,
287 | or household purposes, or (2) anything designed or sold for incorporation
288 | into a dwelling. In determining whether a product is a consumer product,
289 | doubtful cases shall be resolved in favor of coverage. For a particular
290 | product received by a particular user, "normally used" refers to a
291 | typical or common use of that class of product, regardless of the status
292 | of the particular user or of the way in which the particular user
293 | actually uses, or expects or is expected to use, the product. A product
294 | is a consumer product regardless of whether the product has substantial
295 | commercial, industrial or non-consumer uses, unless such uses represent
296 | the only significant mode of use of the product.
297 |
298 | "Installation Information" for a User Product means any methods,
299 | procedures, authorization keys, or other information required to install
300 | and execute modified versions of a covered work in that User Product from
301 | a modified version of its Corresponding Source. The information must
302 | suffice to ensure that the continued functioning of the modified object
303 | code is in no case prevented or interfered with solely because
304 | modification has been made.
305 |
306 | If you convey an object code work under this section in, or with, or
307 | specifically for use in, a User Product, and the conveying occurs as
308 | part of a transaction in which the right of possession and use of the
309 | User Product is transferred to the recipient in perpetuity or for a
310 | fixed term (regardless of how the transaction is characterized), the
311 | Corresponding Source conveyed under this section must be accompanied
312 | by the Installation Information. But this requirement does not apply
313 | if neither you nor any third party retains the ability to install
314 | modified object code on the User Product (for example, the work has
315 | been installed in ROM).
316 |
317 | The requirement to provide Installation Information does not include a
318 | requirement to continue to provide support service, warranty, or updates
319 | for a work that has been modified or installed by the recipient, or for
320 | the User Product in which it has been modified or installed. Access to a
321 | network may be denied when the modification itself materially and
322 | adversely affects the operation of the network or violates the rules and
323 | protocols for communication across the network.
324 |
325 | Corresponding Source conveyed, and Installation Information provided,
326 | in accord with this section must be in a format that is publicly
327 | documented (and with an implementation available to the public in
328 | source code form), and must require no special password or key for
329 | unpacking, reading or copying.
330 |
331 | 7. Additional Terms.
332 |
333 | "Additional permissions" are terms that supplement the terms of this
334 | License by making exceptions from one or more of its conditions.
335 | Additional permissions that are applicable to the entire Program shall
336 | be treated as though they were included in this License, to the extent
337 | that they are valid under applicable law. If additional permissions
338 | apply only to part of the Program, that part may be used separately
339 | under those permissions, but the entire Program remains governed by
340 | this License without regard to the additional permissions.
341 |
342 | When you convey a copy of a covered work, you may at your option
343 | remove any additional permissions from that copy, or from any part of
344 | it. (Additional permissions may be written to require their own
345 | removal in certain cases when you modify the work.) You may place
346 | additional permissions on material, added by you to a covered work,
347 | for which you have or can give appropriate copyright permission.
348 |
349 | Notwithstanding any other provision of this License, for material you
350 | add to a covered work, you may (if authorized by the copyright holders of
351 | that material) supplement the terms of this License with terms:
352 |
353 | a) Disclaiming warranty or limiting liability differently from the
354 | terms of sections 15 and 16 of this License; or
355 |
356 | b) Requiring preservation of specified reasonable legal notices or
357 | author attributions in that material or in the Appropriate Legal
358 | Notices displayed by works containing it; or
359 |
360 | c) Prohibiting misrepresentation of the origin of that material, or
361 | requiring that modified versions of such material be marked in
362 | reasonable ways as different from the original version; or
363 |
364 | d) Limiting the use for publicity purposes of names of licensors or
365 | authors of the material; or
366 |
367 | e) Declining to grant rights under trademark law for use of some
368 | trade names, trademarks, or service marks; or
369 |
370 | f) Requiring indemnification of licensors and authors of that
371 | material by anyone who conveys the material (or modified versions of
372 | it) with contractual assumptions of liability to the recipient, for
373 | any liability that these contractual assumptions directly impose on
374 | those licensors and authors.
375 |
376 | All other non-permissive additional terms are considered "further
377 | restrictions" within the meaning of section 10. If the Program as you
378 | received it, or any part of it, contains a notice stating that it is
379 | governed by this License along with a term that is a further
380 | restriction, you may remove that term. If a license document contains
381 | a further restriction but permits relicensing or conveying under this
382 | License, you may add to a covered work material governed by the terms
383 | of that license document, provided that the further restriction does
384 | not survive such relicensing or conveying.
385 |
386 | If you add terms to a covered work in accord with this section, you
387 | must place, in the relevant source files, a statement of the
388 | additional terms that apply to those files, or a notice indicating
389 | where to find the applicable terms.
390 |
391 | Additional terms, permissive or non-permissive, may be stated in the
392 | form of a separately written license, or stated as exceptions;
393 | the above requirements apply either way.
394 |
395 | 8. Termination.
396 |
397 | You may not propagate or modify a covered work except as expressly
398 | provided under this License. Any attempt otherwise to propagate or
399 | modify it is void, and will automatically terminate your rights under
400 | this License (including any patent licenses granted under the third
401 | paragraph of section 11).
402 |
403 | However, if you cease all violation of this License, then your
404 | license from a particular copyright holder is reinstated (a)
405 | provisionally, unless and until the copyright holder explicitly and
406 | finally terminates your license, and (b) permanently, if the copyright
407 | holder fails to notify you of the violation by some reasonable means
408 | prior to 60 days after the cessation.
409 |
410 | Moreover, your license from a particular copyright holder is
411 | reinstated permanently if the copyright holder notifies you of the
412 | violation by some reasonable means, this is the first time you have
413 | received notice of violation of this License (for any work) from that
414 | copyright holder, and you cure the violation prior to 30 days after
415 | your receipt of the notice.
416 |
417 | Termination of your rights under this section does not terminate the
418 | licenses of parties who have received copies or rights from you under
419 | this License. If your rights have been terminated and not permanently
420 | reinstated, you do not qualify to receive new licenses for the same
421 | material under section 10.
422 |
423 | 9. Acceptance Not Required for Having Copies.
424 |
425 | You are not required to accept this License in order to receive or
426 | run a copy of the Program. Ancillary propagation of a covered work
427 | occurring solely as a consequence of using peer-to-peer transmission
428 | to receive a copy likewise does not require acceptance. However,
429 | nothing other than this License grants you permission to propagate or
430 | modify any covered work. These actions infringe copyright if you do
431 | not accept this License. Therefore, by modifying or propagating a
432 | covered work, you indicate your acceptance of this License to do so.
433 |
434 | 10. Automatic Licensing of Downstream Recipients.
435 |
436 | Each time you convey a covered work, the recipient automatically
437 | receives a license from the original licensors, to run, modify and
438 | propagate that work, subject to this License. You are not responsible
439 | for enforcing compliance by third parties with this License.
440 |
441 | An "entity transaction" is a transaction transferring control of an
442 | organization, or substantially all assets of one, or subdividing an
443 | organization, or merging organizations. If propagation of a covered
444 | work results from an entity transaction, each party to that
445 | transaction who receives a copy of the work also receives whatever
446 | licenses to the work the party's predecessor in interest had or could
447 | give under the previous paragraph, plus a right to possession of the
448 | Corresponding Source of the work from the predecessor in interest, if
449 | the predecessor has it or can get it with reasonable efforts.
450 |
451 | You may not impose any further restrictions on the exercise of the
452 | rights granted or affirmed under this License. For example, you may
453 | not impose a license fee, royalty, or other charge for exercise of
454 | rights granted under this License, and you may not initiate litigation
455 | (including a cross-claim or counterclaim in a lawsuit) alleging that
456 | any patent claim is infringed by making, using, selling, offering for
457 | sale, or importing the Program or any portion of it.
458 |
459 | 11. Patents.
460 |
461 | A "contributor" is a copyright holder who authorizes use under this
462 | License of the Program or a work on which the Program is based. The
463 | work thus licensed is called the contributor's "contributor version".
464 |
465 | A contributor's "essential patent claims" are all patent claims
466 | owned or controlled by the contributor, whether already acquired or
467 | hereafter acquired, that would be infringed by some manner, permitted
468 | by this License, of making, using, or selling its contributor version,
469 | but do not include claims that would be infringed only as a
470 | consequence of further modification of the contributor version. For
471 | purposes of this definition, "control" includes the right to grant
472 | patent sublicenses in a manner consistent with the requirements of
473 | this License.
474 |
475 | Each contributor grants you a non-exclusive, worldwide, royalty-free
476 | patent license under the contributor's essential patent claims, to
477 | make, use, sell, offer for sale, import and otherwise run, modify and
478 | propagate the contents of its contributor version.
479 |
480 | In the following three paragraphs, a "patent license" is any express
481 | agreement or commitment, however denominated, not to enforce a patent
482 | (such as an express permission to practice a patent or covenant not to
483 | sue for patent infringement). To "grant" such a patent license to a
484 | party means to make such an agreement or commitment not to enforce a
485 | patent against the party.
486 |
487 | If you convey a covered work, knowingly relying on a patent license,
488 | and the Corresponding Source of the work is not available for anyone
489 | to copy, free of charge and under the terms of this License, through a
490 | publicly available network server or other readily accessible means,
491 | then you must either (1) cause the Corresponding Source to be so
492 | available, or (2) arrange to deprive yourself of the benefit of the
493 | patent license for this particular work, or (3) arrange, in a manner
494 | consistent with the requirements of this License, to extend the patent
495 | license to downstream recipients. "Knowingly relying" means you have
496 | actual knowledge that, but for the patent license, your conveying the
497 | covered work in a country, or your recipient's use of the covered work
498 | in a country, would infringe one or more identifiable patents in that
499 | country that you have reason to believe are valid.
500 |
501 | If, pursuant to or in connection with a single transaction or
502 | arrangement, you convey, or propagate by procuring conveyance of, a
503 | covered work, and grant a patent license to some of the parties
504 | receiving the covered work authorizing them to use, propagate, modify
505 | or convey a specific copy of the covered work, then the patent license
506 | you grant is automatically extended to all recipients of the covered
507 | work and works based on it.
508 |
509 | A patent license is "discriminatory" if it does not include within
510 | the scope of its coverage, prohibits the exercise of, or is
511 | conditioned on the non-exercise of one or more of the rights that are
512 | specifically granted under this License. You may not convey a covered
513 | work if you are a party to an arrangement with a third party that is
514 | in the business of distributing software, under which you make payment
515 | to the third party based on the extent of your activity of conveying
516 | the work, and under which the third party grants, to any of the
517 | parties who would receive the covered work from you, a discriminatory
518 | patent license (a) in connection with copies of the covered work
519 | conveyed by you (or copies made from those copies), or (b) primarily
520 | for and in connection with specific products or compilations that
521 | contain the covered work, unless you entered into that arrangement,
522 | or that patent license was granted, prior to 28 March 2007.
523 |
524 | Nothing in this License shall be construed as excluding or limiting
525 | any implied license or other defenses to infringement that may
526 | otherwise be available to you under applicable patent law.
527 |
528 | 12. No Surrender of Others' Freedom.
529 |
530 | If conditions are imposed on you (whether by court order, agreement or
531 | otherwise) that contradict the conditions of this License, they do not
532 | excuse you from the conditions of this License. If you cannot convey a
533 | covered work so as to satisfy simultaneously your obligations under this
534 | License and any other pertinent obligations, then as a consequence you may
535 | not convey it at all. For example, if you agree to terms that obligate you
536 | to collect a royalty for further conveying from those to whom you convey
537 | the Program, the only way you could satisfy both those terms and this
538 | License would be to refrain entirely from conveying the Program.
539 |
540 | 13. Remote Network Interaction; Use with the GNU General Public License.
541 |
542 | Notwithstanding any other provision of this License, if you modify the
543 | Program, your modified version must prominently offer all users
544 | interacting with it remotely through a computer network (if your version
545 | supports such interaction) an opportunity to receive the Corresponding
546 | Source of your version by providing access to the Corresponding Source
547 | from a network server at no charge, through some standard or customary
548 | means of facilitating copying of software. This Corresponding Source
549 | shall include the Corresponding Source for any work covered by version 3
550 | of the GNU General Public License that is incorporated pursuant to the
551 | following paragraph.
552 |
553 | Notwithstanding any other provision of this License, you have
554 | permission to link or combine any covered work with a work licensed
555 | under version 3 of the GNU General Public License into a single
556 | combined work, and to convey the resulting work. The terms of this
557 | License will continue to apply to the part which is the covered work,
558 | but the work with which it is combined will remain governed by version
559 | 3 of the GNU General Public License.
560 |
561 | 14. Revised Versions of this License.
562 |
563 | The Free Software Foundation may publish revised and/or new versions of
564 | the GNU Affero General Public License from time to time. Such new versions
565 | will be similar in spirit to the present version, but may differ in detail to
566 | address new problems or concerns.
567 |
568 | Each version is given a distinguishing version number. If the
569 | Program specifies that a certain numbered version of the GNU Affero General
570 | Public License "or any later version" applies to it, you have the
571 | option of following the terms and conditions either of that numbered
572 | version or of any later version published by the Free Software
573 | Foundation. If the Program does not specify a version number of the
574 | GNU Affero General Public License, you may choose any version ever published
575 | by the Free Software Foundation.
576 |
577 | If the Program specifies that a proxy can decide which future
578 | versions of the GNU Affero General Public License can be used, that proxy's
579 | public statement of acceptance of a version permanently authorizes you
580 | to choose that version for the Program.
581 |
582 | Later license versions may give you additional or different
583 | permissions. However, no additional obligations are imposed on any
584 | author or copyright holder as a result of your choosing to follow a
585 | later version.
586 |
587 | 15. Disclaimer of Warranty.
588 |
589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
597 |
598 | 16. Limitation of Liability.
599 |
600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
608 | SUCH DAMAGES.
609 |
610 | 17. Interpretation of Sections 15 and 16.
611 |
612 | If the disclaimer of warranty and limitation of liability provided
613 | above cannot be given local legal effect according to their terms,
614 | reviewing courts shall apply local law that most closely approximates
615 | an absolute waiver of all civil liability in connection with the
616 | Program, unless a warranty or assumption of liability accompanies a
617 | copy of the Program in return for a fee.
618 |
619 | END OF TERMS AND CONDITIONS
620 |
621 | How to Apply These Terms to Your New Programs
622 |
623 | If you develop a new program, and you want it to be of the greatest
624 | possible use to the public, the best way to achieve this is to make it
625 | free software which everyone can redistribute and change under these terms.
626 |
627 | To do so, attach the following notices to the program. It is safest
628 | to attach them to the start of each source file to most effectively
629 | state the exclusion of warranty; and each file should have at least
630 | the "copyright" line and a pointer to where the full notice is found.
631 |
632 |
633 | Copyright (C)
634 |
635 | This program is free software: you can redistribute it and/or modify
636 | it under the terms of the GNU Affero General Public License as published
637 | by the Free Software Foundation, either version 3 of the License, or
638 | (at your option) any later version.
639 |
640 | This program is distributed in the hope that it will be useful,
641 | but WITHOUT ANY WARRANTY; without even the implied warranty of
642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
643 | GNU Affero General Public License for more details.
644 |
645 | You should have received a copy of the GNU Affero General Public License
646 | along with this program. If not, see .
647 |
648 | Also add information on how to contact you by electronic and paper mail.
649 |
650 | If your software can interact with users remotely through a computer
651 | network, you should also make sure that it provides a way for users to
652 | get its source. For example, if your program is a web application, its
653 | interface could display a "Source" link that leads users to an archive
654 | of the code. There are many ways you could offer source, and different
655 | solutions will be better for different programs; see section 13 for the
656 | specific requirements.
657 |
658 | You should also get your employer (if you work as a programmer) or school,
659 | if any, to sign a "copyright disclaimer" for the program, if necessary.
660 | For more information on this, and how to apply and follow the GNU AGPL, see
661 | .
662 |
--------------------------------------------------------------------------------
/js/leaflet-omnivore.min.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.omnivore=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error("Cannot find module '"+o+"'")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0) {
250 | var fn = queue.shift();
251 | fn();
252 | }
253 | }
254 | }, true);
255 |
256 | return function nextTick(fn) {
257 | queue.push(fn);
258 | window.postMessage('process-tick', '*');
259 | };
260 | }
261 |
262 | return function nextTick(fn) {
263 | setTimeout(fn, 0);
264 | };
265 | })();
266 |
267 | process.title = 'browser';
268 | process.browser = true;
269 | process.env = {};
270 | process.argv = [];
271 |
272 | function noop() {}
273 |
274 | process.on = noop;
275 | process.addListener = noop;
276 | process.once = noop;
277 | process.off = noop;
278 | process.removeListener = noop;
279 | process.removeAllListeners = noop;
280 | process.emit = noop;
281 |
282 | process.binding = function (name) {
283 | throw new Error('process.binding is not supported');
284 | }
285 |
286 | // TODO(shtylman)
287 | process.cwd = function () { return '/' };
288 | process.chdir = function (dir) {
289 | throw new Error('process.chdir is not supported');
290 | };
291 |
292 | },{}],5:[function(_dereq_,module,exports){
293 | function corslite(url, callback, cors) {
294 | var sent = false;
295 |
296 | if (typeof window.XMLHttpRequest === 'undefined') {
297 | return callback(Error('Browser not supported'));
298 | }
299 |
300 | if (typeof cors === 'undefined') {
301 | var m = url.match(/^\s*https?:\/\/[^\/]*/);
302 | cors = m && (m[0] !== location.protocol + '//' + location.domain +
303 | (location.port ? ':' + location.port : ''));
304 | }
305 |
306 | var x = new window.XMLHttpRequest();
307 |
308 | function isSuccessful(status) {
309 | return status >= 200 && status < 300 || status === 304;
310 | }
311 |
312 | if (cors && !('withCredentials' in x)) {
313 | // IE8-9
314 | x = new window.XDomainRequest();
315 |
316 | // Ensure callback is never called synchronously, i.e., before
317 | // x.send() returns (this has been observed in the wild).
318 | // See https://github.com/mapbox/mapbox.js/issues/472
319 | var original = callback;
320 | callback = function() {
321 | if (sent) {
322 | original.apply(this, arguments);
323 | } else {
324 | var that = this, args = arguments;
325 | setTimeout(function() {
326 | original.apply(that, args);
327 | }, 0);
328 | }
329 | }
330 | }
331 |
332 | function loaded() {
333 | if (
334 | // XDomainRequest
335 | x.status === undefined ||
336 | // modern browsers
337 | isSuccessful(x.status)) callback.call(x, null, x);
338 | else callback.call(x, x, null);
339 | }
340 |
341 | // Both `onreadystatechange` and `onload` can fire. `onreadystatechange`
342 | // has [been supported for longer](http://stackoverflow.com/a/9181508/229001).
343 | if ('onload' in x) {
344 | x.onload = loaded;
345 | } else {
346 | x.onreadystatechange = function readystate() {
347 | if (x.readyState === 4) {
348 | loaded();
349 | }
350 | };
351 | }
352 |
353 | // Call the callback with the XMLHttpRequest object as an error and prevent
354 | // it from ever being called again by reassigning it to `noop`
355 | x.onerror = function error(evt) {
356 | // XDomainRequest provides no evt parameter
357 | callback.call(this, evt || true, null);
358 | callback = function() { };
359 | };
360 |
361 | // IE9 must have onprogress be set to a unique function.
362 | x.onprogress = function() { };
363 |
364 | x.ontimeout = function(evt) {
365 | callback.call(this, evt, null);
366 | callback = function() { };
367 | };
368 |
369 | x.onabort = function(evt) {
370 | callback.call(this, evt, null);
371 | callback = function() { };
372 | };
373 |
374 | // GET is the only supported HTTP Verb by XDomainRequest and is the
375 | // only one supported here.
376 | x.open('GET', url, true);
377 |
378 | // Send the request. Sending data is not supported.
379 | x.send(null);
380 | sent = true;
381 |
382 | return x;
383 | }
384 |
385 | if (typeof module !== 'undefined') module.exports = corslite;
386 |
387 | },{}],6:[function(_dereq_,module,exports){
388 | var dsv = _dereq_('dsv'),
389 | sexagesimal = _dereq_('sexagesimal');
390 |
391 | function isLat(f) { return !!f.match(/(Lat)(itude)?/gi); }
392 | function isLon(f) { return !!f.match(/(L)(on|ng)(gitude)?/i); }
393 |
394 | function keyCount(o) {
395 | return (typeof o == 'object') ? Object.keys(o).length : 0;
396 | }
397 |
398 | function autoDelimiter(x) {
399 | var delimiters = [',', ';', '\t', '|'];
400 | var results = [];
401 |
402 | delimiters.forEach(function(delimiter) {
403 | var res = dsv(delimiter).parse(x);
404 | if (res.length >= 1) {
405 | var count = keyCount(res[0]);
406 | for (var i = 0; i < res.length; i++) {
407 | if (keyCount(res[i]) !== count) return;
408 | }
409 | results.push({
410 | delimiter: delimiter,
411 | arity: Object.keys(res[0]).length,
412 | });
413 | }
414 | });
415 |
416 | if (results.length) {
417 | return results.sort(function(a, b) {
418 | return b.arity - a.arity;
419 | })[0].delimiter;
420 | } else {
421 | return null;
422 | }
423 | }
424 |
425 | function auto(x) {
426 | var delimiter = autoDelimiter(x);
427 | if (!delimiter) return null;
428 | return dsv(delimiter).parse(x);
429 | }
430 |
431 | function csv2geojson(x, options, callback) {
432 |
433 | if (!callback) {
434 | callback = options;
435 | options = {};
436 | }
437 |
438 | options.delimiter = options.delimiter || ',';
439 |
440 | var latfield = options.latfield || '',
441 | lonfield = options.lonfield || '';
442 |
443 | var features = [],
444 | featurecollection = { type: 'FeatureCollection', features: features };
445 |
446 | if (options.delimiter === 'auto' && typeof x == 'string') {
447 | options.delimiter = autoDelimiter(x);
448 | if (!options.delimiter) return callback({
449 | type: 'Error',
450 | message: 'Could not autodetect delimiter'
451 | });
452 | }
453 |
454 | var parsed = (typeof x == 'string') ? dsv(options.delimiter).parse(x) : x;
455 |
456 | if (!parsed.length) return callback(null, featurecollection);
457 |
458 | if (!latfield || !lonfield) {
459 | for (var f in parsed[0]) {
460 | if (!latfield && isLat(f)) latfield = f;
461 | if (!lonfield && isLon(f)) lonfield = f;
462 | }
463 | if (!latfield || !lonfield) {
464 | var fields = [];
465 | for (var k in parsed[0]) fields.push(k);
466 | return callback({
467 | type: 'Error',
468 | message: 'Latitude and longitude fields not present',
469 | data: parsed,
470 | fields: fields
471 | });
472 | }
473 | }
474 |
475 | var errors = [];
476 | var coords=[];
477 | for (var i = 0; i < parsed.length; i++) {
478 | if (parsed[i][lonfield] !== undefined &&
479 | parsed[i][lonfield] !== undefined) {
480 |
481 | var lonk = parsed[i][lonfield],
482 | latk = parsed[i][latfield],
483 | lonf, latf,
484 | a;
485 |
486 | a = sexagesimal(lonk, 'EW');
487 | if (a) lonk = a;
488 | a = sexagesimal(latk, 'NS');
489 | if (a) latk = a;
490 |
491 | lonf = parseFloat(lonk);
492 | latf = parseFloat(latk);
493 |
494 | if (isNaN(lonf) ||
495 | isNaN(latf)) {
496 | errors.push({
497 | message: 'A row contained an invalid value for latitude or longitude',
498 | row: parsed[i]
499 | });
500 | } else {
501 | if (!options.includeLatLon) {
502 | delete parsed[i][lonfield];
503 | delete parsed[i][latfield];
504 | }
505 | }
506 | if(!('filter' in options) || options.filter(parsed[i])) {
507 | coords.push([parseFloat(lonf),
508 | parseFloat(latf)]);
509 | }
510 | }
511 | }
512 | features.push({
513 | type: 'Feature',
514 | geometry: {
515 | type: 'LineString',
516 | coordinates: coords
517 | }
518 | });
519 |
520 | callback(errors.length ? errors: null, featurecollection);
521 | }
522 |
523 | function toLine(gj) {
524 | var features = gj.features;
525 | var line = {
526 | type: 'Feature',
527 | geometry: {
528 | type: 'LineString',
529 | coordinates: []
530 | }
531 | };
532 | for (var i = 0; i < features.length; i++) {
533 | line.geometry.coordinates.push(features[i].geometry.coordinates);
534 | }
535 | line.properties = features[0].properties;
536 | return {
537 | type: 'FeatureCollection',
538 | features: [line]
539 | };
540 | }
541 |
542 | function toPolygon(gj) {
543 | var features = gj.features;
544 | var poly = {
545 | type: 'Feature',
546 | geometry: {
547 | type: 'Polygon',
548 | coordinates: [[]]
549 | }
550 | };
551 | for (var i = 0; i < features.length; i++) {
552 | poly.geometry.coordinates[0].push(features[i].geometry.coordinates);
553 | }
554 | poly.properties = features[0].properties;
555 | return {
556 | type: 'FeatureCollection',
557 | features: [poly]
558 | };
559 | }
560 |
561 | module.exports = {
562 | isLon: isLon,
563 | isLat: isLat,
564 | csv: dsv.csv.parse,
565 | tsv: dsv.tsv.parse,
566 | dsv: dsv,
567 | auto: auto,
568 | csv2geojson: csv2geojson,
569 | toLine: toLine,
570 | toPolygon: toPolygon
571 | };
572 |
573 | },{"dsv":7,"sexagesimal":8}],7:[function(_dereq_,module,exports){
574 | var fs = _dereq_("fs");
575 |
576 | module.exports = new Function("dsv.version = \"0.0.3\";\n\ndsv.tsv = dsv(\"\\t\");\ndsv.csv = dsv(\",\");\n\nfunction dsv(delimiter) {\n var dsv = {},\n reFormat = new RegExp(\"[\\\"\" + delimiter + \"\\n]\"),\n delimiterCode = delimiter.charCodeAt(0);\n\n dsv.parse = function(text, f) {\n var o;\n return dsv.parseRows(text, function(row, i) {\n if (o) return o(row, i - 1);\n var a = new Function(\"d\", \"return {\" + row.map(function(name, i) {\n return JSON.stringify(name) + \": d[\" + i + \"]\";\n }).join(\",\") + \"}\");\n o = f ? function(row, i) { return f(a(row), i); } : a;\n });\n };\n\n dsv.parseRows = function(text, f) {\n var EOL = {}, // sentinel value for end-of-line\n EOF = {}, // sentinel value for end-of-file\n rows = [], // output rows\n N = text.length,\n I = 0, // current character index\n n = 0, // the current line number\n t, // the current token\n eol; // is the current token followed by EOL?\n\n function token() {\n if (I >= N) return EOF; // special case: end of file\n if (eol) return eol = false, EOL; // special case: end of line\n\n // special case: quotes\n var j = I;\n if (text.charCodeAt(j) === 34) {\n var i = j;\n while (i++ < N) {\n if (text.charCodeAt(i) === 34) {\n if (text.charCodeAt(i + 1) !== 34) break;\n ++i;\n }\n }\n I = i + 2;\n var c = text.charCodeAt(i + 1);\n if (c === 13) {\n eol = true;\n if (text.charCodeAt(i + 2) === 10) ++I;\n } else if (c === 10) {\n eol = true;\n }\n return text.substring(j + 1, i).replace(/\"\"/g, \"\\\"\");\n }\n\n // common case: find next delimiter or newline\n while (I < N) {\n var c = text.charCodeAt(I++), k = 1;\n if (c === 10) eol = true; // \\n\n else if (c === 13) { eol = true; if (text.charCodeAt(I) === 10) ++I, ++k; } // \\r|\\r\\n\n else if (c !== delimiterCode) continue;\n return text.substring(j, I - k);\n }\n\n // special case: last token before EOF\n return text.substring(j);\n }\n\n while ((t = token()) !== EOF) {\n var a = [];\n while (t !== EOL && t !== EOF) {\n a.push(t);\n t = token();\n }\n if (f && !(a = f(a, n++))) continue;\n rows.push(a);\n }\n\n return rows;\n };\n\n dsv.format = function(rows) {\n if (Array.isArray(rows[0])) return dsv.formatRows(rows); // deprecated; use formatRows\n var fieldSet = {}, fields = [];\n\n // Compute unique fields in order of discovery.\n rows.forEach(function(row) {\n for (var field in row) {\n if (!(field in fieldSet)) {\n fields.push(fieldSet[field] = field);\n }\n }\n });\n\n return [fields.map(formatValue).join(delimiter)].concat(rows.map(function(row) {\n return fields.map(function(field) {\n return formatValue(row[field]);\n }).join(delimiter);\n })).join(\"\\n\");\n };\n\n dsv.formatRows = function(rows) {\n return rows.map(formatRow).join(\"\\n\");\n };\n\n function formatRow(row) {\n return row.map(formatValue).join(delimiter);\n }\n\n function formatValue(text) {\n return reFormat.test(text) ? \"\\\"\" + text.replace(/\\\"/g, \"\\\"\\\"\") + \"\\\"\" : text;\n }\n\n return dsv;\n}\n" + ";return dsv")();
577 |
578 | },{"fs":2}],8:[function(_dereq_,module,exports){
579 | module.exports = function(x, dims) {
580 | if (!dims) dims = 'NSEW';
581 | if (typeof x !== 'string') return null;
582 | var r = /^([0-9.]+)°? *(?:([0-9.]+)['’′‘] *)?(?:([0-9.]+)(?:''|"|”|″) *)?([NSEW])?/,
583 | m = x.match(r);
584 | if (!m) return null;
585 | else if (m[4] && dims.indexOf(m[4]) === -1) return null;
586 | else return (((m[1]) ? parseFloat(m[1]) : 0) +
587 | ((m[2] ? parseFloat(m[2]) / 60 : 0)) +
588 | ((m[3] ? parseFloat(m[3]) / 3600 : 0))) *
589 | ((m[4] && m[4] === 'S' || m[4] === 'W') ? -1 : 1);
590 | };
591 |
592 | },{}],9:[function(_dereq_,module,exports){
593 | (function (process){
594 | toGeoJSON = (function() {
595 | 'use strict';
596 |
597 | var removeSpace = (/\s*/g),
598 | trimSpace = (/^\s*|\s*$/g),
599 | splitSpace = (/\s+/);
600 | // generate a short, numeric hash of a string
601 | function okhash(x) {
602 | if (!x || !x.length) return 0;
603 | for (var i = 0, h = 0; i < x.length; i++) {
604 | h = ((h << 5) - h) + x.charCodeAt(i) | 0;
605 | } return h;
606 | }
607 | // all Y children of X
608 | function get(x, y) { return x.getElementsByTagName(y); }
609 | function attr(x, y) { return x.getAttribute(y); }
610 | function attrf(x, y) { return parseFloat(attr(x, y)); }
611 | // one Y child of X, if any, otherwise null
612 | function get1(x, y) { var n = get(x, y); return n.length ? n[0] : null; }
613 | // https://developer.mozilla.org/en-US/docs/Web/API/Node.normalize
614 | function norm(el) { if (el.normalize) { el.normalize(); } return el; }
615 | // cast array x into numbers
616 | function numarray(x) {
617 | for (var j = 0, o = []; j < x.length; j++) o[j] = parseFloat(x[j]);
618 | return o;
619 | }
620 | function clean(x) {
621 | var o = {};
622 | for (var i in x) if (x[i]) o[i] = x[i];
623 | return o;
624 | }
625 | // get the content of a text node, if any
626 | function nodeVal(x) { if (x) {norm(x);} return x && x.firstChild && x.firstChild.nodeValue; }
627 | // get one coordinate from a coordinate array, if any
628 | function coord1(v) { return numarray(v.replace(removeSpace, '').split(',')); }
629 | // get all coordinates from a coordinate array as [[],[]]
630 | function coord(v) {
631 | var coords = v.replace(trimSpace, '').split(splitSpace),
632 | o = [];
633 | for (var i = 0; i < coords.length; i++) {
634 | o.push(coord1(coords[i]));
635 | }
636 | return o;
637 | }
638 | function coordPair(x) {
639 | var ll = [attrf(x, 'lon'), attrf(x, 'lat')],
640 | ele = get1(x, 'ele');
641 | if (ele) ll.push(parseFloat(nodeVal(ele)));
642 | return ll;
643 | }
644 |
645 | // create a new feature collection parent object
646 | function fc() {
647 | return {
648 | type: 'FeatureCollection',
649 | features: []
650 | };
651 | }
652 |
653 | var serializer;
654 | if (typeof XMLSerializer !== 'undefined') {
655 | serializer = new XMLSerializer();
656 | // only require xmldom in a node environment
657 | } else if (typeof exports === 'object' && typeof process === 'object' && !process.browser) {
658 | serializer = new (_dereq_('xmldom').XMLSerializer)();
659 | }
660 | function xml2str(str) { return serializer.serializeToString(str); }
661 |
662 | var t = {
663 | kml: function(doc, o) {
664 | o = o || {};
665 |
666 | var gj = fc(),
667 | // styleindex keeps track of hashed styles in order to match features
668 | styleIndex = {},
669 | // atomic geospatial types supported by KML - MultiGeometry is
670 | // handled separately
671 | geotypes = ['Polygon', 'LineString', 'Point', 'Track'],
672 | // all root placemarks in the file
673 | placemarks = get(doc, 'Placemark'),
674 | styles = get(doc, 'Style');
675 |
676 | for (var k = 0; k < styles.length; k++) {
677 | styleIndex['#' + attr(styles[k], 'id')] = okhash(xml2str(styles[k])).toString(16);
678 | }
679 | for (var j = 0; j < placemarks.length; j++) {
680 | gj.features = gj.features.concat(getPlacemark(placemarks[j]));
681 | }
682 | function gxCoord(v) { return numarray(v.split(' ')); }
683 | function gxCoords(root) {
684 | var elems = get(root, 'coord', 'gx'), coords = [];
685 | for (var i = 0; i < elems.length; i++) coords.push(gxCoord(nodeVal(elems[i])));
686 | return coords;
687 | }
688 | function getGeometry(root) {
689 | var geomNode, geomNodes, i, j, k, geoms = [];
690 | if (get1(root, 'MultiGeometry')) return getGeometry(get1(root, 'MultiGeometry'));
691 | if (get1(root, 'MultiTrack')) return getGeometry(get1(root, 'MultiTrack'));
692 | for (i = 0; i < geotypes.length; i++) {
693 | geomNodes = get(root, geotypes[i]);
694 | if (geomNodes) {
695 | for (j = 0; j < geomNodes.length; j++) {
696 | geomNode = geomNodes[j];
697 | if (geotypes[i] == 'Point') {
698 | geoms.push({
699 | type: 'Point',
700 | coordinates: coord1(nodeVal(get1(geomNode, 'coordinates')))
701 | });
702 | } else if (geotypes[i] == 'LineString') {
703 | geoms.push({
704 | type: 'LineString',
705 | coordinates: coord(nodeVal(get1(geomNode, 'coordinates')))
706 | });
707 | } else if (geotypes[i] == 'Polygon') {
708 | var rings = get(geomNode, 'LinearRing'),
709 | coords = [];
710 | for (k = 0; k < rings.length; k++) {
711 | coords.push(coord(nodeVal(get1(rings[k], 'coordinates'))));
712 | }
713 | geoms.push({
714 | type: 'Polygon',
715 | coordinates: coords
716 | });
717 | } else if (geotypes[i] == 'Track') {
718 | geoms.push({
719 | type: 'LineString',
720 | coordinates: gxCoords(geomNode)
721 | });
722 | }
723 | }
724 | }
725 | }
726 | return geoms;
727 | }
728 | function getPlacemark(root) {
729 | var geoms = getGeometry(root), i, properties = {},
730 | name = nodeVal(get1(root, 'name')),
731 | styleUrl = nodeVal(get1(root, 'styleUrl')),
732 | description = nodeVal(get1(root, 'description')),
733 | timeSpan = get1(root, 'TimeSpan'),
734 | extendedData = get1(root, 'ExtendedData');
735 |
736 | if (!geoms.length) return [];
737 | if (name) properties.name = name;
738 | if (styleUrl && styleIndex[styleUrl]) {
739 | properties.styleUrl = styleUrl;
740 | properties.styleHash = styleIndex[styleUrl];
741 | }
742 | if (description) properties.description = description;
743 | if (timeSpan) {
744 | var begin = nodeVal(get1(timeSpan, 'begin'));
745 | var end = nodeVal(get1(timeSpan, 'end'));
746 | properties.timespan = { begin: begin, end: end };
747 | }
748 | if (extendedData) {
749 | var datas = get(extendedData, 'Data'),
750 | simpleDatas = get(extendedData, 'SimpleData');
751 |
752 | for (i = 0; i < datas.length; i++) {
753 | properties[datas[i].getAttribute('name')] = nodeVal(get1(datas[i], 'value'));
754 | }
755 | for (i = 0; i < simpleDatas.length; i++) {
756 | properties[simpleDatas[i].getAttribute('name')] = nodeVal(simpleDatas[i]);
757 | }
758 | }
759 | return [{
760 | type: 'Feature',
761 | geometry: (geoms.length === 1) ? geoms[0] : {
762 | type: 'GeometryCollection',
763 | geometries: geoms
764 | },
765 | properties: properties
766 | }];
767 | }
768 | return gj;
769 | },
770 | gpx: function(doc, o) {
771 | var i,
772 | tracks = get(doc, 'trk'),
773 | routes = get(doc, 'rte'),
774 | waypoints = get(doc, 'wpt'),
775 | // a feature collection
776 | gj = fc();
777 | for (i = 0; i < tracks.length; i++) {
778 | gj.features.push(getLinestring(tracks[i], 'trkpt'));
779 | }
780 | for (i = 0; i < routes.length; i++) {
781 | gj.features.push(getLinestring(routes[i], 'rtept'));
782 | }
783 | for (i = 0; i < waypoints.length; i++) {
784 | gj.features.push(getPoint(waypoints[i]));
785 | }
786 | function getLinestring(node, pointname) {
787 | var j, pts = get(node, pointname), line = [];
788 | for (j = 0; j < pts.length; j++) {
789 | line.push(coordPair(pts[j]));
790 | }
791 | return {
792 | type: 'Feature',
793 | properties: getProperties(node),
794 | geometry: {
795 | type: 'LineString',
796 | coordinates: line
797 | }
798 | };
799 | }
800 | function getPoint(node) {
801 | var prop = getProperties(node);
802 | prop.sym = nodeVal(get1(node, 'sym'));
803 | return {
804 | type: 'Feature',
805 | properties: prop,
806 | geometry: {
807 | type: 'Point',
808 | coordinates: coordPair(node)
809 | }
810 | };
811 | }
812 | function getProperties(node) {
813 | var meta = ['name', 'desc', 'author', 'copyright', 'link',
814 | 'time', 'keywords'],
815 | prop = {},
816 | k;
817 | for (k = 0; k < meta.length; k++) {
818 | prop[meta[k]] = nodeVal(get1(node, meta[k]));
819 | }
820 | return clean(prop);
821 | }
822 | return gj;
823 | }
824 | };
825 | return t;
826 | })();
827 |
828 | if (typeof module !== 'undefined') module.exports = toGeoJSON;
829 |
830 | }).call(this,_dereq_("FWaASH"))
831 | },{"FWaASH":4,"xmldom":3}],10:[function(_dereq_,module,exports){
832 | !function() {
833 | var topojson = {
834 | version: "1.6.8",
835 | mesh: function(topology) { return object(topology, meshArcs.apply(this, arguments)); },
836 | meshArcs: meshArcs,
837 | merge: function(topology) { return object(topology, mergeArcs.apply(this, arguments)); },
838 | mergeArcs: mergeArcs,
839 | feature: featureOrCollection,
840 | neighbors: neighbors,
841 | presimplify: presimplify
842 | };
843 |
844 | function stitchArcs(topology, arcs) {
845 | var stitchedArcs = {},
846 | fragmentByStart = {},
847 | fragmentByEnd = {},
848 | fragments = [],
849 | emptyIndex = -1;
850 |
851 | // Stitch empty arcs first, since they may be subsumed by other arcs.
852 | arcs.forEach(function(i, j) {
853 | var arc = topology.arcs[i < 0 ? ~i : i], t;
854 | if (arc.length < 3 && !arc[1][0] && !arc[1][1]) {
855 | t = arcs[++emptyIndex], arcs[emptyIndex] = i, arcs[j] = t;
856 | }
857 | });
858 |
859 | arcs.forEach(function(i) {
860 | var e = ends(i),
861 | start = e[0],
862 | end = e[1],
863 | f, g;
864 |
865 | if (f = fragmentByEnd[start]) {
866 | delete fragmentByEnd[f.end];
867 | f.push(i);
868 | f.end = end;
869 | if (g = fragmentByStart[end]) {
870 | delete fragmentByStart[g.start];
871 | var fg = g === f ? f : f.concat(g);
872 | fragmentByStart[fg.start = f.start] = fragmentByEnd[fg.end = g.end] = fg;
873 | } else {
874 | fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
875 | }
876 | } else if (f = fragmentByStart[end]) {
877 | delete fragmentByStart[f.start];
878 | f.unshift(i);
879 | f.start = start;
880 | if (g = fragmentByEnd[start]) {
881 | delete fragmentByEnd[g.end];
882 | var gf = g === f ? f : g.concat(f);
883 | fragmentByStart[gf.start = g.start] = fragmentByEnd[gf.end = f.end] = gf;
884 | } else {
885 | fragmentByStart[f.start] = fragmentByEnd[f.end] = f;
886 | }
887 | } else {
888 | f = [i];
889 | fragmentByStart[f.start = start] = fragmentByEnd[f.end = end] = f;
890 | }
891 | });
892 |
893 | function ends(i) {
894 | var arc = topology.arcs[i < 0 ? ~i : i], p0 = arc[0], p1;
895 | if (topology.transform) p1 = [0, 0], arc.forEach(function(dp) { p1[0] += dp[0], p1[1] += dp[1]; });
896 | else p1 = arc[arc.length - 1];
897 | return i < 0 ? [p1, p0] : [p0, p1];
898 | }
899 |
900 | function flush(fragmentByEnd, fragmentByStart) {
901 | for (var k in fragmentByEnd) {
902 | var f = fragmentByEnd[k];
903 | delete fragmentByStart[f.start];
904 | delete f.start;
905 | delete f.end;
906 | f.forEach(function(i) { stitchedArcs[i < 0 ? ~i : i] = 1; });
907 | fragments.push(f);
908 | }
909 | }
910 |
911 | flush(fragmentByEnd, fragmentByStart);
912 | flush(fragmentByStart, fragmentByEnd);
913 | arcs.forEach(function(i) { if (!stitchedArcs[i < 0 ? ~i : i]) fragments.push([i]); });
914 |
915 | return fragments;
916 | }
917 |
918 | function meshArcs(topology, o, filter) {
919 | var arcs = [];
920 |
921 | if (arguments.length > 1) {
922 | var geomsByArc = [],
923 | geom;
924 |
925 | function arc(i) {
926 | var j = i < 0 ? ~i : i;
927 | (geomsByArc[j] || (geomsByArc[j] = [])).push({i: i, g: geom});
928 | }
929 |
930 | function line(arcs) {
931 | arcs.forEach(arc);
932 | }
933 |
934 | function polygon(arcs) {
935 | arcs.forEach(line);
936 | }
937 |
938 | function geometry(o) {
939 | if (o.type === "GeometryCollection") o.geometries.forEach(geometry);
940 | else if (o.type in geometryType) geom = o, geometryType[o.type](o.arcs);
941 | }
942 |
943 | var geometryType = {
944 | LineString: line,
945 | MultiLineString: polygon,
946 | Polygon: polygon,
947 | MultiPolygon: function(arcs) { arcs.forEach(polygon); }
948 | };
949 |
950 | geometry(o);
951 |
952 | geomsByArc.forEach(arguments.length < 3
953 | ? function(geoms) { arcs.push(geoms[0].i); }
954 | : function(geoms) { if (filter(geoms[0].g, geoms[geoms.length - 1].g)) arcs.push(geoms[0].i); });
955 | } else {
956 | for (var i = 0, n = topology.arcs.length; i < n; ++i) arcs.push(i);
957 | }
958 |
959 | return {type: "MultiLineString", arcs: stitchArcs(topology, arcs)};
960 | }
961 |
962 | function mergeArcs(topology, objects) {
963 | var polygonsByArc = {},
964 | polygons = [],
965 | components = [];
966 |
967 | objects.forEach(function(o) {
968 | if (o.type === "Polygon") register(o.arcs);
969 | else if (o.type === "MultiPolygon") o.arcs.forEach(register);
970 | });
971 |
972 | function register(polygon) {
973 | polygon.forEach(function(ring) {
974 | ring.forEach(function(arc) {
975 | (polygonsByArc[arc = arc < 0 ? ~arc : arc] || (polygonsByArc[arc] = [])).push(polygon);
976 | });
977 | });
978 | polygons.push(polygon);
979 | }
980 |
981 | function exterior(ring) {
982 | return cartesianRingArea(object(topology, {type: "Polygon", arcs: [ring]}).coordinates[0]) > 0; // TODO allow spherical?
983 | }
984 |
985 | polygons.forEach(function(polygon) {
986 | if (!polygon._) {
987 | var component = [],
988 | neighbors = [polygon];
989 | polygon._ = 1;
990 | components.push(component);
991 | while (polygon = neighbors.pop()) {
992 | component.push(polygon);
993 | polygon.forEach(function(ring) {
994 | ring.forEach(function(arc) {
995 | polygonsByArc[arc < 0 ? ~arc : arc].forEach(function(polygon) {
996 | if (!polygon._) {
997 | polygon._ = 1;
998 | neighbors.push(polygon);
999 | }
1000 | });
1001 | });
1002 | });
1003 | }
1004 | }
1005 | });
1006 |
1007 | polygons.forEach(function(polygon) {
1008 | delete polygon._;
1009 | });
1010 |
1011 | return {
1012 | type: "MultiPolygon",
1013 | arcs: components.map(function(polygons) {
1014 | var arcs = [];
1015 |
1016 | // Extract the exterior (unique) arcs.
1017 | polygons.forEach(function(polygon) {
1018 | polygon.forEach(function(ring) {
1019 | ring.forEach(function(arc) {
1020 | if (polygonsByArc[arc < 0 ? ~arc : arc].length < 2) {
1021 | arcs.push(arc);
1022 | }
1023 | });
1024 | });
1025 | });
1026 |
1027 | // Stitch the arcs into one or more rings.
1028 | arcs = stitchArcs(topology, arcs);
1029 |
1030 | // If more than one ring is returned,
1031 | // at most one of these rings can be the exterior;
1032 | // this exterior ring has the same winding order
1033 | // as any exterior ring in the original polygons.
1034 | if ((n = arcs.length) > 1) {
1035 | var sgn = exterior(polygons[0][0]);
1036 | for (var i = 0, t; i < n; ++i) {
1037 | if (sgn === exterior(arcs[i])) {
1038 | t = arcs[0], arcs[0] = arcs[i], arcs[i] = t;
1039 | break;
1040 | }
1041 | }
1042 | }
1043 |
1044 | return arcs;
1045 | })
1046 | };
1047 | }
1048 |
1049 | function featureOrCollection(topology, o) {
1050 | return o.type === "GeometryCollection" ? {
1051 | type: "FeatureCollection",
1052 | features: o.geometries.map(function(o) { return feature(topology, o); })
1053 | } : feature(topology, o);
1054 | }
1055 |
1056 | function feature(topology, o) {
1057 | var f = {
1058 | type: "Feature",
1059 | id: o.id,
1060 | properties: o.properties || {},
1061 | geometry: object(topology, o)
1062 | };
1063 | if (o.id == null) delete f.id;
1064 | return f;
1065 | }
1066 |
1067 | function object(topology, o) {
1068 | var absolute = transformAbsolute(topology.transform),
1069 | arcs = topology.arcs;
1070 |
1071 | function arc(i, points) {
1072 | if (points.length) points.pop();
1073 | for (var a = arcs[i < 0 ? ~i : i], k = 0, n = a.length, p; k < n; ++k) {
1074 | points.push(p = a[k].slice());
1075 | absolute(p, k);
1076 | }
1077 | if (i < 0) reverse(points, n);
1078 | }
1079 |
1080 | function point(p) {
1081 | p = p.slice();
1082 | absolute(p, 0);
1083 | return p;
1084 | }
1085 |
1086 | function line(arcs) {
1087 | var points = [];
1088 | for (var i = 0, n = arcs.length; i < n; ++i) arc(arcs[i], points);
1089 | if (points.length < 2) points.push(points[0].slice());
1090 | return points;
1091 | }
1092 |
1093 | function ring(arcs) {
1094 | var points = line(arcs);
1095 | while (points.length < 4) points.push(points[0].slice());
1096 | return points;
1097 | }
1098 |
1099 | function polygon(arcs) {
1100 | return arcs.map(ring);
1101 | }
1102 |
1103 | function geometry(o) {
1104 | var t = o.type;
1105 | return t === "GeometryCollection" ? {type: t, geometries: o.geometries.map(geometry)}
1106 | : t in geometryType ? {type: t, coordinates: geometryType[t](o)}
1107 | : null;
1108 | }
1109 |
1110 | var geometryType = {
1111 | Point: function(o) { return point(o.coordinates); },
1112 | MultiPoint: function(o) { return o.coordinates.map(point); },
1113 | LineString: function(o) { return line(o.arcs); },
1114 | MultiLineString: function(o) { return o.arcs.map(line); },
1115 | Polygon: function(o) { return polygon(o.arcs); },
1116 | MultiPolygon: function(o) { return o.arcs.map(polygon); }
1117 | };
1118 |
1119 | return geometry(o);
1120 | }
1121 |
1122 | function reverse(array, n) {
1123 | var t, j = array.length, i = j - n; while (i < --j) t = array[i], array[i++] = array[j], array[j] = t;
1124 | }
1125 |
1126 | function bisect(a, x) {
1127 | var lo = 0, hi = a.length;
1128 | while (lo < hi) {
1129 | var mid = lo + hi >>> 1;
1130 | if (a[mid] < x) lo = mid + 1;
1131 | else hi = mid;
1132 | }
1133 | return lo;
1134 | }
1135 |
1136 | function neighbors(objects) {
1137 | var indexesByArc = {}, // arc index -> array of object indexes
1138 | neighbors = objects.map(function() { return []; });
1139 |
1140 | function line(arcs, i) {
1141 | arcs.forEach(function(a) {
1142 | if (a < 0) a = ~a;
1143 | var o = indexesByArc[a];
1144 | if (o) o.push(i);
1145 | else indexesByArc[a] = [i];
1146 | });
1147 | }
1148 |
1149 | function polygon(arcs, i) {
1150 | arcs.forEach(function(arc) { line(arc, i); });
1151 | }
1152 |
1153 | function geometry(o, i) {
1154 | if (o.type === "GeometryCollection") o.geometries.forEach(function(o) { geometry(o, i); });
1155 | else if (o.type in geometryType) geometryType[o.type](o.arcs, i);
1156 | }
1157 |
1158 | var geometryType = {
1159 | LineString: line,
1160 | MultiLineString: polygon,
1161 | Polygon: polygon,
1162 | MultiPolygon: function(arcs, i) { arcs.forEach(function(arc) { polygon(arc, i); }); }
1163 | };
1164 |
1165 | objects.forEach(geometry);
1166 |
1167 | for (var i in indexesByArc) {
1168 | for (var indexes = indexesByArc[i], m = indexes.length, j = 0; j < m; ++j) {
1169 | for (var k = j + 1; k < m; ++k) {
1170 | var ij = indexes[j], ik = indexes[k], n;
1171 | if ((n = neighbors[ij])[i = bisect(n, ik)] !== ik) n.splice(i, 0, ik);
1172 | if ((n = neighbors[ik])[i = bisect(n, ij)] !== ij) n.splice(i, 0, ij);
1173 | }
1174 | }
1175 | }
1176 |
1177 | return neighbors;
1178 | }
1179 |
1180 | function presimplify(topology, triangleArea) {
1181 | var absolute = transformAbsolute(topology.transform),
1182 | relative = transformRelative(topology.transform),
1183 | heap = minAreaHeap(),
1184 | maxArea = 0,
1185 | triangle;
1186 |
1187 | if (!triangleArea) triangleArea = cartesianTriangleArea;
1188 |
1189 | topology.arcs.forEach(function(arc) {
1190 | var triangles = [];
1191 |
1192 | arc.forEach(absolute);
1193 |
1194 | for (var i = 1, n = arc.length - 1; i < n; ++i) {
1195 | triangle = arc.slice(i - 1, i + 2);
1196 | triangle[1][2] = triangleArea(triangle);
1197 | triangles.push(triangle);
1198 | heap.push(triangle);
1199 | }
1200 |
1201 | // Always keep the arc endpoints!
1202 | arc[0][2] = arc[n][2] = Infinity;
1203 |
1204 | for (var i = 0, n = triangles.length; i < n; ++i) {
1205 | triangle = triangles[i];
1206 | triangle.previous = triangles[i - 1];
1207 | triangle.next = triangles[i + 1];
1208 | }
1209 | });
1210 |
1211 | while (triangle = heap.pop()) {
1212 | var previous = triangle.previous,
1213 | next = triangle.next;
1214 |
1215 | // If the area of the current point is less than that of the previous point
1216 | // to be eliminated, use the latter's area instead. This ensures that the
1217 | // current point cannot be eliminated without eliminating previously-
1218 | // eliminated points.
1219 | if (triangle[1][2] < maxArea) triangle[1][2] = maxArea;
1220 | else maxArea = triangle[1][2];
1221 |
1222 | if (previous) {
1223 | previous.next = next;
1224 | previous[2] = triangle[2];
1225 | update(previous);
1226 | }
1227 |
1228 | if (next) {
1229 | next.previous = previous;
1230 | next[0] = triangle[0];
1231 | update(next);
1232 | }
1233 | }
1234 |
1235 | topology.arcs.forEach(function(arc) {
1236 | arc.forEach(relative);
1237 | });
1238 |
1239 | function update(triangle) {
1240 | heap.remove(triangle);
1241 | triangle[1][2] = triangleArea(triangle);
1242 | heap.push(triangle);
1243 | }
1244 |
1245 | return topology;
1246 | };
1247 |
1248 | function cartesianRingArea(ring) {
1249 | var i = -1,
1250 | n = ring.length,
1251 | a,
1252 | b = ring[n - 1],
1253 | area = 0;
1254 |
1255 | while (++i < n) {
1256 | a = b;
1257 | b = ring[i];
1258 | area += a[0] * b[1] - a[1] * b[0];
1259 | }
1260 |
1261 | return area * .5;
1262 | }
1263 |
1264 | function cartesianTriangleArea(triangle) {
1265 | var a = triangle[0], b = triangle[1], c = triangle[2];
1266 | return Math.abs((a[0] - c[0]) * (b[1] - a[1]) - (a[0] - b[0]) * (c[1] - a[1]));
1267 | }
1268 |
1269 | function compareArea(a, b) {
1270 | return a[1][2] - b[1][2];
1271 | }
1272 |
1273 | function minAreaHeap() {
1274 | var heap = {},
1275 | array = [],
1276 | size = 0;
1277 |
1278 | heap.push = function(object) {
1279 | up(array[object._ = size] = object, size++);
1280 | return size;
1281 | };
1282 |
1283 | heap.pop = function() {
1284 | if (size <= 0) return;
1285 | var removed = array[0], object;
1286 | if (--size > 0) object = array[size], down(array[object._ = 0] = object, 0);
1287 | return removed;
1288 | };
1289 |
1290 | heap.remove = function(removed) {
1291 | var i = removed._, object;
1292 | if (array[i] !== removed) return; // invalid request
1293 | if (i !== --size) object = array[size], (compareArea(object, removed) < 0 ? up : down)(array[object._ = i] = object, i);
1294 | return i;
1295 | };
1296 |
1297 | function up(object, i) {
1298 | while (i > 0) {
1299 | var j = ((i + 1) >> 1) - 1,
1300 | parent = array[j];
1301 | if (compareArea(object, parent) >= 0) break;
1302 | array[parent._ = i] = parent;
1303 | array[object._ = i = j] = object;
1304 | }
1305 | }
1306 |
1307 | function down(object, i) {
1308 | while (true) {
1309 | var r = (i + 1) << 1,
1310 | l = r - 1,
1311 | j = i,
1312 | child = array[j];
1313 | if (l < size && compareArea(array[l], child) < 0) child = array[j = l];
1314 | if (r < size && compareArea(array[r], child) < 0) child = array[j = r];
1315 | if (j === i) break;
1316 | array[child._ = i] = child;
1317 | array[object._ = i = j] = object;
1318 | }
1319 | }
1320 |
1321 | return heap;
1322 | }
1323 |
1324 | function transformAbsolute(transform) {
1325 | if (!transform) return noop;
1326 | var x0,
1327 | y0,
1328 | kx = transform.scale[0],
1329 | ky = transform.scale[1],
1330 | dx = transform.translate[0],
1331 | dy = transform.translate[1];
1332 | return function(point, i) {
1333 | if (!i) x0 = y0 = 0;
1334 | point[0] = (x0 += point[0]) * kx + dx;
1335 | point[1] = (y0 += point[1]) * ky + dy;
1336 | };
1337 | }
1338 |
1339 | function transformRelative(transform) {
1340 | if (!transform) return noop;
1341 | var x0,
1342 | y0,
1343 | kx = transform.scale[0],
1344 | ky = transform.scale[1],
1345 | dx = transform.translate[0],
1346 | dy = transform.translate[1];
1347 | return function(point, i) {
1348 | if (!i) x0 = y0 = 0;
1349 | var x1 = (point[0] - dx) / kx | 0,
1350 | y1 = (point[1] - dy) / ky | 0;
1351 | point[0] = x1 - x0;
1352 | point[1] = y1 - y0;
1353 | x0 = x1;
1354 | y0 = y1;
1355 | };
1356 | }
1357 |
1358 | function noop() {}
1359 |
1360 | if (typeof define === "function" && define.amd) define(topojson);
1361 | else if (typeof module === "object" && module.exports) module.exports = topojson;
1362 | else this.topojson = topojson;
1363 | }();
1364 |
1365 | },{}],11:[function(_dereq_,module,exports){
1366 | module.exports = parse;
1367 | module.exports.parse = parse;
1368 | module.exports.stringify = stringify;
1369 |
1370 | /*
1371 | * Parse WKT and return GeoJSON.
1372 | *
1373 | * @param {string} _ A WKT geometry
1374 | * @return {?Object} A GeoJSON geometry object
1375 | */
1376 | function parse(_) {
1377 | var parts = _.split(";"),
1378 | _ = parts.pop(),
1379 | srid = (parts.shift() || "").split("=").pop();
1380 |
1381 | var i = 0;
1382 |
1383 | function $(re) {
1384 | var match = _.substring(i).match(re);
1385 | if (!match) return null;
1386 | else {
1387 | i += match[0].length;
1388 | return match[0];
1389 | }
1390 | }
1391 |
1392 | function crs(obj) {
1393 | if (obj && srid.match(/\d+/)) {
1394 | obj.crs = {
1395 | type: 'name',
1396 | properties: {
1397 | name: 'urn:ogc:def:crs:EPSG::' + srid
1398 | }
1399 | };
1400 | }
1401 |
1402 | return obj;
1403 | }
1404 |
1405 | function white() { $(/^\s*/); }
1406 |
1407 | function multicoords() {
1408 | white();
1409 | var depth = 0, rings = [], stack = [rings],
1410 | pointer = rings, elem;
1411 |
1412 | while (elem =
1413 | $(/^(\()/) ||
1414 | $(/^(\))/) ||
1415 | $(/^(\,)/) ||
1416 | $(/^[-+]?([0-9]*\.[0-9]+|[0-9]+)/)) {
1417 | if (elem == '(') {
1418 | stack.push(pointer);
1419 | pointer = [];
1420 | stack[stack.length - 1].push(pointer);
1421 | depth++;
1422 | } else if (elem == ')') {
1423 | pointer = stack.pop();
1424 | // the stack was empty, input was malformed
1425 | if (!pointer) return;
1426 | depth--;
1427 | if (depth === 0) break;
1428 | } else if (elem === ',') {
1429 | pointer = [];
1430 | stack[stack.length - 1].push(pointer);
1431 | } else if (!isNaN(parseFloat(elem))) {
1432 | pointer.push(parseFloat(elem));
1433 | } else {
1434 | return null;
1435 | }
1436 | white();
1437 | }
1438 |
1439 | if (depth !== 0) return null;
1440 | return rings;
1441 | }
1442 |
1443 | function coords() {
1444 | var list = [], item, pt;
1445 | while (pt =
1446 | $(/^[-+]?([0-9]*\.[0-9]+|[0-9]+)/) ||
1447 | $(/^(\,)/)) {
1448 | if (pt == ',') {
1449 | list.push(item);
1450 | item = [];
1451 | } else {
1452 | if (!item) item = [];
1453 | item.push(parseFloat(pt));
1454 | }
1455 | white();
1456 | }
1457 | if (item) list.push(item);
1458 | return list.length ? list : null;
1459 | }
1460 |
1461 | function point() {
1462 | if (!$(/^(point)/i)) return null;
1463 | white();
1464 | if (!$(/^(\()/)) return null;
1465 | var c = coords();
1466 | if (!c) return null;
1467 | white();
1468 | if (!$(/^(\))/)) return null;
1469 | return {
1470 | type: 'Point',
1471 | coordinates: c[0]
1472 | };
1473 | }
1474 |
1475 | function multipoint() {
1476 | if (!$(/^(multipoint)/i)) return null;
1477 | white();
1478 | var c = multicoords();
1479 | if (!c) return null;
1480 | white();
1481 | return {
1482 | type: 'MultiPoint',
1483 | coordinates: c
1484 | };
1485 | }
1486 |
1487 | function multilinestring() {
1488 | if (!$(/^(multilinestring)/i)) return null;
1489 | white();
1490 | var c = multicoords();
1491 | if (!c) return null;
1492 | white();
1493 | return {
1494 | type: 'MultiLineString',
1495 | coordinates: c
1496 | };
1497 | }
1498 |
1499 | function linestring() {
1500 | if (!$(/^(linestring)/i)) return null;
1501 | white();
1502 | if (!$(/^(\()/)) return null;
1503 | var c = coords();
1504 | if (!c) return null;
1505 | if (!$(/^(\))/)) return null;
1506 | return {
1507 | type: 'LineString',
1508 | coordinates: c
1509 | };
1510 | }
1511 |
1512 | function polygon() {
1513 | if (!$(/^(polygon)/i)) return null;
1514 | white();
1515 | return {
1516 | type: 'Polygon',
1517 | coordinates: multicoords()
1518 | };
1519 | }
1520 |
1521 | function multipolygon() {
1522 | if (!$(/^(multipolygon)/i)) return null;
1523 | white();
1524 | return {
1525 | type: 'MultiPolygon',
1526 | coordinates: multicoords()
1527 | };
1528 | }
1529 |
1530 | function geometrycollection() {
1531 | var geometries = [], geometry;
1532 |
1533 | if (!$(/^(geometrycollection)/i)) return null;
1534 | white();
1535 |
1536 | if (!$(/^(\()/)) return null;
1537 | while (geometry = root()) {
1538 | geometries.push(geometry);
1539 | white();
1540 | $(/^(\,)/);
1541 | white();
1542 | }
1543 | if (!$(/^(\))/)) return null;
1544 |
1545 | return {
1546 | type: 'GeometryCollection',
1547 | geometries: geometries
1548 | };
1549 | }
1550 |
1551 | function root() {
1552 | return point() ||
1553 | linestring() ||
1554 | polygon() ||
1555 | multipoint() ||
1556 | multilinestring() ||
1557 | multipolygon() ||
1558 | geometrycollection();
1559 | }
1560 |
1561 | return crs(root());
1562 | }
1563 |
1564 | /**
1565 | * Stringifies a GeoJSON object into WKT
1566 | */
1567 | function stringify(gj) {
1568 | if (gj.type === 'Feature') {
1569 | gj = gj.geometry;
1570 | }
1571 |
1572 | function pairWKT(c) {
1573 | if (c.length === 2) {
1574 | return c[0] + ' ' + c[1];
1575 | } else if (c.length === 3) {
1576 | return c[0] + ' ' + c[1] + ' ' + c[2];
1577 | }
1578 | }
1579 |
1580 | function ringWKT(r) {
1581 | return r.map(pairWKT).join(', ');
1582 | }
1583 |
1584 | function ringsWKT(r) {
1585 | return r.map(ringWKT).map(wrapParens).join(', ');
1586 | }
1587 |
1588 | function multiRingsWKT(r) {
1589 | return r.map(ringsWKT).map(wrapParens).join(', ');
1590 | }
1591 |
1592 | function wrapParens(s) { return '(' + s + ')'; }
1593 |
1594 | switch (gj.type) {
1595 | case 'Point':
1596 | return 'POINT (' + pairWKT(gj.coordinates) + ')';
1597 | case 'LineString':
1598 | return 'LINESTRING (' + ringWKT(gj.coordinates) + ')';
1599 | case 'Polygon':
1600 | return 'POLYGON (' + ringsWKT(gj.coordinates) + ')';
1601 | case 'MultiPoint':
1602 | return 'MULTIPOINT (' + ringWKT(gj.coordinates) + ')';
1603 | case 'MultiPolygon':
1604 | return 'MULTIPOLYGON (' + multiRingsWKT(gj.coordinates) + ')';
1605 | case 'MultiLineString':
1606 | return 'MULTILINESTRING (' + ringsWKT(gj.coordinates) + ')';
1607 | case 'GeometryCollection':
1608 | return 'GEOMETRYCOLLECTION (' + gj.geometries.map(stringify).join(', ') + ')';
1609 | default:
1610 | throw new Error('stringify requires a valid GeoJSON Feature or geometry object as input');
1611 | }
1612 | }
1613 |
1614 | },{}]},{},[1])
1615 | (1)
1616 | });
--------------------------------------------------------------------------------