├── .gitignore ├── LICENSE ├── README.md ├── data ├── create-data.js ├── flows.json ├── input │ ├── input-topo.json │ └── metro-to-metro-2014-2018.csv ├── msas.json └── topo.json ├── full ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── rollup.config.js ├── snowpack.config.json ├── snowpack │ └── index.html ├── src │ ├── App.svelte │ ├── MapControls.svelte │ ├── index.html │ ├── index.js │ ├── map │ │ ├── Control.svelte │ │ ├── Curve.svelte │ │ ├── GeoJson.svelte │ │ ├── Leaflet.svelte │ │ ├── Pane.svelte │ │ ├── Polyline.svelte │ │ ├── Popup.svelte │ │ ├── Tooltip.svelte │ │ ├── curves.ts │ │ ├── leaflet.curve.js │ │ └── popup.ts │ ├── types.ts │ └── utils.css ├── static │ └── favicon.png ├── svelte.config.js ├── tailwind.config.js └── tsconfig.json ├── package-lock.json ├── package.json ├── pnpm-lock.yaml └── skeleton ├── .eslintrc.js ├── .gitignore ├── .prettierignore ├── .prettierrc ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── postcss.config.js ├── rollup.config.js ├── snowpack.config.json ├── snowpack └── index.html ├── src ├── App.svelte ├── MapControls.svelte ├── index.html ├── index.js ├── map │ ├── Control.svelte │ ├── Curve.svelte │ ├── GeoJson.svelte │ ├── Leaflet.svelte │ ├── Pane.svelte │ ├── Polyline.svelte │ ├── Popup.svelte │ ├── Tooltip.svelte │ ├── curves.ts │ ├── leaflet.curve.js │ └── popup.ts ├── types.ts └── utils.css ├── static └── favicon.png ├── svelte.config.js ├── tailwind.config.js └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .vscode 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2020 Daniel Imfeld 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Svelte and Leaflet Demo 2 | 3 | This is a demo application using Svelte and Leaflet to visualize the US Census "Metro Area-to-Metro Area Migration Flows” dataset. 4 | 5 | If you like this, check out my [Svelte MapLibre library](https://github.com/dimfeld/svelte-maplibre) too. 6 | 7 | This repository contains two versions of the application. The `full` directory is the 8 | finished product, while the `skeleton` directory just contains a basic shell that renders nothing, which I 9 | filled in during the presentation to build into the complete app. 10 | 11 | Developed for a [presentation to JavascriptLA](https://www.youtube.com/watch?v=-klB-EocorE&t=770s) (video) on November 11, 2020. 12 | 13 | 14 | 15 | ## References 16 | 17 | ### Concepts 18 | 19 | * [Metropolitan Statistical Areas (US Census)](https://www.census.gov/topics/housing/housing-patterns/about/core-based-statistical-areas.html) 20 | * [Wikipedia on MSAs](https://en.wikipedia.org/wiki/Metropolitan_statistical_area) 21 | * [Creating Consistently Curved Lines in Leaflet](https://medium.com/@ryancatalani/creating-consistently-curved-lines-on-leaflet-b59bc03fa9dc) 22 | * [geojson.io](https://geojson.io) for playing with GeoJSON. 23 | 24 | ### Software 25 | 26 | * [Svelte](https://svelte.dev) 27 | * [Leaflet](https://leafletjs.com/) 28 | * [Tailwind CSS](https://tailwindcss.com) 29 | * [Leaflet Curve Plugin](https://github.com/elfalem/Leaflet.curve) 30 | * [TopoJSON](https://github.com/topojson/topojson) 31 | * [Turf.js](https://turfjs.org/) for manipulating GeoJSON 32 | 33 | ### Data 34 | 35 | * [MSA Migration Data](https://www.census.gov/data/tables/2018/demo/geographic-mobility/metro-to-metro-migration.html) 36 | * Specifically [this file](https://www2.census.gov/programs-surveys/demo/tables/geographic-mobility/2018/metro-to-metro-migration/metro-to-metro-2014-2018.xlsx) 37 | * [Shape data for MSAs](https://www.census.gov/geographies/mapping-files/time-series/geo/carto-boundary-file.html) 38 | * We used [cb_2018_us_cbsa_500k.zip](https://www2.census.gov/geo/tiger/GENZ2018/shp/cb_2018_us_cbsa_500k.zip) 39 | * And converted from ESRI Shapefile to TopoJson using [MapShaper](https://mapshaper.org/) 40 | -------------------------------------------------------------------------------- /data/create-data.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | const dsv = require('d3-dsv'); 3 | const fs = require('fs'); 4 | const topojson = require('topojson-client'); 5 | const topojsonServer= require('topojson-server'); 6 | const turf = require('@turf/turf'); 7 | 8 | function formatNumber(s) { 9 | return +s.replace(/,/g, ''); 10 | } 11 | 12 | let metroData = dsv.csvParseRows(fs.readFileSync('input/metro-to-metro-2014-2018.csv').toString(), (row, i) => { 13 | if(i < 4) { 14 | // SKip useless header rows. 15 | return; 16 | } 17 | 18 | if(row[0].length !== 5) { 19 | // Skip footer data and blank rows at the end 20 | return; 21 | } 22 | 23 | return { 24 | destination: { 25 | id: row[0].toString().padStart(5, '0'), 26 | population: formatNumber(row[3]), 27 | totalIncoming: formatNumber(row[9]), 28 | }, 29 | source: { 30 | id: row[1].toString().padStart(5, '0'), 31 | population: formatNumber(row[16]), 32 | totalOutgoing: formatNumber(row[22]), 33 | }, 34 | flow: formatNumber(row[26]), 35 | }; 36 | }); 37 | 38 | console.log(`Got ${metroData.length} rows`); 39 | 40 | let topoData = JSON.parse(fs.readFileSync('input/input-topo.json')); 41 | 42 | let geojson = topojson.feature(topoData, 'cb_2018_us_cbsa_500k'); 43 | let msaData = new Map(); 44 | 45 | for(let feature of geojson.features) { 46 | let centroid; 47 | 48 | if(feature.geometry.type === 'MultiPolygon') { 49 | // For MultiPolygons, find the area of the largest polygon and use the centroid 50 | // of that one. This is mostly to make Oahu look correct, for which the GeoJSON 51 | // has a lot of huge outliers. 52 | let largest = feature.geometry.coordinates.map((c) => { 53 | let poly = turf.polygon(c); 54 | return { 55 | area: turf.area(poly), 56 | poly, 57 | } 58 | }).sort((a, b) => b.area - a.area); 59 | 60 | centroid = turf.centerOfMass(largest[0].poly); 61 | } else { 62 | centroid = turf.centerOfMass(feature); 63 | } 64 | 65 | 66 | msaData.set(feature.properties.CBSAFP, { 67 | id: feature.properties.CBSAFP, 68 | centroid: centroid.geometry.coordinates 69 | }); 70 | } 71 | 72 | let flowMap = new Map(); 73 | let missingGeo = new Set(); 74 | 75 | for(let {source, destination, flow} of metroData) { 76 | let reverse = destination.id < source.id; 77 | let flowKey = reverse ? `${destination.id}:${source.id}` : `${source.id}:${destination.id}`; 78 | let flowAmount = reverse ? -flow : flow; 79 | 80 | flowMap.set(flowKey, (flowMap.get(flowKey) || 0) + flowAmount); 81 | 82 | let sourceData = msaData.get(source.id); 83 | if(!sourceData) { 84 | missingGeo.add(source.id); 85 | } else { 86 | sourceData.population = source.population; 87 | sourceData.totalOutgoing = source.totalOutgoing; 88 | } 89 | 90 | let destData = msaData.get(destination.id); 91 | if(!destData) { 92 | missingGeo.add(destination.id); 93 | } else { 94 | destData.totalIncoming = destination.totalIncoming; 95 | destData.population = destination.population; 96 | } 97 | } 98 | 99 | let flows = Array.from(flowMap.entries(), ([key, count]) => { 100 | return [...key.split(':'), count]; 101 | }); 102 | 103 | // Just a sanity check to make sure we're including everything relevant. 104 | // console.log(Array.from(missingGeo)); 105 | 106 | let msaOutput = Array.from(msaData.values()).filter((msa) => msa.population > 0); 107 | let presentMsas = new Set(msaOutput.map((m) => m.id)); 108 | let presentFeatures = geojson.features.filter((f) => presentMsas.has(f.properties.CBSAFP)); 109 | let outputGeojson = { 110 | ...geojson, 111 | features: presentFeatures, 112 | }; 113 | 114 | 115 | console.log(`Saving ${msaOutput.length} MSAs`); 116 | fs.writeFileSync('flows.json', JSON.stringify(flows)); 117 | fs.writeFileSync('msas.json', JSON.stringify(msaOutput)); 118 | 119 | // A lot of the MSAs in the topojson don't show up in the data, so regenerate the topojson with only 120 | // the ones we use, to save space. 121 | let outputTopo = topojsonServer.topology({ msas: outputGeojson }, 1e4); 122 | fs.writeFileSync('topo.json', JSON.stringify(outputTopo)); 123 | -------------------------------------------------------------------------------- /data/msas.json: -------------------------------------------------------------------------------- 1 | [{"id":"31340","centroid":[-79.21977891047733,37.365007890476114],"population":253138,"totalOutgoing":9085,"totalIncoming":11977},{"id":"42220","centroid":[-122.88740026603415,38.52828177524599],"population":492180,"totalOutgoing":18369,"totalIncoming":20895},{"id":"46140","centroid":[-96.16628997642798,36.250429001181224],"population":970210,"totalOutgoing":25994,"totalIncoming":23709},{"id":"41740","centroid":[-116.73529906444958,33.034139142546564],"population":3252610,"totalOutgoing":147720,"totalIncoming":121572},{"id":"38220","centroid":[-91.94990428356924,34.07729500756963],"population":90885,"totalOutgoing":2976,"totalIncoming":3047},{"id":"14500","centroid":[-105.36193923007293,40.092500283088576],"population":308489,"totalOutgoing":25639,"totalIncoming":31231},{"id":"39140","centroid":[-112.55389570526312,34.599902656938625],"population":215405,"totalOutgoing":10395,"totalIncoming":16635},{"id":"17020","centroid":[-121.60067881718864,39.66694281780381],"population":222941,"totalOutgoing":10160,"totalIncoming":11787},{"id":"43620","centroid":[-96.99004692377005,43.499691698979184],"population":252091,"totalOutgoing":7604,"totalIncoming":6702},{"id":"12260","centroid":[-81.98295170744763,33.46102934542993],"population":580641,"totalOutgoing":20002,"totalIncoming":22769},{"id":"17460","centroid":[-81.68380546014906,41.375381390799284],"population":2046425,"totalOutgoing":57844,"totalIncoming":44825},{"id":"13980","centroid":[-80.5329043588124,37.12087104192895],"population":175119,"totalOutgoing":9468,"totalIncoming":12920},{"id":"29340","centroid":[-93.25968701224674,30.017396213718882],"population":203921,"totalOutgoing":5438,"totalIncoming":5707},{"id":"25220","centroid":[-90.40568037648428,30.626636203208832],"population":125818,"totalOutgoing":4618,"totalIncoming":7758},{"id":"13140","centroid":[-94.07154790501706,30.303792311161406],"population":404323,"totalOutgoing":14889,"totalIncoming":14933},{"id":"36420","centroid":[-97.50388721250954,35.42985046501356],"population":1336724,"totalOutgoing":34392,"totalIncoming":42038},{"id":"43580","centroid":[-96.37218109041784,42.5787816017505],"population":167837,"totalOutgoing":6307,"totalIncoming":4662},{"id":"17140","centroid":[-84.42749135307402,39.07151768440855],"population":2132196,"totalOutgoing":55385,"totalIncoming":57022},{"id":"28940","centroid":[-84.13737797974203,36.04483426105577],"population":852363,"totalOutgoing":24630,"totalIncoming":29106},{"id":"16740","centroid":[-80.86701971460855,35.18875887227213],"population":2412915,"totalOutgoing":76289,"totalIncoming":97592},{"id":"29100","centroid":[-91.3196905529119,43.77926687816804],"population":132162,"totalOutgoing":4787,"totalIncoming":6158},{"id":"14260","centroid":[-116.14320362848551,43.01607263136109],"population":673971,"totalOutgoing":18765,"totalIncoming":27305},{"id":"31740","centroid":[-96.50693496360074,39.344468086857454],"population":96228,"totalOutgoing":8478,"totalIncoming":7989},{"id":"36540","centroid":[-95.99912545099612,41.29007801301011],"population":908343,"totalOutgoing":28890,"totalIncoming":25456},{"id":"16620","centroid":[-81.49149214857404,38.27182066855662],"population":216009,"totalOutgoing":7075,"totalIncoming":5773},{"id":"24220","centroid":[-96.84448447189662,47.83596694760277],"population":98825,"totalOutgoing":5462,"totalIncoming":5883},{"id":"42700","centroid":[-81.34103964869955,27.34330637810145],"population":98928,"totalOutgoing":4431,"totalIncoming":5311},{"id":"41900","centroid":[-67.06192286413253,18.05679353321019],"population":128924,"totalOutgoing":4206,"totalIncoming":1639},{"id":"25980","centroid":[-81.60192171681346,31.795824964211093],"population":77608,"totalOutgoing":9909,"totalIncoming":8350},{"id":"31420","centroid":[-83.71489824090753,32.85769224592142],"population":224766,"totalOutgoing":8187,"totalIncoming":9476},{"id":"22180","centroid":[-78.98032229484184,35.03703155081214],"population":374117,"totalOutgoing":28518,"totalIncoming":27228},{"id":"36260","centroid":[-112.81593316153676,41.432685135409265],"population":638146,"totalOutgoing":25526,"totalIncoming":26919},{"id":"34100","centroid":[-83.38178898598201,36.11065357632416],"population":115128,"totalOutgoing":4765,"totalIncoming":4557},{"id":"11260","centroid":[-149.54231176769417,62.243491196453064],"population":403607,"totalOutgoing":25899,"totalIncoming":13966},{"id":"24340","centroid":[-85.48829758566254,42.99925614279305],"population":1028518,"totalOutgoing":27681,"totalIncoming":33408},{"id":"34060","centroid":[-79.80450680607628,39.527333370489934],"population":133829,"totalOutgoing":6309,"totalIncoming":8547},{"id":"18140","centroid":[-82.83679650791015,39.96812976867252],"population":2008095,"totalOutgoing":54422,"totalIncoming":63748},{"id":"29180","centroid":[-92.06613828117446,30.038906972249364],"population":479942,"totalOutgoing":11920,"totalIncoming":12248},{"id":"30300","centroid":[-116.94375697729701,46.2691194824595],"population":61593,"totalOutgoing":1970,"totalIncoming":1848},{"id":"35380","centroid":[-89.96758873001606,29.92697750733387],"population":1254161,"totalOutgoing":41877,"totalIncoming":33961},{"id":"22500","centroid":[-79.80842175561793,34.15200972987542],"population":205095,"totalOutgoing":7713,"totalIncoming":4683},{"id":"36140","centroid":[-74.80020362953559,39.148997013084184],"population":92453,"totalOutgoing":4746,"totalIncoming":5184},{"id":"44100","centroid":[-89.69690932330046,39.829612183549834],"population":208642,"totalOutgoing":8039,"totalIncoming":6132},{"id":"38660","centroid":[-66.68409907834922,18.05876428095343],"population":308909,"totalOutgoing":10152,"totalIncoming":4945},{"id":"39300","centroid":[-71.39955459137273,41.723544970476354],"population":1586887,"totalOutgoing":43512,"totalIncoming":47827},{"id":"48620","centroid":[-97.39821176777117,37.62500408894324],"population":635534,"totalOutgoing":18682,"totalIncoming":15745},{"id":"47460","centroid":[-118.24886492086351,46.2570273915684],"population":62362,"totalOutgoing":4701,"totalIncoming":4588},{"id":"24500","centroid":[-111.34703577926652,47.30795615031704],"population":80290,"totalOutgoing":3767,"totalIncoming":2896},{"id":"42540","centroid":[-75.89573319515033,41.32322473625947],"population":547868,"totalOutgoing":14602,"totalIncoming":17443},{"id":"42660","centroid":[-121.82784982136688,47.56424534880192],"population":3710762,"totalOutgoing":128518,"totalIncoming":145500},{"id":"22380","centroid":[-111.77048412887488,35.838745842923096],"population":133556,"totalOutgoing":10983,"totalIncoming":15480},{"id":"20700","centroid":[-75.33952719840845,41.058054297214326],"population":165358,"totalOutgoing":8249,"totalIncoming":8617},{"id":"43340","centroid":[-93.66877801421967,32.489844158445926],"population":439551,"totalOutgoing":13566,"totalIncoming":8934},{"id":"15380","centroid":[-78.7366732807765,42.910912073333485],"population":1118557,"totalOutgoing":26569,"totalIncoming":23092},{"id":"31540","centroid":[-89.5922313304881,43.08016174346716],"population":632143,"totalOutgoing":25668,"totalIncoming":27520},{"id":"21340","centroid":[-105.54101514166565,31.51313026928414],"population":828790,"totalOutgoing":37659,"totalIncoming":28661},{"id":"25020","centroid":[-66.08366279440585,18.01318419212889],"population":78002,"totalOutgoing":3428,"totalIncoming":2082},{"id":"43900","centroid":[-81.84751676209912,34.83788182921987],"population":321982,"totalOutgoing":10762,"totalIncoming":14641},{"id":"37460","centroid":[-85.46600497553251,30.14387915275368],"population":196163,"totalOutgoing":13050,"totalIncoming":13120},{"id":"33100","centroid":[-80.50634703066498,26.158451057188888],"population":5950963,"totalOutgoing":168383,"totalIncoming":135187},{"id":"19100","centroid":[-97.02520651941224,32.81815007407698],"population":7073136,"totalOutgoing":174694,"totalIncoming":210743},{"id":"17900","centroid":[-81.04244820242344,34.09175636407194],"population":783433,"totalOutgoing":29902,"totalIncoming":45785},{"id":"46300","centroid":[-114.5707827199849,42.435776754139184],"population":103379,"totalOutgoing":3256,"totalIncoming":3879},{"id":"40900","centroid":[-120.99755984590989,38.78047331307332],"population":2234520,"totalOutgoing":76258,"totalIncoming":91162},{"id":"32820","centroid":[-89.81524205822987,35.00767851302501],"population":1329162,"totalOutgoing":35239,"totalIncoming":27800},{"id":"17980","centroid":[-84.90874943363866,32.44143568439897],"population":299121,"totalOutgoing":18668,"totalIncoming":21323},{"id":"33860","centroid":[-86.40324881587512,32.36060607309142],"population":367426,"totalOutgoing":15717,"totalIncoming":14094},{"id":"24860","centroid":[-82.41384714797529,34.68510962676439],"population":860443,"totalOutgoing":29319,"totalIncoming":35635},{"id":"27740","centroid":[-82.33446532743953,36.2537735254266],"population":198227,"totalOutgoing":8455,"totalIncoming":7770},{"id":"21660","centroid":[-122.84748431819197,43.938812054112475],"population":355864,"totalOutgoing":14875,"totalIncoming":20800},{"id":"25500","centroid":[-78.87570896796332,38.51051893673159],"population":126310,"totalOutgoing":6111,"totalIncoming":9712},{"id":"35100","centroid":[-77.09052081591807,35.0980767321262],"population":123616,"totalOutgoing":9884,"totalIncoming":9143},{"id":"19300","centroid":[-87.72256116936462,30.727487407618867],"population":203155,"totalOutgoing":7202,"totalIncoming":9002},{"id":"13220","centroid":[-81.16090087447847,37.906225756952445],"population":121633,"totalOutgoing":4659,"totalIncoming":2370},{"id":"47580","centroid":[-83.63636626996761,32.40760349892759],"population":184808,"totalOutgoing":8999,"totalIncoming":10263},{"id":"44060","centroid":[-117.57179821352764,48.193781776028565],"population":533762,"totalOutgoing":16941,"totalIncoming":26655},{"id":"41620","centroid":[-113.01050683378543,40.47058044650397],"population":1158370,"totalOutgoing":49088,"totalIncoming":49770},{"id":"21820","centroid":[-146.56541764353884,64.80776589773697],"population":98721,"totalOutgoing":8109,"totalIncoming":6530},{"id":"49700","centroid":[-121.51790280966848,39.15531623053985],"population":167342,"totalOutgoing":6573,"totalIncoming":7642},{"id":"41940","centroid":[-121.37589299789371,36.90949358071908],"population":1945356,"totalOutgoing":99399,"totalIncoming":79904},{"id":"44940","centroid":[-80.38225222171829,33.916203139291575],"population":105829,"totalOutgoing":5541,"totalIncoming":4523},{"id":"41060","centroid":[-94.47205066087514,45.58587308091638],"population":189506,"totalOutgoing":7560,"totalIncoming":10317},{"id":"36500","centroid":[-122.83319043733889,46.925775520231866],"population":266590,"totalOutgoing":15452,"totalIncoming":18367},{"id":"12100","centroid":[-74.6609700321421,39.47773155227118],"population":266175,"totalOutgoing":12405,"totalIncoming":10897},{"id":"45940","centroid":[-74.70176101939649,40.283435828545635],"population":362173,"totalOutgoing":21625,"totalIncoming":21789},{"id":"41980","centroid":[-66.1123693422472,18.275973893289088],"population":2175385,"totalOutgoing":69797,"totalIncoming":19624},{"id":"40220","centroid":[-79.94656107968719,37.28608232816075],"population":309262,"totalOutgoing":12308,"totalIncoming":11408},{"id":"47260","centroid":[-76.41859093209567,36.65593723621089],"population":1679761,"totalOutgoing":78316,"totalIncoming":84131},{"id":"44140","centroid":[-72.64650755068901,42.2300493436416],"population":616521,"totalOutgoing":19770,"totalIncoming":25141},{"id":"25420","centroid":[-77.10119105984485,40.3266673702821],"population":550827,"totalOutgoing":23464,"totalIncoming":29285},{"id":"38900","centroid":[-122.4783185975509,45.5983759472492],"population":2366884,"totalOutgoing":81891,"totalIncoming":91348},{"id":"41180","centroid":[-90.35010113339519,38.73525508262465],"population":2781797,"totalOutgoing":69459,"totalIncoming":55196},{"id":"17780","centroid":[-96.48889476123075,30.756520219991113],"population":239369,"totalOutgoing":17678,"totalIncoming":23731},{"id":"19060","centroid":[-78.80448665442918,39.53168750445143],"population":98135,"totalOutgoing":3359,"totalIncoming":3754},{"id":"45060","centroid":[-76.0335575068406,43.154462537297924],"population":646394,"totalOutgoing":23837,"totalIncoming":21935},{"id":"41700","centroid":[-98.60229521832419,29.42873039378048],"population":2366874,"totalOutgoing":79967,"totalIncoming":89939},{"id":"31020","centroid":[-122.68100400226278,46.19323987280277],"population":104016,"totalOutgoing":5326,"totalIncoming":5305},{"id":"21060","centroid":[-85.97382100177786,37.73766099775791],"population":146148,"totalOutgoing":9600,"totalIncoming":10684},{"id":"22140","centroid":[-108.32061074817807,36.50852088437349],"population":126364,"totalOutgoing":3776,"totalIncoming":3338},{"id":"22660","centroid":[-105.46112895154045,40.66638258794885],"population":327681,"totalOutgoing":21256,"totalIncoming":26189},{"id":"28420","centroid":[-119.25441823552399,46.36350775271266],"population":279687,"totalOutgoing":9153,"totalIncoming":10183},{"id":"20260","centroid":[-92.4088043961568,47.33330112188319],"population":273447,"totalOutgoing":8622,"totalIncoming":9802},{"id":"24780","centroid":[-77.37449156406194,35.593294232215754],"population":167381,"totalOutgoing":8406,"totalIncoming":13786},{"id":"13460","centroid":[-121.22809710522333,43.91503508154249],"population":178401,"totalOutgoing":8989,"totalIncoming":10459},{"id":"13780","centroid":[-76.02513032369178,42.16450392520661],"population":238686,"totalOutgoing":7225,"totalIncoming":8737},{"id":"44700","centroid":[-121.27139472032621,37.93475533294045],"population":710953,"totalOutgoing":25194,"totalIncoming":34232},{"id":"42340","centroid":[-81.30183662429062,32.130531741995064],"population":369738,"totalOutgoing":21472,"totalIncoming":24281},{"id":"47300","centroid":[-118.80049103028645,36.22016751847846],"population":454050,"totalOutgoing":12409,"totalIncoming":10476},{"id":"33780","centroid":[-83.53749019063018,41.9286876022383],"population":148086,"totalOutgoing":5991,"totalIncoming":6019},{"id":"36100","centroid":[-82.05666044768752,29.210211612045185],"population":337696,"totalOutgoing":14886,"totalIncoming":20921},{"id":"29020","centroid":[-86.11696747101327,40.48360849957797],"population":81382,"totalOutgoing":2814,"totalIncoming":2622},{"id":"14740","centroid":[-122.67208706111458,47.61330080104672],"population":253936,"totalOutgoing":14046,"totalIncoming":18856},{"id":"29700","centroid":[-99.33151352433725,27.761106815162783],"population":267920,"totalOutgoing":6613,"totalIncoming":2740},{"id":"47900","centroid":[-77.47440167659462,38.83347350869931],"population":6028387,"totalOutgoing":241889,"totalIncoming":202058},{"id":"16980","centroid":[-87.96272988109011,41.7026425816275],"population":9479646,"totalOutgoing":249120,"totalIncoming":157098},{"id":"14540","centroid":[-86.40699796500394,37.03861985431623],"population":162646,"totalOutgoing":5015,"totalIncoming":9195},{"id":"19660","centroid":[-81.2196847468417,29.17403443571755],"population":616189,"totalOutgoing":26471,"totalIncoming":36211},{"id":"11640","centroid":[-66.75693181763114,18.413105077616844],"population":186348,"totalOutgoing":6513,"totalIncoming":2677},{"id":"15540","centroid":[-73.03078304900261,44.687205361243976],"population":211401,"totalOutgoing":7021,"totalIncoming":8287},{"id":"37620","centroid":[-81.46279153670406,39.138850185148584],"population":90617,"totalOutgoing":2047,"totalIncoming":2042},{"id":"40140","centroid":[-116.12967898106672,34.55164941228395],"population":4405214,"totalOutgoing":141272,"totalIncoming":180455},{"id":"37860","centroid":[-87.15617709585277,30.688030127472842],"population":468141,"totalOutgoing":27760,"totalIncoming":33434},{"id":"39660","centroid":[-102.8998870018002,44.19147075411888],"population":141783,"totalOutgoing":6127,"totalIncoming":5783},{"id":"17300","centroid":[-87.56251500383206,36.746850892735395],"population":277162,"totalOutgoing":22186,"totalIncoming":20085},{"id":"15980","centroid":[-81.81996015587278,26.57911278376274],"population":695126,"totalOutgoing":25676,"totalIncoming":37381},{"id":"21500","centroid":[-80.0328125970671,41.992588491583675],"population":273533,"totalOutgoing":10666,"totalIncoming":8478},{"id":"46660","centroid":[-83.24154726536817,30.829427579777885],"population":142124,"totalOutgoing":6808,"totalIncoming":6616},{"id":"23420","centroid":[-119.64931830532569,36.75819028709072],"population":960098,"totalOutgoing":27652,"totalIncoming":27872},{"id":"25060","centroid":[-89.01966023778509,30.500499271580654],"population":384229,"totalOutgoing":17041,"totalIncoming":16974},{"id":"13740","centroid":[-108.71762646524164,45.780694639088594],"population":167921,"totalOutgoing":4964,"totalIncoming":3698},{"id":"47220","centroid":[-75.11074900436931,39.37383979027761],"population":151678,"totalOutgoing":7016,"totalIncoming":7152},{"id":"34820","centroid":[-78.66399551501094,33.987011906768835],"population":435176,"totalOutgoing":18014,"totalIncoming":25547},{"id":"26380","centroid":[-90.66592032576123,29.48518510844245],"population":207307,"totalOutgoing":3805,"totalIncoming":4526},{"id":"25180","centroid":[-77.90090478449689,39.546796684036416],"population":258129,"totalOutgoing":10915,"totalIncoming":13025},{"id":"19460","centroid":[-87.10257006757875,34.49064044157397],"population":150094,"totalOutgoing":4397,"totalIncoming":4557},{"id":"33700","centroid":[-120.99768288501825,37.55914038668041],"population":529199,"totalOutgoing":18199,"totalIncoming":19309},{"id":"25540","centroid":[-72.57734416463256,41.734626624770506],"population":1196226,"totalOutgoing":43609,"totalIncoming":38729},{"id":"49180","centroid":[-80.34581022887306,36.07663632289534],"population":649949,"totalOutgoing":23888,"totalIncoming":26886},{"id":"33460","centroid":[-93.34558708746027,45.06500504822576],"population":3507277,"totalOutgoing":85245,"totalIncoming":72698},{"id":"40340","centroid":[-92.33807183463539,43.955988600268086],"population":213452,"totalOutgoing":7609,"totalIncoming":6139},{"id":"30860","centroid":[-111.76898127262625,41.88956578583493],"population":131717,"totalOutgoing":8126,"totalIncoming":7648},{"id":"29820","centroid":[-115.01353269152919,36.21523038060011],"population":2081248,"totalOutgoing":66197,"totalIncoming":89802},{"id":"10580","centroid":[-73.94198887191097,42.788511888199935],"population":867402,"totalOutgoing":29156,"totalIncoming":29421},{"id":"39580","centroid":[-78.46086692967947,35.75724983934009],"population":1264270,"totalOutgoing":49638,"totalIncoming":63012},{"id":"28740","centroid":[-74.25854419873329,41.88813506957871],"population":176659,"totalOutgoing":6450,"totalIncoming":7544},{"id":"40060","centroid":[-77.47356546772028,37.46200713206555],"population":1254237,"totalOutgoing":44014,"totalIncoming":48251},{"id":"22520","centroid":[-87.72396499192521,34.80826223327141],"population":143797,"totalOutgoing":3896,"totalIncoming":4990},{"id":"10420","centroid":[-81.34953563728817,41.14871834295054],"totalIncoming":25996,"population":694922,"totalOutgoing":25964},{"id":"23060","centroid":[-85.21678156814913,41.005417509260475],"population":425413,"totalOutgoing":11487,"totalIncoming":10784},{"id":"14010","centroid":[-88.86172500906028,40.41065466216124],"population":187284,"totalOutgoing":10640,"totalIncoming":9832},{"id":"17860","centroid":[-92.30967054318971,38.990627405086855],"population":172016,"totalOutgoing":12607,"totalIncoming":12553},{"id":"28140","centroid":[-94.4443728819982,38.93714640889055],"population":2077504,"totalOutgoing":61043,"totalIncoming":58075},{"id":"16700","centroid":[-80.04430040052706,33.042564787300336],"population":742044,"totalOutgoing":32733,"totalIncoming":36082},{"id":"27620","centroid":[-92.09170757893568,38.63980693046525],"population":149098,"totalOutgoing":5369,"totalIncoming":5956},{"id":"32780","centroid":[-122.72849953452875,42.432189539550954],"population":210823,"totalOutgoing":9217,"totalIncoming":9206},{"id":"47380","centroid":[-97.0897947810793,31.426423527468597],"population":257731,"totalOutgoing":11668,"totalIncoming":14788},{"id":"37340","centroid":[-80.73226959232124,28.29371773976019],"population":560204,"totalOutgoing":23068,"totalIncoming":30913},{"id":"24300","centroid":[-108.46643085982707,39.018288696041466],"population":145239,"totalOutgoing":5480,"totalIncoming":7123},{"id":"34580","centroid":[-121.7188267196097,48.47842988014873],"population":120510,"totalOutgoing":5263,"totalIncoming":7331},{"id":"42100","centroid":[-122.00184086940025,37.05618005990083],"population":267744,"totalOutgoing":14662,"totalIncoming":16607},{"id":"33660","centroid":[-88.20646580584146,30.791515923583663],"population":412116,"totalOutgoing":13419,"totalIncoming":9697},{"id":"49340","centroid":[-71.92740065428318,42.22281378315466],"population":931291,"totalOutgoing":38882,"totalIncoming":32356},{"id":"45460","centroid":[-87.34400269963203,39.39190431106162],"population":164641,"totalOutgoing":5312,"totalIncoming":7705},{"id":"49620","centroid":[-76.72651209153926,39.91995754038726],"population":438249,"totalOutgoing":15814,"totalIncoming":15592},{"id":"13380","centroid":[-121.71193056834014,48.826226623246114],"population":208926,"totalOutgoing":9384,"totalIncoming":12726},{"id":"15680","centroid":[-76.60578224076886,38.30243517206398],"population":109171,"totalOutgoing":6911,"totalIncoming":7548},{"id":"27340","centroid":[-77.4327682842837,34.732385746450596],"population":181494,"totalOutgoing":21744,"totalIncoming":26682},{"id":"32900","centroid":[-120.71765470093608,37.1918924082313],"population":263220,"totalOutgoing":11150,"totalIncoming":12137},{"id":"39340","centroid":[-112.35258355061582,39.86451873693232],"population":575335,"totalOutgoing":30466,"totalIncoming":35465},{"id":"18700","centroid":[-123.42926986192019,44.491792332533976],"population":85988,"totalOutgoing":8742,"totalIncoming":10182},{"id":"10380","centroid":[-66.9534689756726,18.33539940264403],"totalIncoming":3598,"population":313882,"totalOutgoing":11704},{"id":"30700","centroid":[-96.87081354342973,40.819917365586676],"population":313184,"totalOutgoing":10669,"totalIncoming":14738},{"id":"11100","centroid":[-101.90950753932285,35.248874790386566],"population":262324,"totalOutgoing":11114,"totalIncoming":6424},{"id":"19820","centroid":[-83.23270891674714,42.719197307387844],"population":4273143,"totalOutgoing":98787,"totalIncoming":74200},{"id":"12700","centroid":[-70.291468420632,41.72418368535354],"population":212809,"totalOutgoing":9007,"totalIncoming":7154},{"id":"46520","centroid":[-157.9728605595027,21.457323759506384],"population":973539,"totalOutgoing":47717,"totalIncoming":38438},{"id":"31080","centroid":[-118.1410681044627,34.2496393902452],"population":13137459,"totalOutgoing":365670,"totalIncoming":245061},{"id":"31140","centroid":[-85.6708501495599,38.33675203722141],"population":1270956,"totalOutgoing":32289,"totalIncoming":28094},{"id":"41860","centroid":[-121.90851662387782,37.784168943536024],"population":4595540,"totalOutgoing":184898,"totalIncoming":161240},{"id":"35980","centroid":[-72.10147788985178,41.48660940080825],"population":264050,"totalOutgoing":14311,"totalIncoming":15670},{"id":"30980","centroid":[-94.82943331799872,32.371668943870944],"population":210866,"totalOutgoing":7331,"totalIncoming":9651},{"id":"41420","centroid":[-122.90336622467568,44.903397729642734],"population":407228,"totalOutgoing":17094,"totalIncoming":19562},{"id":"18580","centroid":[-97.56268025061628,27.864816672583437],"population":440791,"totalOutgoing":16452,"totalIncoming":19299},{"id":"28700","centroid":[-82.44261316218726,36.607772296987434],"population":301786,"totalOutgoing":8525,"totalIncoming":8834},{"id":"34900","centroid":[-122.33051904043957,38.506480285894746],"population":141071,"totalOutgoing":8073,"totalIncoming":6165},{"id":"46540","centroid":[-75.18139031450718,43.33741583693321],"population":289015,"totalOutgoing":8123,"totalIncoming":8528},{"id":"39820","centroid":[-122.04050796713213,40.763707918289],"population":176576,"totalOutgoing":7066,"totalIncoming":6858},{"id":"12940","centroid":[-91.13278442388855,30.57107481378725],"population":815741,"totalOutgoing":23096,"totalIncoming":26238},{"id":"41500","centroid":[-121.23919425672122,36.21715708349306],"population":423784,"totalOutgoing":20754,"totalIncoming":20682},{"id":"27180","centroid":[-88.85288475274106,35.60953450327306],"population":128199,"totalOutgoing":3600,"totalIncoming":2728},{"id":"24020","centroid":[-73.64881609880116,43.443544914574424],"population":125045,"totalOutgoing":4616,"totalIncoming":4553},{"id":"10540","centroid":[-122.53499247914424,44.488877520668105],"population":120610,"totalOutgoing":6686,"totalIncoming":7829},{"id":"49020","centroid":[-78.4738799350256,39.272173420725615],"population":133790,"totalOutgoing":5429,"totalIncoming":5747},{"id":"27140","centroid":[-90.22046404367492,32.31702056348438],"population":570495,"totalOutgoing":14195,"totalIncoming":14697},{"id":"16020","centroid":[-89.769817820826,37.32447086546932],"population":95624,"totalOutgoing":3595,"totalIncoming":3228},{"id":"37100","centroid":[-119.07822973852554,34.47159530368873],"population":839769,"totalOutgoing":33561,"totalIncoming":28054},{"id":"29540","centroid":[-76.24772926979058,40.042432784975034],"population":529775,"totalOutgoing":17884,"totalIncoming":18580},{"id":"15180","centroid":[-97.52221536102066,26.131957825040303],"population":419983,"totalOutgoing":11813,"totalIncoming":5707},{"id":"33740","centroid":[-92.2845926860352,32.68685412885709],"population":176439,"totalOutgoing":4924,"totalIncoming":4025},{"id":"41140","centroid":[-94.78382951670349,39.83408306897171],"population":124005,"totalOutgoing":5105,"totalIncoming":5010},{"id":"35620","centroid":[-74.08870824935313,40.92247434472604],"population":19847550,"totalOutgoing":460501,"totalIncoming":232214},{"id":"37980","centroid":[-75.30194655041657,39.90563296376845],"population":5988725,"totalOutgoing":158969,"totalIncoming":143308},{"id":"26420","centroid":[-95.40393444250026,29.790532809740483],"population":6593374,"totalOutgoing":148881,"totalIncoming":175013},{"id":"43300","centroid":[-96.67771954901613,33.62676954269254],"population":123250,"totalOutgoing":3757,"totalIncoming":6965},{"id":"16580","centroid":[-88.29452175254535,40.226682984475275],"population":225693,"totalOutgoing":14261,"totalIncoming":21216},{"id":"16820","centroid":[-78.5763212668309,37.85206592495693],"population":224492,"totalOutgoing":14055,"totalIncoming":15675},{"id":"16860","centroid":[-85.3591460699924,35.051976733387896],"population":542050,"totalOutgoing":15221,"totalIncoming":18067},{"id":"48660","centroid":[-98.49141100190886,33.77468570466272],"population":145742,"totalOutgoing":8357,"totalIncoming":9486},{"id":"22900","centroid":[-94.56609692852284,35.190475176615124],"population":276746,"totalOutgoing":7331,"totalIncoming":7457},{"id":"15260","centroid":[-81.63656665242503,31.3117918471666],"population":113211,"totalOutgoing":4024,"totalIncoming":4735},{"id":"15500","centroid":[-79.39943745370725,36.04374084677399],"population":157351,"totalOutgoing":7031,"totalIncoming":8196},{"id":"33340","centroid":[-88.17241810671469,43.17664265606479],"population":1561879,"totalOutgoing":45034,"totalIncoming":36695},{"id":"26620","centroid":[-86.73466871109687,34.783200579777294],"population":440895,"totalOutgoing":16284,"totalIncoming":18854},{"id":"12420","centroid":[-97.65444553568648,30.262591026848387],"population":1990877,"totalOutgoing":81180,"totalIncoming":108135},{"id":"49420","centroid":[-120.73846602924971,46.457064301265916],"population":244791,"totalOutgoing":6521,"totalIncoming":7168},{"id":"24420","centroid":[-123.55547496511662,42.36547434583094],"population":82481,"totalOutgoing":3008,"totalIncoming":4800},{"id":"31460","centroid":[-119.76266847019161,37.21798987825665],"population":154618,"totalOutgoing":9386,"totalIncoming":7264},{"id":"48900","centroid":[-77.90104635852381,34.46678960137711],"population":275693,"totalOutgoing":14570,"totalIncoming":17510},{"id":"17420","centroid":[-84.66739478870895,35.13454861084555],"population":120066,"totalOutgoing":5171,"totalIncoming":4527},{"id":"44300","centroid":[-77.81994987928034,40.91932652827721],"population":149848,"totalOutgoing":9219,"totalIncoming":15524},{"id":"33540","centroid":[-113.92370936350243,47.036511978448026],"population":112126,"totalOutgoing":4361,"totalIncoming":5474},{"id":"23460","centroid":[-86.03477283528302,34.04525586637633],"population":101586,"totalOutgoing":3024,"totalIncoming":2941},{"id":"26820","centroid":[-112.42791815514842,43.62250568343766],"population":138261,"totalOutgoing":4451,"totalIncoming":6340},{"id":"28660","centroid":[-97.7877004524827,31.208098208640425],"population":420634,"totalOutgoing":31732,"totalIncoming":35758},{"id":"18020","centroid":[-85.89758711112447,39.2059563988753],"population":80471,"totalOutgoing":4011,"totalIncoming":3146},{"id":"26300","centroid":[-93.15042061317482,34.576663889086696],"population":97080,"totalOutgoing":3746,"totalIncoming":4093},{"id":"46060","centroid":[-111.78992646107346,32.09742837271627],"population":989984,"totalOutgoing":42440,"totalIncoming":49778},{"id":"19380","centroid":[-84.14194765456716,39.82946598162232],"population":786968,"totalOutgoing":30496,"totalIncoming":30989},{"id":"48540","centroid":[-80.84132068632847,39.97477030390249],"population":141036,"totalOutgoing":3447,"totalIncoming":3638},{"id":"23580","centroid":[-83.81966535896163,34.31691030750534],"population":191354,"totalOutgoing":6864,"totalIncoming":8675},{"id":"38060","centroid":[-112.07046317841404,33.1857083135379],"population":4539283,"totalOutgoing":129467,"totalIncoming":177642},{"id":"26140","centroid":[-82.4786448195623,28.84907710339455],"population":140360,"totalOutgoing":7103,"totalIncoming":8733},{"id":"44180","centroid":[-93.17706845346412,37.36168148523521],"population":445982,"totalOutgoing":12986,"totalIncoming":16209},{"id":"10740","centroid":[-106.47157567375328,35.12222665430155],"population":897771,"totalOutgoing":28944,"totalIncoming":26097},{"id":"24580","centroid":[-88.07653663391626,44.77289829912982],"population":312887,"totalOutgoing":10096,"totalIncoming":9631},{"id":"47020","centroid":[-97.19500226091074,28.727940426666294],"population":99172,"totalOutgoing":3794,"totalIncoming":2675},{"id":"40380","centroid":[-77.50789820520173,42.96592493293086],"population":1056347,"totalOutgoing":30712,"totalIncoming":32024},{"id":"41100","centroid":[-113.50476315077788,37.28037884998011],"population":156174,"totalOutgoing":8654,"totalIncoming":10665},{"id":"14100","centroid":[-76.45900346948467,41.04427663065203],"population":83493,"totalOutgoing":4016,"totalIncoming":4157},{"id":"31900","centroid":[-82.53648847855189,40.77465496144519],"population":120636,"totalOutgoing":4192,"totalIncoming":4632},{"id":"21140","centroid":[-85.85874458965431,41.597391184383454],"population":202631,"totalOutgoing":7971,"totalIncoming":5890},{"id":"48060","centroid":[-75.91801709961582,44.05013302116186],"population":113268,"totalOutgoing":11881,"totalIncoming":8771},{"id":"19140","centroid":[-84.8480995796864,34.79627410981646],"population":142012,"totalOutgoing":4053,"totalIncoming":3427},{"id":"11540","centroid":[-88.37116354457027,44.28901993165939],"population":234922,"totalOutgoing":10192,"totalIncoming":7941},{"id":"16220","centroid":[-106.79849288524338,42.962052574291235],"population":80056,"totalOutgoing":2263,"totalIncoming":1834},{"id":"16060","centroid":[-89.19025838181076,37.761850439857106],"population":122569,"totalOutgoing":5767,"totalIncoming":6827},{"id":"16180","centroid":[-119.74740325350957,39.151144767211186],"population":52685,"totalOutgoing":2669,"totalIncoming":3954},{"id":"14020","centroid":[-86.6759445363331,39.23470426942268],"population":153660,"totalOutgoing":9350,"totalIncoming":15532},{"id":"40980","centroid":[-84.05317847142034,43.33504071670162],"population":191176,"totalOutgoing":7255,"totalIncoming":5912},{"id":"29420","centroid":[-113.75795050174551,35.7040898051281],"population":198939,"totalOutgoing":10850,"totalIncoming":14813},{"id":"28020","centroid":[-85.78378319272896,42.2484849184587],"population":327243,"totalOutgoing":14459,"totalIncoming":16441},{"id":"27900","centroid":[-94.33994411547722,37.056553557884506],"population":175107,"totalOutgoing":5608,"totalIncoming":4971},{"id":"19740","centroid":[-104.93314657647944,39.44279358856491],"population":2793498,"totalOutgoing":111643,"totalIncoming":120185},{"id":"22420","centroid":[-83.70670520204465,43.02171299220497],"population":409690,"totalOutgoing":16155,"totalIncoming":11522},{"id":"11180","centroid":[-93.46504628364463,42.03623014976688],"totalIncoming":8939,"population":90534,"totalOutgoing":7130},{"id":"39380","centroid":[-104.5127376595403,38.17352376438713],"population":161359,"totalOutgoing":6241,"totalIncoming":6957},{"id":"17820","centroid":[-104.6577870938409,38.84251223637318],"population":685984,"totalOutgoing":46857,"totalIncoming":57150},{"id":"42680","centroid":[-80.60622672349265,27.694315330059652],"population":145426,"totalOutgoing":6042,"totalIncoming":9209},{"id":"34740","centroid":[-86.15203523059672,43.291243445926135],"population":171068,"totalOutgoing":6700,"totalIncoming":6328},{"id":"13020","centroid":[-83.99174516321874,43.708044443559494],"population":104447,"totalOutgoing":3695,"totalIncoming":2561},{"id":"36220","centroid":[-102.54288036515717,31.86919144747354],"population":155773,"totalOutgoing":8009,"totalIncoming":7869},{"id":"38540","centroid":[-112.22461111322681,42.668485465951214],"population":83412,"totalOutgoing":4420,"totalIncoming":3984},{"id":"23900","centroid":[-77.21787447860055,39.87148854725977],"population":101394,"totalOutgoing":5561,"totalIncoming":5325},{"id":"36780","centroid":[-88.6446497061956,44.06888075803715],"population":165035,"totalOutgoing":7887,"totalIncoming":10431},{"id":"26900","centroid":[-86.20613178327208,39.747420338213566],"population":1982144,"totalOutgoing":60649,"totalIncoming":54501},{"id":"25620","centroid":[-89.22874264440281,31.187400112436592],"population":145365,"totalOutgoing":6717,"totalIncoming":5988},{"id":"31180","centroid":[-101.64468450229714,33.46855690493853],"population":300064,"totalOutgoing":14942,"totalIncoming":20168},{"id":"21420","centroid":[-97.78271674649967,36.37906374416364],"population":61031,"totalOutgoing":2830,"totalIncoming":2837},{"id":"32420","centroid":[-67.10998367118528,18.19536622946558],"population":92476,"totalOutgoing":4190,"totalIncoming":4461},{"id":"19780","centroid":[-93.9404423008203,41.547817923637474],"population":616933,"totalOutgoing":17947,"totalIncoming":22327},{"id":"44420","centroid":[-79.1289058462202,38.16288220060635],"population":118094,"totalOutgoing":4958,"totalIncoming":5851},{"id":"42200","centroid":[-120.02227679764468,34.72481221658118],"population":429155,"totalOutgoing":20370,"totalIncoming":25772},{"id":"19500","centroid":[-88.96155322725211,39.8599901075048],"population":105138,"totalOutgoing":2981,"totalIncoming":3295},{"id":"37900","centroid":[-89.51590241892289,40.78915913062872],"population":373669,"totalOutgoing":13409,"totalIncoming":8795},{"id":"27260","centroid":[-81.79207453642587,30.236717546084236],"population":1430040,"totalOutgoing":54526,"totalIncoming":72138},{"id":"43100","centroid":[-87.94536378123698,43.721173783633056],"population":114275,"totalOutgoing":3887,"totalIncoming":3173},{"id":"16300","centroid":[-91.63135045555875,42.091475788065615],"population":264105,"totalOutgoing":8907,"totalIncoming":9334},{"id":"12220","centroid":[-85.35547075770234,32.60113416783121],"population":152075,"totalOutgoing":10146,"totalIncoming":12756},{"id":"45220","centroid":[-84.28616754438579,30.40501967205732],"population":364852,"totalOutgoing":19992,"totalIncoming":28003},{"id":"13900","centroid":[-100.99126203004337,46.72679975498895],"population":127572,"totalOutgoing":3653,"totalIncoming":3267},{"id":"17660","centroid":[-116.70181080180137,47.67437162664832],"population":150739,"totalOutgoing":5568,"totalIncoming":6675},{"id":"30780","centroid":[-92.39717184322248,34.75721003166211],"population":728049,"totalOutgoing":22852,"totalIncoming":18137},{"id":"45500","centroid":[-94.21373923046791,33.4736080275712],"population":148783,"totalOutgoing":5236,"totalIncoming":3318},{"id":"38860","centroid":[-70.47130038828529,43.69605958345985],"population":521417,"totalOutgoing":17606,"totalIncoming":17801},{"id":"30620","centroid":[-84.10579330952636,40.77153662710219],"population":101852,"totalOutgoing":3356,"totalIncoming":3059},{"id":"27500","centroid":[-89.07157642092481,42.671234437311014],"population":160046,"totalOutgoing":4998,"totalIncoming":5361},{"id":"35660","centroid":[-86.41225880868762,41.95468625367055],"population":152651,"totalOutgoing":7625,"totalIncoming":6721},{"id":"19340","centroid":[-90.46591106833837,41.39686827032191],"population":380587,"totalOutgoing":11577,"totalIncoming":8775},{"id":"22220","centroid":[-94.12156702563081,36.1961676435836],"population":509526,"totalOutgoing":14820,"totalIncoming":21312},{"id":"27100","centroid":[-84.42341882132878,42.24848551825214],"population":156145,"totalOutgoing":5835,"totalIncoming":6312},{"id":"20500","centroid":[-79.09963176006767,35.9909467769231],"population":546072,"totalOutgoing":36334,"totalIncoming":36331},{"id":"32580","centroid":[-98.18119802486879,26.396880420165537],"population":837883,"totalOutgoing":17702,"totalIncoming":12910},{"id":"30020","centroid":[-98.43475868360694,34.52407866679185],"population":121514,"totalOutgoing":9802,"totalIncoming":11909},{"id":"20220","centroid":[-90.88245416449409,42.468812140657064],"population":93604,"totalOutgoing":2577,"totalIncoming":3850},{"id":"38940","centroid":[-80.45072607608947,27.219825778587783],"population":445780,"totalOutgoing":18239,"totalIncoming":28837},{"id":"27860","centroid":[-90.64839888489159,35.69817032567709],"population":126995,"totalOutgoing":3692,"totalIncoming":3168},{"id":"22540","centroid":[-88.48826251948759,43.753572395420186],"population":101606,"totalOutgoing":3963,"totalIncoming":3341},{"id":"43780","centroid":[-86.13411026639889,41.77363022242228],"population":316043,"totalOutgoing":15439,"totalIncoming":13800},{"id":"29740","centroid":[-106.83278215672763,32.352644461436995],"population":210381,"totalOutgoing":9508,"totalIncoming":9853},{"id":"43420","centroid":[-109.75117157356337,31.879611254127507],"population":123799,"totalOutgoing":10259,"totalIncoming":10038},{"id":"28100","centroid":[-87.86183762937728,41.13770405798336],"population":110694,"totalOutgoing":4579,"totalIncoming":3832},{"id":"38340","centroid":[-73.20635221592082,42.37069953329378],"population":125567,"totalOutgoing":4165,"totalIncoming":3942},{"id":"39740","centroid":[-75.9259741645684,40.41630727291276],"population":409910,"totalOutgoing":14976,"totalIncoming":16025},{"id":"45540","centroid":[-82.080985062211,28.704766040560447],"population":116849,"totalOutgoing":5550,"totalIncoming":7968},{"id":"39900","centroid":[-119.6591086711893,40.619030981041625],"population":443039,"totalOutgoing":16532,"totalIncoming":21041},{"id":"10180","centroid":[-99.71769650063735,32.44969932393424],"totalIncoming":11121,"population":162660,"totalOutgoing":8270},{"id":"16940","centroid":[-104.68949403577261,41.30694883566864],"population":97212,"totalOutgoing":5214,"totalIncoming":3637},{"id":"24260","centroid":[-98.2795617134842,41.03317649838205],"population":84537,"totalOutgoing":2702,"totalIncoming":1854},{"id":"29940","centroid":[-95.29257873461856,38.88466463815761],"population":114185,"totalOutgoing":9561,"totalIncoming":11218},{"id":"27780","centroid":[-78.71370941539489,40.495272877437046],"population":133057,"totalOutgoing":4117,"totalIncoming":4230},{"id":"26980","centroid":[-91.64997619750302,41.51135123512182],"population":163705,"totalOutgoing":11655,"totalIncoming":10643},{"id":"48140","centroid":[-89.75908304696436,44.89829046231593],"population":134036,"totalOutgoing":4073,"totalIncoming":3195},{"id":"21300","centroid":[-76.76003638118401,42.141265147830616],"population":86451,"totalOutgoing":2975,"totalIncoming":2087},{"id":"19180","centroid":[-87.73281327338208,40.18341523940647],"population":78150,"totalOutgoing":2442,"totalIncoming":1990},{"id":"34620","centroid":[-85.39690272508862,40.22754482906938],"population":111621,"totalOutgoing":5773,"totalIncoming":7127},{"id":"39540","centroid":[-88.0612073920038,42.74745742049676],"population":193334,"totalOutgoing":8082,"totalIncoming":8156},{"id":"10500","centroid":[-84.17391944810625,31.58949466834173],"population":150960,"totalOutgoing":5509,"totalIncoming":5271},{"id":"24140","centroid":[-78.00400023169554,35.36396076797352],"population":120861,"totalOutgoing":4796,"totalIncoming":5993},{"id":"11500","centroid":[-85.82603932702473,33.771416634176816],"population":113503,"totalOutgoing":5278,"totalIncoming":4199},{"id":"21780","centroid":[-87.57908517187951,37.971134644816615],"population":309600,"totalOutgoing":7584,"totalIncoming":7687},{"id":"23540","centroid":[-82.47649744396945,29.68845853623631],"population":274531,"totalOutgoing":20375,"totalIncoming":22847},{"id":"46220","centroid":[-87.72184802108808,33.168059876396434],"population":232081,"totalOutgoing":9076,"totalIncoming":14161},{"id":"25940","centroid":[-80.87702455268884,32.41047982001975],"population":204011,"totalOutgoing":13240,"totalIncoming":15746},{"id":"41540","centroid":[-75.46388778699028,38.42022849493691],"population":389110,"totalOutgoing":13383,"totalIncoming":17862},{"id":"34940","centroid":[-81.3475645583453,26.110720035312877],"population":349519,"totalOutgoing":16240,"totalIncoming":21459},{"id":"29460","centroid":[-81.69758641360337,27.948880567459803],"population":650823,"totalOutgoing":31573,"totalIncoming":38490},{"id":"46340","centroid":[-95.2691739475883,32.37503229025402],"population":219583,"totalOutgoing":7710,"totalIncoming":9475},{"id":"31860","centroid":[-94.13558645727755,44.15442979546815],"population":97204,"totalOutgoing":5687,"totalIncoming":5900},{"id":"18880","centroid":[-86.36565856676295,30.665735230310172],"population":260138,"totalOutgoing":19018,"totalIncoming":19416},{"id":"36980","centroid":[-87.06949180936147,37.69966364813021],"population":116785,"totalOutgoing":3821,"totalIncoming":3094},{"id":"27980","centroid":[-156.3372350736001,20.7908111049152],"population":161711,"totalOutgoing":5550,"totalIncoming":5694},{"id":"16540","centroid":[-77.72128395290827,39.9274116712345],"population":152849,"totalOutgoing":6297,"totalIncoming":5253},{"id":"34980","centroid":[-86.72447551722279,36.08907196544388],"population":1812296,"totalOutgoing":53127,"totalIncoming":73298},{"id":"13820","centroid":[-86.81387443367989,33.46381824816824],"population":1129024,"totalOutgoing":32198,"totalIncoming":32894},{"id":"38300","centroid":[-79.83095629379706,40.43902359434117],"population":2314794,"totalOutgoing":57703,"totalIncoming":51019},{"id":"36740","centroid":[-81.36301633193351,28.434337565472667],"population":2364996,"totalOutgoing":99470,"totalIncoming":135072},{"id":"47940","centroid":[-92.47118614183225,42.53622808413449],"population":166451,"totalOutgoing":6781,"totalIncoming":5653},{"id":"12580","centroid":[-76.67615419470228,39.384567506050836],"population":2757814,"totalOutgoing":97429,"totalIncoming":86593},{"id":"33140","centroid":[-86.73996211078939,41.545987035115836],"population":106702,"totalOutgoing":4860,"totalIncoming":6904},{"id":"46700","centroid":[-121.93285046213879,38.269975082016096],"population":424606,"totalOutgoing":20714,"totalIncoming":27776},{"id":"35300","centroid":[-72.93225009147011,41.410625415587546],"population":849990,"totalOutgoing":34174,"totalIncoming":30634},{"id":"48700","centroid":[-77.06452304915689,41.343400913143014],"population":113062,"totalOutgoing":3140,"totalIncoming":3407},{"id":"40660","centroid":[-85.2142581296146,34.263186341927494],"population":95299,"totalOutgoing":2798,"totalIncoming":3516},{"id":"30460","centroid":[-84.43306110060375,38.09051543000129],"population":491535,"totalOutgoing":17403,"totalIncoming":21231},{"id":"30140","centroid":[-76.45770982026379,40.36722516805555],"population":135606,"totalOutgoing":5922,"totalIncoming":6958},{"id":"25260","centroid":[-119.81554108803869,36.07534832037939],"population":144677,"totalOutgoing":9208,"totalIncoming":12391},{"id":"39460","centroid":[-81.91224150204987,26.90548578911923],"population":168903,"totalOutgoing":8680,"totalIncoming":13732},{"id":"24660","centroid":[-79.7916006321928,36.0258629113403],"population":743039,"totalOutgoing":27732,"totalIncoming":30428},{"id":"20740","centroid":[-91.28218722845894,44.938827419154634],"population":162212,"totalOutgoing":4942,"totalIncoming":5925},{"id":"11460","centroid":[-83.83876239846369,42.253217458740885],"population":350526,"totalOutgoing":25718,"totalIncoming":31312},{"id":"33220","centroid":[-84.38812678463356,43.6468437790163],"population":82317,"totalOutgoing":3544,"totalIncoming":2784},{"id":"49660","centroid":[-80.56761579430864,41.237835726531216],"population":542353,"totalOutgoing":16091,"totalIncoming":12961},{"id":"22020","centroid":[-96.96548624878402,46.91781759820481],"population":229006,"totalOutgoing":9352,"totalIncoming":10744},{"id":"12060","centroid":[-84.33973325935013,33.73263991714003],"population":5667591,"totalOutgoing":161709,"totalIncoming":174130},{"id":"15940","centroid":[-81.25348334362806,40.71864738275839],"population":398656,"totalOutgoing":12292,"totalIncoming":9779},{"id":"42140","centroid":[-105.97626349685544,35.506908157466405],"population":147130,"totalOutgoing":5852,"totalIncoming":5696},{"id":"29620","centroid":[-84.60697424405832,42.713309101757446],"population":459253,"totalOutgoing":18620,"totalIncoming":25934},{"id":"25860","centroid":[-81.4542776651643,35.81289868584043],"population":359093,"totalOutgoing":9757,"totalIncoming":11545},{"id":"26580","centroid":[-82.37912183152388,38.3813673809656],"population":357961,"totalOutgoing":9917,"totalIncoming":6684},{"id":"20940","centroid":[-115.36535522772137,33.03950915319923],"population":176937,"totalOutgoing":6185,"totalIncoming":5189},{"id":"29200","centroid":[-86.92955045438244,40.51432462012345],"population":205655,"totalOutgoing":10680,"totalIncoming":14526},{"id":"35840","centroid":[-82.32228618930826,27.347841264841087],"population":759244,"totalOutgoing":29443,"totalIncoming":43783},{"id":"31700","centroid":[-71.71609235403028,42.91534104717215],"population":398223,"totalOutgoing":13426,"totalIncoming":19218},{"id":"11020","centroid":[-78.34860335594603,40.48099294918067],"population":122936,"totalOutgoing":3839,"totalIncoming":3122},{"id":"27060","centroid":[-76.47363388288318,42.45202606694531],"population":96907,"totalOutgoing":7547,"totalIncoming":10370},{"id":"44220","centroid":[-83.78386631783063,39.916810922033875],"population":132200,"totalOutgoing":5549,"totalIncoming":5859},{"id":"40420","centroid":[-89.04205850701764,42.33161398863439],"population":334722,"totalOutgoing":10454,"totalIncoming":11465},{"id":"41660","centroid":[-100.67301215802212,31.363696705227767],"population":116314,"totalOutgoing":6321,"totalIncoming":7217},{"id":"12540","centroid":[-118.72992035400243,35.34286311159376],"population":864685,"totalOutgoing":30492,"totalIncoming":35524},{"id":"30340","centroid":[-70.20647067514312,44.16580195756288],"population":106155,"totalOutgoing":3711,"totalIncoming":4175},{"id":"12980","centroid":[-85.00558678788316,42.24654529798705],"population":132955,"totalOutgoing":5128,"totalIncoming":5511},{"id":"11700","centroid":[-82.68267400715612,35.600657745068844],"population":441165,"totalOutgoing":14690,"totalIncoming":17975},{"id":"45780","centroid":[-83.78234870268784,41.49830923047687],"population":594339,"totalOutgoing":18526,"totalIncoming":18312},{"id":"14460","centroid":[-71.10062404834406,42.55813693694584],"population":4731478,"totalOutgoing":153369,"totalIncoming":131200},{"id":"33260","centroid":[-101.99102164810996,32.08972148080117],"population":164728,"totalOutgoing":9835,"totalIncoming":10740},{"id":"12620","centroid":[-68.64945660203469,45.400641306819914],"population":147532,"totalOutgoing":3780,"totalIncoming":5118},{"id":"24540","centroid":[-104.39245245005208,40.55484536679784],"population":282322,"totalOutgoing":14404,"totalIncoming":22811},{"id":"48260","centroid":[-80.70425939278111,40.3880275427839],"population":116503,"totalOutgoing":3476,"totalIncoming":4224},{"id":"45820","centroid":[-95.8031383995934,39.04381679977791],"population":232020,"totalOutgoing":9135,"totalIncoming":6569},{"id":"40580","centroid":[-77.79847169525515,35.94100415498621],"population":146596,"totalOutgoing":5861,"totalIncoming":4711},{"id":"45300","centroid":[-82.40541663109217,28.1540144965835],"population":2953610,"totalOutgoing":103404,"totalIncoming":126404},{"id":"10900","centroid":[-75.40382030262701,40.7893607694078],"population":822660,"totalOutgoing":29613,"totalIncoming":31113},{"id":"10780","centroid":[-92.54184166879028,31.33055771099685],"totalIncoming":3974,"population":152082,"totalOutgoing":4148},{"id":"12020","centroid":[-83.21379954849613,33.94903147816022],"population":189170,"totalOutgoing":8686,"totalIncoming":19815},{"id":"20100","centroid":[-75.5684255670387,39.08617433361269],"population":169248,"totalOutgoing":7135,"totalIncoming":10092},{"id":"42020","centroid":[-120.40451813478877,35.38708294060535],"population":272731,"totalOutgoing":13875,"totalIncoming":19728},{"id":"49740","centroid":[-113.90555686431634,32.769387136219066],"population":200078,"totalOutgoing":9222,"totalIncoming":10354},{"id":"20020","centroid":[-85.46174589331966,31.252927115465717],"population":147581,"totalOutgoing":4306,"totalIncoming":3102},{"id":"14860","centroid":[-73.38930511594414,41.27119694190032],"population":934440,"totalOutgoing":40512,"totalIncoming":32038},{"id":"48300","centroid":[-120.26559993027507,47.818484704321094],"population":116405,"totalOutgoing":4044,"totalIncoming":3535}] -------------------------------------------------------------------------------- /full/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const jsRules = { 2 | 'no-unused-vars': ['warn', { args: 'none' }], 3 | 'no-return-await': 'warn', 4 | 'no-use-before-define': 'error', 5 | 'no-mixed-spaces-and-tabs': 'error', 6 | 'no-trailing-spaces': 'warn', 7 | }; 8 | 9 | module.exports = { 10 | env: { 11 | browser: true, 12 | es6: true, 13 | node: true, 14 | }, 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | project: 'tsconfig.json', 18 | ecmaVersion: 2019, 19 | sourceType: 'module', 20 | }, 21 | overrides: [ 22 | { 23 | files: ['**/*.svelte'], 24 | parser: 'espree', 25 | processor: 'svelte3/svelte3', 26 | extends: ['eslint:recommended'], 27 | rules: jsRules, 28 | }, 29 | { 30 | files: ['**/*.js'], 31 | extends: ['eslint:recommended'], 32 | rules: jsRules, 33 | }, 34 | ], 35 | plugins: ['@typescript-eslint/eslint-plugin', 'svelte3'], 36 | extends: [ 37 | 'plugin:@typescript-eslint/eslint-recommended', 38 | 'prettier/@typescript-eslint', 39 | ], 40 | rules: {}, 41 | }; 42 | -------------------------------------------------------------------------------- /full/.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | public/bundle.* 3 | public/utils.* 4 | build 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # node-waf configuration 18 | .lock-wscript 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directories 24 | node_modules 25 | jspm_packages 26 | 27 | # Optional npm cache directory 28 | .npm 29 | 30 | # Optional eslint cache 31 | .eslintcache 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # Output of 'npm pack' 37 | *.tgz 38 | 39 | ### Git ### 40 | *.orig 41 | 42 | ### macOS ### 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Thumbnails 48 | ._* 49 | # Files that might appear in the root of a volume 50 | .DocumentRevisions-V100 51 | .fseventsd 52 | .Spotlight-V100 53 | .TemporaryItems 54 | .Trashes 55 | .VolumeIcon.icns 56 | .com.apple.timemachine.donotpresent 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | -------------------------------------------------------------------------------- /full/.prettierignore: -------------------------------------------------------------------------------- 1 | .git/* 2 | .DS_Store 3 | 4 | license 5 | yarn.lock 6 | .travis.yml 7 | 8 | .yarnclean 9 | .eslintignore 10 | .prettierignore 11 | .npmignore 12 | .gitignore 13 | .dockerignore 14 | 15 | dist 16 | build 17 | packages/*/lib/app 18 | consoles/*/lib/app 19 | 20 | *.ico 21 | *.html 22 | *.log 23 | *.svg 24 | *.map 25 | *.png 26 | *.snap 27 | *.ttf 28 | *.sh 29 | *.txt -------------------------------------------------------------------------------- /full/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "arrowParens": "always" 5 | } 6 | -------------------------------------------------------------------------------- /full/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Daniel Imfeld 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /full/README.md: -------------------------------------------------------------------------------- 1 | This is the full version of the Svelte Leaflet demo application, developed before the talk to show what we're aiming for. 2 | 3 | To run: 4 | 5 | 1. Install dependencies with yarn, npm, pnpm, etc. 6 | 2. `npm run dev` to start the Snowpack development server. 7 | -------------------------------------------------------------------------------- /full/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-leaflet-demo", 3 | "author": "Daniel Imfeld", 4 | "license": "MIT", 5 | "version": "0.0.2", 6 | "scripts": { 7 | "clean": "rimraf public", 8 | "rollup": "rollup -c", 9 | "rollup:watch": "rollup -c -w", 10 | "copy-snowpack-templates": "copyfiles -u 1 snowpack/* public", 11 | "build": "NODE_ENV=production run-s clean rollup", 12 | "dev": "npm-run-all clean copy-snowpack-templates start:snowpack", 13 | "dev:rollup": "NODE_ENV=development npm-run-all clean --parallel start:dev rollup:watch", 14 | "start": "sirv public --single -H 0.0.0.0", 15 | "start:dev": "NODE_ENV=development sirv public --single --dev -H 0.0.0.0", 16 | "start:snowpack": "NODE_ENV=development snowpack dev" 17 | }, 18 | "dependencies": { 19 | "@elfalem/leaflet-curve": "^0.6.0", 20 | "d3-scale": "^3.2.3", 21 | "just-flush": "^1.1.0", 22 | "leaflet": "~1.6", 23 | "sirv-cli": "^0.4.4" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.10.0", 27 | "@babel/plugin-proposal-class-properties": "^7.8.3", 28 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", 29 | "@babel/plugin-proposal-object-rest-spread": "^7.8.3", 30 | "@babel/plugin-proposal-optional-chaining": "^7.8.3", 31 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 32 | "@babel/plugin-transform-runtime": "^7.9.0", 33 | "@babel/preset-env": "^7.10.0", 34 | "@babel/preset-typescript": "^7.9.0", 35 | "@babel/runtime": "^7.10.0", 36 | "@fullhuman/postcss-purgecss": "^3.0.0", 37 | "@rollup/plugin-babel": "^5.0.3", 38 | "@rollup/plugin-commonjs": "^15.0.0", 39 | "@rollup/plugin-html": "^0.2.0", 40 | "@rollup/plugin-json": "^4.1.0", 41 | "@rollup/plugin-node-resolve": "^8.1.0", 42 | "@rollup/plugin-replace": "^2.3.3", 43 | "@snowpack/app-scripts-svelte": "^1.9.2", 44 | "@snowpack/plugin-postcss": "^1.0.7", 45 | "@tailwindcss/ui": "^0.6.2", 46 | "@types/d3-scale": "^3.2.1", 47 | "@types/leaflet": "^1.5.19", 48 | "@types/topojson-client": "^3.0.0", 49 | "@typescript-eslint/eslint-plugin": "^2.26.0", 50 | "@typescript-eslint/parser": "^2.26.0", 51 | "autoprefixer": "^9", 52 | "babel-eslint": "^10.0.3", 53 | "copyfiles": "^2.4.0", 54 | "cssnano": "^4.1.10", 55 | "eslint": "^6.8.0", 56 | "eslint-config-prettier": "^6.10.1", 57 | "eslint-plugin-svelte3": "^2.7.3", 58 | "lodash.template": "^4.5.0", 59 | "npm-run-all": "^4.1.5", 60 | "postcss": "^7", 61 | "postcss-cli": "^6.1.3", 62 | "postcss-import": "^12.0.1", 63 | "prettier": "^2", 64 | "prettier-plugin-svelte": "^1", 65 | "rimraf": "^3.0.2", 66 | "rollup": "^2.10.0", 67 | "rollup-plugin-copy": "^3.3.0", 68 | "rollup-plugin-livereload": "^2.0.0", 69 | "rollup-plugin-postcss": "^3.0.0", 70 | "rollup-plugin-svelte": "^6", 71 | "rollup-plugin-terser": "^7.0.0", 72 | "snowpack": "^2.16.1", 73 | "svelte": "^3.29.4", 74 | "svelte-preprocess": "^4.5.2", 75 | "tailwindcss": "^1.9.0", 76 | "typescript": "^4.0.5" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /full/postcss.config.js: -------------------------------------------------------------------------------- 1 | const production = process.env.NODE_ENV !== 'development'; 2 | const purgecss = require('@fullhuman/postcss-purgecss'); 3 | const cssnano = require('cssnano'); 4 | 5 | module.exports = { 6 | plugins: [ 7 | require('postcss-import')(), 8 | require('tailwindcss'), 9 | require('autoprefixer'), 10 | production && 11 | purgecss({ 12 | content: ['./src/**/*.html', './static/**/*.html', './src/**/*.svelte'], 13 | whitelistPatterns: [/^svelte-/], 14 | defaultExtractor: (content) => { 15 | const regExp = new RegExp(/[A-Za-z0-9-_:/.]+/g); 16 | 17 | const matchedTokens = []; 18 | 19 | let match = regExp.exec(content); 20 | // To make sure that you do not lose any tailwind classes used in class directive. 21 | // https://github.com/tailwindcss/discuss/issues/254#issuecomment-517918397 22 | while (match) { 23 | if (match[0].startsWith('class:')) { 24 | matchedTokens.push(match[0].substring(6)); 25 | } else { 26 | matchedTokens.push(match[0]); 27 | } 28 | 29 | match = regExp.exec(content); 30 | } 31 | 32 | return matchedTokens; 33 | }, 34 | }), 35 | production && cssnano(), 36 | ].filter(Boolean), 37 | }; 38 | -------------------------------------------------------------------------------- /full/rollup.config.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import template from 'lodash.template'; 3 | import svelte from 'rollup-plugin-svelte'; 4 | import resolve from '@rollup/plugin-node-resolve'; 5 | import replace from '@rollup/plugin-replace'; 6 | import commonjs from '@rollup/plugin-commonjs'; 7 | import livereload from 'rollup-plugin-livereload'; 8 | import { terser } from 'rollup-plugin-terser'; 9 | import postcss from 'rollup-plugin-postcss'; 10 | import babel from '@rollup/plugin-babel'; 11 | import json from '@rollup/plugin-json'; 12 | import html, { makeHtmlAttributes } from '@rollup/plugin-html'; 13 | import copy from 'rollup-plugin-copy'; 14 | 15 | const production = process.env.NODE_ENV !== 'development'; 16 | 17 | const babelConfig = { 18 | extensions: ['.js', '.mjs', '.html', '.svelte', '.ts'], 19 | babelHelpers: 'bundled', 20 | exclude: ['node_modules/@babel/**', 'static/**', 'build/**', 'public/**'], 21 | presets: [ 22 | ['@babel/preset-env', { targets: { esmodules: true } }], 23 | '@babel/preset-typescript', 24 | ], 25 | plugins: [ 26 | '@babel/plugin-syntax-dynamic-import', 27 | '@babel/proposal-class-properties', 28 | '@babel/plugin-proposal-object-rest-spread', 29 | '@babel/plugin-proposal-nullish-coalescing-operator', 30 | '@babel/plugin-proposal-optional-chaining', 31 | ], 32 | }; 33 | 34 | export default { 35 | input: 'src/index.js', 36 | output: { 37 | dir: 'public', 38 | entryFileNames: '[name].[hash].js', 39 | chunkFileNames: '[name].[hash].js', 40 | assetFileNames: '[name].[hash][extname]', 41 | sourcemap: production ? true : 'inline', 42 | format: 'esm', 43 | manualChunks(id) { 44 | if (id.includes('node_modules')) { 45 | return 'vendor'; 46 | } else if (id.endsWith('.json')) { 47 | return 'data'; 48 | } 49 | }, 50 | }, 51 | plugins: [ 52 | svelte({ 53 | preprocess: require('./svelte.config').preprocess, 54 | // enable run-time checks when not in production 55 | dev: !production, 56 | // extract any component CSS out into 57 | // a separate file — better for performance 58 | // css: css => { 59 | // css.write('public/bundle.css'); 60 | // }, 61 | // Instead, emit CSS as a file for processing through rollup 62 | emitCss: true, 63 | }), 64 | postcss({ 65 | extract: true, 66 | }), 67 | 68 | resolve({ 69 | mainFields: ['module', 'browser', 'main'], 70 | extensions: ['.mjs', '.js', '.json', '.ts', '.svelte'], 71 | dedupe: ['svelte'], 72 | }), 73 | copy({ 74 | targets: [{ src: 'static/**/*', dest: 'public/' }], 75 | }), 76 | commonjs(), 77 | babel(babelConfig), 78 | json(), 79 | 80 | replace({ 81 | 'process.env.NODE_ENV': JSON.stringify( 82 | process.env.NODE_ENV || 'development' 83 | ), 84 | }), 85 | 86 | // Watch the `public` directory and refresh the 87 | // browser on changes when not in production 88 | !production && livereload('public'), 89 | 90 | // If we're building for production (npm run build 91 | // instead of npm run dev), minify 92 | production && terser(), 93 | 94 | html({ 95 | title: 'Metro Flow Visualizer', 96 | template: ({ attributes, files, publicPath, title }) => { 97 | let templateFile = fs.readFileSync('src/index.html'); 98 | 99 | // This is adapted from the default template function in the HTML plugin. 100 | const scripts = (files.js || []) 101 | .map(({ fileName }) => { 102 | const attrs = makeHtmlAttributes(attributes.script); 103 | return ``; 104 | }) 105 | .join('\n'); 106 | 107 | const links = (files.css || []) 108 | .map(({ fileName }) => { 109 | const attrs = makeHtmlAttributes(attributes.link); 110 | return ``; 111 | }) 112 | .join('\n'); 113 | 114 | let exec = template(templateFile.toString()); 115 | return exec({ 116 | attributes, 117 | title, 118 | scripts, 119 | links, 120 | htmlAttributes: makeHtmlAttributes(attributes.html), 121 | }); 122 | }, 123 | }), 124 | ], 125 | watch: { 126 | clearScreen: false, 127 | }, 128 | }; 129 | -------------------------------------------------------------------------------- /full/snowpack.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@snowpack/app-scripts-svelte", 3 | "scripts": {}, 4 | "plugins": [ 5 | "@snowpack/plugin-postcss" 6 | ], 7 | "mount": { 8 | "../data": "/data" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /full/snowpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Metro Flow Visualizer 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /full/src/App.svelte: -------------------------------------------------------------------------------- 1 | 208 | 209 | 217 | 218 | (loaded = true)} /> 219 | 220 |
221 |
222 | 223 | {#if loaded || document.readyState === 'complete'} 224 | 225 | 232 | {#each allShownMsas as msa (msa.id)} 233 | (clickMsa = msa)} 240 | on:mouseover={() => { 241 | hoverMsa = msa; 242 | hoveringInList = false; 243 | }} 244 | on:mouseout={() => { 245 | if (hoverMsa === msa) { 246 | hoverMsa = undefined; 247 | } 248 | }} /> 249 | {/each} 250 | 251 | 252 | {#if showLines} 253 | {#each lines as line} 254 | 261 | {/each} 262 | {/if} 263 | 264 | 265 | {/if} 266 |
267 | 268 |
271 | {#if listMsa} 272 |
273 |

Top Sources

274 | {#each listMsa.incoming.slice(0, 10) as msa} 275 |

(clickMsa = msas.get(msa.id))} 278 | on:mouseover={() => { 279 | hoverMsa = msas.get(msa.id); 280 | hoveringInList = true; 281 | }} 282 | on:mouseout={() => (hoverMsa = null)}> 283 | {msas.get(msa.id).name} 284 | : {msa.count} 285 |

286 | {/each} 287 |
288 | 289 |
290 |

Top Destinations

291 | {#each listMsa.outgoing.slice(0, 10) as msa} 292 |

{ 295 | hoverMsa = msas.get(msa.id); 296 | hoveringInList = true; 297 | }} 298 | on:mouseout={() => (hoverMsa = null)} 299 | on:click={() => (clickMsa = msas.get(msa.id))}> 300 | {msas.get(msa.id).name} 301 | : {msa.count} 302 |

303 | {/each} 304 |
305 | {/if} 306 |
307 |
308 | -------------------------------------------------------------------------------- /full/src/MapControls.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | 24 | 27 | 41 | 56 | 57 | 58 | 61 |

Regions

62 | 67 | 72 | 77 | 78 |

79 | Show top 80 | 85 | flows 86 |

87 |
88 | 89 | {#if infoMsa} 90 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 104 | 105 | 106 | 107 | 111 | 112 | 113 | 114 | 118 | 119 |
Name:{infoMsa.name}
Incoming: 101 | {infoMsa.totalIncoming} 102 | ({Math.abs((infoMsa.totalIncoming / infoMsa.population) * 100).toFixed(1)}%) 103 |
Outgoing: 108 | {infoMsa.totalOutgoing} 109 | ({Math.abs((infoMsa.totalOutgoing / infoMsa.population) * 100).toFixed(1)}%) 110 |
Net Flow: 115 | {infoMsa.net} 116 | ({Math.abs(infoMsa.netAsPercent).toFixed(1)}%) 117 |
120 |
121 | {/if} 122 | -------------------------------------------------------------------------------- /full/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | > 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 | <%= links %> 11 | 12 | 13 | 14 | <%= scripts %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /full/src/index.js: -------------------------------------------------------------------------------- 1 | import './utils.css'; 2 | import App from './App.svelte'; 3 | 4 | const app = new App({ 5 | target: document.body, 6 | }); 7 | 8 | export default app; 9 | 10 | // Hot Module Replacement (HMR) - Remove this snippet to remove HMR. 11 | // Learn more: https://www.snowpack.dev/#hot-module-replacement 12 | if (import.meta.hot) { 13 | import.meta.hot.accept(); 14 | import.meta.hot.dispose(() => { 15 | app.$destroy(); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /full/src/map/Control.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 42 | 43 | 50 | -------------------------------------------------------------------------------- /full/src/map/Curve.svelte: -------------------------------------------------------------------------------- 1 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /full/src/map/GeoJson.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /full/src/map/Leaflet.svelte: -------------------------------------------------------------------------------- 1 | 55 | 56 | 61 | 62 |
63 | {#if map} 64 | 65 | {/if} 66 |
67 | -------------------------------------------------------------------------------- /full/src/map/Pane.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /full/src/map/Polyline.svelte: -------------------------------------------------------------------------------- 1 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /full/src/map/Popup.svelte: -------------------------------------------------------------------------------- 1 | 45 | 46 | 53 | -------------------------------------------------------------------------------- /full/src/map/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /full/src/map/curves.ts: -------------------------------------------------------------------------------- 1 | import * as L from 'leaflet'; 2 | 3 | const curveMidpointTheta = Math.PI / 10; 4 | const cosCurveMidpointTheta = Math.cos(curveMidpointTheta); 5 | 6 | function calculateCurve(from: L.LatLng, to: L.LatLng) { 7 | let lineAngle = Math.atan2(to.lat - from.lat, to.lng - from.lng); 8 | let lineMidPointDistance = 9 | Math.sqrt((to.lng - from.lng) ** 2 + (to.lat - from.lat) ** 2) / 2; 10 | let hypotenuse = lineMidPointDistance / cosCurveMidpointTheta; 11 | let totalTheta = lineAngle + curveMidpointTheta; 12 | 13 | return L.latLng( 14 | hypotenuse * Math.sin(totalTheta) + from.lat, 15 | hypotenuse * Math.cos(totalTheta) + from.lng 16 | ); 17 | } 18 | 19 | export default function makeLineCoordinates( 20 | map: L.Map, 21 | from: L.LatLng, 22 | to: L.LatLng, 23 | reverse: boolean 24 | ): (string | [number, number])[] { 25 | let curveMidpoint = calculateCurve(reverse ? to : from, reverse ? from : to); 26 | // Designed for use with the Curve component. 27 | return [ 28 | 'M', 29 | [from.lat, from.lng], 30 | 'Q', 31 | [curveMidpoint.lat, curveMidpoint.lng], 32 | [to.lat, to.lng], 33 | ]; 34 | } 35 | -------------------------------------------------------------------------------- /full/src/map/leaflet.curve.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Leaflet.curve v0.6.0 - a plugin for Leaflet mapping library. https://github.com/elfalem/Leaflet.curve 3 | * (c) elfalem 2015-2020 4 | */ 5 | /* 6 | * note that SVG (x, y) corresponds to (long, lat) 7 | */ 8 | 9 | // This is modified from the original to work better with ES Modules. No other changes were made. 10 | import * as L from 'leaflet'; 11 | 12 | export const Curve = L.Path.extend({ 13 | options: { 14 | }, 15 | 16 | initialize: function(path, options){ 17 | L.setOptions(this, options); 18 | this._setPath(path); 19 | }, 20 | 21 | // Added to follow the naming convention of L.Polyline and other Leaflet component classes: 22 | // (https://leafletjs.com/reference-1.6.0.html#polyline-setlatlngs) 23 | setLatLngs: function(path) { 24 | return this.setPath(path); 25 | }, 26 | 27 | _updateBounds: function() { 28 | // Empty function to satisfy L.Path.setStyle() method 29 | }, 30 | 31 | getPath: function(){ 32 | return this._coords; 33 | }, 34 | 35 | setPath: function(path){ 36 | this._setPath(path); 37 | return this.redraw(); 38 | }, 39 | 40 | getBounds: function() { 41 | return this._bounds; 42 | }, 43 | 44 | _setPath: function(path){ 45 | this._coords = path; 46 | this._bounds = this._computeBounds(); 47 | }, 48 | 49 | _computeBounds: function(){ 50 | var bound = new L.LatLngBounds(); 51 | var lastPoint; 52 | var lastCommand; 53 | var coord; 54 | for(var i = 0; i < this._coords.length; i++){ 55 | coord = this._coords[i]; 56 | if(typeof coord == 'string' || coord instanceof String){ 57 | lastCommand = coord; 58 | }else if(lastCommand == 'H'){ 59 | bound.extend([lastPoint.lat,coord[0]]); 60 | lastPoint = new L.latLng(lastPoint.lat,coord[0]); 61 | }else if(lastCommand == 'V'){ 62 | bound.extend([coord[0], lastPoint.lng]); 63 | lastPoint = new L.latLng(coord[0], lastPoint.lng); 64 | }else if(lastCommand == 'C'){ 65 | var controlPoint1 = new L.latLng(coord[0], coord[1]); 66 | coord = this._coords[++i]; 67 | var controlPoint2 = new L.latLng(coord[0], coord[1]); 68 | coord = this._coords[++i]; 69 | var endPoint = new L.latLng(coord[0], coord[1]); 70 | 71 | bound.extend(controlPoint1); 72 | bound.extend(controlPoint2); 73 | bound.extend(endPoint); 74 | 75 | endPoint.controlPoint1 = controlPoint1; 76 | endPoint.controlPoint2 = controlPoint2; 77 | lastPoint = endPoint; 78 | }else if(lastCommand == 'S'){ 79 | var controlPoint2 = new L.latLng(coord[0], coord[1]); 80 | coord = this._coords[++i]; 81 | var endPoint = new L.latLng(coord[0], coord[1]); 82 | 83 | var controlPoint1 = lastPoint; 84 | if(lastPoint.controlPoint2){ 85 | var diffLat = lastPoint.lat - lastPoint.controlPoint2.lat; 86 | var diffLng = lastPoint.lng - lastPoint.controlPoint2.lng; 87 | controlPoint1 = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng); 88 | } 89 | 90 | bound.extend(controlPoint1); 91 | bound.extend(controlPoint2); 92 | bound.extend(endPoint); 93 | 94 | endPoint.controlPoint1 = controlPoint1; 95 | endPoint.controlPoint2 = controlPoint2; 96 | lastPoint = endPoint; 97 | }else if(lastCommand == 'Q'){ 98 | var controlPoint = new L.latLng(coord[0], coord[1]); 99 | coord = this._coords[++i]; 100 | var endPoint = new L.latLng(coord[0], coord[1]); 101 | 102 | bound.extend(controlPoint); 103 | bound.extend(endPoint); 104 | 105 | endPoint.controlPoint = controlPoint; 106 | lastPoint = endPoint; 107 | }else if(lastCommand == 'T'){ 108 | var endPoint = new L.latLng(coord[0], coord[1]); 109 | 110 | var controlPoint = lastPoint; 111 | if(lastPoint.controlPoint){ 112 | var diffLat = lastPoint.lat - lastPoint.controlPoint.lat; 113 | var diffLng = lastPoint.lng - lastPoint.controlPoint.lng; 114 | controlPoint = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng); 115 | } 116 | 117 | bound.extend(controlPoint); 118 | bound.extend(endPoint); 119 | 120 | endPoint.controlPoint = controlPoint; 121 | lastPoint = endPoint; 122 | }else{ 123 | bound.extend(coord); 124 | lastPoint = new L.latLng(coord[0], coord[1]); 125 | } 126 | } 127 | return bound; 128 | }, 129 | 130 | getCenter: function () { 131 | return this._bounds.getCenter(); 132 | }, 133 | 134 | _update: function(){ 135 | if (!this._map) { return; } 136 | 137 | this._updatePath(); 138 | }, 139 | 140 | _updatePath: function() { 141 | if(this._usingCanvas){ 142 | this._updateCurveCanvas(); 143 | }else{ 144 | this._updateCurveSvg(); 145 | } 146 | }, 147 | 148 | _project: function() { 149 | var coord, lastCoord, curCommand, curPoint; 150 | 151 | this._points = []; 152 | 153 | for(var i = 0; i < this._coords.length; i++){ 154 | coord = this._coords[i]; 155 | if(typeof coord == 'string' || coord instanceof String){ 156 | this._points.push(coord); 157 | curCommand = coord; 158 | }else { 159 | switch(coord.length){ 160 | case 2: 161 | curPoint = this._latLngToPointFn.call(this._map, coord); 162 | lastCoord = coord; 163 | break; 164 | case 1: 165 | if(curCommand == 'H'){ 166 | curPoint = this._latLngToPointFn.call(this._map, [lastCoord[0], coord[0]]); 167 | lastCoord = [lastCoord[0], coord[0]]; 168 | }else{ 169 | curPoint = this._latLngToPointFn.call(this._map, [coord[0], lastCoord[1]]); 170 | lastCoord = [coord[0], lastCoord[1]]; 171 | } 172 | break; 173 | } 174 | this._points.push(curPoint); 175 | } 176 | } 177 | }, 178 | 179 | _curvePointsToPath: function(points){ 180 | var point, curCommand, str = ''; 181 | for(var i = 0; i < points.length; i++){ 182 | point = points[i]; 183 | if(typeof point == 'string' || point instanceof String){ 184 | curCommand = point; 185 | str += curCommand; 186 | }else{ 187 | switch(curCommand){ 188 | case 'H': 189 | str += point.x + ' '; 190 | break; 191 | case 'V': 192 | str += point.y + ' '; 193 | break; 194 | default: 195 | str += point.x + ',' + point.y + ' '; 196 | break; 197 | } 198 | } 199 | } 200 | return str || 'M0 0'; 201 | }, 202 | 203 | beforeAdd: function(map){ 204 | L.Path.prototype.beforeAdd.call(this, map); 205 | 206 | this._usingCanvas = this._renderer instanceof L.Canvas; 207 | 208 | this._latLngToPointFn = this._usingCanvas ? map.latLngToContainerPoint : map.latLngToLayerPoint; 209 | if(this._usingCanvas){ 210 | this._pathSvgElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 211 | } 212 | }, 213 | 214 | onAdd: function(map){ 215 | if(this._usingCanvas){ 216 | // determine if dash array is set by user 217 | this._canvasSetDashArray = !this.options.dashArray; 218 | } 219 | 220 | L.Path.prototype.onAdd.call(this, map); // calls _update() 221 | 222 | if(this._usingCanvas){ 223 | this._animationCanvasElement = this._insertCustomCanvasElement(); 224 | 225 | this._resizeCanvas(); 226 | 227 | map.on('resize', this._resizeCanvas, this); 228 | 229 | if(this.options.animate && typeof(TWEEN) === 'object'){ 230 | this._pathLength = this._pathSvgElement.getTotalLength(); 231 | 232 | this._normalizeCanvasAnimationOptions(); 233 | 234 | this._tweenedObject = {offset: this._pathLength}; 235 | this._tween = new TWEEN.Tween(this._tweenedObject) 236 | .to({offset: 0}, this.options.animate.duration) 237 | // difference of behavior with SVG, delay occurs on every iteration 238 | .delay(this.options.animate.delay) 239 | .repeat(this.options.animate.iterations - 1) 240 | .onComplete(function(scope){ 241 | return function(){ 242 | scope._canvasAnimating = false; 243 | } 244 | }(this)) 245 | .start(); 246 | 247 | this._canvasAnimating = true; 248 | this._animateCanvas(); 249 | }else{ 250 | this._canvasAnimating = false; 251 | } 252 | }else{ 253 | if(this.options.animate && this._path.animate){ 254 | var length = this._svgSetDashArray(); 255 | 256 | this._path.animate([ 257 | {strokeDashoffset: length}, 258 | {strokeDashoffset: 0} 259 | ], this.options.animate); 260 | } 261 | } 262 | }, 263 | 264 | onRemove: function(map){ 265 | L.Path.prototype.onRemove.call(this, map); 266 | 267 | if(this._usingCanvas){ 268 | this._clearCanvas(); 269 | L.DomUtil.remove(this._animationCanvasElement); 270 | map.off('resize', this._resizeCanvas, this); 271 | } 272 | }, 273 | 274 | // SVG specific logic 275 | _updateCurveSvg: function(){ 276 | this._renderer._setPath(this, this._curvePointsToPath(this._points)); 277 | 278 | if(this.options.animate){ 279 | this._svgSetDashArray(); 280 | } 281 | }, 282 | 283 | _svgSetDashArray: function(){ 284 | var path = this._path; 285 | var length = path.getTotalLength(); 286 | 287 | if(!this.options.dashArray){ 288 | path.style.strokeDasharray = length + ' ' + length; 289 | } 290 | return length; 291 | }, 292 | 293 | // Needed by the `Canvas` renderer for interactivity 294 | _containsPoint: function(layerPoint) { 295 | return this._bounds.contains(this._map.layerPointToLatLng(layerPoint)); 296 | }, 297 | 298 | // Canvas specific logic below here 299 | _insertCustomCanvasElement: function(){ 300 | var element = L.DomUtil.create('canvas', 'leaflet-zoom-animated'); 301 | var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin']); 302 | element.style[originProp] = '50% 50%'; 303 | var pane = this._map.getPane(this.options.pane); 304 | pane.insertBefore(element, pane.firstChild); 305 | 306 | return element; 307 | }, 308 | 309 | _normalizeCanvasAnimationOptions: function(){ 310 | var opts = { 311 | delay: 0, 312 | duration: 0, 313 | iterations: 1 314 | }; 315 | if(typeof(this.options.animate) == "number"){ 316 | opts.duration = this.options.animate; 317 | }else{ 318 | if(this.options.animate.duration){ 319 | opts.duration = this.options.animate.duration; 320 | } 321 | if(this.options.animate.delay){ 322 | opts.delay =this.options.animate.delay; 323 | } 324 | if(this.options.animate.iterations){ 325 | opts.iterations = this.options.animate.iterations; 326 | } 327 | } 328 | 329 | this.options.animate = opts; 330 | }, 331 | 332 | _updateCurveCanvas: function(){ 333 | this._project(); 334 | 335 | var pathString = this._curvePointsToPath(this._points); 336 | this._pathSvgElement.setAttribute('d', pathString); 337 | 338 | if(this.options.animate && typeof(TWEEN) === 'object' && this._canvasSetDashArray){ 339 | this._pathLength = this._pathSvgElement.getTotalLength(); 340 | this.options.dashArray = this._pathLength + ''; 341 | this._renderer._updateDashArray(this); 342 | } 343 | 344 | this._path2d = new Path2D(pathString); 345 | 346 | if(this._animationCanvasElement){ 347 | this._resetCanvas(); 348 | } 349 | 350 | 351 | }, 352 | 353 | _animationCanvasElement: null, 354 | 355 | _resizeCanvas: function() { 356 | var size = this._map.getSize(); 357 | this._animationCanvasElement.width = size.x; 358 | this._animationCanvasElement.height = size.y; 359 | 360 | this._resetCanvas(); 361 | }, 362 | 363 | _resetCanvas: function() { 364 | var topLeft = this._map.containerPointToLayerPoint([0, 0]); 365 | L.DomUtil.setPosition(this._animationCanvasElement, topLeft); 366 | 367 | this._redrawCanvas(); 368 | }, 369 | 370 | _redrawCanvas: function(){ 371 | if(!this._canvasAnimating){ 372 | this._clearCanvas(); 373 | var ctx = this._animationCanvasElement.getContext('2d'); 374 | this._curveFillStroke(this._path2d, ctx); 375 | } 376 | }, 377 | 378 | _clearCanvas: function() { 379 | this._animationCanvasElement.getContext('2d').clearRect(0, 0, this._animationCanvasElement.width, this._animationCanvasElement.height); 380 | }, 381 | 382 | _animateCanvas: function(time){ 383 | TWEEN.update(time); 384 | 385 | var ctx = this._animationCanvasElement.getContext('2d'); 386 | ctx.clearRect(0, 0, this._animationCanvasElement.width, this._animationCanvasElement.height); 387 | ctx.lineDashOffset = this._tweenedObject.offset; 388 | 389 | this._curveFillStroke(this._path2d, ctx); 390 | 391 | if(this._canvasAnimating){ 392 | this._animationFrameId = L.Util.requestAnimFrame(this._animateCanvas, this); 393 | } 394 | }, 395 | 396 | // similar to Canvas._fillStroke(ctx, layer) 397 | _curveFillStroke: function (path2d, ctx) { 398 | var options = this.options; 399 | 400 | if (options.fill) { 401 | ctx.globalAlpha = options.fillOpacity; 402 | ctx.fillStyle = options.fillColor || options.color; 403 | ctx.fill(path2d, options.fillRule || 'evenodd'); 404 | } 405 | 406 | if (options.stroke && options.weight !== 0) { 407 | if (ctx.setLineDash) { 408 | ctx.setLineDash(this.options && this.options._dashArray || []); 409 | } 410 | ctx.globalAlpha = options.opacity; 411 | ctx.lineWidth = options.weight; 412 | ctx.strokeStyle = options.color; 413 | ctx.lineCap = options.lineCap; 414 | ctx.lineJoin = options.lineJoin; 415 | ctx.stroke(path2d); 416 | } 417 | }, 418 | 419 | // path tracing logic below here 420 | trace: function(t){ 421 | t = t.filter(function(element){ 422 | return element >= 0 && element <= 1; 423 | }); 424 | 425 | var point, curCommand, curStartPoint, curEndPoint; 426 | var p1, p2, p3; 427 | var samples = []; 428 | for(var i = 0; i < this._points.length; i++){ 429 | point = this._points[i]; 430 | if(typeof point == 'string' || point instanceof String){ 431 | curCommand = point; 432 | 433 | if(curCommand == 'Z'){ 434 | samples = samples.concat(this._linearTrace(t, curEndPoint, curStartPoint)); 435 | } 436 | }else{ 437 | switch(curCommand){ 438 | case 'M': 439 | curStartPoint = point; 440 | curEndPoint = point; 441 | break; 442 | case 'L': 443 | case 'H': 444 | case 'V': 445 | samples = samples.concat(this._linearTrace(t, curEndPoint, point)); 446 | 447 | curEndPoint = point; 448 | break; 449 | case 'C': 450 | p1 = point; 451 | p2 = this._points[++i]; 452 | p3 = this._points[++i]; 453 | samples = samples.concat(this._cubicTrace(t, curEndPoint, p1, p2, p3)); 454 | 455 | curEndPoint = p3; 456 | break; 457 | case 'S': 458 | p1 = this._reflectPoint(p2, curEndPoint); 459 | p2 = point; 460 | p3 = this._points[++i]; 461 | samples = samples.concat(this._cubicTrace(t, curEndPoint, p1, p2, p3)); 462 | 463 | curEndPoint = p3; 464 | break; 465 | case 'Q': 466 | p1 = point; 467 | p2 = this._points[++i]; 468 | samples = samples.concat(this._quadraticTrace(t, curEndPoint, p1, p2)); 469 | 470 | curEndPoint = p2; 471 | break; 472 | case 'T': 473 | p1 = this._reflectPoint(p1, curEndPoint); 474 | p2 = point; 475 | samples = samples.concat(this._quadraticTrace(t, curEndPoint, p1, p2)); 476 | 477 | curEndPoint = p2; 478 | break; 479 | default: 480 | break; 481 | } 482 | } 483 | } 484 | return samples; 485 | }, 486 | _linearTrace: function(t, p0, p1){ 487 | return t.map(interval => { 488 | var x = this._singleLinearTrace(interval, p0.x, p1.x); 489 | var y = this._singleLinearTrace(interval, p0.y, p1.y); 490 | return this._map.layerPointToLatLng([x, y]); 491 | }); 492 | }, 493 | _quadraticTrace: function(t, p0, p1, p2){ 494 | return t.map(interval => { 495 | var x = this._singleQuadraticTrace(interval, p0.x, p1.x, p2.x); 496 | var y = this._singleQuadraticTrace(interval, p0.y, p1.y, p2.y); 497 | return this._map.layerPointToLatLng([x, y]); 498 | }); 499 | }, 500 | _cubicTrace: function(t, p0, p1, p2, p3){ 501 | return t.map(interval => { 502 | var x = this._singleCubicTrace(interval, p0.x, p1.x, p2.x, p3.x); 503 | var y = this._singleCubicTrace(interval, p0.y, p1.y, p2.y, p3.y); 504 | return this._map.layerPointToLatLng([x, y]); 505 | }); 506 | }, 507 | _singleLinearTrace: function(t, p0, p1){ 508 | return p0 + t * (p1 - p0); 509 | }, 510 | _singleQuadraticTrace: function(t, p0, p1, p2){ 511 | var oneMinusT = 1 - t; 512 | return Math.pow(oneMinusT, 2) * p0 + 513 | 2 * oneMinusT * t * p1 + 514 | Math.pow(t, 2) * p2; 515 | }, 516 | _singleCubicTrace: function(t, p0, p1, p2, p3){ 517 | var oneMinusT = 1 - t; 518 | return Math.pow(oneMinusT, 3) * p0 + 519 | 3 * Math.pow(oneMinusT, 2) * t * p1 + 520 | 3 * oneMinusT * Math.pow(t, 2) * p2 + 521 | Math.pow(t, 3) * p3; 522 | }, 523 | _reflectPoint: function(point, over){ 524 | x = over.x + (over.x - point.x); 525 | y = over.y + (over.y - point.y); 526 | return L.point(x, y); 527 | } 528 | }); 529 | 530 | export function curve(path, options){ 531 | return new Curve(path, options); 532 | }; -------------------------------------------------------------------------------- /full/src/map/popup.ts: -------------------------------------------------------------------------------- 1 | import { SvelteComponent } from 'svelte'; 2 | import * as L from 'leaflet'; 3 | 4 | export function bindPopup( 5 | layer: L.Layer, 6 | popup?: string | typeof SvelteComponent, 7 | props?: object 8 | ) { 9 | if (!popup) { 10 | return; 11 | } 12 | 13 | if (typeof popup === 'string') { 14 | layer.bindPopup(popup); 15 | } else { 16 | let popupComponent: SvelteComponent | undefined; 17 | layer.bindPopup(() => { 18 | let container = L.DomUtil.create('div'); 19 | popupComponent = new popup({ 20 | target: container, 21 | props: { ...(props || {}) }, 22 | }); 23 | return container; 24 | }); 25 | 26 | layer.on('popupclose', () => { 27 | if (popupComponent) { 28 | let old = popupComponent; 29 | popupComponent = undefined; 30 | // Wait for the popup to completely fade out before destroying it. 31 | // Otherwise the fade out looks weird as the contents disappear too early. 32 | setTimeout(() => { 33 | old.$destroy(); 34 | }, 500); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /full/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface Flow { 2 | id: string; 3 | count: number; 4 | } 5 | 6 | export interface Msa { 7 | id: string; 8 | name: string; 9 | net: number; 10 | centroid: number[]; 11 | totalIncoming: number; 12 | totalOutgoing: number; 13 | netAsPercent: number; 14 | population: number; 15 | feature: any; 16 | outgoing: Flow[]; 17 | incoming: Flow[]; 18 | } 19 | -------------------------------------------------------------------------------- /full/src/utils.css: -------------------------------------------------------------------------------- 1 | /* Import Tailwind as Global Utils */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | * { 7 | position: relative; 8 | } 9 | -------------------------------------------------------------------------------- /full/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimfeld/svelte-leaflet-demo/897b5aab379cc8926ed9ee65a9df846fbae62df7/full/static/favicon.png -------------------------------------------------------------------------------- /full/svelte.config.js: -------------------------------------------------------------------------------- 1 | const sveltePreprocess = require('svelte-preprocess'); 2 | 3 | const dev = process.env.NODE_ENV === 'development'; 4 | 5 | module.exports = { 6 | preprocess: sveltePreprocess({ 7 | postcss: require('./postcss.config'), 8 | typescript: true, 9 | aliases: [['ts', 'typescript']], 10 | }), 11 | }; 12 | -------------------------------------------------------------------------------- /full/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: false, 3 | theme: { 4 | extend: { 5 | animation: { 6 | 'dash-offset': 'dash-offset var(--animation-speed, 2s) linear infinite', 7 | }, 8 | keyframes: { 9 | 'dash-offset': { 10 | from: { 11 | 'stroke-dashoffset': 'var(--dash-length, 18)', 12 | }, 13 | to: { 14 | 'stroke-dashoffset': '0', 15 | }, 16 | }, 17 | }, 18 | }, 19 | }, 20 | variants: {}, 21 | plugins: [require('@tailwindcss/ui')], 22 | experimental: { 23 | applyComplexClasses: true, 24 | defaultLineHeights: true, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /full/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "target": "es2020", 7 | "noEmit": true, 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "allowSyntheticDefaultImports": true, 11 | "lib": ["dom", "es5", "ES2015", "es2016", "es2017", "es2018", "ES2019"] 12 | }, 13 | "paths": { 14 | "^/*": ["src/*"] 15 | }, 16 | "include": ["src"], 17 | "compileOnSave": false 18 | } 19 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-leaflet-demo", 3 | "version": "1.0.0", 4 | "description": "Visualize Migrations in the US with Svelte and Leaflet", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/dimfeld/svelte-leaflet-demo.git" 12 | }, 13 | "author": "Daniel Imfeld ", 14 | "license": "MIT", 15 | "bugs": { 16 | "url": "https://github.com/dimfeld/svelte-leaflet-demo/issues" 17 | }, 18 | "homepage": "https://github.com/dimfeld/svelte-leaflet-demo#readme", 19 | "dependencies": { 20 | "@turf/turf": "^5.1.6", 21 | "d3-dsv": "^2.0.0", 22 | "topojson-client": "^3.1.0", 23 | "topojson-server": "^3.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | dependencies: 2 | '@turf/turf': 5.1.6 3 | d3-dsv: 2.0.0 4 | topojson-client: 3.1.0 5 | topojson-server: 3.0.1 6 | lockfileVersion: 5.2 7 | packages: 8 | /@turf/along/5.1.5: 9 | dependencies: 10 | '@turf/bearing': 5.1.5 11 | '@turf/destination': 5.1.5 12 | '@turf/distance': 5.1.5 13 | '@turf/helpers': 5.1.5 14 | dev: false 15 | resolution: 16 | integrity: sha1-YdbmplhKzdq1asVYTge/jL5fi+s= 17 | /@turf/area/5.1.5: 18 | dependencies: 19 | '@turf/helpers': 5.1.5 20 | '@turf/meta': 5.1.6 21 | dev: false 22 | resolution: 23 | integrity: sha1-79iZv9Jgzb0VQbKjwVX4pdLu+h0= 24 | /@turf/bbox-clip/5.1.5: 25 | dependencies: 26 | '@turf/helpers': 5.1.5 27 | '@turf/invariant': 5.1.5 28 | lineclip: 1.1.5 29 | dev: false 30 | resolution: 31 | integrity: sha1-M2S1Mo3/nzz0HZ4C7a/zdNFQzIQ= 32 | /@turf/bbox-polygon/5.1.5: 33 | dependencies: 34 | '@turf/helpers': 5.1.5 35 | dev: false 36 | resolution: 37 | integrity: sha1-auuk7VHYXSluD3w4uIwznwHu4CQ= 38 | /@turf/bbox/5.1.5: 39 | dependencies: 40 | '@turf/helpers': 5.1.5 41 | '@turf/meta': 5.1.6 42 | dev: false 43 | resolution: 44 | integrity: sha1-MFHfUUrUxQ9KT5uKLRX9i2hA7aM= 45 | /@turf/bearing/5.1.5: 46 | dependencies: 47 | '@turf/helpers': 5.1.5 48 | '@turf/invariant': 5.1.5 49 | dev: false 50 | resolution: 51 | integrity: sha1-egt5ATbE70eX8CRjBdRcvi0ns/c= 52 | /@turf/bearing/6.0.1: 53 | dependencies: 54 | '@turf/helpers': 6.1.4 55 | '@turf/invariant': 6.1.2 56 | dev: false 57 | resolution: 58 | integrity: sha512-mXY1NozqV9EFfBTbUItujwfqfQF0G/Xe2fzvnZle90ekPEUfhi4Dgf5JswJTd96J9LiT8kcd6Jonp5khnx0wIg== 59 | /@turf/bezier-spline/5.1.5: 60 | dependencies: 61 | '@turf/helpers': 5.1.5 62 | '@turf/invariant': 5.1.5 63 | dev: false 64 | resolution: 65 | integrity: sha1-WaJ7ul17l+8Vqz/VpA+9I4cEm8o= 66 | /@turf/boolean-clockwise/5.1.5: 67 | dependencies: 68 | '@turf/helpers': 5.1.5 69 | '@turf/invariant': 5.1.5 70 | dev: false 71 | resolution: 72 | integrity: sha1-MwK32sYsXikaB4nimvcoM4f6nes= 73 | /@turf/boolean-contains/5.1.5: 74 | dependencies: 75 | '@turf/bbox': 5.1.5 76 | '@turf/boolean-point-in-polygon': 5.1.5 77 | '@turf/boolean-point-on-line': 5.1.5 78 | '@turf/helpers': 5.1.5 79 | '@turf/invariant': 5.1.5 80 | dev: false 81 | resolution: 82 | integrity: sha1-WW1jruY2961T7pn5/yTJaZSg7xQ= 83 | /@turf/boolean-crosses/5.1.5: 84 | dependencies: 85 | '@turf/boolean-point-in-polygon': 5.1.5 86 | '@turf/helpers': 5.1.5 87 | '@turf/invariant': 5.1.5 88 | '@turf/line-intersect': 5.1.5 89 | '@turf/polygon-to-line': 5.1.5 90 | dev: false 91 | resolution: 92 | integrity: sha1-Ab+uollvFk3kpNMlCU3HwlXHFdY= 93 | /@turf/boolean-disjoint/5.1.6: 94 | dependencies: 95 | '@turf/boolean-point-in-polygon': 5.1.5 96 | '@turf/helpers': 5.1.5 97 | '@turf/line-intersect': 5.1.5 98 | '@turf/meta': 5.1.6 99 | '@turf/polygon-to-line': 5.1.5 100 | dev: false 101 | resolution: 102 | integrity: sha512-KHvUS6SBNYHBCLIJEJrg04pF5Oy+Fqn8V5G9U+9pti5vI9tyX7Ln2g7RSB7iJ1Cxsz8QAi6OukhXjEF2/8ZpGg== 103 | /@turf/boolean-equal/5.1.5: 104 | dependencies: 105 | '@turf/clean-coords': 5.1.5 106 | '@turf/helpers': 5.1.5 107 | '@turf/invariant': 5.1.5 108 | geojson-equality: 0.1.6 109 | dev: false 110 | resolution: 111 | integrity: sha1-Kfj21gu4RQff12WzIlTbjnLJOKQ= 112 | /@turf/boolean-overlap/5.1.5: 113 | dependencies: 114 | '@turf/helpers': 5.1.5 115 | '@turf/invariant': 5.1.5 116 | '@turf/line-intersect': 5.1.5 117 | '@turf/line-overlap': 5.1.5 118 | '@turf/meta': 5.1.6 119 | geojson-equality: 0.1.6 120 | dev: false 121 | resolution: 122 | integrity: sha1-DU5kxSx3CijpPZ7834qLg3OsznU= 123 | /@turf/boolean-parallel/5.1.5: 124 | dependencies: 125 | '@turf/clean-coords': 5.1.5 126 | '@turf/helpers': 5.1.5 127 | '@turf/line-segment': 5.1.5 128 | '@turf/rhumb-bearing': 5.1.5 129 | dev: false 130 | resolution: 131 | integrity: sha1-c5NYR16ltlx+GCejw+DopofTqF0= 132 | /@turf/boolean-point-in-polygon/5.1.5: 133 | dependencies: 134 | '@turf/helpers': 5.1.5 135 | '@turf/invariant': 5.1.5 136 | dev: false 137 | resolution: 138 | integrity: sha1-8BzBlNHgMKVIv9qYHLpDz9YpQbc= 139 | /@turf/boolean-point-on-line/5.1.5: 140 | dependencies: 141 | '@turf/helpers': 5.1.5 142 | '@turf/invariant': 5.1.5 143 | dev: false 144 | resolution: 145 | integrity: sha1-9jPF/4Aq0ku48Vja269v9KAj3Xs= 146 | /@turf/boolean-within/5.1.5: 147 | dependencies: 148 | '@turf/bbox': 5.1.5 149 | '@turf/boolean-point-in-polygon': 5.1.5 150 | '@turf/boolean-point-on-line': 5.1.5 151 | '@turf/helpers': 5.1.5 152 | '@turf/invariant': 5.1.5 153 | dev: false 154 | resolution: 155 | integrity: sha1-RxBdVtB1Kp0Pv81Dw2pfkUnchpc= 156 | /@turf/buffer/5.1.5: 157 | dependencies: 158 | '@turf/bbox': 5.1.5 159 | '@turf/center': 5.1.5 160 | '@turf/helpers': 5.1.5 161 | '@turf/meta': 5.1.6 162 | '@turf/projection': 5.1.5 163 | d3-geo: 1.7.1 164 | turf-jsts: 1.2.3 165 | dev: false 166 | resolution: 167 | integrity: sha1-hByWJ8+5dLEirE4alW8EZrwCMcQ= 168 | /@turf/center-mean/5.1.5: 169 | dependencies: 170 | '@turf/bbox': 5.1.5 171 | '@turf/helpers': 5.1.5 172 | '@turf/meta': 5.1.6 173 | dev: false 174 | resolution: 175 | integrity: sha1-jI6YdTkeXwnw5uePXWYbiLIQigo= 176 | /@turf/center-median/5.1.5: 177 | dependencies: 178 | '@turf/center-mean': 5.1.5 179 | '@turf/centroid': 5.1.5 180 | '@turf/distance': 5.1.5 181 | '@turf/helpers': 5.1.5 182 | '@turf/meta': 5.1.6 183 | dev: false 184 | resolution: 185 | integrity: sha1-u0Yb/noqSGAdikcnaFcYcjoUqHI= 186 | /@turf/center-of-mass/5.1.5: 187 | dependencies: 188 | '@turf/centroid': 5.1.5 189 | '@turf/convex': 5.1.5 190 | '@turf/helpers': 5.1.5 191 | '@turf/invariant': 5.1.5 192 | '@turf/meta': 5.1.6 193 | dev: false 194 | resolution: 195 | integrity: sha1-TTvXnYhJjbq4Mk1PafAyL2Uguco= 196 | /@turf/center/5.1.5: 197 | dependencies: 198 | '@turf/bbox': 5.1.5 199 | '@turf/helpers': 5.1.5 200 | dev: false 201 | resolution: 202 | integrity: sha1-RKss2VT2PA03dX9xWKmcPvURS4A= 203 | /@turf/centroid/5.1.5: 204 | dependencies: 205 | '@turf/helpers': 5.1.5 206 | '@turf/meta': 5.1.6 207 | dev: false 208 | resolution: 209 | integrity: sha1-d4radCFjNQIa2P0OemWoNJ1Tx2k= 210 | /@turf/circle/5.1.5: 211 | dependencies: 212 | '@turf/destination': 5.1.5 213 | '@turf/helpers': 5.1.5 214 | dev: false 215 | resolution: 216 | integrity: sha1-mxV3g1UIq1L7HBCypQZcuiuHtqU= 217 | /@turf/clean-coords/5.1.5: 218 | dependencies: 219 | '@turf/helpers': 5.1.5 220 | '@turf/invariant': 5.1.5 221 | dev: false 222 | resolution: 223 | integrity: sha1-EoAKmKeMmkUqcuxChJPEOs8q2h8= 224 | /@turf/clone/5.1.5: 225 | dependencies: 226 | '@turf/helpers': 5.1.5 227 | dev: false 228 | resolution: 229 | integrity: sha1-JT6NNUdxgZduM636tQoPAqfw42c= 230 | /@turf/clone/6.0.2: 231 | dependencies: 232 | '@turf/helpers': 6.1.4 233 | dev: false 234 | resolution: 235 | integrity: sha512-UVpYPnW3wRj3bPncR6Z2PRbowBk+nEdVWgGewPxrKKLfvswtVtG9n/OIyvbU3E3ZOadBVxTH2uAMEMOz4800FA== 236 | /@turf/clusters-dbscan/5.1.5: 237 | dependencies: 238 | '@turf/clone': 5.1.5 239 | '@turf/distance': 5.1.5 240 | '@turf/helpers': 5.1.5 241 | '@turf/invariant': 5.1.5 242 | '@turf/meta': 5.1.6 243 | density-clustering: 1.3.0 244 | dev: false 245 | resolution: 246 | integrity: sha1-V4H7TmVsdHoLjpk333MYHAMJ4m8= 247 | /@turf/clusters-kmeans/5.1.5: 248 | dependencies: 249 | '@turf/clone': 5.1.5 250 | '@turf/helpers': 5.1.5 251 | '@turf/invariant': 5.1.5 252 | '@turf/meta': 5.1.6 253 | skmeans: 0.9.7 254 | dev: false 255 | resolution: 256 | integrity: sha1-/W3+qLEzuovcI3CsPKzuFYejAvE= 257 | /@turf/clusters/5.1.5: 258 | dependencies: 259 | '@turf/helpers': 5.1.5 260 | '@turf/meta': 5.1.6 261 | dev: false 262 | resolution: 263 | integrity: sha1-ZzpeXxsZycq6vFfJCO6t1oIiTdQ= 264 | /@turf/collect/5.1.5: 265 | dependencies: 266 | '@turf/bbox': 5.1.5 267 | '@turf/boolean-point-in-polygon': 5.1.5 268 | '@turf/helpers': 5.1.5 269 | rbush: 2.0.2 270 | dev: false 271 | resolution: 272 | integrity: sha1-/pjJqMIY7PJP/DPXApUXt8GbKj4= 273 | /@turf/combine/5.1.5: 274 | dependencies: 275 | '@turf/helpers': 5.1.5 276 | '@turf/meta': 5.1.6 277 | dev: false 278 | resolution: 279 | integrity: sha1-uxS976VVBDVxlfwaEkzX1TqMiQU= 280 | /@turf/concave/5.1.5: 281 | dependencies: 282 | '@turf/clone': 5.1.5 283 | '@turf/distance': 5.1.5 284 | '@turf/helpers': 5.1.5 285 | '@turf/invariant': 5.1.5 286 | '@turf/meta': 5.1.6 287 | '@turf/tin': 5.1.5 288 | topojson-client: 3.1.0 289 | topojson-server: 3.0.1 290 | dev: false 291 | resolution: 292 | integrity: sha1-I7uqw4fQNLlldKG9cNBZI3qdIRA= 293 | /@turf/convex/5.1.5: 294 | dependencies: 295 | '@turf/helpers': 5.1.5 296 | '@turf/meta': 5.1.6 297 | concaveman: 1.2.0 298 | dev: false 299 | resolution: 300 | integrity: sha1-Dfk3fdACIWzpghsH9wXgN9rj4B0= 301 | /@turf/destination/5.1.5: 302 | dependencies: 303 | '@turf/helpers': 5.1.5 304 | '@turf/invariant': 5.1.5 305 | dev: false 306 | resolution: 307 | integrity: sha1-7TU4G9zoO73cvQei4rzivd/7zCY= 308 | /@turf/difference/5.1.5: 309 | dependencies: 310 | '@turf/area': 5.1.5 311 | '@turf/helpers': 5.1.5 312 | '@turf/invariant': 5.1.5 313 | '@turf/meta': 5.1.6 314 | turf-jsts: 1.2.3 315 | dev: false 316 | resolution: 317 | integrity: sha1-ok1pCnvKgD8QkKnuO52Qb8Q3H0I= 318 | /@turf/dissolve/5.1.5: 319 | dependencies: 320 | '@turf/boolean-overlap': 5.1.5 321 | '@turf/clone': 5.1.5 322 | '@turf/helpers': 5.1.5 323 | '@turf/invariant': 5.1.5 324 | '@turf/line-intersect': 5.1.5 325 | '@turf/meta': 5.1.6 326 | '@turf/union': 5.1.5 327 | geojson-rbush: 2.1.0 328 | get-closest: 0.0.4 329 | dev: false 330 | resolution: 331 | integrity: sha1-LPEzqQIdIWODHD16lY1lB/nYGTg= 332 | /@turf/distance/5.1.5: 333 | dependencies: 334 | '@turf/helpers': 5.1.5 335 | '@turf/invariant': 5.1.5 336 | dev: false 337 | resolution: 338 | integrity: sha1-Oc8YIEu/h1h9cH5gmmARiQkVZAk= 339 | /@turf/distance/6.0.1: 340 | dependencies: 341 | '@turf/helpers': 6.1.4 342 | '@turf/invariant': 6.1.2 343 | dev: false 344 | resolution: 345 | integrity: sha512-q7t7rWIWfkg7MP1Vt4uLjSEhe5rPfCO2JjpKmk7JC+QZKEQkuvHEqy3ejW1iC7Kw5ZcZNR3qdMGGz+6HnVwqvg== 346 | /@turf/ellipse/5.1.5: 347 | dependencies: 348 | '@turf/helpers': 5.1.5 349 | '@turf/invariant': 5.1.5 350 | '@turf/rhumb-destination': 5.1.5 351 | '@turf/transform-rotate': 5.1.5 352 | dev: false 353 | resolution: 354 | integrity: sha1-1XyrhTmFkgzeYCKKeNgEWAJcVL4= 355 | /@turf/envelope/5.1.5: 356 | dependencies: 357 | '@turf/bbox': 5.1.5 358 | '@turf/bbox-polygon': 5.1.5 359 | '@turf/helpers': 5.1.5 360 | dev: false 361 | resolution: 362 | integrity: sha1-UBMwnFP91D369LWIplw/7X28EIo= 363 | /@turf/explode/5.1.5: 364 | dependencies: 365 | '@turf/helpers': 5.1.5 366 | '@turf/meta': 5.1.6 367 | dev: false 368 | resolution: 369 | integrity: sha1-sSsvd0AEobSPYrqVsgocZVo94Rg= 370 | /@turf/flatten/5.1.5: 371 | dependencies: 372 | '@turf/helpers': 5.1.5 373 | '@turf/meta': 5.1.6 374 | dev: false 375 | resolution: 376 | integrity: sha1-2iknBnEz7WFpsLnWB7khVoiqE1g= 377 | /@turf/flip/5.1.5: 378 | dependencies: 379 | '@turf/clone': 5.1.5 380 | '@turf/helpers': 5.1.5 381 | '@turf/meta': 5.1.6 382 | dev: false 383 | resolution: 384 | integrity: sha1-Q29kOnIvDKU7n85jjkaT2zYIpoo= 385 | /@turf/great-circle/5.1.5: 386 | dependencies: 387 | '@turf/helpers': 5.1.5 388 | '@turf/invariant': 5.1.5 389 | dev: false 390 | resolution: 391 | integrity: sha1-3r+2cc5HVQnLY3MBwV/PzPo1mpM= 392 | /@turf/helpers/5.1.5: 393 | dev: false 394 | resolution: 395 | integrity: sha1-FTQFInq5M9AEpbuWQantmZ/L4M8= 396 | /@turf/helpers/6.1.4: 397 | dev: false 398 | resolution: 399 | integrity: sha512-vJvrdOZy1ngC7r3MDA7zIGSoIgyrkWcGnNIEaqn/APmw+bVLF2gAW7HIsdTxd12s5wQMqEpqIQrmrbRRZ0xC7g== 400 | /@turf/hex-grid/5.1.5: 401 | dependencies: 402 | '@turf/distance': 5.1.5 403 | '@turf/helpers': 5.1.5 404 | '@turf/intersect': 5.1.6 405 | '@turf/invariant': 5.1.5 406 | dev: false 407 | resolution: 408 | integrity: sha1-m3ul/s9QUfHoWJL3E/zlxVBQKmo= 409 | /@turf/interpolate/5.1.5: 410 | dependencies: 411 | '@turf/bbox': 5.1.5 412 | '@turf/centroid': 5.1.5 413 | '@turf/clone': 5.1.5 414 | '@turf/distance': 5.1.5 415 | '@turf/helpers': 5.1.5 416 | '@turf/hex-grid': 5.1.5 417 | '@turf/invariant': 5.1.5 418 | '@turf/meta': 5.1.6 419 | '@turf/point-grid': 5.1.5 420 | '@turf/square-grid': 5.1.5 421 | '@turf/triangle-grid': 5.1.5 422 | dev: false 423 | resolution: 424 | integrity: sha1-DxLwq3VtbdEK+ykMpuh3ve8BPqo= 425 | /@turf/intersect/5.1.6: 426 | dependencies: 427 | '@turf/clean-coords': 5.1.5 428 | '@turf/helpers': 5.1.5 429 | '@turf/invariant': 5.1.5 430 | '@turf/truncate': 5.1.5 431 | turf-jsts: 1.2.3 432 | dev: false 433 | resolution: 434 | integrity: sha512-KXyNv/GXdoGAOy03qZF53rgtXC2tNhF/4jLwTKiVRrBQH6kcEpipGStdJ+QkYIlarQPa8f7I9UlVAB19et4MfQ== 435 | /@turf/invariant/5.1.5: 436 | dependencies: 437 | '@turf/helpers': 5.1.5 438 | dev: false 439 | resolution: 440 | integrity: sha1-9Z9P76CSJLFdzhZR+QPIaNV6JOE= 441 | /@turf/invariant/6.1.2: 442 | dependencies: 443 | '@turf/helpers': 6.1.4 444 | dev: false 445 | resolution: 446 | integrity: sha512-WU08Ph8j0J2jVGlQCKChXoCtI50BB3yEH21V++V0T4cR1T27HKCxkehV2sYMwTierfMBgjwSwDIsxnR4/2mWXg== 447 | /@turf/isobands/5.1.5: 448 | dependencies: 449 | '@turf/area': 5.1.5 450 | '@turf/bbox': 5.1.5 451 | '@turf/boolean-point-in-polygon': 5.1.5 452 | '@turf/explode': 5.1.5 453 | '@turf/helpers': 5.1.5 454 | '@turf/invariant': 5.1.5 455 | '@turf/meta': 5.1.6 456 | dev: false 457 | resolution: 458 | integrity: sha1-a0TO9YTVUaMTBBh68jtKFYLj8I0= 459 | /@turf/isolines/5.1.5: 460 | dependencies: 461 | '@turf/bbox': 5.1.5 462 | '@turf/helpers': 5.1.5 463 | '@turf/invariant': 5.1.5 464 | '@turf/meta': 5.1.6 465 | dev: false 466 | resolution: 467 | integrity: sha1-irTn9Cuz38VGFOW/FVln9+VdLeE= 468 | /@turf/kinks/5.1.5: 469 | dependencies: 470 | '@turf/helpers': 5.1.5 471 | dev: false 472 | resolution: 473 | integrity: sha1-irtpYdm7AQchO63fLCwmQNAlaYA= 474 | /@turf/length/5.1.5: 475 | dependencies: 476 | '@turf/distance': 5.1.5 477 | '@turf/helpers': 5.1.5 478 | '@turf/meta': 5.1.6 479 | dev: false 480 | resolution: 481 | integrity: sha1-86X4ZMK5lqi7RxeUU1ofrxLuvvs= 482 | /@turf/line-arc/5.1.5: 483 | dependencies: 484 | '@turf/circle': 5.1.5 485 | '@turf/destination': 5.1.5 486 | '@turf/helpers': 5.1.5 487 | dev: false 488 | resolution: 489 | integrity: sha1-AHinRHg1oSrkFKIR+aZNEYYVDhU= 490 | /@turf/line-chunk/5.1.5: 491 | dependencies: 492 | '@turf/helpers': 5.1.5 493 | '@turf/length': 5.1.5 494 | '@turf/line-slice-along': 5.1.5 495 | '@turf/meta': 5.1.6 496 | dev: false 497 | resolution: 498 | integrity: sha1-kQqFwFwG2dD5w4l3oF4IGNUIXEI= 499 | /@turf/line-intersect/5.1.5: 500 | dependencies: 501 | '@turf/helpers': 5.1.5 502 | '@turf/invariant': 5.1.5 503 | '@turf/line-segment': 5.1.5 504 | '@turf/meta': 5.1.6 505 | geojson-rbush: 2.1.0 506 | dev: false 507 | resolution: 508 | integrity: sha1-DikHGuQDKV5JFyO8SfXPrI0R3fM= 509 | /@turf/line-offset/5.1.5: 510 | dependencies: 511 | '@turf/helpers': 5.1.5 512 | '@turf/invariant': 5.1.5 513 | '@turf/meta': 5.1.6 514 | dev: false 515 | resolution: 516 | integrity: sha1-KrWy8In4yRPiMdmUN4553KkLWh4= 517 | /@turf/line-overlap/5.1.5: 518 | dependencies: 519 | '@turf/boolean-point-on-line': 5.1.5 520 | '@turf/helpers': 5.1.5 521 | '@turf/invariant': 5.1.5 522 | '@turf/line-segment': 5.1.5 523 | '@turf/meta': 5.1.6 524 | '@turf/nearest-point-on-line': 5.1.5 525 | geojson-rbush: 2.1.0 526 | dev: false 527 | resolution: 528 | integrity: sha1-lDxvh6A4bcQ9+sEdKz/5wRLNP2A= 529 | /@turf/line-segment/5.1.5: 530 | dependencies: 531 | '@turf/helpers': 5.1.5 532 | '@turf/invariant': 5.1.5 533 | '@turf/meta': 5.1.6 534 | dev: false 535 | resolution: 536 | integrity: sha1-Mgeq7lRqskw9jcPMY/kcdwuAE+U= 537 | /@turf/line-slice-along/5.1.5: 538 | dependencies: 539 | '@turf/bearing': 5.1.5 540 | '@turf/destination': 5.1.5 541 | '@turf/distance': 5.1.5 542 | '@turf/helpers': 5.1.5 543 | dev: false 544 | resolution: 545 | integrity: sha1-7drQoh70efKWihG9LdcomiEy6aU= 546 | /@turf/line-slice/5.1.5: 547 | dependencies: 548 | '@turf/helpers': 5.1.5 549 | '@turf/invariant': 5.1.5 550 | '@turf/nearest-point-on-line': 5.1.5 551 | dev: false 552 | resolution: 553 | integrity: sha1-Hs/OFGKjeFeXVM7fRGTN4mgp8rU= 554 | /@turf/line-split/5.1.5: 555 | dependencies: 556 | '@turf/bbox': 5.1.5 557 | '@turf/helpers': 5.1.5 558 | '@turf/invariant': 5.1.5 559 | '@turf/line-intersect': 5.1.5 560 | '@turf/line-segment': 5.1.5 561 | '@turf/meta': 5.1.6 562 | '@turf/nearest-point-on-line': 5.1.5 563 | '@turf/square': 5.1.5 564 | '@turf/truncate': 5.1.5 565 | geojson-rbush: 2.1.0 566 | dev: false 567 | resolution: 568 | integrity: sha1-Wy30w3YZty73JbUWPPmSbVVArLc= 569 | /@turf/line-to-polygon/5.1.5: 570 | dependencies: 571 | '@turf/bbox': 5.1.5 572 | '@turf/helpers': 5.1.5 573 | '@turf/invariant': 5.1.5 574 | dev: false 575 | resolution: 576 | integrity: sha1-ITz0Gmj4Ikd4ujnTGH3sPouBhlo= 577 | /@turf/mask/5.1.5: 578 | dependencies: 579 | '@turf/bbox': 5.1.5 580 | '@turf/helpers': 5.1.5 581 | '@turf/meta': 5.1.6 582 | '@turf/union': 5.1.5 583 | rbush: 2.0.2 584 | dev: false 585 | resolution: 586 | integrity: sha1-mrD+8aJyyY/j70kvn/thggayQtU= 587 | /@turf/meta/5.1.6: 588 | dependencies: 589 | '@turf/helpers': 5.1.5 590 | dev: false 591 | resolution: 592 | integrity: sha1-wgqGPt7Qhp+yhUje6Ik0G8y0akY= 593 | /@turf/meta/6.0.2: 594 | dependencies: 595 | '@turf/helpers': 6.1.4 596 | dev: false 597 | resolution: 598 | integrity: sha512-VA7HJkx7qF1l3+GNGkDVn2oXy4+QoLP6LktXAaZKjuT1JI0YESat7quUkbCMy4zP9lAUuvS4YMslLyTtr919FA== 599 | /@turf/midpoint/5.1.5: 600 | dependencies: 601 | '@turf/bearing': 5.1.5 602 | '@turf/destination': 5.1.5 603 | '@turf/distance': 5.1.5 604 | '@turf/helpers': 5.1.5 605 | dev: false 606 | resolution: 607 | integrity: sha1-4mH2srDqgSTM7/VSomLdRlydBfA= 608 | /@turf/nearest-point-on-line/5.1.5: 609 | dependencies: 610 | '@turf/bearing': 5.1.5 611 | '@turf/destination': 5.1.5 612 | '@turf/distance': 5.1.5 613 | '@turf/helpers': 5.1.5 614 | '@turf/invariant': 5.1.5 615 | '@turf/line-intersect': 5.1.5 616 | '@turf/meta': 5.1.6 617 | dev: false 618 | resolution: 619 | integrity: sha1-VgauKX8VlHUkvqUaKp71HsG/nDY= 620 | /@turf/nearest-point-to-line/5.1.6: 621 | dependencies: 622 | '@turf/helpers': 6.1.4 623 | '@turf/invariant': 6.1.2 624 | '@turf/meta': 6.0.2 625 | '@turf/point-to-line-distance': 5.1.6 626 | object-assign: 4.1.1 627 | dev: false 628 | resolution: 629 | integrity: sha512-ZSvDIEiHhifn/vNwLXZI/E8xmEz5yBPqfUR7BVHRZrB1cP7jLhKZvkbidjG//uW8Fr1Ulc+PFOXczLspIcx/lw== 630 | /@turf/nearest-point/5.1.5: 631 | dependencies: 632 | '@turf/clone': 5.1.5 633 | '@turf/distance': 5.1.5 634 | '@turf/helpers': 5.1.5 635 | '@turf/meta': 5.1.6 636 | dev: false 637 | resolution: 638 | integrity: sha1-EgUN5Bw5hEMiTHl43g9iE5ANNPs= 639 | /@turf/planepoint/5.1.5: 640 | dependencies: 641 | '@turf/helpers': 5.1.5 642 | '@turf/invariant': 5.1.5 643 | dev: false 644 | resolution: 645 | integrity: sha1-GLvfAG91ne9eQsagBsn53oGyt/8= 646 | /@turf/point-grid/5.1.5: 647 | dependencies: 648 | '@turf/boolean-within': 5.1.5 649 | '@turf/distance': 5.1.5 650 | '@turf/helpers': 5.1.5 651 | '@turf/invariant': 5.1.5 652 | dev: false 653 | resolution: 654 | integrity: sha1-MFFBJI9Quv42zn5mukuX56sjaIc= 655 | /@turf/point-on-feature/5.1.5: 656 | dependencies: 657 | '@turf/boolean-point-in-polygon': 5.1.5 658 | '@turf/center': 5.1.5 659 | '@turf/explode': 5.1.5 660 | '@turf/helpers': 5.1.5 661 | '@turf/nearest-point': 5.1.5 662 | dev: false 663 | resolution: 664 | integrity: sha1-MMfwMkMCd8ZBjZbSieRba/shP+c= 665 | /@turf/point-to-line-distance/5.1.6: 666 | dependencies: 667 | '@turf/bearing': 6.0.1 668 | '@turf/distance': 6.0.1 669 | '@turf/helpers': 6.1.4 670 | '@turf/invariant': 6.1.2 671 | '@turf/meta': 6.0.2 672 | '@turf/projection': 6.0.1 673 | '@turf/rhumb-bearing': 6.0.1 674 | '@turf/rhumb-distance': 6.0.1 675 | dev: false 676 | resolution: 677 | integrity: sha512-PE3hiTeeDEi4ZLPtI8XAzFYW9nHo1EVsZGm/4ZVV8jo39d3X1oLVHxY3e1PkCmWwRapXy4QLqvnTQ7nU4wspNw== 678 | /@turf/points-within-polygon/5.1.5: 679 | dependencies: 680 | '@turf/boolean-point-in-polygon': 5.1.5 681 | '@turf/helpers': 5.1.5 682 | '@turf/meta': 5.1.6 683 | dev: false 684 | resolution: 685 | integrity: sha1-K4VaXfOq2lfC7oIKB1SrlJKKIzc= 686 | /@turf/polygon-tangents/5.1.5: 687 | dependencies: 688 | '@turf/helpers': 5.1.5 689 | '@turf/invariant': 5.1.5 690 | dev: false 691 | resolution: 692 | integrity: sha1-K/AJkUcwJbF44lDcfLmuVAm71lI= 693 | /@turf/polygon-to-line/5.1.5: 694 | dependencies: 695 | '@turf/helpers': 5.1.5 696 | '@turf/invariant': 5.1.5 697 | dev: false 698 | resolution: 699 | integrity: sha1-I7tEjYTcTGUZmaxhGjbZHFklA2o= 700 | /@turf/polygonize/5.1.5: 701 | dependencies: 702 | '@turf/boolean-point-in-polygon': 5.1.5 703 | '@turf/envelope': 5.1.5 704 | '@turf/helpers': 5.1.5 705 | '@turf/invariant': 5.1.5 706 | '@turf/meta': 5.1.6 707 | dev: false 708 | resolution: 709 | integrity: sha1-BJP6EYefOdELmtAs5qI+lC0IqjI= 710 | /@turf/projection/5.1.5: 711 | dependencies: 712 | '@turf/clone': 5.1.5 713 | '@turf/helpers': 5.1.5 714 | '@turf/meta': 5.1.6 715 | dev: false 716 | resolution: 717 | integrity: sha1-JFF+7rLzaBa6n3EueubWo2jt91c= 718 | /@turf/projection/6.0.1: 719 | dependencies: 720 | '@turf/clone': 6.0.2 721 | '@turf/helpers': 6.1.4 722 | '@turf/meta': 6.0.2 723 | dev: false 724 | resolution: 725 | integrity: sha512-Y3RvGT6I53MjYKLG69e9sMk45wJXcLbrEO1t6P3WQQQGqA2gYhhMJyV41vE2Z2llrJpvs2dDx/tIeQzGd0HHMQ== 726 | /@turf/random/5.1.5: 727 | dependencies: 728 | '@turf/helpers': 5.1.5 729 | dev: false 730 | resolution: 731 | integrity: sha1-sy78k0Vgroulfo67UfJBw5+6Lns= 732 | /@turf/rewind/5.1.5: 733 | dependencies: 734 | '@turf/boolean-clockwise': 5.1.5 735 | '@turf/clone': 5.1.5 736 | '@turf/helpers': 5.1.5 737 | '@turf/invariant': 5.1.5 738 | '@turf/meta': 5.1.6 739 | dev: false 740 | resolution: 741 | integrity: sha1-nqPbSmi3PB/R3RH1djGxQ8/vock= 742 | /@turf/rhumb-bearing/5.1.5: 743 | dependencies: 744 | '@turf/helpers': 5.1.5 745 | '@turf/invariant': 5.1.5 746 | dev: false 747 | resolution: 748 | integrity: sha1-rPalAkJ+uMSeGM2mrg7/qwxd3NI= 749 | /@turf/rhumb-bearing/6.0.1: 750 | dependencies: 751 | '@turf/helpers': 6.1.4 752 | '@turf/invariant': 6.1.2 753 | dev: false 754 | resolution: 755 | integrity: sha512-MVBra8OVfjM4+/N0B3o6cBIYg9p/uRKzA9uk05RfrzasEbUL1vdD23LkTooVL74Yw4UxL8BQD9hS5Re2COJFDA== 756 | /@turf/rhumb-destination/5.1.5: 757 | dependencies: 758 | '@turf/helpers': 5.1.5 759 | '@turf/invariant': 5.1.5 760 | dev: false 761 | resolution: 762 | integrity: sha1-sbKuuSFUfyrAwamUtqEw+SRjx0I= 763 | /@turf/rhumb-distance/5.1.5: 764 | dependencies: 765 | '@turf/helpers': 5.1.5 766 | '@turf/invariant': 5.1.5 767 | dev: false 768 | resolution: 769 | integrity: sha1-GAaFdiX0IlOE2tQT5p85U4/192U= 770 | /@turf/rhumb-distance/6.0.1: 771 | dependencies: 772 | '@turf/helpers': 6.1.4 773 | '@turf/invariant': 6.1.2 774 | dev: false 775 | resolution: 776 | integrity: sha512-3G45DQtQByzzfHFPcCyJdUZFwsd45zfZ7sAb1ddF7mhEj4G70+T2G3GKjInymqDNrbyh2gbG6wQiZSToC8Uf9g== 777 | /@turf/sample/5.1.5: 778 | dependencies: 779 | '@turf/helpers': 5.1.5 780 | dev: false 781 | resolution: 782 | integrity: sha1-6ctEikeJzFbuPeLdZ4HiNDQ1tBE= 783 | /@turf/sector/5.1.5: 784 | dependencies: 785 | '@turf/circle': 5.1.5 786 | '@turf/helpers': 5.1.5 787 | '@turf/invariant': 5.1.5 788 | '@turf/line-arc': 5.1.5 789 | '@turf/meta': 5.1.6 790 | dev: false 791 | resolution: 792 | integrity: sha1-rCu5TBPt1gNPb9wrZwCBNdIPXgc= 793 | /@turf/shortest-path/5.1.5: 794 | dependencies: 795 | '@turf/bbox': 5.1.5 796 | '@turf/bbox-polygon': 5.1.5 797 | '@turf/boolean-point-in-polygon': 5.1.5 798 | '@turf/clean-coords': 5.1.5 799 | '@turf/distance': 5.1.5 800 | '@turf/helpers': 5.1.5 801 | '@turf/invariant': 5.1.5 802 | '@turf/meta': 5.1.6 803 | '@turf/transform-scale': 5.1.5 804 | dev: false 805 | resolution: 806 | integrity: sha1-hUroCW9rw+EwD6ynfz6PZ9j5Nas= 807 | /@turf/simplify/5.1.5: 808 | dependencies: 809 | '@turf/clean-coords': 5.1.5 810 | '@turf/clone': 5.1.5 811 | '@turf/helpers': 5.1.5 812 | '@turf/meta': 5.1.6 813 | dev: false 814 | resolution: 815 | integrity: sha1-Csjyei60IYGD7dmZjDJ1q+QIuSY= 816 | /@turf/square-grid/5.1.5: 817 | dependencies: 818 | '@turf/boolean-contains': 5.1.5 819 | '@turf/boolean-overlap': 5.1.5 820 | '@turf/distance': 5.1.5 821 | '@turf/helpers': 5.1.5 822 | '@turf/intersect': 5.1.6 823 | '@turf/invariant': 5.1.5 824 | dev: false 825 | resolution: 826 | integrity: sha1-G9X3uesU8LYLwjH+/nNR0aMvGlE= 827 | /@turf/square/5.1.5: 828 | dependencies: 829 | '@turf/distance': 5.1.5 830 | '@turf/helpers': 5.1.5 831 | dev: false 832 | resolution: 833 | integrity: sha1-qnsh5gM8ySUsOlvW89iNq9b+0YA= 834 | /@turf/standard-deviational-ellipse/5.1.5: 835 | dependencies: 836 | '@turf/center-mean': 5.1.5 837 | '@turf/ellipse': 5.1.5 838 | '@turf/helpers': 5.1.5 839 | '@turf/invariant': 5.1.5 840 | '@turf/meta': 5.1.6 841 | '@turf/points-within-polygon': 5.1.5 842 | dev: false 843 | resolution: 844 | integrity: sha1-hc0oO14ayljyG9ZkEuQUtW2FIyQ= 845 | /@turf/tag/5.1.5: 846 | dependencies: 847 | '@turf/boolean-point-in-polygon': 5.1.5 848 | '@turf/clone': 5.1.5 849 | '@turf/helpers': 5.1.5 850 | '@turf/meta': 5.1.6 851 | dev: false 852 | resolution: 853 | integrity: sha1-0e4aUIjs/UoUEQGcmCOczypJfSA= 854 | /@turf/tesselate/5.1.5: 855 | dependencies: 856 | '@turf/helpers': 5.1.5 857 | earcut: 2.2.2 858 | dev: false 859 | resolution: 860 | integrity: sha1-MqWU6cIaAEIKn5DSxD3z4RZgYc0= 861 | /@turf/tin/5.1.5: 862 | dependencies: 863 | '@turf/helpers': 5.1.5 864 | dev: false 865 | resolution: 866 | integrity: sha1-KCI+r8X76a6azKgc3P6l0UJMkX0= 867 | /@turf/transform-rotate/5.1.5: 868 | dependencies: 869 | '@turf/centroid': 5.1.5 870 | '@turf/clone': 5.1.5 871 | '@turf/helpers': 5.1.5 872 | '@turf/invariant': 5.1.5 873 | '@turf/meta': 5.1.6 874 | '@turf/rhumb-bearing': 5.1.5 875 | '@turf/rhumb-destination': 5.1.5 876 | '@turf/rhumb-distance': 5.1.5 877 | dev: false 878 | resolution: 879 | integrity: sha1-0Jbt2eMA/jFQadVNjkWMQJIh7fs= 880 | /@turf/transform-scale/5.1.5: 881 | dependencies: 882 | '@turf/bbox': 5.1.5 883 | '@turf/center': 5.1.5 884 | '@turf/centroid': 5.1.5 885 | '@turf/clone': 5.1.5 886 | '@turf/helpers': 5.1.5 887 | '@turf/invariant': 5.1.5 888 | '@turf/meta': 5.1.6 889 | '@turf/rhumb-bearing': 5.1.5 890 | '@turf/rhumb-destination': 5.1.5 891 | '@turf/rhumb-distance': 5.1.5 892 | dev: false 893 | resolution: 894 | integrity: sha1-cP064BhWz3uunxWtVhzf6PiQAbk= 895 | /@turf/transform-translate/5.1.5: 896 | dependencies: 897 | '@turf/clone': 5.1.5 898 | '@turf/helpers': 5.1.5 899 | '@turf/invariant': 5.1.5 900 | '@turf/meta': 5.1.6 901 | '@turf/rhumb-destination': 5.1.5 902 | dev: false 903 | resolution: 904 | integrity: sha1-Uwolf7Hccmja3Ks05nkB6yo97GM= 905 | /@turf/triangle-grid/5.1.5: 906 | dependencies: 907 | '@turf/distance': 5.1.5 908 | '@turf/helpers': 5.1.5 909 | '@turf/intersect': 5.1.6 910 | '@turf/invariant': 5.1.5 911 | dev: false 912 | resolution: 913 | integrity: sha1-ezZ2IQhVTBTyjK/zxIsc/ILI3IE= 914 | /@turf/truncate/5.1.5: 915 | dependencies: 916 | '@turf/helpers': 5.1.5 917 | '@turf/meta': 5.1.6 918 | dev: false 919 | resolution: 920 | integrity: sha1-nu37Oxi6gfLJjT6tCUMcyhiErYk= 921 | /@turf/turf/5.1.6: 922 | dependencies: 923 | '@turf/along': 5.1.5 924 | '@turf/area': 5.1.5 925 | '@turf/bbox': 5.1.5 926 | '@turf/bbox-clip': 5.1.5 927 | '@turf/bbox-polygon': 5.1.5 928 | '@turf/bearing': 5.1.5 929 | '@turf/bezier-spline': 5.1.5 930 | '@turf/boolean-clockwise': 5.1.5 931 | '@turf/boolean-contains': 5.1.5 932 | '@turf/boolean-crosses': 5.1.5 933 | '@turf/boolean-disjoint': 5.1.6 934 | '@turf/boolean-equal': 5.1.5 935 | '@turf/boolean-overlap': 5.1.5 936 | '@turf/boolean-parallel': 5.1.5 937 | '@turf/boolean-point-in-polygon': 5.1.5 938 | '@turf/boolean-point-on-line': 5.1.5 939 | '@turf/boolean-within': 5.1.5 940 | '@turf/buffer': 5.1.5 941 | '@turf/center': 5.1.5 942 | '@turf/center-mean': 5.1.5 943 | '@turf/center-median': 5.1.5 944 | '@turf/center-of-mass': 5.1.5 945 | '@turf/centroid': 5.1.5 946 | '@turf/circle': 5.1.5 947 | '@turf/clean-coords': 5.1.5 948 | '@turf/clone': 5.1.5 949 | '@turf/clusters': 5.1.5 950 | '@turf/clusters-dbscan': 5.1.5 951 | '@turf/clusters-kmeans': 5.1.5 952 | '@turf/collect': 5.1.5 953 | '@turf/combine': 5.1.5 954 | '@turf/concave': 5.1.5 955 | '@turf/convex': 5.1.5 956 | '@turf/destination': 5.1.5 957 | '@turf/difference': 5.1.5 958 | '@turf/dissolve': 5.1.5 959 | '@turf/distance': 5.1.5 960 | '@turf/ellipse': 5.1.5 961 | '@turf/envelope': 5.1.5 962 | '@turf/explode': 5.1.5 963 | '@turf/flatten': 5.1.5 964 | '@turf/flip': 5.1.5 965 | '@turf/great-circle': 5.1.5 966 | '@turf/helpers': 5.1.5 967 | '@turf/hex-grid': 5.1.5 968 | '@turf/interpolate': 5.1.5 969 | '@turf/intersect': 5.1.6 970 | '@turf/invariant': 5.1.5 971 | '@turf/isobands': 5.1.5 972 | '@turf/isolines': 5.1.5 973 | '@turf/kinks': 5.1.5 974 | '@turf/length': 5.1.5 975 | '@turf/line-arc': 5.1.5 976 | '@turf/line-chunk': 5.1.5 977 | '@turf/line-intersect': 5.1.5 978 | '@turf/line-offset': 5.1.5 979 | '@turf/line-overlap': 5.1.5 980 | '@turf/line-segment': 5.1.5 981 | '@turf/line-slice': 5.1.5 982 | '@turf/line-slice-along': 5.1.5 983 | '@turf/line-split': 5.1.5 984 | '@turf/line-to-polygon': 5.1.5 985 | '@turf/mask': 5.1.5 986 | '@turf/meta': 5.1.6 987 | '@turf/midpoint': 5.1.5 988 | '@turf/nearest-point': 5.1.5 989 | '@turf/nearest-point-on-line': 5.1.5 990 | '@turf/nearest-point-to-line': 5.1.6 991 | '@turf/planepoint': 5.1.5 992 | '@turf/point-grid': 5.1.5 993 | '@turf/point-on-feature': 5.1.5 994 | '@turf/point-to-line-distance': 5.1.6 995 | '@turf/points-within-polygon': 5.1.5 996 | '@turf/polygon-tangents': 5.1.5 997 | '@turf/polygon-to-line': 5.1.5 998 | '@turf/polygonize': 5.1.5 999 | '@turf/projection': 5.1.5 1000 | '@turf/random': 5.1.5 1001 | '@turf/rewind': 5.1.5 1002 | '@turf/rhumb-bearing': 5.1.5 1003 | '@turf/rhumb-destination': 5.1.5 1004 | '@turf/rhumb-distance': 5.1.5 1005 | '@turf/sample': 5.1.5 1006 | '@turf/sector': 5.1.5 1007 | '@turf/shortest-path': 5.1.5 1008 | '@turf/simplify': 5.1.5 1009 | '@turf/square': 5.1.5 1010 | '@turf/square-grid': 5.1.5 1011 | '@turf/standard-deviational-ellipse': 5.1.5 1012 | '@turf/tag': 5.1.5 1013 | '@turf/tesselate': 5.1.5 1014 | '@turf/tin': 5.1.5 1015 | '@turf/transform-rotate': 5.1.5 1016 | '@turf/transform-scale': 5.1.5 1017 | '@turf/transform-translate': 5.1.5 1018 | '@turf/triangle-grid': 5.1.5 1019 | '@turf/truncate': 5.1.5 1020 | '@turf/union': 5.1.5 1021 | '@turf/unkink-polygon': 5.1.5 1022 | '@turf/voronoi': 5.1.5 1023 | dev: false 1024 | resolution: 1025 | integrity: sha1-wxIlkoh+0jS3VGi4qMRb+Ib7+PY= 1026 | /@turf/union/5.1.5: 1027 | dependencies: 1028 | '@turf/helpers': 5.1.5 1029 | turf-jsts: 1.2.3 1030 | dev: false 1031 | resolution: 1032 | integrity: sha1-UyhbYJQEf8WNlqrA6pCGXsNNRUs= 1033 | /@turf/unkink-polygon/5.1.5: 1034 | dependencies: 1035 | '@turf/area': 5.1.5 1036 | '@turf/boolean-point-in-polygon': 5.1.5 1037 | '@turf/helpers': 5.1.5 1038 | '@turf/meta': 5.1.6 1039 | rbush: 2.0.2 1040 | dev: false 1041 | resolution: 1042 | integrity: sha1-ewGEfFD7V0riV54Z5Ey6hSbSE8M= 1043 | /@turf/voronoi/5.1.5: 1044 | dependencies: 1045 | '@turf/helpers': 5.1.5 1046 | '@turf/invariant': 5.1.5 1047 | d3-voronoi: 1.1.2 1048 | dev: false 1049 | resolution: 1050 | integrity: sha1-6FbpQG3MLyXWbdyJhYTifC6/ymY= 1051 | /call-bind/1.0.0: 1052 | dependencies: 1053 | function-bind: 1.1.1 1054 | get-intrinsic: 1.0.1 1055 | dev: false 1056 | resolution: 1057 | integrity: sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== 1058 | /commander/2.20.3: 1059 | dev: false 1060 | resolution: 1061 | integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== 1062 | /concaveman/1.2.0: 1063 | dependencies: 1064 | point-in-polygon: 1.0.1 1065 | rbush: 3.0.1 1066 | robust-predicates: 2.0.4 1067 | tinyqueue: 2.0.3 1068 | dev: false 1069 | resolution: 1070 | integrity: sha512-OcqechF2/kubbffomKqjGEkb0ndlYhEbmyg/fxIGqdfYp5AZjD2Kl5hc97Hh3ngEuHU2314Z4KDbxL7qXGWrQQ== 1071 | /d3-array/1.2.4: 1072 | dev: false 1073 | resolution: 1074 | integrity: sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw== 1075 | /d3-dsv/2.0.0: 1076 | dependencies: 1077 | commander: 2.20.3 1078 | iconv-lite: 0.4.24 1079 | rw: 1.3.3 1080 | dev: false 1081 | hasBin: true 1082 | resolution: 1083 | integrity: sha512-E+Pn8UJYx9mViuIUkoc93gJGGYut6mSDKy2+XaPwccwkRGlR+LO97L2VCCRjQivTwLHkSnAJG7yo00BWY6QM+w== 1084 | /d3-geo/1.7.1: 1085 | dependencies: 1086 | d3-array: 1.2.4 1087 | dev: false 1088 | resolution: 1089 | integrity: sha512-O4AempWAr+P5qbk2bC2FuN/sDW4z+dN2wDf9QV3bxQt4M5HfOEeXLgJ/UKQW0+o1Dj8BE+L5kiDbdWUMjsmQpw== 1090 | /d3-voronoi/1.1.2: 1091 | dev: false 1092 | resolution: 1093 | integrity: sha1-Fodmfo8TotFYyAwUgMWinLDYlzw= 1094 | /deep-equal/1.1.1: 1095 | dependencies: 1096 | is-arguments: 1.0.4 1097 | is-date-object: 1.0.2 1098 | is-regex: 1.1.1 1099 | object-is: 1.1.3 1100 | object-keys: 1.1.1 1101 | regexp.prototype.flags: 1.3.0 1102 | dev: false 1103 | resolution: 1104 | integrity: sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== 1105 | /define-properties/1.1.3: 1106 | dependencies: 1107 | object-keys: 1.1.1 1108 | dev: false 1109 | engines: 1110 | node: '>= 0.4' 1111 | resolution: 1112 | integrity: sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 1113 | /density-clustering/1.3.0: 1114 | dev: false 1115 | resolution: 1116 | integrity: sha1-3J9ZyPCrl+FiSsZJMP0xlIF9ysU= 1117 | /earcut/2.2.2: 1118 | dev: false 1119 | resolution: 1120 | integrity: sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ== 1121 | /es-abstract/1.17.7: 1122 | dependencies: 1123 | es-to-primitive: 1.2.1 1124 | function-bind: 1.1.1 1125 | has: 1.0.3 1126 | has-symbols: 1.0.1 1127 | is-callable: 1.2.2 1128 | is-regex: 1.1.1 1129 | object-inspect: 1.8.0 1130 | object-keys: 1.1.1 1131 | object.assign: 4.1.2 1132 | string.prototype.trimend: 1.0.2 1133 | string.prototype.trimstart: 1.0.2 1134 | dev: false 1135 | engines: 1136 | node: '>= 0.4' 1137 | resolution: 1138 | integrity: sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== 1139 | /es-abstract/1.18.0-next.1: 1140 | dependencies: 1141 | es-to-primitive: 1.2.1 1142 | function-bind: 1.1.1 1143 | has: 1.0.3 1144 | has-symbols: 1.0.1 1145 | is-callable: 1.2.2 1146 | is-negative-zero: 2.0.0 1147 | is-regex: 1.1.1 1148 | object-inspect: 1.8.0 1149 | object-keys: 1.1.1 1150 | object.assign: 4.1.2 1151 | string.prototype.trimend: 1.0.2 1152 | string.prototype.trimstart: 1.0.2 1153 | dev: false 1154 | engines: 1155 | node: '>= 0.4' 1156 | resolution: 1157 | integrity: sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== 1158 | /es-to-primitive/1.2.1: 1159 | dependencies: 1160 | is-callable: 1.2.2 1161 | is-date-object: 1.0.2 1162 | is-symbol: 1.0.3 1163 | dev: false 1164 | engines: 1165 | node: '>= 0.4' 1166 | resolution: 1167 | integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 1168 | /function-bind/1.1.1: 1169 | dev: false 1170 | resolution: 1171 | integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 1172 | /geojson-equality/0.1.6: 1173 | dependencies: 1174 | deep-equal: 1.1.1 1175 | dev: false 1176 | resolution: 1177 | integrity: sha1-oXE3TvBD5dR5eZWEC65GSOB1LXI= 1178 | /geojson-rbush/2.1.0: 1179 | dependencies: 1180 | '@turf/helpers': 6.1.4 1181 | '@turf/meta': 6.0.2 1182 | rbush: 3.0.1 1183 | dev: false 1184 | resolution: 1185 | integrity: sha1-O9c745H8ELCuaT2bis6iquC4Oo0= 1186 | /get-closest/0.0.4: 1187 | dev: false 1188 | resolution: 1189 | integrity: sha1-JprHdtHmAiqg/Vht1wjop9Miaa8= 1190 | /get-intrinsic/1.0.1: 1191 | dependencies: 1192 | function-bind: 1.1.1 1193 | has: 1.0.3 1194 | has-symbols: 1.0.1 1195 | dev: false 1196 | resolution: 1197 | integrity: sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== 1198 | /has-symbols/1.0.1: 1199 | dev: false 1200 | engines: 1201 | node: '>= 0.4' 1202 | resolution: 1203 | integrity: sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== 1204 | /has/1.0.3: 1205 | dependencies: 1206 | function-bind: 1.1.1 1207 | dev: false 1208 | engines: 1209 | node: '>= 0.4.0' 1210 | resolution: 1211 | integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 1212 | /iconv-lite/0.4.24: 1213 | dependencies: 1214 | safer-buffer: 2.1.2 1215 | dev: false 1216 | engines: 1217 | node: '>=0.10.0' 1218 | resolution: 1219 | integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== 1220 | /is-arguments/1.0.4: 1221 | dev: false 1222 | engines: 1223 | node: '>= 0.4' 1224 | resolution: 1225 | integrity: sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== 1226 | /is-callable/1.2.2: 1227 | dev: false 1228 | engines: 1229 | node: '>= 0.4' 1230 | resolution: 1231 | integrity: sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== 1232 | /is-date-object/1.0.2: 1233 | dev: false 1234 | engines: 1235 | node: '>= 0.4' 1236 | resolution: 1237 | integrity: sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== 1238 | /is-negative-zero/2.0.0: 1239 | dev: false 1240 | engines: 1241 | node: '>= 0.4' 1242 | resolution: 1243 | integrity: sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= 1244 | /is-regex/1.1.1: 1245 | dependencies: 1246 | has-symbols: 1.0.1 1247 | dev: false 1248 | engines: 1249 | node: '>= 0.4' 1250 | resolution: 1251 | integrity: sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== 1252 | /is-symbol/1.0.3: 1253 | dependencies: 1254 | has-symbols: 1.0.1 1255 | dev: false 1256 | engines: 1257 | node: '>= 0.4' 1258 | resolution: 1259 | integrity: sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== 1260 | /lineclip/1.1.5: 1261 | dev: false 1262 | resolution: 1263 | integrity: sha1-K/JgZ9lDVP6r+R5CdoI221YW/RM= 1264 | /object-assign/4.1.1: 1265 | dev: false 1266 | engines: 1267 | node: '>=0.10.0' 1268 | resolution: 1269 | integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= 1270 | /object-inspect/1.8.0: 1271 | dev: false 1272 | resolution: 1273 | integrity: sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== 1274 | /object-is/1.1.3: 1275 | dependencies: 1276 | define-properties: 1.1.3 1277 | es-abstract: 1.18.0-next.1 1278 | dev: false 1279 | engines: 1280 | node: '>= 0.4' 1281 | resolution: 1282 | integrity: sha512-teyqLvFWzLkq5B9ki8FVWA902UER2qkxmdA4nLf+wjOLAWgxzCWZNCxpDq9MvE8MmhWNr+I8w3BN49Vx36Y6Xg== 1283 | /object-keys/1.1.1: 1284 | dev: false 1285 | engines: 1286 | node: '>= 0.4' 1287 | resolution: 1288 | integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 1289 | /object.assign/4.1.2: 1290 | dependencies: 1291 | call-bind: 1.0.0 1292 | define-properties: 1.1.3 1293 | has-symbols: 1.0.1 1294 | object-keys: 1.1.1 1295 | dev: false 1296 | engines: 1297 | node: '>= 0.4' 1298 | resolution: 1299 | integrity: sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== 1300 | /point-in-polygon/1.0.1: 1301 | dev: false 1302 | resolution: 1303 | integrity: sha1-1Ztk6P7kHElFiqyCtWcYxZV7Kvc= 1304 | /quickselect/1.1.1: 1305 | dev: false 1306 | resolution: 1307 | integrity: sha512-qN0Gqdw4c4KGPsBOQafj6yj/PA6c/L63f6CaZ/DCF/xF4Esu3jVmKLUDYxghFx8Kb/O7y9tI7x2RjTSXwdK1iQ== 1308 | /quickselect/2.0.0: 1309 | dev: false 1310 | resolution: 1311 | integrity: sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw== 1312 | /rbush/2.0.2: 1313 | dependencies: 1314 | quickselect: 1.1.1 1315 | dev: false 1316 | resolution: 1317 | integrity: sha512-XBOuALcTm+O/H8G90b6pzu6nX6v2zCKiFG4BJho8a+bY6AER6t8uQUZdi5bomQc0AprCWhEGa7ncAbbRap0bRA== 1318 | /rbush/3.0.1: 1319 | dependencies: 1320 | quickselect: 2.0.0 1321 | dev: false 1322 | resolution: 1323 | integrity: sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w== 1324 | /regexp.prototype.flags/1.3.0: 1325 | dependencies: 1326 | define-properties: 1.1.3 1327 | es-abstract: 1.17.7 1328 | dev: false 1329 | engines: 1330 | node: '>= 0.4' 1331 | resolution: 1332 | integrity: sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== 1333 | /robust-predicates/2.0.4: 1334 | dev: false 1335 | resolution: 1336 | integrity: sha512-l4NwboJM74Ilm4VKfbAtFeGq7aEjWL+5kVFcmgFA2MrdnQWx9iE/tUGvxY5HyMI7o/WpSIUFLbC5fbeaHgSCYg== 1337 | /rw/1.3.3: 1338 | dev: false 1339 | resolution: 1340 | integrity: sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q= 1341 | /safer-buffer/2.1.2: 1342 | dev: false 1343 | resolution: 1344 | integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== 1345 | /skmeans/0.9.7: 1346 | dev: false 1347 | resolution: 1348 | integrity: sha512-hNj1/oZ7ygsfmPZ7ZfN5MUBRoGg1gtpnImuJBgLO0ljQ67DtJuiQaiYdS4lUA6s0KCwnPhGivtC/WRwIZLkHyg== 1349 | /string.prototype.trimend/1.0.2: 1350 | dependencies: 1351 | define-properties: 1.1.3 1352 | es-abstract: 1.18.0-next.1 1353 | dev: false 1354 | resolution: 1355 | integrity: sha512-8oAG/hi14Z4nOVP0z6mdiVZ/wqjDtWSLygMigTzAb+7aPEDTleeFf+WrF+alzecxIRkckkJVn+dTlwzJXORATw== 1356 | /string.prototype.trimstart/1.0.2: 1357 | dependencies: 1358 | define-properties: 1.1.3 1359 | es-abstract: 1.18.0-next.1 1360 | dev: false 1361 | resolution: 1362 | integrity: sha512-7F6CdBTl5zyu30BJFdzSTlSlLPwODC23Od+iLoVH8X6+3fvDPPuBVVj9iaB1GOsSTSIgVfsfm27R2FGrAPznWg== 1363 | /tinyqueue/2.0.3: 1364 | dev: false 1365 | resolution: 1366 | integrity: sha512-ppJZNDuKGgxzkHihX8v9v9G5f+18gzaTfrukGrq6ueg0lmH4nqVnA2IPG0AEH3jKEk2GRJCUhDoqpoiw3PHLBA== 1367 | /topojson-client/3.1.0: 1368 | dependencies: 1369 | commander: 2.20.3 1370 | dev: false 1371 | hasBin: true 1372 | resolution: 1373 | integrity: sha512-605uxS6bcYxGXw9qi62XyrV6Q3xwbndjachmNxu8HWTtVPxZfEJN9fd/SZS1Q54Sn2y0TMyMxFj/cJINqGHrKw== 1374 | /topojson-server/3.0.1: 1375 | dependencies: 1376 | commander: 2.20.3 1377 | dev: false 1378 | hasBin: true 1379 | resolution: 1380 | integrity: sha512-/VS9j/ffKr2XAOjlZ9CgyyeLmgJ9dMwq6Y0YEON8O7p/tGGk+dCWnrE03zEdu7i4L7YsFZLEPZPzCvcB7lEEXw== 1381 | /turf-jsts/1.2.3: 1382 | dev: false 1383 | resolution: 1384 | integrity: sha512-Ja03QIJlPuHt4IQ2FfGex4F4JAr8m3jpaHbFbQrgwr7s7L6U8ocrHiF3J1+wf9jzhGKxvDeaCAnGDot8OjGFyA== 1385 | specifiers: 1386 | '@turf/turf': ^5.1.6 1387 | d3-dsv: ^2.0.0 1388 | topojson-client: ^3.1.0 1389 | topojson-server: ^3.0.1 1390 | -------------------------------------------------------------------------------- /skeleton/.eslintrc.js: -------------------------------------------------------------------------------- 1 | const jsRules = { 2 | 'no-unused-vars': ['warn', { args: 'none' }], 3 | 'no-return-await': 'warn', 4 | 'no-use-before-define': 'error', 5 | 'no-mixed-spaces-and-tabs': 'error', 6 | 'no-trailing-spaces': 'warn', 7 | }; 8 | 9 | module.exports = { 10 | env: { 11 | browser: true, 12 | es6: true, 13 | node: true, 14 | }, 15 | parser: '@typescript-eslint/parser', 16 | parserOptions: { 17 | project: 'tsconfig.json', 18 | ecmaVersion: 2019, 19 | sourceType: 'module', 20 | }, 21 | overrides: [ 22 | { 23 | files: ['**/*.svelte'], 24 | parser: 'espree', 25 | processor: 'svelte3/svelte3', 26 | extends: ['eslint:recommended'], 27 | rules: jsRules, 28 | }, 29 | { 30 | files: ['**/*.js'], 31 | extends: ['eslint:recommended'], 32 | rules: jsRules, 33 | }, 34 | ], 35 | plugins: ['@typescript-eslint/eslint-plugin', 'svelte3'], 36 | extends: [ 37 | 'plugin:@typescript-eslint/eslint-recommended', 38 | 'prettier/@typescript-eslint', 39 | ], 40 | rules: {}, 41 | }; 42 | -------------------------------------------------------------------------------- /skeleton/.gitignore: -------------------------------------------------------------------------------- 1 | public 2 | public/bundle.* 3 | public/utils.* 4 | build 5 | ### Node ### 6 | # Logs 7 | logs 8 | *.log 9 | npm-debug.log* 10 | 11 | # Runtime data 12 | pids 13 | *.pid 14 | *.seed 15 | *.pid.lock 16 | 17 | # node-waf configuration 18 | .lock-wscript 19 | 20 | # Compiled binary addons (http://nodejs.org/api/addons.html) 21 | build/Release 22 | 23 | # Dependency directories 24 | node_modules 25 | jspm_packages 26 | 27 | # Optional npm cache directory 28 | .npm 29 | 30 | # Optional eslint cache 31 | .eslintcache 32 | 33 | # Optional REPL history 34 | .node_repl_history 35 | 36 | # Output of 'npm pack' 37 | *.tgz 38 | 39 | ### Git ### 40 | *.orig 41 | 42 | ### macOS ### 43 | .DS_Store 44 | .AppleDouble 45 | .LSOverride 46 | 47 | # Thumbnails 48 | ._* 49 | # Files that might appear in the root of a volume 50 | .DocumentRevisions-V100 51 | .fseventsd 52 | .Spotlight-V100 53 | .TemporaryItems 54 | .Trashes 55 | .VolumeIcon.icns 56 | .com.apple.timemachine.donotpresent 57 | # Directories potentially created on remote AFP share 58 | .AppleDB 59 | .AppleDesktop 60 | Network Trash Folder 61 | Temporary Items 62 | .apdisk 63 | -------------------------------------------------------------------------------- /skeleton/.prettierignore: -------------------------------------------------------------------------------- 1 | .git/* 2 | .DS_Store 3 | 4 | license 5 | yarn.lock 6 | .travis.yml 7 | 8 | .yarnclean 9 | .eslintignore 10 | .prettierignore 11 | .npmignore 12 | .gitignore 13 | .dockerignore 14 | 15 | dist 16 | build 17 | packages/*/lib/app 18 | consoles/*/lib/app 19 | 20 | *.ico 21 | *.html 22 | *.log 23 | *.svg 24 | *.map 25 | *.png 26 | *.snap 27 | *.ttf 28 | *.sh 29 | *.txt -------------------------------------------------------------------------------- /skeleton/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "es5", 4 | "arrowParens": "always" 5 | } 6 | -------------------------------------------------------------------------------- /skeleton/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Daniel Imfeld 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /skeleton/README.md: -------------------------------------------------------------------------------- 1 | This is the skeleton version of the Svelte Leaflet demo application. All the map components and data loading are provided, but the application itself needs to be filled in. 2 | 3 | To run: 4 | 5 | 1. Install dependencies with yarn, npm, pnpm, etc. 6 | 2. `npm run dev` to start the Snowpack development server. 7 | -------------------------------------------------------------------------------- /skeleton/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte-leaflet-demo", 3 | "author": "Daniel Imfeld", 4 | "license": "MIT", 5 | "version": "0.0.2", 6 | "scripts": { 7 | "clean": "rimraf public", 8 | "rollup": "rollup -c", 9 | "rollup:watch": "rollup -c -w", 10 | "copy-snowpack-templates": "copyfiles -u 1 snowpack/* public", 11 | "build": "NODE_ENV=production run-s clean rollup", 12 | "dev": "npm-run-all clean copy-snowpack-templates start:snowpack", 13 | "dev:rollup": "NODE_ENV=development npm-run-all clean --parallel start:dev rollup:watch", 14 | "start": "sirv public --single -H 0.0.0.0", 15 | "start:dev": "NODE_ENV=development sirv public --single --dev -H 0.0.0.0", 16 | "start:snowpack": "NODE_ENV=development snowpack dev" 17 | }, 18 | "dependencies": { 19 | "@elfalem/leaflet-curve": "^0.6.0", 20 | "d3-scale": "^3.2.3", 21 | "just-flush": "^1.1.0", 22 | "leaflet": "~1.6", 23 | "sirv-cli": "^0.4.4" 24 | }, 25 | "devDependencies": { 26 | "@babel/core": "^7.10.0", 27 | "@babel/plugin-proposal-class-properties": "^7.8.3", 28 | "@babel/plugin-proposal-nullish-coalescing-operator": "^7.8.3", 29 | "@babel/plugin-proposal-object-rest-spread": "^7.8.3", 30 | "@babel/plugin-proposal-optional-chaining": "^7.8.3", 31 | "@babel/plugin-syntax-dynamic-import": "^7.8.3", 32 | "@babel/plugin-transform-runtime": "^7.9.0", 33 | "@babel/preset-env": "^7.10.0", 34 | "@babel/preset-typescript": "^7.9.0", 35 | "@babel/runtime": "^7.10.0", 36 | "@fullhuman/postcss-purgecss": "^3.0.0", 37 | "@rollup/plugin-babel": "^5.0.3", 38 | "@rollup/plugin-commonjs": "^15.0.0", 39 | "@rollup/plugin-html": "^0.2.0", 40 | "@rollup/plugin-json": "^4.1.0", 41 | "@rollup/plugin-node-resolve": "^8.1.0", 42 | "@rollup/plugin-replace": "^2.3.3", 43 | "@snowpack/app-scripts-svelte": "^1.9.2", 44 | "@snowpack/plugin-postcss": "^1.0.7", 45 | "@tailwindcss/ui": "^0.6.2", 46 | "@types/d3-scale": "^3.2.1", 47 | "@types/leaflet": "^1.5.19", 48 | "@types/topojson-client": "^3.0.0", 49 | "@typescript-eslint/eslint-plugin": "^2.26.0", 50 | "@typescript-eslint/parser": "^2.26.0", 51 | "autoprefixer": "^9", 52 | "babel-eslint": "^10.0.3", 53 | "copyfiles": "^2.4.0", 54 | "cssnano": "^4.1.10", 55 | "eslint": "^6.8.0", 56 | "eslint-config-prettier": "^6.10.1", 57 | "eslint-plugin-svelte3": "^2.7.3", 58 | "lodash.template": "^4.5.0", 59 | "npm-run-all": "^4.1.5", 60 | "postcss": "^7", 61 | "postcss-cli": "^6.1.3", 62 | "postcss-import": "^12.0.1", 63 | "prettier": "^2", 64 | "prettier-plugin-svelte": "^1", 65 | "rimraf": "^3.0.2", 66 | "rollup": "^2.10.0", 67 | "rollup-plugin-copy": "^3.3.0", 68 | "rollup-plugin-livereload": "^2.0.0", 69 | "rollup-plugin-postcss": "^3.0.0", 70 | "rollup-plugin-svelte": "^6", 71 | "rollup-plugin-terser": "^7.0.0", 72 | "snowpack": "^2.16.1", 73 | "svelte": "^3.29.4", 74 | "svelte-preprocess": "^4.5.2", 75 | "tailwindcss": "^1.9.0", 76 | "typescript": "^4.0.5" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /skeleton/postcss.config.js: -------------------------------------------------------------------------------- 1 | const production = process.env.NODE_ENV !== 'development'; 2 | const purgecss = require('@fullhuman/postcss-purgecss'); 3 | const cssnano = require('cssnano'); 4 | 5 | module.exports = { 6 | plugins: [ 7 | require('postcss-import')(), 8 | require('tailwindcss'), 9 | require('autoprefixer'), 10 | production && 11 | purgecss({ 12 | content: ['./src/**/*.html', './static/**/*.html', './src/**/*.svelte'], 13 | whitelistPatterns: [/^svelte-/], 14 | defaultExtractor: (content) => { 15 | const regExp = new RegExp(/[A-Za-z0-9-_:/.]+/g); 16 | 17 | const matchedTokens = []; 18 | 19 | let match = regExp.exec(content); 20 | // To make sure that you do not lose any tailwind classes used in class directive. 21 | // https://github.com/tailwindcss/discuss/issues/254#issuecomment-517918397 22 | while (match) { 23 | if (match[0].startsWith('class:')) { 24 | matchedTokens.push(match[0].substring(6)); 25 | } else { 26 | matchedTokens.push(match[0]); 27 | } 28 | 29 | match = regExp.exec(content); 30 | } 31 | 32 | return matchedTokens; 33 | }, 34 | }), 35 | production && cssnano(), 36 | ].filter(Boolean), 37 | }; 38 | -------------------------------------------------------------------------------- /skeleton/rollup.config.js: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import template from 'lodash.template'; 3 | import svelte from 'rollup-plugin-svelte'; 4 | import resolve from '@rollup/plugin-node-resolve'; 5 | import replace from '@rollup/plugin-replace'; 6 | import commonjs from '@rollup/plugin-commonjs'; 7 | import livereload from 'rollup-plugin-livereload'; 8 | import { terser } from 'rollup-plugin-terser'; 9 | import postcss from 'rollup-plugin-postcss'; 10 | import babel from '@rollup/plugin-babel'; 11 | import json from '@rollup/plugin-json'; 12 | import html, { makeHtmlAttributes } from '@rollup/plugin-html'; 13 | import copy from 'rollup-plugin-copy'; 14 | 15 | const production = process.env.NODE_ENV !== 'development'; 16 | 17 | const babelConfig = { 18 | extensions: ['.js', '.mjs', '.html', '.svelte', '.ts'], 19 | babelHelpers: 'bundled', 20 | exclude: ['node_modules/@babel/**', 'static/**', 'build/**', 'public/**'], 21 | presets: [ 22 | ['@babel/preset-env', { targets: { esmodules: true } }], 23 | '@babel/preset-typescript', 24 | ], 25 | plugins: [ 26 | '@babel/plugin-syntax-dynamic-import', 27 | '@babel/proposal-class-properties', 28 | '@babel/plugin-proposal-object-rest-spread', 29 | '@babel/plugin-proposal-nullish-coalescing-operator', 30 | '@babel/plugin-proposal-optional-chaining', 31 | ], 32 | }; 33 | 34 | export default { 35 | input: 'src/index.js', 36 | output: { 37 | dir: 'public', 38 | entryFileNames: '[name].[hash].js', 39 | chunkFileNames: '[name].[hash].js', 40 | assetFileNames: '[name].[hash][extname]', 41 | sourcemap: production ? true : 'inline', 42 | format: 'esm', 43 | manualChunks(id) { 44 | if (id.includes('node_modules')) { 45 | return 'vendor'; 46 | } else if (id.endsWith('.json')) { 47 | return 'data'; 48 | } 49 | }, 50 | }, 51 | plugins: [ 52 | svelte({ 53 | preprocess: require('./svelte.config').preprocess, 54 | // enable run-time checks when not in production 55 | dev: !production, 56 | // extract any component CSS out into 57 | // a separate file — better for performance 58 | // css: css => { 59 | // css.write('public/bundle.css'); 60 | // }, 61 | // Instead, emit CSS as a file for processing through rollup 62 | emitCss: true, 63 | }), 64 | postcss({ 65 | extract: true, 66 | }), 67 | 68 | resolve({ 69 | mainFields: ['module', 'browser', 'main'], 70 | extensions: ['.mjs', '.js', '.json', '.ts', '.svelte'], 71 | dedupe: ['svelte'], 72 | }), 73 | copy({ 74 | targets: [{ src: 'static/**/*', dest: 'public/' }], 75 | }), 76 | commonjs(), 77 | babel(babelConfig), 78 | json(), 79 | 80 | replace({ 81 | 'process.env.NODE_ENV': JSON.stringify( 82 | process.env.NODE_ENV || 'development' 83 | ), 84 | }), 85 | 86 | // Watch the `public` directory and refresh the 87 | // browser on changes when not in production 88 | !production && livereload('public'), 89 | 90 | // If we're building for production (npm run build 91 | // instead of npm run dev), minify 92 | production && terser(), 93 | 94 | html({ 95 | title: 'Metro Flow Visualizer', 96 | template: ({ attributes, files, publicPath, title }) => { 97 | let templateFile = fs.readFileSync('src/index.html'); 98 | 99 | // This is adapted from the default template function in the HTML plugin. 100 | const scripts = (files.js || []) 101 | .map(({ fileName }) => { 102 | const attrs = makeHtmlAttributes(attributes.script); 103 | return ``; 104 | }) 105 | .join('\n'); 106 | 107 | const links = (files.css || []) 108 | .map(({ fileName }) => { 109 | const attrs = makeHtmlAttributes(attributes.link); 110 | return ``; 111 | }) 112 | .join('\n'); 113 | 114 | let exec = template(templateFile.toString()); 115 | return exec({ 116 | attributes, 117 | title, 118 | scripts, 119 | links, 120 | htmlAttributes: makeHtmlAttributes(attributes.html), 121 | }); 122 | }, 123 | }), 124 | ], 125 | watch: { 126 | clearScreen: false, 127 | }, 128 | }; 129 | -------------------------------------------------------------------------------- /skeleton/snowpack.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@snowpack/app-scripts-svelte", 3 | "scripts": {}, 4 | "plugins": [ 5 | "@snowpack/plugin-postcss" 6 | ], 7 | "mount": { 8 | "../data": "/data" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /skeleton/snowpack/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Metro Flow Visualizer 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /skeleton/src/App.svelte: -------------------------------------------------------------------------------- 1 | 138 | 139 | (loaded = true)} /> 140 | 141 |
142 |
Map
143 |
Info
144 |
145 | -------------------------------------------------------------------------------- /skeleton/src/MapControls.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /skeleton/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | > 3 | 4 | 5 | 6 | 7 | <%= title %> 8 | 9 | 10 | <%= links %> 11 | 12 | 13 | 14 | <%= scripts %> 15 | 16 | 17 | -------------------------------------------------------------------------------- /skeleton/src/index.js: -------------------------------------------------------------------------------- 1 | import './utils.css'; 2 | import App from './App.svelte'; 3 | 4 | const app = new App({ 5 | target: document.body, 6 | }); 7 | 8 | export default app; 9 | 10 | // Hot Module Replacement (HMR) - Remove this snippet to remove HMR. 11 | // Learn more: https://www.snowpack.dev/#hot-module-replacement 12 | if (import.meta.hot) { 13 | import.meta.hot.accept(); 14 | import.meta.hot.dispose(() => { 15 | app.$destroy(); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /skeleton/src/map/Control.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 42 | 43 | 50 | -------------------------------------------------------------------------------- /skeleton/src/map/Curve.svelte: -------------------------------------------------------------------------------- 1 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /skeleton/src/map/GeoJson.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /skeleton/src/map/Leaflet.svelte: -------------------------------------------------------------------------------- 1 | 55 | 56 | 61 | 62 |
63 | {#if map} 64 | 65 | {/if} 66 |
67 | -------------------------------------------------------------------------------- /skeleton/src/map/Pane.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /skeleton/src/map/Polyline.svelte: -------------------------------------------------------------------------------- 1 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /skeleton/src/map/Popup.svelte: -------------------------------------------------------------------------------- 1 | 45 | 46 | 53 | -------------------------------------------------------------------------------- /skeleton/src/map/Tooltip.svelte: -------------------------------------------------------------------------------- 1 | 53 | 54 | 61 | -------------------------------------------------------------------------------- /skeleton/src/map/curves.ts: -------------------------------------------------------------------------------- 1 | import * as L from 'leaflet'; 2 | 3 | const curveMidpointTheta = Math.PI / 10; 4 | const cosCurveMidpointTheta = Math.cos(curveMidpointTheta); 5 | 6 | function calculateCurve(from: L.LatLng, to: L.LatLng) { 7 | let lineAngle = Math.atan2(to.lat - from.lat, to.lng - from.lng); 8 | let lineMidPointDistance = 9 | Math.sqrt((to.lng - from.lng) ** 2 + (to.lat - from.lat) ** 2) / 2; 10 | let hypotenuse = lineMidPointDistance / cosCurveMidpointTheta; 11 | let totalTheta = lineAngle + curveMidpointTheta; 12 | 13 | return L.latLng( 14 | hypotenuse * Math.sin(totalTheta) + from.lat, 15 | hypotenuse * Math.cos(totalTheta) + from.lng 16 | ); 17 | } 18 | 19 | export default function makeLineCoordinates( 20 | map: L.Map, 21 | from: L.LatLng, 22 | to: L.LatLng, 23 | reverse: boolean 24 | ): (string | [number, number])[] { 25 | let curveMidpoint = calculateCurve(reverse ? to : from, reverse ? from : to); 26 | // Designed for use with the Curve component. 27 | return [ 28 | 'M', 29 | [from.lat, from.lng], 30 | 'Q', 31 | [curveMidpoint.lat, curveMidpoint.lng], 32 | [to.lat, to.lng], 33 | ]; 34 | } 35 | -------------------------------------------------------------------------------- /skeleton/src/map/leaflet.curve.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Leaflet.curve v0.6.0 - a plugin for Leaflet mapping library. https://github.com/elfalem/Leaflet.curve 3 | * (c) elfalem 2015-2020 4 | */ 5 | /* 6 | * note that SVG (x, y) corresponds to (long, lat) 7 | */ 8 | 9 | // This is modified from the original to work better with ES Modules. No other changes were made. 10 | import * as L from 'leaflet'; 11 | 12 | export const Curve = L.Path.extend({ 13 | options: { 14 | }, 15 | 16 | initialize: function(path, options){ 17 | L.setOptions(this, options); 18 | this._setPath(path); 19 | }, 20 | 21 | // Added to follow the naming convention of L.Polyline and other Leaflet component classes: 22 | // (https://leafletjs.com/reference-1.6.0.html#polyline-setlatlngs) 23 | setLatLngs: function(path) { 24 | return this.setPath(path); 25 | }, 26 | 27 | _updateBounds: function() { 28 | // Empty function to satisfy L.Path.setStyle() method 29 | }, 30 | 31 | getPath: function(){ 32 | return this._coords; 33 | }, 34 | 35 | setPath: function(path){ 36 | this._setPath(path); 37 | return this.redraw(); 38 | }, 39 | 40 | getBounds: function() { 41 | return this._bounds; 42 | }, 43 | 44 | _setPath: function(path){ 45 | this._coords = path; 46 | this._bounds = this._computeBounds(); 47 | }, 48 | 49 | _computeBounds: function(){ 50 | var bound = new L.LatLngBounds(); 51 | var lastPoint; 52 | var lastCommand; 53 | var coord; 54 | for(var i = 0; i < this._coords.length; i++){ 55 | coord = this._coords[i]; 56 | if(typeof coord == 'string' || coord instanceof String){ 57 | lastCommand = coord; 58 | }else if(lastCommand == 'H'){ 59 | bound.extend([lastPoint.lat,coord[0]]); 60 | lastPoint = new L.latLng(lastPoint.lat,coord[0]); 61 | }else if(lastCommand == 'V'){ 62 | bound.extend([coord[0], lastPoint.lng]); 63 | lastPoint = new L.latLng(coord[0], lastPoint.lng); 64 | }else if(lastCommand == 'C'){ 65 | var controlPoint1 = new L.latLng(coord[0], coord[1]); 66 | coord = this._coords[++i]; 67 | var controlPoint2 = new L.latLng(coord[0], coord[1]); 68 | coord = this._coords[++i]; 69 | var endPoint = new L.latLng(coord[0], coord[1]); 70 | 71 | bound.extend(controlPoint1); 72 | bound.extend(controlPoint2); 73 | bound.extend(endPoint); 74 | 75 | endPoint.controlPoint1 = controlPoint1; 76 | endPoint.controlPoint2 = controlPoint2; 77 | lastPoint = endPoint; 78 | }else if(lastCommand == 'S'){ 79 | var controlPoint2 = new L.latLng(coord[0], coord[1]); 80 | coord = this._coords[++i]; 81 | var endPoint = new L.latLng(coord[0], coord[1]); 82 | 83 | var controlPoint1 = lastPoint; 84 | if(lastPoint.controlPoint2){ 85 | var diffLat = lastPoint.lat - lastPoint.controlPoint2.lat; 86 | var diffLng = lastPoint.lng - lastPoint.controlPoint2.lng; 87 | controlPoint1 = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng); 88 | } 89 | 90 | bound.extend(controlPoint1); 91 | bound.extend(controlPoint2); 92 | bound.extend(endPoint); 93 | 94 | endPoint.controlPoint1 = controlPoint1; 95 | endPoint.controlPoint2 = controlPoint2; 96 | lastPoint = endPoint; 97 | }else if(lastCommand == 'Q'){ 98 | var controlPoint = new L.latLng(coord[0], coord[1]); 99 | coord = this._coords[++i]; 100 | var endPoint = new L.latLng(coord[0], coord[1]); 101 | 102 | bound.extend(controlPoint); 103 | bound.extend(endPoint); 104 | 105 | endPoint.controlPoint = controlPoint; 106 | lastPoint = endPoint; 107 | }else if(lastCommand == 'T'){ 108 | var endPoint = new L.latLng(coord[0], coord[1]); 109 | 110 | var controlPoint = lastPoint; 111 | if(lastPoint.controlPoint){ 112 | var diffLat = lastPoint.lat - lastPoint.controlPoint.lat; 113 | var diffLng = lastPoint.lng - lastPoint.controlPoint.lng; 114 | controlPoint = new L.latLng(lastPoint.lat + diffLat, lastPoint.lng + diffLng); 115 | } 116 | 117 | bound.extend(controlPoint); 118 | bound.extend(endPoint); 119 | 120 | endPoint.controlPoint = controlPoint; 121 | lastPoint = endPoint; 122 | }else{ 123 | bound.extend(coord); 124 | lastPoint = new L.latLng(coord[0], coord[1]); 125 | } 126 | } 127 | return bound; 128 | }, 129 | 130 | getCenter: function () { 131 | return this._bounds.getCenter(); 132 | }, 133 | 134 | _update: function(){ 135 | if (!this._map) { return; } 136 | 137 | this._updatePath(); 138 | }, 139 | 140 | _updatePath: function() { 141 | if(this._usingCanvas){ 142 | this._updateCurveCanvas(); 143 | }else{ 144 | this._updateCurveSvg(); 145 | } 146 | }, 147 | 148 | _project: function() { 149 | var coord, lastCoord, curCommand, curPoint; 150 | 151 | this._points = []; 152 | 153 | for(var i = 0; i < this._coords.length; i++){ 154 | coord = this._coords[i]; 155 | if(typeof coord == 'string' || coord instanceof String){ 156 | this._points.push(coord); 157 | curCommand = coord; 158 | }else { 159 | switch(coord.length){ 160 | case 2: 161 | curPoint = this._latLngToPointFn.call(this._map, coord); 162 | lastCoord = coord; 163 | break; 164 | case 1: 165 | if(curCommand == 'H'){ 166 | curPoint = this._latLngToPointFn.call(this._map, [lastCoord[0], coord[0]]); 167 | lastCoord = [lastCoord[0], coord[0]]; 168 | }else{ 169 | curPoint = this._latLngToPointFn.call(this._map, [coord[0], lastCoord[1]]); 170 | lastCoord = [coord[0], lastCoord[1]]; 171 | } 172 | break; 173 | } 174 | this._points.push(curPoint); 175 | } 176 | } 177 | }, 178 | 179 | _curvePointsToPath: function(points){ 180 | var point, curCommand, str = ''; 181 | for(var i = 0; i < points.length; i++){ 182 | point = points[i]; 183 | if(typeof point == 'string' || point instanceof String){ 184 | curCommand = point; 185 | str += curCommand; 186 | }else{ 187 | switch(curCommand){ 188 | case 'H': 189 | str += point.x + ' '; 190 | break; 191 | case 'V': 192 | str += point.y + ' '; 193 | break; 194 | default: 195 | str += point.x + ',' + point.y + ' '; 196 | break; 197 | } 198 | } 199 | } 200 | return str || 'M0 0'; 201 | }, 202 | 203 | beforeAdd: function(map){ 204 | L.Path.prototype.beforeAdd.call(this, map); 205 | 206 | this._usingCanvas = this._renderer instanceof L.Canvas; 207 | 208 | this._latLngToPointFn = this._usingCanvas ? map.latLngToContainerPoint : map.latLngToLayerPoint; 209 | if(this._usingCanvas){ 210 | this._pathSvgElement = document.createElementNS('http://www.w3.org/2000/svg', 'path'); 211 | } 212 | }, 213 | 214 | onAdd: function(map){ 215 | if(this._usingCanvas){ 216 | // determine if dash array is set by user 217 | this._canvasSetDashArray = !this.options.dashArray; 218 | } 219 | 220 | L.Path.prototype.onAdd.call(this, map); // calls _update() 221 | 222 | if(this._usingCanvas){ 223 | this._animationCanvasElement = this._insertCustomCanvasElement(); 224 | 225 | this._resizeCanvas(); 226 | 227 | map.on('resize', this._resizeCanvas, this); 228 | 229 | if(this.options.animate && typeof(TWEEN) === 'object'){ 230 | this._pathLength = this._pathSvgElement.getTotalLength(); 231 | 232 | this._normalizeCanvasAnimationOptions(); 233 | 234 | this._tweenedObject = {offset: this._pathLength}; 235 | this._tween = new TWEEN.Tween(this._tweenedObject) 236 | .to({offset: 0}, this.options.animate.duration) 237 | // difference of behavior with SVG, delay occurs on every iteration 238 | .delay(this.options.animate.delay) 239 | .repeat(this.options.animate.iterations - 1) 240 | .onComplete(function(scope){ 241 | return function(){ 242 | scope._canvasAnimating = false; 243 | } 244 | }(this)) 245 | .start(); 246 | 247 | this._canvasAnimating = true; 248 | this._animateCanvas(); 249 | }else{ 250 | this._canvasAnimating = false; 251 | } 252 | }else{ 253 | if(this.options.animate && this._path.animate){ 254 | var length = this._svgSetDashArray(); 255 | 256 | this._path.animate([ 257 | {strokeDashoffset: length}, 258 | {strokeDashoffset: 0} 259 | ], this.options.animate); 260 | } 261 | } 262 | }, 263 | 264 | onRemove: function(map){ 265 | L.Path.prototype.onRemove.call(this, map); 266 | 267 | if(this._usingCanvas){ 268 | this._clearCanvas(); 269 | L.DomUtil.remove(this._animationCanvasElement); 270 | map.off('resize', this._resizeCanvas, this); 271 | } 272 | }, 273 | 274 | // SVG specific logic 275 | _updateCurveSvg: function(){ 276 | this._renderer._setPath(this, this._curvePointsToPath(this._points)); 277 | 278 | if(this.options.animate){ 279 | this._svgSetDashArray(); 280 | } 281 | }, 282 | 283 | _svgSetDashArray: function(){ 284 | var path = this._path; 285 | var length = path.getTotalLength(); 286 | 287 | if(!this.options.dashArray){ 288 | path.style.strokeDasharray = length + ' ' + length; 289 | } 290 | return length; 291 | }, 292 | 293 | // Needed by the `Canvas` renderer for interactivity 294 | _containsPoint: function(layerPoint) { 295 | return this._bounds.contains(this._map.layerPointToLatLng(layerPoint)); 296 | }, 297 | 298 | // Canvas specific logic below here 299 | _insertCustomCanvasElement: function(){ 300 | var element = L.DomUtil.create('canvas', 'leaflet-zoom-animated'); 301 | var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin']); 302 | element.style[originProp] = '50% 50%'; 303 | var pane = this._map.getPane(this.options.pane); 304 | pane.insertBefore(element, pane.firstChild); 305 | 306 | return element; 307 | }, 308 | 309 | _normalizeCanvasAnimationOptions: function(){ 310 | var opts = { 311 | delay: 0, 312 | duration: 0, 313 | iterations: 1 314 | }; 315 | if(typeof(this.options.animate) == "number"){ 316 | opts.duration = this.options.animate; 317 | }else{ 318 | if(this.options.animate.duration){ 319 | opts.duration = this.options.animate.duration; 320 | } 321 | if(this.options.animate.delay){ 322 | opts.delay =this.options.animate.delay; 323 | } 324 | if(this.options.animate.iterations){ 325 | opts.iterations = this.options.animate.iterations; 326 | } 327 | } 328 | 329 | this.options.animate = opts; 330 | }, 331 | 332 | _updateCurveCanvas: function(){ 333 | this._project(); 334 | 335 | var pathString = this._curvePointsToPath(this._points); 336 | this._pathSvgElement.setAttribute('d', pathString); 337 | 338 | if(this.options.animate && typeof(TWEEN) === 'object' && this._canvasSetDashArray){ 339 | this._pathLength = this._pathSvgElement.getTotalLength(); 340 | this.options.dashArray = this._pathLength + ''; 341 | this._renderer._updateDashArray(this); 342 | } 343 | 344 | this._path2d = new Path2D(pathString); 345 | 346 | if(this._animationCanvasElement){ 347 | this._resetCanvas(); 348 | } 349 | 350 | 351 | }, 352 | 353 | _animationCanvasElement: null, 354 | 355 | _resizeCanvas: function() { 356 | var size = this._map.getSize(); 357 | this._animationCanvasElement.width = size.x; 358 | this._animationCanvasElement.height = size.y; 359 | 360 | this._resetCanvas(); 361 | }, 362 | 363 | _resetCanvas: function() { 364 | var topLeft = this._map.containerPointToLayerPoint([0, 0]); 365 | L.DomUtil.setPosition(this._animationCanvasElement, topLeft); 366 | 367 | this._redrawCanvas(); 368 | }, 369 | 370 | _redrawCanvas: function(){ 371 | if(!this._canvasAnimating){ 372 | this._clearCanvas(); 373 | var ctx = this._animationCanvasElement.getContext('2d'); 374 | this._curveFillStroke(this._path2d, ctx); 375 | } 376 | }, 377 | 378 | _clearCanvas: function() { 379 | this._animationCanvasElement.getContext('2d').clearRect(0, 0, this._animationCanvasElement.width, this._animationCanvasElement.height); 380 | }, 381 | 382 | _animateCanvas: function(time){ 383 | TWEEN.update(time); 384 | 385 | var ctx = this._animationCanvasElement.getContext('2d'); 386 | ctx.clearRect(0, 0, this._animationCanvasElement.width, this._animationCanvasElement.height); 387 | ctx.lineDashOffset = this._tweenedObject.offset; 388 | 389 | this._curveFillStroke(this._path2d, ctx); 390 | 391 | if(this._canvasAnimating){ 392 | this._animationFrameId = L.Util.requestAnimFrame(this._animateCanvas, this); 393 | } 394 | }, 395 | 396 | // similar to Canvas._fillStroke(ctx, layer) 397 | _curveFillStroke: function (path2d, ctx) { 398 | var options = this.options; 399 | 400 | if (options.fill) { 401 | ctx.globalAlpha = options.fillOpacity; 402 | ctx.fillStyle = options.fillColor || options.color; 403 | ctx.fill(path2d, options.fillRule || 'evenodd'); 404 | } 405 | 406 | if (options.stroke && options.weight !== 0) { 407 | if (ctx.setLineDash) { 408 | ctx.setLineDash(this.options && this.options._dashArray || []); 409 | } 410 | ctx.globalAlpha = options.opacity; 411 | ctx.lineWidth = options.weight; 412 | ctx.strokeStyle = options.color; 413 | ctx.lineCap = options.lineCap; 414 | ctx.lineJoin = options.lineJoin; 415 | ctx.stroke(path2d); 416 | } 417 | }, 418 | 419 | // path tracing logic below here 420 | trace: function(t){ 421 | t = t.filter(function(element){ 422 | return element >= 0 && element <= 1; 423 | }); 424 | 425 | var point, curCommand, curStartPoint, curEndPoint; 426 | var p1, p2, p3; 427 | var samples = []; 428 | for(var i = 0; i < this._points.length; i++){ 429 | point = this._points[i]; 430 | if(typeof point == 'string' || point instanceof String){ 431 | curCommand = point; 432 | 433 | if(curCommand == 'Z'){ 434 | samples = samples.concat(this._linearTrace(t, curEndPoint, curStartPoint)); 435 | } 436 | }else{ 437 | switch(curCommand){ 438 | case 'M': 439 | curStartPoint = point; 440 | curEndPoint = point; 441 | break; 442 | case 'L': 443 | case 'H': 444 | case 'V': 445 | samples = samples.concat(this._linearTrace(t, curEndPoint, point)); 446 | 447 | curEndPoint = point; 448 | break; 449 | case 'C': 450 | p1 = point; 451 | p2 = this._points[++i]; 452 | p3 = this._points[++i]; 453 | samples = samples.concat(this._cubicTrace(t, curEndPoint, p1, p2, p3)); 454 | 455 | curEndPoint = p3; 456 | break; 457 | case 'S': 458 | p1 = this._reflectPoint(p2, curEndPoint); 459 | p2 = point; 460 | p3 = this._points[++i]; 461 | samples = samples.concat(this._cubicTrace(t, curEndPoint, p1, p2, p3)); 462 | 463 | curEndPoint = p3; 464 | break; 465 | case 'Q': 466 | p1 = point; 467 | p2 = this._points[++i]; 468 | samples = samples.concat(this._quadraticTrace(t, curEndPoint, p1, p2)); 469 | 470 | curEndPoint = p2; 471 | break; 472 | case 'T': 473 | p1 = this._reflectPoint(p1, curEndPoint); 474 | p2 = point; 475 | samples = samples.concat(this._quadraticTrace(t, curEndPoint, p1, p2)); 476 | 477 | curEndPoint = p2; 478 | break; 479 | default: 480 | break; 481 | } 482 | } 483 | } 484 | return samples; 485 | }, 486 | _linearTrace: function(t, p0, p1){ 487 | return t.map(interval => { 488 | var x = this._singleLinearTrace(interval, p0.x, p1.x); 489 | var y = this._singleLinearTrace(interval, p0.y, p1.y); 490 | return this._map.layerPointToLatLng([x, y]); 491 | }); 492 | }, 493 | _quadraticTrace: function(t, p0, p1, p2){ 494 | return t.map(interval => { 495 | var x = this._singleQuadraticTrace(interval, p0.x, p1.x, p2.x); 496 | var y = this._singleQuadraticTrace(interval, p0.y, p1.y, p2.y); 497 | return this._map.layerPointToLatLng([x, y]); 498 | }); 499 | }, 500 | _cubicTrace: function(t, p0, p1, p2, p3){ 501 | return t.map(interval => { 502 | var x = this._singleCubicTrace(interval, p0.x, p1.x, p2.x, p3.x); 503 | var y = this._singleCubicTrace(interval, p0.y, p1.y, p2.y, p3.y); 504 | return this._map.layerPointToLatLng([x, y]); 505 | }); 506 | }, 507 | _singleLinearTrace: function(t, p0, p1){ 508 | return p0 + t * (p1 - p0); 509 | }, 510 | _singleQuadraticTrace: function(t, p0, p1, p2){ 511 | var oneMinusT = 1 - t; 512 | return Math.pow(oneMinusT, 2) * p0 + 513 | 2 * oneMinusT * t * p1 + 514 | Math.pow(t, 2) * p2; 515 | }, 516 | _singleCubicTrace: function(t, p0, p1, p2, p3){ 517 | var oneMinusT = 1 - t; 518 | return Math.pow(oneMinusT, 3) * p0 + 519 | 3 * Math.pow(oneMinusT, 2) * t * p1 + 520 | 3 * oneMinusT * Math.pow(t, 2) * p2 + 521 | Math.pow(t, 3) * p3; 522 | }, 523 | _reflectPoint: function(point, over){ 524 | x = over.x + (over.x - point.x); 525 | y = over.y + (over.y - point.y); 526 | return L.point(x, y); 527 | } 528 | }); 529 | 530 | export function curve(path, options){ 531 | return new Curve(path, options); 532 | }; -------------------------------------------------------------------------------- /skeleton/src/map/popup.ts: -------------------------------------------------------------------------------- 1 | import { SvelteComponent } from 'svelte'; 2 | import * as L from 'leaflet'; 3 | 4 | export function bindPopup( 5 | layer: L.Layer, 6 | popup?: string | typeof SvelteComponent, 7 | props?: object 8 | ) { 9 | if (!popup) { 10 | return; 11 | } 12 | 13 | if (typeof popup === 'string') { 14 | layer.bindPopup(popup); 15 | } else { 16 | let popupComponent: SvelteComponent | undefined; 17 | layer.bindPopup(() => { 18 | let container = L.DomUtil.create('div'); 19 | popupComponent = new popup({ 20 | target: container, 21 | props: { ...(props || {}) }, 22 | }); 23 | return container; 24 | }); 25 | 26 | layer.on('popupclose', () => { 27 | if (popupComponent) { 28 | let old = popupComponent; 29 | popupComponent = undefined; 30 | // Wait for the popup to completely fade out before destroying it. 31 | // Otherwise the fade out looks weird as the contents disappear too early. 32 | setTimeout(() => { 33 | old.$destroy(); 34 | }, 500); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /skeleton/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface Flow { 2 | id: string; 3 | count: number; 4 | } 5 | 6 | export interface Msa { 7 | id: string; 8 | name: string; 9 | net: number; 10 | centroid: number[]; 11 | totalIncoming: number; 12 | totalOutgoing: number; 13 | netAsPercent: number; 14 | population: number; 15 | feature: any; 16 | outgoing: Flow[]; 17 | incoming: Flow[]; 18 | } 19 | -------------------------------------------------------------------------------- /skeleton/src/utils.css: -------------------------------------------------------------------------------- 1 | /* Import Tailwind as Global Utils */ 2 | @tailwind base; 3 | @tailwind components; 4 | @tailwind utilities; 5 | 6 | * { 7 | position: relative; 8 | } 9 | -------------------------------------------------------------------------------- /skeleton/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dimfeld/svelte-leaflet-demo/897b5aab379cc8926ed9ee65a9df846fbae62df7/skeleton/static/favicon.png -------------------------------------------------------------------------------- /skeleton/svelte.config.js: -------------------------------------------------------------------------------- 1 | const sveltePreprocess = require('svelte-preprocess'); 2 | 3 | const dev = process.env.NODE_ENV === 'development'; 4 | 5 | module.exports = { 6 | preprocess: sveltePreprocess({ 7 | postcss: require('./postcss.config'), 8 | typescript: true, 9 | aliases: [['ts', 'typescript']], 10 | }), 11 | }; 12 | -------------------------------------------------------------------------------- /skeleton/tailwind.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | purge: false, 3 | theme: { 4 | extend: { 5 | animation: { 6 | 'dash-offset': 'dash-offset var(--animation-speed, 2s) linear infinite', 7 | }, 8 | keyframes: { 9 | 'dash-offset': { 10 | from: { 11 | 'stroke-dashoffset': 'var(--dash-length, 18)', 12 | }, 13 | to: { 14 | 'stroke-dashoffset': '0', 15 | }, 16 | }, 17 | }, 18 | }, 19 | }, 20 | variants: {}, 21 | plugins: [require('@tailwindcss/ui')], 22 | experimental: { 23 | applyComplexClasses: true, 24 | defaultLineHeights: true, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /skeleton/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "sourceMap": true, 4 | "module": "ESNext", 5 | "moduleResolution": "node", 6 | "target": "es2020", 7 | "noEmit": true, 8 | "strict": true, 9 | "resolveJsonModule": true, 10 | "allowSyntheticDefaultImports": true, 11 | "lib": ["dom", "es5", "ES2015", "es2016", "es2017", "es2018", "ES2019"] 12 | }, 13 | "paths": { 14 | "^/*": ["src/*"] 15 | }, 16 | "include": ["src"], 17 | "compileOnSave": false 18 | } 19 | --------------------------------------------------------------------------------