├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .npmignore
├── LICENSE.md
├── README.md
├── example-browser
├── index.html
├── leaflet-tooltip-layout.dist.js
├── main.js
└── styles.css
├── example
├── Config.js
├── Icon.js
├── TestData.js
├── images
│ ├── circle-grey.png
│ └── circle-red.png
├── index.html
├── main.js
└── styles.css
├── lib
└── index.js
├── package-lock.json
├── package.json
└── prettier.config.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | [
4 | "env",
5 | {
6 | "modules": false,
7 | "targets": {
8 | "browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
9 | }
10 | }
11 | ],
12 | "stage-2"
13 | ],
14 | "plugins": ["transform-runtime"]
15 | }
16 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /*.js
5 | /node_modules/
6 | src/components/CitySelect
7 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | parser: 'babel-eslint',
4 | parserOptions: {
5 | sourceType: 'module'
6 | },
7 | env: {
8 | browser: true
9 | },
10 | rules: {
11 | 'arrow-parens': 0,
12 | 'generator-star-spacing': 0,
13 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0,
14 | 'space-before-function-paren': 0,
15 | semi: 0
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/node,linux,macos,windows,intellij+all,visualstudio,visualstudiocode
3 |
4 | ### Intellij+all ###
5 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
6 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
7 |
8 | # User-specific stuff
9 | .idea/**/workspace.xml
10 | .idea/**/tasks.xml
11 | .idea/**/usage.statistics.xml
12 | .idea/**/dictionaries
13 | .idea/**/shelf
14 |
15 | # Generated files
16 | .idea/**/contentModel.xml
17 |
18 | # Sensitive or high-churn files
19 | .idea/**/dataSources/
20 | .idea/**/dataSources.ids
21 | .idea/**/dataSources.local.xml
22 | .idea/**/sqlDataSources.xml
23 | .idea/**/dynamic.xml
24 | .idea/**/uiDesigner.xml
25 | .idea/**/dbnavigator.xml
26 |
27 | # Gradle
28 | .idea/**/gradle.xml
29 | .idea/**/libraries
30 |
31 | # Gradle and Maven with auto-import
32 | # When using Gradle or Maven with auto-import, you should exclude module files,
33 | # since they will be recreated, and may cause churn. Uncomment if using
34 | # auto-import.
35 | # .idea/modules.xml
36 | # .idea/*.iml
37 | # .idea/modules
38 |
39 | # CMake
40 | cmake-build-*/
41 |
42 | # Mongo Explorer plugin
43 | .idea/**/mongoSettings.xml
44 |
45 | # File-based project format
46 | *.iws
47 |
48 | # IntelliJ
49 | out/
50 |
51 | # mpeltonen/sbt-idea plugin
52 | .idea_modules/
53 |
54 | # JIRA plugin
55 | atlassian-ide-plugin.xml
56 |
57 | # Cursive Clojure plugin
58 | .idea/replstate.xml
59 |
60 | # Crashlytics plugin (for Android Studio and IntelliJ)
61 | com_crashlytics_export_strings.xml
62 | crashlytics.properties
63 | crashlytics-build.properties
64 | fabric.properties
65 |
66 | # Editor-based Rest Client
67 | .idea/httpRequests
68 |
69 | ### Intellij+all Patch ###
70 | # Ignores the whole .idea folder and all .iml files
71 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
72 |
73 | .idea/
74 |
75 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
76 |
77 | *.iml
78 | modules.xml
79 | .idea/misc.xml
80 | *.ipr
81 |
82 | ### Linux ###
83 | *~
84 |
85 | # temporary files which can be created if a process still has a handle open of a deleted file
86 | .fuse_hidden*
87 |
88 | # KDE directory preferences
89 | .directory
90 |
91 | # Linux trash folder which might appear on any partition or disk
92 | .Trash-*
93 |
94 | # .nfs files are created when an open file is removed but is still being accessed
95 | .nfs*
96 |
97 | ### macOS ###
98 | # General
99 | .DS_Store
100 | .AppleDouble
101 | .LSOverride
102 |
103 | # Icon must end with two \r
104 | Icon
105 |
106 | # Thumbnails
107 | ._*
108 |
109 | # Files that might appear in the root of a volume
110 | .DocumentRevisions-V100
111 | .fseventsd
112 | .Spotlight-V100
113 | .TemporaryItems
114 | .Trashes
115 | .VolumeIcon.icns
116 | .com.apple.timemachine.donotpresent
117 |
118 | # Directories potentially created on remote AFP share
119 | .AppleDB
120 | .AppleDesktop
121 | Network Trash Folder
122 | Temporary Items
123 | .apdisk
124 |
125 | ### Node ###
126 | # Logs
127 | logs
128 | *.log
129 | npm-debug.log*
130 | yarn-debug.log*
131 | yarn-error.log*
132 |
133 | # Runtime data
134 | pids
135 | *.pid
136 | *.seed
137 | *.pid.lock
138 |
139 | # Directory for instrumented libs generated by jscoverage/JSCover
140 | lib-cov
141 |
142 | # Coverage directory used by tools like istanbul
143 | coverage
144 |
145 | # nyc test coverage
146 | .nyc_output
147 |
148 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
149 | .grunt
150 |
151 | # Bower dependency directory (https://bower.io/)
152 | bower_components
153 |
154 | # node-waf configuration
155 | .lock-wscript
156 |
157 | # Compiled binary addons (https://nodejs.org/api/addons.html)
158 | build/Release
159 |
160 | # Dependency directories
161 | node_modules/
162 | jspm_packages/
163 |
164 | # TypeScript v1 declaration files
165 | typings/
166 |
167 | # Optional npm cache directory
168 | .npm
169 |
170 | # Optional eslint cache
171 | .eslintcache
172 |
173 | # Optional REPL history
174 | .node_repl_history
175 |
176 | # Output of 'npm pack'
177 | *.tgz
178 |
179 | # Yarn Integrity file
180 | .yarn-integrity
181 |
182 | # dotenv environment variables file
183 | .env
184 |
185 | # parcel-bundler cache (https://parceljs.org/)
186 | .cache
187 |
188 | # next.js build output
189 | .next
190 |
191 | # nuxt.js build output
192 | .nuxt
193 |
194 | # vuepress build output
195 | .vuepress/dist
196 |
197 | # Serverless directories
198 | .serverless
199 |
200 | ### VisualStudioCode ###
201 | .vscode/*
202 | !.vscode/settings.json
203 | !.vscode/tasks.json
204 | !.vscode/launch.json
205 | !.vscode/extensions.json
206 |
207 | ### Windows ###
208 | # Windows thumbnail cache files
209 | Thumbs.db
210 | ehthumbs.db
211 | ehthumbs_vista.db
212 |
213 | # Dump file
214 | *.stackdump
215 |
216 | # Folder config file
217 | [Dd]esktop.ini
218 |
219 | # Recycle Bin used on file shares
220 | $RECYCLE.BIN/
221 |
222 | # Windows Installer files
223 | *.cab
224 | *.msi
225 | *.msix
226 | *.msm
227 | *.msp
228 |
229 | # Windows shortcuts
230 | *.lnk
231 |
232 | ### VisualStudio ###
233 | ## Ignore Visual Studio temporary files, build results, and
234 | ## files generated by popular Visual Studio add-ons.
235 | ##
236 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
237 |
238 | # User-specific files
239 | *.suo
240 | *.user
241 | *.userosscache
242 | *.sln.docstates
243 |
244 | # User-specific files (MonoDevelop/Xamarin Studio)
245 | *.userprefs
246 |
247 | # Build results
248 | [Dd]ebug/
249 | [Dd]ebugPublic/
250 | [Rr]elease/
251 | [Rr]eleases/
252 | x64/
253 | x86/
254 | bld/
255 | [Bb]in/
256 | [Oo]bj/
257 | [Ll]og/
258 |
259 | # Visual Studio 2015/2017 cache/options directory
260 | .vs/
261 | # Uncomment if you have tasks that create the project's static files in wwwroot
262 | #wwwroot/
263 |
264 | # Visual Studio 2017 auto generated files
265 | Generated\ Files/
266 |
267 | # MSTest test Results
268 | [Tt]est[Rr]esult*/
269 | [Bb]uild[Ll]og.*
270 |
271 | # NUNIT
272 | *.VisualState.xml
273 | TestResult.xml
274 |
275 | # Build Results of an ATL Project
276 | [Dd]ebugPS/
277 | [Rr]eleasePS/
278 | dlldata.c
279 |
280 | # Benchmark Results
281 | BenchmarkDotNet.Artifacts/
282 |
283 | # .NET Core
284 | project.lock.json
285 | project.fragment.lock.json
286 | artifacts/
287 |
288 | # StyleCop
289 | StyleCopReport.xml
290 |
291 | # Files built by Visual Studio
292 | *_i.c
293 | *_p.c
294 | *_h.h
295 | *.ilk
296 | *.meta
297 | *.obj
298 | *.iobj
299 | *.pch
300 | *.pdb
301 | *.ipdb
302 | *.pgc
303 | *.pgd
304 | *.rsp
305 | *.sbr
306 | *.tlb
307 | *.tli
308 | *.tlh
309 | *.tmp
310 | *.tmp_proj
311 | *.vspscc
312 | *.vssscc
313 | .builds
314 | *.pidb
315 | *.svclog
316 | *.scc
317 |
318 | # Chutzpah Test files
319 | _Chutzpah*
320 |
321 | # Visual C++ cache files
322 | ipch/
323 | *.aps
324 | *.ncb
325 | *.opendb
326 | *.opensdf
327 | *.sdf
328 | *.cachefile
329 | *.VC.db
330 | *.VC.VC.opendb
331 |
332 | # Visual Studio profiler
333 | *.psess
334 | *.vsp
335 | *.vspx
336 | *.sap
337 |
338 | # Visual Studio Trace Files
339 | *.e2e
340 |
341 | # TFS 2012 Local Workspace
342 | $tf/
343 |
344 | # Guidance Automation Toolkit
345 | *.gpState
346 |
347 | # ReSharper is a .NET coding add-in
348 | _ReSharper*/
349 | *.[Rr]e[Ss]harper
350 | *.DotSettings.user
351 |
352 | # JustCode is a .NET coding add-in
353 | .JustCode
354 |
355 | # TeamCity is a build add-in
356 | _TeamCity*
357 |
358 | # DotCover is a Code Coverage Tool
359 | *.dotCover
360 |
361 | # AxoCover is a Code Coverage Tool
362 | .axoCover/*
363 | !.axoCover/settings.json
364 |
365 | # Visual Studio code coverage results
366 | *.coverage
367 | *.coveragexml
368 |
369 | # NCrunch
370 | _NCrunch_*
371 | .*crunch*.local.xml
372 | nCrunchTemp_*
373 |
374 | # MightyMoose
375 | *.mm.*
376 | AutoTest.Net/
377 |
378 | # Web workbench (sass)
379 | .sass-cache/
380 |
381 | # Installshield output folder
382 | [Ee]xpress/
383 |
384 | # DocProject is a documentation generator add-in
385 | DocProject/buildhelp/
386 | DocProject/Help/*.HxT
387 | DocProject/Help/*.HxC
388 | DocProject/Help/*.hhc
389 | DocProject/Help/*.hhk
390 | DocProject/Help/*.hhp
391 | DocProject/Help/Html2
392 | DocProject/Help/html
393 |
394 | # Click-Once directory
395 | publish/
396 |
397 | # Publish Web Output
398 | *.[Pp]ublish.xml
399 | *.azurePubxml
400 | # Note: Comment the next line if you want to checkin your web deploy settings,
401 | # but database connection strings (with potential passwords) will be unencrypted
402 | *.pubxml
403 | *.publishproj
404 |
405 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
406 | # checkin your Azure Web App publish settings, but sensitive information contained
407 | # in these scripts will be unencrypted
408 | PublishScripts/
409 |
410 | # NuGet Packages
411 | *.nupkg
412 | # The packages folder can be ignored because of Package Restore
413 | **/[Pp]ackages/*
414 | # except build/, which is used as an MSBuild target.
415 | !**/[Pp]ackages/build/
416 | # Uncomment if necessary however generally it will be regenerated when needed
417 | #!**/[Pp]ackages/repositories.config
418 | # NuGet v3's project.json files produces more ignorable files
419 | *.nuget.props
420 | *.nuget.targets
421 |
422 | # Microsoft Azure Build Output
423 | csx/
424 | *.build.csdef
425 |
426 | # Microsoft Azure Emulator
427 | ecf/
428 | rcf/
429 |
430 | # Windows Store app package directories and files
431 | AppPackages/
432 | BundleArtifacts/
433 | Package.StoreAssociation.xml
434 | _pkginfo.txt
435 | *.appx
436 |
437 | # Visual Studio cache files
438 | # files ending in .cache can be ignored
439 | *.[Cc]ache
440 | # but keep track of directories ending in .cache
441 | !*.[Cc]ache/
442 |
443 | # Others
444 | ClientBin/
445 | ~$*
446 | *.dbmdl
447 | *.dbproj.schemaview
448 | *.jfm
449 | *.pfx
450 | *.publishsettings
451 | orleans.codegen.cs
452 |
453 | # Including strong name files can present a security risk
454 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
455 | #*.snk
456 |
457 | # Since there are multiple workflows, uncomment next line to ignore bower_components
458 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
459 | #bower_components/
460 |
461 | # RIA/Silverlight projects
462 | Generated_Code/
463 |
464 | # Backup & report files from converting an old project file
465 | # to a newer Visual Studio version. Backup files are not needed,
466 | # because we have git ;-)
467 | _UpgradeReport_Files/
468 | Backup*/
469 | UpgradeLog*.XML
470 | UpgradeLog*.htm
471 | ServiceFabricBackup/
472 | *.rptproj.bak
473 |
474 | # SQL Server files
475 | *.mdf
476 | *.ldf
477 | *.ndf
478 |
479 | # Business Intelligence projects
480 | *.rdl.data
481 | *.bim.layout
482 | *.bim_*.settings
483 | *.rptproj.rsuser
484 |
485 | # Microsoft Fakes
486 | FakesAssemblies/
487 |
488 | # GhostDoc plugin setting file
489 | *.GhostDoc.xml
490 |
491 | # Node.js Tools for Visual Studio
492 | .ntvs_analysis.dat
493 |
494 | # Visual Studio 6 build log
495 | *.plg
496 |
497 | # Visual Studio 6 workspace options file
498 | *.opt
499 |
500 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
501 | *.vbw
502 |
503 | # Visual Studio LightSwitch build output
504 | **/*.HTMLClient/GeneratedArtifacts
505 | **/*.DesktopClient/GeneratedArtifacts
506 | **/*.DesktopClient/ModelManifest.xml
507 | **/*.Server/GeneratedArtifacts
508 | **/*.Server/ModelManifest.xml
509 | _Pvt_Extensions
510 |
511 | # Paket dependency manager
512 | .paket/paket.exe
513 | paket-files/
514 |
515 | # FAKE - F# Make
516 | .fake/
517 |
518 | # JetBrains Rider
519 | *.sln.iml
520 |
521 | # CodeRush
522 | .cr/
523 |
524 | # Python Tools for Visual Studio (PTVS)
525 | __pycache__/
526 | *.pyc
527 |
528 | # Cake - Uncomment if you are using it
529 | # tools/**
530 | # !tools/packages.config
531 |
532 | # Tabs Studio
533 | *.tss
534 |
535 | # Telerik's JustMock configuration file
536 | *.jmconfig
537 |
538 | # BizTalk build output
539 | *.btp.cs
540 | *.btm.cs
541 | *.odx.cs
542 | *.xsd.cs
543 |
544 | # OpenCover UI analysis results
545 | OpenCover/
546 |
547 | # Azure Stream Analytics local run output
548 | ASALocalRun/
549 |
550 | # MSBuild Binary and Structured Log
551 | *.binlog
552 |
553 | # NVidia Nsight GPU debugger configuration file
554 | *.nvuser
555 |
556 | # MFractors (Xamarin productivity tool) working folder
557 | .mfractor/
558 |
559 | # Local History for Visual Studio
560 | .localhistory/
561 |
562 |
563 | # End of https://www.gitignore.io/api/node,linux,macos,windows,intellij+all,visualstudio,visualstudiocode
564 |
565 | # custom rules
566 | dist/
567 |
568 | .cache/
569 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .cache
2 | node_modules
3 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 ZijingP
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # leaflet-tooltip-layout
2 |
3 | This plugin is designed to avoid tooltip overlapping and make users find out the relationship between each tooltip and marker easily. It is based on [Force-Directed Drawing Algorithms](http://cs.brown.edu/people/rtamassi/gdhandbook/chapters/force-directed.pdf) in the chapter 12 of the book Handbook of Graph Drawing and Visualization written by Stephen G. Kobourov.
4 |
5 | [Here is the demo](https://zijingpeng.github.io/overlapping-avoided-tooltip/)
6 |
7 |
8 |
9 | ## Installation
10 |
11 | ```shell
12 | npm i leaflet-tooltip-layout --save
13 | # or
14 | yarn add leaflet-tooltip-layout
15 | ```
16 |
17 | Or you can just copy `./lib/index.js` to your project and rename it to what you want.
18 |
19 |
20 |
21 | ## Getting Started
22 |
23 | ### *ES6*
24 |
25 | ```js
26 | import * as tooltipLayout from 'leaflet-tooltip-layout';
27 | // or
28 | import { resetMarker, getMarkers, getLine, initialize, getLine } from 'leaflet-tooltip-layout';
29 | ```
30 |
31 |
32 |
33 | ### *CommonJS*
34 |
35 | ```js
36 | const tooltipLayout = require('leaflet-tooltip-layout');
37 | ```
38 |
39 |
40 |
41 | ### *Browser*
42 |
43 | Make sure `leaflet` is imported before this plugin, and `window.L` is available
44 |
45 | ```html
46 |
47 | ```
48 |
49 |
50 |
51 | ## API Reference
52 |
53 | 1. `L.tooltipLayout.resetMarker(marker)`
54 |
55 | Create the marker, bind tooltip to the marker, then use this function.
56 |
57 | Usage example:
58 |
59 | ```js
60 | var marker = L.marker(coord, {
61 | icon: icon
62 | }).addTo(map);
63 | marker.bindTooltip('Hello world!');
64 | L.tooltipLayout.resetMarker(marker);
65 | ```
66 |
67 | 2. `L.tooltipLayout.getMarkers()`
68 |
69 | Get the all the markers in this layout.
70 |
71 | Usage example:
72 |
73 | ```js
74 | var markerList = getMarkers();
75 | for (i = 0; i < markerList.length; i++) {
76 | marker = markerList[i];
77 | tooltip = marker.getTooltip();
78 | marker._icon.addEventListener('mouseover', function (){
79 | // your code
80 | });
81 | tooltip._container.addEventListener('mouseover', function (){
82 | // your code
83 | });
84 | }
85 | ```
86 |
87 | 3. `L.tooltipLayout.getLine(marker)`
88 |
89 | Get the line between one marker and its tooltip.
90 |
91 | 4. `L.tooltipLayout.initialize(map, onPolylineCreated)`
92 |
93 | After adding all the markers and tooltips, use this function to create the layout.
94 |
95 | `onPolylineCreated` is a callback function that allows you to define the style of the line between markers and tooltips, if you want the default one, let this parameter `null`.
96 |
97 | Or you can define the function like this:
98 |
99 | ```js
100 | function onPolylineCreated(ply) {
101 | ply.setStyle({
102 | color: '#40809d'
103 | })
104 | }
105 | ```
106 |
107 | 5. `L.tooltipLayout.addMarker(marker)`
108 |
109 | Add new marker to internal marker list
110 |
111 | 6. `L.tooltipLayout.deleteMarker(marker)`
112 |
113 | Delete specific marker from internal marker list
114 |
115 | 7. `L.tooltipLayout.resetMapContent()`
116 |
117 | Rmove all exisiting markers, tooltips, lines from the map.
118 | If content is dynamically generated to the map, for example realtime contents. It is recommanded to use this function to clear all contents and regenterated content again.
119 |
120 |
121 |
122 | ## Build Guide
123 |
124 | ```shell
125 | git clone git@github.com:ZijingPeng/leaflet-tooltip-layout.git
126 | cd ./leaflet-tooltip-layout
127 |
128 | npm i # install dependencies
129 | npm run build # build lib & example
130 |
131 | # or
132 | npm run serve # enter dev zone
133 | ```
134 |
135 |
136 |
137 | ## License
138 |
139 | MIT License
140 |
--------------------------------------------------------------------------------
/example-browser/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | leaflet-plugin-tooltip-layout DEMO
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/example-browser/leaflet-tooltip-layout.dist.js:
--------------------------------------------------------------------------------
1 | (function(factory, window) {
2 | if (typeof define === 'function' && define.amd) {
3 | define(['leaflet'], factory);
4 | } else if (typeof exports === 'object') {
5 | module.exports = factory(require('leaflet'));
6 | }
7 | if (typeof window !== 'undefined' && window.L) {
8 | window.L.tooltipLayout = factory(L);
9 | }
10 | })(function(L) {
11 | var TooltipLayout = {};
12 |
13 | // global variables
14 | let map;
15 | let markerList = []; // all markers here
16 | let polylineList = []; // all polylines here
17 |
18 | // events
19 | let _onPolylineCreated = null; // will be called after polyline has been created
20 |
21 | function initialize(leafletMap, onPolylineCreated) {
22 | map = leafletMap;
23 | polylineList = [];
24 |
25 | //default style
26 | if (onPolylineCreated) {
27 | _onPolylineCreated = onPolylineCreated;
28 | } else {
29 | _onPolylineCreated = ply => {
30 | ply.setStyle({
31 | color: '#90A4AE'
32 | });
33 | };
34 | }
35 |
36 | setRandomPos(map);
37 | layoutByForce();
38 | setEdgePosition();
39 | drawLine(map);
40 |
41 | // event registrations
42 | map.on('zoomstart', function() {
43 | removeAllPolyline(map);
44 | });
45 |
46 | map.on('zoomend', function() {
47 | setRandomPos(map);
48 | layoutByForce();
49 | setEdgePosition();
50 | drawLine(map);
51 | });
52 |
53 | map.on('dragend', function() {
54 | removeAllPolyline(map);
55 | setRandomPos(map);
56 | layoutByForce();
57 | setEdgePosition();
58 | drawLine(map);
59 | });
60 |
61 | map.on('resize', function() {
62 | removeAllPolyline(map);
63 | setRandomPos(map);
64 | layoutByForce();
65 | setEdgePosition();
66 | drawLine(map);
67 | });
68 | }
69 |
70 | function resetMarker(marker) {
71 | var name = marker.getTooltip().getContent();
72 | var options = marker.getTooltip().options;
73 | marker.unbindTooltip();
74 |
75 | marker.bindTooltip(name, {
76 | pane: options.pane,
77 | offset: options.offset,
78 | classname: 'heading',
79 | permanent: true,
80 | interactive: true,
81 | direction: 'left',
82 | sticky: 'none',
83 | opacity: options.opacity
84 | });
85 | markerList.push(marker);
86 | }
87 |
88 | function getMarkers() {
89 | return markerList;
90 | }
91 |
92 | function getLine(marker) {
93 | return marker.__ply;
94 | }
95 |
96 | function removeAllPolyline(map) {
97 | var i;
98 | for (i = 0; i < polylineList.length; i++) {
99 | map.removeLayer(polylineList[i]);
100 | }
101 | polylineList = [];
102 | }
103 |
104 | /**
105 | * Draw lines between markers and tooltips
106 | * @param map leaflet map
107 | */
108 | function drawLine(map) {
109 | removeAllPolyline(map);
110 | for (var i = 0; i < markerList.length; i++) {
111 | var marker = markerList[i];
112 | var markerDom = marker._icon;
113 | var markerPosition = getPosition(markerDom);
114 | var label = marker.getTooltip();
115 |
116 | var labelDom = label._container;
117 | var labelPosition = getPosition(labelDom);
118 |
119 | var x1 = labelPosition.x;
120 | var y1 = labelPosition.y;
121 |
122 | var x = markerPosition.x;
123 | var y = markerPosition.y;
124 |
125 | x1 -= 5;
126 | y1 += 2;
127 | if (x1 - x !== 0 || y1 - y !== 0) {
128 | if (x1 + labelDom.offsetWidth < markerPosition.x) {
129 | x1 += labelDom.offsetWidth;
130 | }
131 | if (y1 + labelDom.offsetHeight < markerPosition.y) {
132 | y1 += labelDom.offsetHeight;
133 | }
134 | var lineDest = L.point(x1, y1);
135 | var destLatLng = map.layerPointToLatLng(lineDest);
136 |
137 | setTimeout(
138 | ((marker, destLatLng) => () => {
139 | let ply = L.polyline([marker.getLatLng(), destLatLng]);
140 | _onPolylineCreated && _onPolylineCreated(ply);
141 | marker.__ply = ply;
142 | polylineList.push(ply);
143 | ply.addTo(map);
144 | })(marker, destLatLng),
145 | 0
146 | );
147 | }
148 | }
149 | }
150 |
151 | function setRandomPos() {
152 | for (var i = 0; i < markerList.length; i++) {
153 | var marker = markerList[i];
154 | var label = marker.getTooltip();
155 | var labelDom = label._container;
156 | var markerDom = marker._icon;
157 | var markerPosition = getPosition(markerDom);
158 | // var angle = Math.floor(Math.random() * 19 + 1) * 2 * Math.PI / 20;
159 | var angle = ((2 * Math.PI) / 6) * i;
160 | var x = markerPosition.x;
161 | var y = markerPosition.y;
162 | var dest = L.point(
163 | Math.ceil(x + 50 * Math.sin(angle)),
164 | Math.ceil(y + 50 * Math.cos(angle))
165 | );
166 | L.DomUtil.setPosition(labelDom, dest);
167 | }
168 | }
169 |
170 | function scaleTo(a, b) {
171 | return L.point(a.x * b.x, a.y * b.y);
172 | }
173 |
174 | function normalize(a) {
175 | var l = a.distanceTo(L.point(0, 0));
176 | if (l === 0) {
177 | return a;
178 | }
179 | return L.point(a.x / l, a.y / l);
180 | }
181 |
182 | function fa(x, k) {
183 | return (x * x) / k;
184 | }
185 |
186 | function fr(x, k) {
187 | return (k * k) / x;
188 | }
189 |
190 | /**
191 | * get position form el.style.transform
192 | */
193 | function getPosition(el) {
194 | var translateString = el.style.transform
195 | .split('(')[1]
196 | .split(')')[0]
197 | .split(',');
198 | return L.point(parseInt(translateString[0]), parseInt(translateString[1]));
199 | }
200 |
201 | /**
202 | * t is the temperature in the system
203 | */
204 | function computePositionStep(t) {
205 | var area = (window.innerWidth * window.innerHeight) / 10;
206 | var k = Math.sqrt(area / markerList.length);
207 | var dpos = L.point(0, 0);
208 | var v_pos;
209 | var v;
210 | var i;
211 |
212 | for (i = 0; i < markerList.length; i++) {
213 | v = markerList[i];
214 | // get position of label v
215 | v.disp = L.point(0, 0);
216 | v_pos = getPosition(v.getTooltip()._container);
217 |
218 | // compute gravitational force
219 | for (var j = 0; j < markerList.length; j++) {
220 | var u = markerList[j];
221 | if (i !== j) {
222 | var u_pos = getPosition(u.getTooltip()._container);
223 | dpos = v_pos.subtract(u_pos);
224 | if (dpos !== 0) {
225 | v.disp = v.disp.add(
226 | normalize(dpos).multiplyBy(fr(dpos.distanceTo(L.point(0, 0)), k))
227 | );
228 | }
229 | }
230 | }
231 | }
232 |
233 | // compute force between marker and tooltip
234 | for (i = 0; i < markerList.length; i++) {
235 | v = markerList[i];
236 | v_pos = getPosition(v.getTooltip()._container);
237 | dpos = v_pos.subtract(getPosition(v._icon));
238 | v.disp = v.disp.subtract(
239 | normalize(dpos).multiplyBy(fa(dpos.distanceTo(L.point(0, 0)), k))
240 | );
241 | }
242 |
243 | // calculate layout
244 | for (i = 0; i < markerList.length; i++) {
245 | var disp = markerList[i].disp;
246 | var p = getPosition(markerList[i].getTooltip()._container);
247 | var d = scaleTo(
248 | normalize(disp),
249 | L.point(Math.min(Math.abs(disp.x), t), Math.min(Math.abs(disp.y), t))
250 | );
251 | p = p.add(d);
252 | p = L.point(Math.ceil(p.x), Math.ceil(p.y));
253 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, p);
254 | }
255 | }
256 |
257 | function layoutByForce() {
258 | var start = Math.ceil(window.innerWidth / 10);
259 | var times = 50;
260 | var t;
261 | for (var i = 0; i < times; i += 1) {
262 | t = start * (1 - i / (times - 1));
263 | computePositionStep(t);
264 | }
265 |
266 | for (i = 0; i < markerList.length; i++) {
267 | var disp = markerList[i].disp;
268 | var p = getPosition(markerList[i].getTooltip()._container);
269 | var width = markerList[i].getTooltip()._container.offsetWidth;
270 | var height = markerList[i].getTooltip()._container.offsetHeight;
271 | p = L.point(Math.ceil(p.x - width / 2), Math.ceil(p.y - height / 2));
272 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, p);
273 | }
274 | }
275 |
276 | function setEdgePosition() {
277 | var bounds = map.getBounds();
278 | var northWest = map.latLngToLayerPoint(bounds.getNorthWest());
279 | var southEast = map.latLngToLayerPoint(bounds.getSouthEast());
280 |
281 | for (let i = 0; i < markerList.length; i++) {
282 | var tooltip = getPosition(markerList[i].getTooltip()._container);
283 | var marker = getPosition(markerList[i]._icon);
284 | var width = markerList[i].getTooltip()._container.offsetWidth;
285 | var height = markerList[i].getTooltip()._container.offsetHeight;
286 |
287 | var isEdge = false;
288 | if (marker.x > northWest.x && tooltip.x < northWest.x) {
289 | tooltip.x = northWest.x;
290 | isEdge = true;
291 | } else if (marker.x < southEast.x && tooltip.x > southEast.x - width) {
292 | tooltip.x = southEast.x - width;
293 | isEdge = true;
294 | }
295 |
296 | if (marker.y > northWest.y && tooltip.y < northWest.y) {
297 | tooltip.y = northWest.y;
298 | isEdge = true;
299 | } else if (marker.y < southEast.y && tooltip.y > southEast.y - height) {
300 | tooltip.y = southEast.y - height;
301 | isEdge = true;
302 | }
303 |
304 | if (!isEdge) {
305 | if (marker.x < northWest.x && tooltip.x > northWest.x - width) {
306 | tooltip.x = northWest.x - width;
307 | } else if (marker.x > southEast.x && tooltip.x < southEast.x) {
308 | tooltip.x = southEast.x;
309 | }
310 |
311 | if (marker.y < northWest.y && tooltip.y > northWest.y - height) {
312 | tooltip.y = northWest.y - height;
313 | } else if (marker.y > southEast.y && tooltip.y < southEast.y) {
314 | tooltip.y = southEast.y;
315 | }
316 | }
317 |
318 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, tooltip);
319 | }
320 | }
321 |
322 | TooltipLayout['initialize'] = initialize;
323 | TooltipLayout['resetMarker'] = resetMarker;
324 | TooltipLayout['getMarkers'] = getMarkers;
325 | TooltipLayout['getLine'] = getLine;
326 | TooltipLayout['removeAllPolyline'] = removeAllPolyline;
327 |
328 | return TooltipLayout;
329 | }, window);
330 |
--------------------------------------------------------------------------------
/example-browser/main.js:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZijingPeng/leaflet-tooltip-layout/d9bf9793947b29ea26e5ba5474c4ad8c5d3b176b/example-browser/main.js
--------------------------------------------------------------------------------
/example-browser/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | /* translate animation */
7 | .leaflet-tooltip {
8 | transition: transform 0.3s ease-in-out;
9 | }
10 |
11 | /* remove arrow in tooltips */
12 | .leaflet-tooltip-top:before,
13 | .leaflet-tooltip-bottom:before,
14 | .leaflet-tooltip-left:before,
15 | .leaflet-tooltip-right:before {
16 | visibility: hidden;
17 | }
18 |
--------------------------------------------------------------------------------
/example/Config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | mapTileLayerUrlTemplate:
3 | 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw'
4 | };
5 |
--------------------------------------------------------------------------------
/example/Icon.js:
--------------------------------------------------------------------------------
1 | import IconCircleGrey from './images/circle-grey.png';
2 | import IconCircleRed from './images/circle-red.png';
3 |
4 | export const icon = L.icon({
5 | iconUrl: IconCircleGrey,
6 | iconSize: [10, 10]
7 | });
8 |
9 | export const iconlarge = L.icon({
10 | iconUrl: IconCircleRed,
11 | iconSize: [14, 14]
12 | });
13 |
--------------------------------------------------------------------------------
/example/TestData.js:
--------------------------------------------------------------------------------
1 | export default {
2 | 'features': [{
3 | 'geometry': {
4 | 'coordinates': [-86.87529, 40.4167],
5 | 'type': 'Point'
6 | },
7 | 'type': 'Feature',
8 | 'properties': {
9 | 'geoNameId': 4922462,
10 | 'alternateNames': null,
11 | 'toponym': 'Lafayette',
12 | 'featureCode': 'PPLA2',
13 | 'countryCode': 'US',
14 | 'featureClass': 'P',
15 | 'hierarchy': {
16 | 'features': [{
17 | 'geometry': null,
18 | 'type': 'Feature',
19 | 'properties': {
20 | 'geoNameId': 4927230,
21 | 'toponym': 'Tippecanoe County',
22 | 'hierarchy': null,
23 | 'name': 'Lafayette',
24 | 'locationType': null,
25 | 'positions': [10],
26 | 'type': 'location'
27 | }
28 | }, {
29 | 'geometry': null,
30 | 'type': 'Feature',
31 | 'properties': {
32 | 'geoNameId': 4921868,
33 | 'toponym': 'Indiana',
34 | 'hierarchy': null,
35 | 'name': 'Lafayette',
36 | 'locationType': null,
37 | 'positions': [10],
38 | 'type': 'location'
39 | }
40 | }, {
41 | 'geometry': null,
42 | 'type': 'Feature',
43 | 'properties': {
44 | 'geoNameId': 6252001,
45 | 'toponym': 'United States',
46 | 'hierarchy': null,
47 | 'name': 'Lafayette',
48 | 'locationType': null,
49 | 'positions': [10],
50 | 'type': 'location'
51 | }
52 | }, {
53 | 'geometry': null,
54 | 'type': 'Feature',
55 | 'properties': {
56 | 'geoNameId': 6255149,
57 | 'toponym': 'North America',
58 | 'hierarchy': null,
59 | 'name': 'Lafayette',
60 | 'locationType': null,
61 | 'positions': [10],
62 | 'type': 'location'
63 | }
64 | }],
65 | 'type': 'FeatureCollection'
66 | },
67 | 'name': 'Lafayette',
68 | 'positions': [10],
69 | 'type': 'location',
70 | 'alternates': {
71 | 'features': [{
72 | 'geometry': {
73 | 'coordinates': [-86.87529, 40.4167],
74 | 'type': 'Point'
75 | },
76 | 'type': 'Feature',
77 | 'properties': {
78 | 'geoNameId': 4922462,
79 | 'toponym': 'Lafayette',
80 | 'featureCode': 'PPLA2',
81 | 'countryCode': 'US',
82 | 'featureClass': 'P',
83 | 'hierarchy': {
84 | 'features': [{
85 | 'geometry': null,
86 | 'type': 'Feature',
87 | 'properties': {
88 | 'geoNameId': 4927230,
89 | 'toponym': 'Tippecanoe County',
90 | 'hierarchy': null,
91 | 'name': 'Lafayette',
92 | 'locationType': null,
93 | 'positions': [10],
94 | 'type': 'location'
95 | }
96 | }, {
97 | 'geometry': null,
98 | 'type': 'Feature',
99 | 'properties': {
100 | 'geoNameId': 4921868,
101 | 'toponym': 'Indiana',
102 | 'hierarchy': null,
103 | 'name': 'Lafayette',
104 | 'locationType': null,
105 | 'positions': [10],
106 | 'type': 'location'
107 | }
108 | }, {
109 | 'geometry': null,
110 | 'type': 'Feature',
111 | 'properties': {
112 | 'geoNameId': 6252001,
113 | 'toponym': 'United States',
114 | 'hierarchy': null,
115 | 'name': 'Lafayette',
116 | 'locationType': null,
117 | 'positions': [10],
118 | 'type': 'location'
119 | }
120 | }, {
121 | 'geometry': null,
122 | 'type': 'Feature',
123 | 'properties': {
124 | 'geoNameId': 6255149,
125 | 'toponym': 'North America',
126 | 'hierarchy': null,
127 | 'name': 'Lafayette',
128 | 'locationType': null,
129 | 'positions': [10],
130 | 'type': 'location'
131 | }
132 | }],
133 | 'type': 'FeatureCollection'
134 | },
135 | 'name': 'Lafayette',
136 | 'positions': [10],
137 | 'type': 'location'
138 | }
139 | }, {
140 | 'geometry': {
141 | 'coordinates': [-92.01984, 30.22409],
142 | 'type': 'Point'
143 | },
144 | 'type': 'Feature',
145 | 'properties': {
146 | 'geoNameId': 4330145,
147 | 'alternateNames': ['لافایت، لوئیزیانا', 'лафейетт', 'la fa ye', 'lapies', 'لافیت، لوسیانہ', 'vermillion bridge', 'لافاييت', 'לפאייט', 'vermillionville', 'lafejet', 'lafayyt', 'village of saint jean au vermilion', 'lafajet', 'pinhool', '拉法葉', 'лафайет', 'лафејет', 'lafayette', 'ラファイエット', 'lafayt lwyyzyana', 'lafejett', '라피엣', 'vermilionville', 'rafaietto', 'little manchac', 'vermilion', 'la fayette', 'lft'],
148 | 'toponym': 'Lafayette',
149 | 'featureCode': 'PPLA2',
150 | 'countryCode': 'US',
151 | 'featureClass': 'P',
152 | 'hierarchy': {
153 | 'features': [{
154 | 'geometry': null,
155 | 'type': 'Feature',
156 | 'properties': {
157 | 'geoNameId': 4330160,
158 | 'toponym': 'Lafayette Parish',
159 | 'hierarchy': {
160 | 'features': [],
161 | 'type': 'FeatureCollection'
162 | },
163 | 'name': 'Lafayette',
164 | 'locationType': null,
165 | 'positions': [10],
166 | 'type': 'location'
167 | }
168 | }, {
169 | 'geometry': null,
170 | 'type': 'Feature',
171 | 'properties': {
172 | 'geoNameId': 4331987,
173 | 'toponym': 'Louisiana',
174 | 'hierarchy': {
175 | 'features': [],
176 | 'type': 'FeatureCollection'
177 | },
178 | 'name': 'Lafayette',
179 | 'locationType': null,
180 | 'positions': [10],
181 | 'type': 'location'
182 | }
183 | }, {
184 | 'geometry': null,
185 | 'type': 'Feature',
186 | 'properties': {
187 | 'geoNameId': 6252001,
188 | 'toponym': 'United States',
189 | 'hierarchy': {
190 | 'features': [],
191 | 'type': 'FeatureCollection'
192 | },
193 | 'name': 'Lafayette',
194 | 'locationType': null,
195 | 'positions': [10],
196 | 'type': 'location'
197 | }
198 | }, {
199 | 'geometry': null,
200 | 'type': 'Feature',
201 | 'properties': {
202 | 'geoNameId': 6255149,
203 | 'toponym': 'North America',
204 | 'hierarchy': {
205 | 'features': [],
206 | 'type': 'FeatureCollection'
207 | },
208 | 'name': 'Lafayette',
209 | 'locationType': null,
210 | 'positions': [10],
211 | 'type': 'location'
212 | }
213 | }],
214 | 'type': 'FeatureCollection'
215 | },
216 | 'name': 'Lafayette',
217 | 'locationType': null,
218 | 'positions': [10],
219 | 'type': 'location'
220 | }
221 | }, {
222 | 'geometry': {
223 | 'coordinates': [-85.2819, 34.7048],
224 | 'type': 'Point'
225 | },
226 | 'type': 'Feature',
227 | 'properties': {
228 | 'geoNameId': 4204241,
229 | 'alternateNames': ['chattooga', 'лафејет', 'lafayette', 'la phayeta', 'ला फायेट', 'لافاييت', 'لافایت، جورجیا', 'benton', 'lafejet', 'lafayyt', 'lafayt jwrjya', 'لافائت، جارجیا', 'la fayette', 'lafayt jarjya'],
230 | 'toponym': 'LaFayette',
231 | 'featureCode': 'PPLA2',
232 | 'countryCode': 'US',
233 | 'featureClass': 'P',
234 | 'hierarchy': {
235 | 'features': [{
236 | 'geometry': null,
237 | 'type': 'Feature',
238 | 'properties': {
239 | 'geoNameId': 4229163,
240 | 'toponym': 'Walker County',
241 | 'hierarchy': {
242 | 'features': [],
243 | 'type': 'FeatureCollection'
244 | },
245 | 'name': 'Lafayette',
246 | 'locationType': null,
247 | 'positions': [10],
248 | 'type': 'location'
249 | }
250 | }, {
251 | 'geometry': null,
252 | 'type': 'Feature',
253 | 'properties': {
254 | 'geoNameId': 4197000,
255 | 'toponym': 'Georgia',
256 | 'hierarchy': {
257 | 'features': [],
258 | 'type': 'FeatureCollection'
259 | },
260 | 'name': 'Lafayette',
261 | 'locationType': null,
262 | 'positions': [10],
263 | 'type': 'location'
264 | }
265 | }, {
266 | 'geometry': null,
267 | 'type': 'Feature',
268 | 'properties': {
269 | 'geoNameId': 6252001,
270 | 'toponym': 'United States',
271 | 'hierarchy': {
272 | 'features': [],
273 | 'type': 'FeatureCollection'
274 | },
275 | 'name': 'Lafayette',
276 | 'locationType': null,
277 | 'positions': [10],
278 | 'type': 'location'
279 | }
280 | }, {
281 | 'geometry': null,
282 | 'type': 'Feature',
283 | 'properties': {
284 | 'geoNameId': 6255149,
285 | 'toponym': 'North America',
286 | 'hierarchy': {
287 | 'features': [],
288 | 'type': 'FeatureCollection'
289 | },
290 | 'name': 'Lafayette',
291 | 'locationType': null,
292 | 'positions': [10],
293 | 'type': 'location'
294 | }
295 | }],
296 | 'type': 'FeatureCollection'
297 | },
298 | 'name': 'Lafayette',
299 | 'locationType': null,
300 | 'positions': [10],
301 | 'type': 'location'
302 | }
303 | }, {
304 | 'geometry': {
305 | 'coordinates': [-86.02637, 36.52116],
306 | 'type': 'Point'
307 | },
308 | 'type': 'Feature',
309 | 'properties': {
310 | 'geoNameId': 4635093,
311 | 'alternateNames': ['lfyt tnsy', 'لافاييت', 'лафејет', 'lafayette', 'lafejet', 'lafayyt', 'لافیت، ٹینیسی', 'la fayette', 'lafajet', 'لفیت، تنسی', 'лафайет'],
312 | 'toponym': 'Lafayette',
313 | 'featureCode': 'PPLA2',
314 | 'countryCode': 'US',
315 | 'featureClass': 'P',
316 | 'hierarchy': {
317 | 'features': [{
318 | 'geometry': null,
319 | 'type': 'Feature',
320 | 'properties': {
321 | 'geoNameId': 4638991,
322 | 'toponym': 'Macon County',
323 | 'hierarchy': {
324 | 'features': [],
325 | 'type': 'FeatureCollection'
326 | },
327 | 'name': 'Lafayette',
328 | 'locationType': null,
329 | 'positions': [10],
330 | 'type': 'location'
331 | }
332 | }, {
333 | 'geometry': null,
334 | 'type': 'Feature',
335 | 'properties': {
336 | 'geoNameId': 4662168,
337 | 'toponym': 'Tennessee',
338 | 'hierarchy': {
339 | 'features': [],
340 | 'type': 'FeatureCollection'
341 | },
342 | 'name': 'Lafayette',
343 | 'locationType': null,
344 | 'positions': [10],
345 | 'type': 'location'
346 | }
347 | }, {
348 | 'geometry': null,
349 | 'type': 'Feature',
350 | 'properties': {
351 | 'geoNameId': 6252001,
352 | 'toponym': 'United States',
353 | 'hierarchy': {
354 | 'features': [],
355 | 'type': 'FeatureCollection'
356 | },
357 | 'name': 'Lafayette',
358 | 'locationType': null,
359 | 'positions': [10],
360 | 'type': 'location'
361 | }
362 | }, {
363 | 'geometry': null,
364 | 'type': 'Feature',
365 | 'properties': {
366 | 'geoNameId': 6255149,
367 | 'toponym': 'North America',
368 | 'hierarchy': {
369 | 'features': [],
370 | 'type': 'FeatureCollection'
371 | },
372 | 'name': 'Lafayette',
373 | 'locationType': null,
374 | 'positions': [10],
375 | 'type': 'location'
376 | }
377 | }],
378 | 'type': 'FeatureCollection'
379 | },
380 | 'name': 'Lafayette',
381 | 'locationType': null,
382 | 'positions': [10],
383 | 'type': 'location'
384 | }
385 | }, {
386 | 'geometry': {
387 | 'coordinates': [-85.40106, 32.89985],
388 | 'type': 'Point'
389 | },
390 | 'type': 'Feature',
391 | 'properties': {
392 | 'geoNameId': 4071267,
393 | 'alternateNames': ['lafayette', 'layfayette', 'la fayette', 'lafajet', 'chambersville', 'лафайет'],
394 | 'toponym': 'Lafayette',
395 | 'featureCode': 'PPLA2',
396 | 'countryCode': 'US',
397 | 'featureClass': 'P',
398 | 'hierarchy': {
399 | 'features': [{
400 | 'geometry': null,
401 | 'type': 'Feature',
402 | 'properties': {
403 | 'geoNameId': 4054621,
404 | 'toponym': 'Chambers County',
405 | 'hierarchy': {
406 | 'features': [],
407 | 'type': 'FeatureCollection'
408 | },
409 | 'name': 'Lafayette',
410 | 'locationType': null,
411 | 'positions': [10],
412 | 'type': 'location'
413 | }
414 | }, {
415 | 'geometry': null,
416 | 'type': 'Feature',
417 | 'properties': {
418 | 'geoNameId': 4829764,
419 | 'toponym': 'Alabama',
420 | 'hierarchy': {
421 | 'features': [],
422 | 'type': 'FeatureCollection'
423 | },
424 | 'name': 'Lafayette',
425 | 'locationType': null,
426 | 'positions': [10],
427 | 'type': 'location'
428 | }
429 | }, {
430 | 'geometry': null,
431 | 'type': 'Feature',
432 | 'properties': {
433 | 'geoNameId': 6252001,
434 | 'toponym': 'United States',
435 | 'hierarchy': {
436 | 'features': [],
437 | 'type': 'FeatureCollection'
438 | },
439 | 'name': 'Lafayette',
440 | 'locationType': null,
441 | 'positions': [10],
442 | 'type': 'location'
443 | }
444 | }, {
445 | 'geometry': null,
446 | 'type': 'Feature',
447 | 'properties': {
448 | 'geoNameId': 6255149,
449 | 'toponym': 'North America',
450 | 'hierarchy': {
451 | 'features': [],
452 | 'type': 'FeatureCollection'
453 | },
454 | 'name': 'Lafayette',
455 | 'locationType': null,
456 | 'positions': [10],
457 | 'type': 'location'
458 | }
459 | }],
460 | 'type': 'FeatureCollection'
461 | },
462 | 'name': 'Lafayette',
463 | 'locationType': null,
464 | 'positions': [10],
465 | 'type': 'location'
466 | }
467 | }, {
468 | 'geometry': {
469 | 'coordinates': [-85.89112, 42.21782],
470 | 'type': 'Point'
471 | },
472 | 'type': 'Feature',
473 | 'properties': {
474 | 'geoNameId': 5005126,
475 | 'alternateNames': ['paw paw', 'lafayette'],
476 | 'toponym': 'Paw Paw',
477 | 'featureCode': 'PPLA2',
478 | 'countryCode': 'US',
479 | 'featureClass': 'P',
480 | 'hierarchy': {
481 | 'features': [{
482 | 'geometry': null,
483 | 'type': 'Feature',
484 | 'properties': {
485 | 'geoNameId': 5013104,
486 | 'toponym': 'Van Buren County',
487 | 'hierarchy': {
488 | 'features': [],
489 | 'type': 'FeatureCollection'
490 | },
491 | 'name': 'Lafayette',
492 | 'locationType': null,
493 | 'positions': [10],
494 | 'type': 'location'
495 | }
496 | }, {
497 | 'geometry': null,
498 | 'type': 'Feature',
499 | 'properties': {
500 | 'geoNameId': 5001836,
501 | 'toponym': 'Michigan',
502 | 'hierarchy': {
503 | 'features': [],
504 | 'type': 'FeatureCollection'
505 | },
506 | 'name': 'Lafayette',
507 | 'locationType': null,
508 | 'positions': [10],
509 | 'type': 'location'
510 | }
511 | }, {
512 | 'geometry': null,
513 | 'type': 'Feature',
514 | 'properties': {
515 | 'geoNameId': 6252001,
516 | 'toponym': 'United States',
517 | 'hierarchy': {
518 | 'features': [],
519 | 'type': 'FeatureCollection'
520 | },
521 | 'name': 'Lafayette',
522 | 'locationType': null,
523 | 'positions': [10],
524 | 'type': 'location'
525 | }
526 | }, {
527 | 'geometry': null,
528 | 'type': 'Feature',
529 | 'properties': {
530 | 'geoNameId': 6255149,
531 | 'toponym': 'North America',
532 | 'hierarchy': {
533 | 'features': [],
534 | 'type': 'FeatureCollection'
535 | },
536 | 'name': 'Lafayette',
537 | 'locationType': null,
538 | 'positions': [10],
539 | 'type': 'location'
540 | }
541 | }],
542 | 'type': 'FeatureCollection'
543 | },
544 | 'name': 'Lafayette',
545 | 'locationType': null,
546 | 'positions': [10],
547 | 'type': 'location'
548 | }
549 | }, {
550 | 'geometry': {
551 | 'coordinates': [-93.57768, 33.35846],
552 | 'type': 'Point'
553 | },
554 | 'type': 'Feature',
555 | 'properties': {
556 | 'geoNameId': 4118755,
557 | 'alternateNames': ['new lewisville', 'луисвил', 'luisvil', 'lwysfyl', 'लिविसभिल', 'l\'juisvil\'', 'lafayette court house', 'lewisville', 'لويسفيل', 'льюисвилл', 'لوئیسویل', 'livisabhila', 'l\'juisvill', 'льюїсвіль'],
558 | 'toponym': 'Lewisville',
559 | 'featureCode': 'PPLA2',
560 | 'countryCode': 'US',
561 | 'featureClass': 'P',
562 | 'hierarchy': {
563 | 'features': [{
564 | 'geometry': null,
565 | 'type': 'Feature',
566 | 'properties': {
567 | 'geoNameId': 4117774,
568 | 'toponym': 'Lafayette County',
569 | 'hierarchy': {
570 | 'features': [],
571 | 'type': 'FeatureCollection'
572 | },
573 | 'name': 'Lafayette',
574 | 'locationType': null,
575 | 'positions': [10],
576 | 'type': 'location'
577 | }
578 | }, {
579 | 'geometry': null,
580 | 'type': 'Feature',
581 | 'properties': {
582 | 'geoNameId': 4099753,
583 | 'toponym': 'Arkansas',
584 | 'hierarchy': {
585 | 'features': [],
586 | 'type': 'FeatureCollection'
587 | },
588 | 'name': 'Lafayette',
589 | 'locationType': null,
590 | 'positions': [10],
591 | 'type': 'location'
592 | }
593 | }, {
594 | 'geometry': null,
595 | 'type': 'Feature',
596 | 'properties': {
597 | 'geoNameId': 6252001,
598 | 'toponym': 'United States',
599 | 'hierarchy': {
600 | 'features': [],
601 | 'type': 'FeatureCollection'
602 | },
603 | 'name': 'Lafayette',
604 | 'locationType': null,
605 | 'positions': [10],
606 | 'type': 'location'
607 | }
608 | }, {
609 | 'geometry': null,
610 | 'type': 'Feature',
611 | 'properties': {
612 | 'geoNameId': 6255149,
613 | 'toponym': 'North America',
614 | 'hierarchy': {
615 | 'features': [],
616 | 'type': 'FeatureCollection'
617 | },
618 | 'name': 'Lafayette',
619 | 'locationType': null,
620 | 'positions': [10],
621 | 'type': 'location'
622 | }
623 | }],
624 | 'type': 'FeatureCollection'
625 | },
626 | 'name': 'Lafayette',
627 | 'locationType': null,
628 | 'positions': [10],
629 | 'type': 'location'
630 | }
631 | }, {
632 | 'geometry': {
633 | 'coordinates': [-87.83085, 33.68455],
634 | 'type': 'Point'
635 | },
636 | 'type': 'Feature',
637 | 'properties': {
638 | 'geoNameId': 4061586,
639 | 'alternateNames': ['faett', 'فاييت', 'lafayette', 'фејет', 'fayette', 'layfayette', 'fayette depot', 'فایت، آلاباما', 'fayyt', 'fei ye te', '페이엣', 'frog level', 'fejet', 'fayt alabama', 'fayette courthouse', 'latona', 'peies', 'фаєтт', 'fayette depot town', 'fayetteville', 'فایێت، ئەلاباما', '费耶特', 'latone'],
640 | 'toponym': 'Fayette',
641 | 'featureCode': 'PPLA2',
642 | 'countryCode': 'US',
643 | 'featureClass': 'P',
644 | 'hierarchy': {
645 | 'features': [{
646 | 'geometry': null,
647 | 'type': 'Feature',
648 | 'properties': {
649 | 'geoNameId': 4061590,
650 | 'toponym': 'Fayette County',
651 | 'hierarchy': {
652 | 'features': [],
653 | 'type': 'FeatureCollection'
654 | },
655 | 'name': 'Lafayette',
656 | 'locationType': null,
657 | 'positions': [10],
658 | 'type': 'location'
659 | }
660 | }, {
661 | 'geometry': null,
662 | 'type': 'Feature',
663 | 'properties': {
664 | 'geoNameId': 4829764,
665 | 'toponym': 'Alabama',
666 | 'hierarchy': {
667 | 'features': [],
668 | 'type': 'FeatureCollection'
669 | },
670 | 'name': 'Lafayette',
671 | 'locationType': null,
672 | 'positions': [10],
673 | 'type': 'location'
674 | }
675 | }, {
676 | 'geometry': null,
677 | 'type': 'Feature',
678 | 'properties': {
679 | 'geoNameId': 6252001,
680 | 'toponym': 'United States',
681 | 'hierarchy': {
682 | 'features': [],
683 | 'type': 'FeatureCollection'
684 | },
685 | 'name': 'Lafayette',
686 | 'locationType': null,
687 | 'positions': [10],
688 | 'type': 'location'
689 | }
690 | }, {
691 | 'geometry': null,
692 | 'type': 'Feature',
693 | 'properties': {
694 | 'geoNameId': 6255149,
695 | 'toponym': 'North America',
696 | 'hierarchy': {
697 | 'features': [],
698 | 'type': 'FeatureCollection'
699 | },
700 | 'name': 'Lafayette',
701 | 'locationType': null,
702 | 'positions': [10],
703 | 'type': 'location'
704 | }
705 | }],
706 | 'type': 'FeatureCollection'
707 | },
708 | 'name': 'Lafayette',
709 | 'locationType': null,
710 | 'positions': [10],
711 | 'type': 'location'
712 | }
713 | }, {
714 | 'geometry': {
715 | 'coordinates': [-105.08971, 39.9936],
716 | 'type': 'Point'
717 | },
718 | 'type': 'Feature',
719 | 'properties': {
720 | 'geoNameId': 5427771,
721 | 'alternateNames': ['лафејет', 'lafayette', 'la fa ye', 'лафайетт', 'lafayt klradw', 'لافائیٹ، کولوراڈو', '라피엣', 'lapies', '拉法叶', 'لافاييت', 'لافایت، کلرادو', 'lafejet', 'lafayyt', 'लाफायेट', 'lafajett', 'lafajet', 'лафайет', 'laphayeta'],
722 | 'toponym': 'Lafayette',
723 | 'featureCode': 'PPL',
724 | 'countryCode': 'US',
725 | 'featureClass': 'P',
726 | 'hierarchy': {
727 | 'features': [{
728 | 'geometry': null,
729 | 'type': 'Feature',
730 | 'properties': {
731 | 'geoNameId': 5574999,
732 | 'toponym': 'Boulder County',
733 | 'hierarchy': {
734 | 'features': [],
735 | 'type': 'FeatureCollection'
736 | },
737 | 'name': 'Lafayette',
738 | 'locationType': null,
739 | 'positions': [10],
740 | 'type': 'location'
741 | }
742 | }, {
743 | 'geometry': null,
744 | 'type': 'Feature',
745 | 'properties': {
746 | 'geoNameId': 5417618,
747 | 'toponym': 'Colorado',
748 | 'hierarchy': {
749 | 'features': [],
750 | 'type': 'FeatureCollection'
751 | },
752 | 'name': 'Lafayette',
753 | 'locationType': null,
754 | 'positions': [10],
755 | 'type': 'location'
756 | }
757 | }, {
758 | 'geometry': null,
759 | 'type': 'Feature',
760 | 'properties': {
761 | 'geoNameId': 6252001,
762 | 'toponym': 'United States',
763 | 'hierarchy': {
764 | 'features': [],
765 | 'type': 'FeatureCollection'
766 | },
767 | 'name': 'Lafayette',
768 | 'locationType': null,
769 | 'positions': [10],
770 | 'type': 'location'
771 | }
772 | }, {
773 | 'geometry': null,
774 | 'type': 'Feature',
775 | 'properties': {
776 | 'geoNameId': 6255149,
777 | 'toponym': 'North America',
778 | 'hierarchy': {
779 | 'features': [],
780 | 'type': 'FeatureCollection'
781 | },
782 | 'name': 'Lafayette',
783 | 'locationType': null,
784 | 'positions': [10],
785 | 'type': 'location'
786 | }
787 | }],
788 | 'type': 'FeatureCollection'
789 | },
790 | 'name': 'Lafayette',
791 | 'locationType': null,
792 | 'positions': [10],
793 | 'type': 'location'
794 | }
795 | }, {
796 | 'geometry': {
797 | 'coordinates': [-122.11802, 37.88576],
798 | 'type': 'Point'
799 | },
800 | 'type': 'Feature',
801 | 'properties': {
802 | 'geoNameId': 5364226,
803 | 'alternateNames': ['лафејет', 'lafayette', 'ラファイエット', '라피엣', 'lapies', 'lafayt kalyfrnya', 'lafayyty', 'rafaietto', '拉斐特', 'لافاييتي', 'la fei te', 'lafejet', 'लाफायेट', 'la fayette', 'لافایت، کالیفرنیا', 'lafajet', 'лафайет', 'laphayeta'],
804 | 'toponym': 'Lafayette',
805 | 'featureCode': 'PPL',
806 | 'countryCode': 'US',
807 | 'featureClass': 'P',
808 | 'hierarchy': {
809 | 'features': [{
810 | 'geometry': null,
811 | 'type': 'Feature',
812 | 'properties': {
813 | 'geoNameId': 5339268,
814 | 'toponym': 'Contra Costa County',
815 | 'hierarchy': {
816 | 'features': [],
817 | 'type': 'FeatureCollection'
818 | },
819 | 'name': 'Lafayette',
820 | 'locationType': null,
821 | 'positions': [10],
822 | 'type': 'location'
823 | }
824 | }, {
825 | 'geometry': null,
826 | 'type': 'Feature',
827 | 'properties': {
828 | 'geoNameId': 5332921,
829 | 'toponym': 'California',
830 | 'hierarchy': {
831 | 'features': [],
832 | 'type': 'FeatureCollection'
833 | },
834 | 'name': 'Lafayette',
835 | 'locationType': null,
836 | 'positions': [10],
837 | 'type': 'location'
838 | }
839 | }, {
840 | 'geometry': null,
841 | 'type': 'Feature',
842 | 'properties': {
843 | 'geoNameId': 6252001,
844 | 'toponym': 'United States',
845 | 'hierarchy': {
846 | 'features': [],
847 | 'type': 'FeatureCollection'
848 | },
849 | 'name': 'Lafayette',
850 | 'locationType': null,
851 | 'positions': [10],
852 | 'type': 'location'
853 | }
854 | }, {
855 | 'geometry': null,
856 | 'type': 'Feature',
857 | 'properties': {
858 | 'geoNameId': 6255149,
859 | 'toponym': 'North America',
860 | 'hierarchy': {
861 | 'features': [],
862 | 'type': 'FeatureCollection'
863 | },
864 | 'name': 'Lafayette',
865 | 'locationType': null,
866 | 'positions': [10],
867 | 'type': 'location'
868 | }
869 | }],
870 | 'type': 'FeatureCollection'
871 | },
872 | 'name': 'Lafayette',
873 | 'locationType': null,
874 | 'positions': [10],
875 | 'type': 'location'
876 | }
877 | }, {
878 | 'geometry': {
879 | 'coordinates': [-123.11483, 45.24428],
880 | 'type': 'Point'
881 | },
882 | 'type': 'Feature',
883 | 'properties': {
884 | 'geoNameId': 5735609,
885 | 'alternateNames': ['лафејет', 'لافایت، اورگن', 'lafayette', 'lafayt awrgn', 'لافیت، اوریگان', 'lafajehtt', 'лафайэтт', 'لافاييت', 'lafejet', 'lafayyt', 'lafyt awrygan', 'lafajet', 'лафайет'],
886 | 'toponym': 'Lafayette',
887 | 'featureCode': 'PPL',
888 | 'countryCode': 'US',
889 | 'featureClass': 'P',
890 | 'hierarchy': {
891 | 'features': [{
892 | 'geometry': null,
893 | 'type': 'Feature',
894 | 'properties': {
895 | 'geoNameId': 5761960,
896 | 'toponym': 'Yamhill County',
897 | 'hierarchy': {
898 | 'features': [],
899 | 'type': 'FeatureCollection'
900 | },
901 | 'name': 'Lafayette',
902 | 'locationType': null,
903 | 'positions': [10],
904 | 'type': 'location'
905 | }
906 | }, {
907 | 'geometry': null,
908 | 'type': 'Feature',
909 | 'properties': {
910 | 'geoNameId': 5744337,
911 | 'toponym': 'Oregon',
912 | 'hierarchy': {
913 | 'features': [],
914 | 'type': 'FeatureCollection'
915 | },
916 | 'name': 'Lafayette',
917 | 'locationType': null,
918 | 'positions': [10],
919 | 'type': 'location'
920 | }
921 | }, {
922 | 'geometry': null,
923 | 'type': 'Feature',
924 | 'properties': {
925 | 'geoNameId': 6252001,
926 | 'toponym': 'United States',
927 | 'hierarchy': {
928 | 'features': [],
929 | 'type': 'FeatureCollection'
930 | },
931 | 'name': 'Lafayette',
932 | 'locationType': null,
933 | 'positions': [10],
934 | 'type': 'location'
935 | }
936 | }, {
937 | 'geometry': null,
938 | 'type': 'Feature',
939 | 'properties': {
940 | 'geoNameId': 6255149,
941 | 'toponym': 'North America',
942 | 'hierarchy': {
943 | 'features': [],
944 | 'type': 'FeatureCollection'
945 | },
946 | 'name': 'Lafayette',
947 | 'locationType': null,
948 | 'positions': [10],
949 | 'type': 'location'
950 | }
951 | }],
952 | 'type': 'FeatureCollection'
953 | },
954 | 'name': 'Lafayette',
955 | 'locationType': null,
956 | 'positions': [10],
957 | 'type': 'location'
958 | }
959 | }, {
960 | 'geometry': {
961 | 'coordinates': [-85.71609, 36.18589],
962 | 'type': 'Point'
963 | },
964 | 'type': 'Feature',
965 | 'properties': {
966 | 'geoNameId': 4635094,
967 | 'alternateNames': ['lafayette'],
968 | 'toponym': 'Lafayette',
969 | 'featureCode': 'PPL',
970 | 'countryCode': 'US',
971 | 'featureClass': 'P',
972 | 'hierarchy': {
973 | 'features': [{
974 | 'geometry': null,
975 | 'type': 'Feature',
976 | 'properties': {
977 | 'geoNameId': 4651744,
978 | 'toponym': 'Putnam County',
979 | 'hierarchy': {
980 | 'features': [],
981 | 'type': 'FeatureCollection'
982 | },
983 | 'name': 'Lafayette',
984 | 'locationType': null,
985 | 'positions': [10],
986 | 'type': 'location'
987 | }
988 | }, {
989 | 'geometry': null,
990 | 'type': 'Feature',
991 | 'properties': {
992 | 'geoNameId': 4662168,
993 | 'toponym': 'Tennessee',
994 | 'hierarchy': {
995 | 'features': [],
996 | 'type': 'FeatureCollection'
997 | },
998 | 'name': 'Lafayette',
999 | 'locationType': null,
1000 | 'positions': [10],
1001 | 'type': 'location'
1002 | }
1003 | }, {
1004 | 'geometry': null,
1005 | 'type': 'Feature',
1006 | 'properties': {
1007 | 'geoNameId': 6252001,
1008 | 'toponym': 'United States',
1009 | 'hierarchy': {
1010 | 'features': [],
1011 | 'type': 'FeatureCollection'
1012 | },
1013 | 'name': 'Lafayette',
1014 | 'locationType': null,
1015 | 'positions': [10],
1016 | 'type': 'location'
1017 | }
1018 | }, {
1019 | 'geometry': null,
1020 | 'type': 'Feature',
1021 | 'properties': {
1022 | 'geoNameId': 6255149,
1023 | 'toponym': 'North America',
1024 | 'hierarchy': {
1025 | 'features': [],
1026 | 'type': 'FeatureCollection'
1027 | },
1028 | 'name': 'Lafayette',
1029 | 'locationType': null,
1030 | 'positions': [10],
1031 | 'type': 'location'
1032 | }
1033 | }],
1034 | 'type': 'FeatureCollection'
1035 | },
1036 | 'name': 'Lafayette',
1037 | 'locationType': null,
1038 | 'positions': [10],
1039 | 'type': 'location'
1040 | }
1041 | }, {
1042 | 'geometry': {
1043 | 'coordinates': [-83.94855, 40.76033],
1044 | 'type': 'Point'
1045 | },
1046 | 'type': 'Feature',
1047 | 'properties': {
1048 | 'geoNameId': 5160028,
1049 | 'alternateNames': ['lafayette', 'herring', 'la fayette', 'lafajet', 'лафайет'],
1050 | 'toponym': 'Lafayette',
1051 | 'featureCode': 'PPL',
1052 | 'countryCode': 'US',
1053 | 'featureClass': 'P',
1054 | 'hierarchy': {
1055 | 'features': [{
1056 | 'geometry': null,
1057 | 'type': 'Feature',
1058 | 'properties': {
1059 | 'geoNameId': 5145576,
1060 | 'toponym': 'Allen County',
1061 | 'hierarchy': {
1062 | 'features': [],
1063 | 'type': 'FeatureCollection'
1064 | },
1065 | 'name': 'Lafayette',
1066 | 'locationType': null,
1067 | 'positions': [10],
1068 | 'type': 'location'
1069 | }
1070 | }, {
1071 | 'geometry': null,
1072 | 'type': 'Feature',
1073 | 'properties': {
1074 | 'geoNameId': 5165418,
1075 | 'toponym': 'Ohio',
1076 | 'hierarchy': {
1077 | 'features': [],
1078 | 'type': 'FeatureCollection'
1079 | },
1080 | 'name': 'Lafayette',
1081 | 'locationType': null,
1082 | 'positions': [10],
1083 | 'type': 'location'
1084 | }
1085 | }, {
1086 | 'geometry': null,
1087 | 'type': 'Feature',
1088 | 'properties': {
1089 | 'geoNameId': 6252001,
1090 | 'toponym': 'United States',
1091 | 'hierarchy': {
1092 | 'features': [],
1093 | 'type': 'FeatureCollection'
1094 | },
1095 | 'name': 'Lafayette',
1096 | 'locationType': null,
1097 | 'positions': [10],
1098 | 'type': 'location'
1099 | }
1100 | }, {
1101 | 'geometry': null,
1102 | 'type': 'Feature',
1103 | 'properties': {
1104 | 'geoNameId': 6255149,
1105 | 'toponym': 'North America',
1106 | 'hierarchy': {
1107 | 'features': [],
1108 | 'type': 'FeatureCollection'
1109 | },
1110 | 'name': 'Lafayette',
1111 | 'locationType': null,
1112 | 'positions': [10],
1113 | 'type': 'location'
1114 | }
1115 | }],
1116 | 'type': 'FeatureCollection'
1117 | },
1118 | 'name': 'Lafayette',
1119 | 'locationType': null,
1120 | 'positions': [10],
1121 | 'type': 'location'
1122 | }
1123 | }, {
1124 | 'geometry': {
1125 | 'coordinates': [-94.39525, 44.44663],
1126 | 'type': 'Point'
1127 | },
1128 | 'type': 'Feature',
1129 | 'properties': {
1130 | 'geoNameId': 5033667,
1131 | 'alternateNames': ['city of lafayette', 'lafayette', 'lafajet', 'лафайет'],
1132 | 'toponym': 'Lafayette',
1133 | 'featureCode': 'PPL',
1134 | 'countryCode': 'US',
1135 | 'featureClass': 'P',
1136 | 'hierarchy': {
1137 | 'features': [{
1138 | 'geometry': null,
1139 | 'type': 'Feature',
1140 | 'properties': {
1141 | 'geoNameId': 5039249,
1142 | 'toponym': 'Nicollet County',
1143 | 'hierarchy': {
1144 | 'features': [],
1145 | 'type': 'FeatureCollection'
1146 | },
1147 | 'name': 'Lafayette',
1148 | 'locationType': null,
1149 | 'positions': [10],
1150 | 'type': 'location'
1151 | }
1152 | }, {
1153 | 'geometry': null,
1154 | 'type': 'Feature',
1155 | 'properties': {
1156 | 'geoNameId': 5037779,
1157 | 'toponym': 'Minnesota',
1158 | 'hierarchy': {
1159 | 'features': [],
1160 | 'type': 'FeatureCollection'
1161 | },
1162 | 'name': 'Lafayette',
1163 | 'locationType': null,
1164 | 'positions': [10],
1165 | 'type': 'location'
1166 | }
1167 | }, {
1168 | 'geometry': null,
1169 | 'type': 'Feature',
1170 | 'properties': {
1171 | 'geoNameId': 6252001,
1172 | 'toponym': 'United States',
1173 | 'hierarchy': {
1174 | 'features': [],
1175 | 'type': 'FeatureCollection'
1176 | },
1177 | 'name': 'Lafayette',
1178 | 'locationType': null,
1179 | 'positions': [10],
1180 | 'type': 'location'
1181 | }
1182 | }, {
1183 | 'geometry': null,
1184 | 'type': 'Feature',
1185 | 'properties': {
1186 | 'geoNameId': 6255149,
1187 | 'toponym': 'North America',
1188 | 'hierarchy': {
1189 | 'features': [],
1190 | 'type': 'FeatureCollection'
1191 | },
1192 | 'name': 'Lafayette',
1193 | 'locationType': null,
1194 | 'positions': [10],
1195 | 'type': 'location'
1196 | }
1197 | }],
1198 | 'type': 'FeatureCollection'
1199 | },
1200 | 'name': 'Lafayette',
1201 | 'locationType': null,
1202 | 'positions': [10],
1203 | 'type': 'location'
1204 | }
1205 | }, {
1206 | 'geometry': {
1207 | 'coordinates': [-83.40659, 39.93756],
1208 | 'type': 'Point'
1209 | },
1210 | 'type': 'Feature',
1211 | 'properties': {
1212 | 'geoNameId': 4516099,
1213 | 'alternateNames': ['lafayette', 'fayette', 'limerick', 'la fayette', 'lawrenceville'],
1214 | 'toponym': 'Lafayette',
1215 | 'featureCode': 'PPL',
1216 | 'countryCode': 'US',
1217 | 'featureClass': 'P',
1218 | 'hierarchy': {
1219 | 'features': [{
1220 | 'geometry': null,
1221 | 'type': 'Feature',
1222 | 'properties': {
1223 | 'geoNameId': 4517365,
1224 | 'toponym': 'Madison County',
1225 | 'hierarchy': {
1226 | 'features': [],
1227 | 'type': 'FeatureCollection'
1228 | },
1229 | 'name': 'Lafayette',
1230 | 'locationType': null,
1231 | 'positions': [10],
1232 | 'type': 'location'
1233 | }
1234 | }, {
1235 | 'geometry': null,
1236 | 'type': 'Feature',
1237 | 'properties': {
1238 | 'geoNameId': 5165418,
1239 | 'toponym': 'Ohio',
1240 | 'hierarchy': {
1241 | 'features': [],
1242 | 'type': 'FeatureCollection'
1243 | },
1244 | 'name': 'Lafayette',
1245 | 'locationType': null,
1246 | 'positions': [10],
1247 | 'type': 'location'
1248 | }
1249 | }, {
1250 | 'geometry': null,
1251 | 'type': 'Feature',
1252 | 'properties': {
1253 | 'geoNameId': 6252001,
1254 | 'toponym': 'United States',
1255 | 'hierarchy': {
1256 | 'features': [],
1257 | 'type': 'FeatureCollection'
1258 | },
1259 | 'name': 'Lafayette',
1260 | 'locationType': null,
1261 | 'positions': [10],
1262 | 'type': 'location'
1263 | }
1264 | }, {
1265 | 'geometry': null,
1266 | 'type': 'Feature',
1267 | 'properties': {
1268 | 'geoNameId': 6255149,
1269 | 'toponym': 'North America',
1270 | 'hierarchy': {
1271 | 'features': [],
1272 | 'type': 'FeatureCollection'
1273 | },
1274 | 'name': 'Lafayette',
1275 | 'locationType': null,
1276 | 'positions': [10],
1277 | 'type': 'location'
1278 | }
1279 | }],
1280 | 'type': 'FeatureCollection'
1281 | },
1282 | 'name': 'Lafayette',
1283 | 'locationType': null,
1284 | 'positions': [10],
1285 | 'type': 'location'
1286 | }
1287 | }],
1288 | 'type': 'FeatureCollection'
1289 | }
1290 | }
1291 | }, {
1292 | 'geometry': {
1293 | 'coordinates': [-86.25027, 40.00032],
1294 | 'type': 'Point'
1295 | },
1296 | 'type': 'Feature',
1297 | 'properties': {
1298 | 'geoNameId': 4921868,
1299 | 'alternateNames': null,
1300 | 'toponym': 'Indiana',
1301 | 'featureCode': 'ADM1',
1302 | 'countryCode': 'US',
1303 | 'featureClass': 'A',
1304 | 'hierarchy': {
1305 | 'features': [{
1306 | 'geometry': null,
1307 | 'type': 'Feature',
1308 | 'properties': {
1309 | 'geoNameId': 6252001,
1310 | 'toponym': 'United States',
1311 | 'hierarchy': null,
1312 | 'name': 'Indiana',
1313 | 'locationType': null,
1314 | 'positions': [21],
1315 | 'type': 'location'
1316 | }
1317 | }, {
1318 | 'geometry': null,
1319 | 'type': 'Feature',
1320 | 'properties': {
1321 | 'geoNameId': 6255149,
1322 | 'toponym': 'North America',
1323 | 'hierarchy': null,
1324 | 'name': 'Indiana',
1325 | 'locationType': null,
1326 | 'positions': [21],
1327 | 'type': 'location'
1328 | }
1329 | }],
1330 | 'type': 'FeatureCollection'
1331 | },
1332 | 'name': 'Indiana',
1333 | 'positions': [21],
1334 | 'type': 'location',
1335 | 'alternates': {
1336 | 'features': [{
1337 | 'geometry': {
1338 | 'coordinates': [-86.25027, 40.00032],
1339 | 'type': 'Point'
1340 | },
1341 | 'type': 'Feature',
1342 | 'properties': {
1343 | 'geoNameId': 4921868,
1344 | 'toponym': 'Indiana',
1345 | 'featureCode': 'ADM1',
1346 | 'countryCode': 'US',
1347 | 'featureClass': 'A',
1348 | 'hierarchy': {
1349 | 'features': [{
1350 | 'geometry': null,
1351 | 'type': 'Feature',
1352 | 'properties': {
1353 | 'geoNameId': 6252001,
1354 | 'toponym': 'United States',
1355 | 'hierarchy': null,
1356 | 'name': 'Indiana',
1357 | 'locationType': null,
1358 | 'positions': [21],
1359 | 'type': 'location'
1360 | }
1361 | }, {
1362 | 'geometry': null,
1363 | 'type': 'Feature',
1364 | 'properties': {
1365 | 'geoNameId': 6255149,
1366 | 'toponym': 'North America',
1367 | 'hierarchy': null,
1368 | 'name': 'Indiana',
1369 | 'locationType': null,
1370 | 'positions': [21],
1371 | 'type': 'location'
1372 | }
1373 | }],
1374 | 'type': 'FeatureCollection'
1375 | },
1376 | 'name': 'Indiana',
1377 | 'positions': [21],
1378 | 'type': 'location'
1379 | }
1380 | }, {
1381 | 'geometry': {
1382 | 'coordinates': [-79.15253, 40.62146],
1383 | 'type': 'Point'
1384 | },
1385 | 'type': 'Feature',
1386 | 'properties': {
1387 | 'geoNameId': 5194868,
1388 | 'alternateNames': ['индиана', 'indijana', 'ayndyana pnsylwanya', 'ایندیانا، پنسیلوانیا', 'インディアナ', 'индијана', 'idi', 'indiana'],
1389 | 'toponym': 'Indiana',
1390 | 'featureCode': 'PPLA2',
1391 | 'countryCode': 'US',
1392 | 'featureClass': 'P',
1393 | 'hierarchy': {
1394 | 'features': [{
1395 | 'geometry': null,
1396 | 'type': 'Feature',
1397 | 'properties': {
1398 | 'geoNameId': 5194872,
1399 | 'toponym': 'Indiana County',
1400 | 'hierarchy': {
1401 | 'features': [],
1402 | 'type': 'FeatureCollection'
1403 | },
1404 | 'name': 'Indiana',
1405 | 'locationType': null,
1406 | 'positions': [21],
1407 | 'type': 'location'
1408 | }
1409 | }, {
1410 | 'geometry': null,
1411 | 'type': 'Feature',
1412 | 'properties': {
1413 | 'geoNameId': 6254927,
1414 | 'toponym': 'Pennsylvania',
1415 | 'hierarchy': {
1416 | 'features': [],
1417 | 'type': 'FeatureCollection'
1418 | },
1419 | 'name': 'Indiana',
1420 | 'locationType': null,
1421 | 'positions': [21],
1422 | 'type': 'location'
1423 | }
1424 | }, {
1425 | 'geometry': null,
1426 | 'type': 'Feature',
1427 | 'properties': {
1428 | 'geoNameId': 6252001,
1429 | 'toponym': 'United States',
1430 | 'hierarchy': {
1431 | 'features': [],
1432 | 'type': 'FeatureCollection'
1433 | },
1434 | 'name': 'Indiana',
1435 | 'locationType': null,
1436 | 'positions': [21],
1437 | 'type': 'location'
1438 | }
1439 | }, {
1440 | 'geometry': null,
1441 | 'type': 'Feature',
1442 | 'properties': {
1443 | 'geoNameId': 6255149,
1444 | 'toponym': 'North America',
1445 | 'hierarchy': {
1446 | 'features': [],
1447 | 'type': 'FeatureCollection'
1448 | },
1449 | 'name': 'Indiana',
1450 | 'locationType': null,
1451 | 'positions': [21],
1452 | 'type': 'location'
1453 | }
1454 | }],
1455 | 'type': 'FeatureCollection'
1456 | },
1457 | 'name': 'Indiana',
1458 | 'locationType': null,
1459 | 'positions': [21],
1460 | 'type': 'location'
1461 | }
1462 | }, {
1463 | 'geometry': {
1464 | 'coordinates': [72, -6],
1465 | 'type': 'Point'
1466 | },
1467 | 'type': 'Feature',
1468 | 'properties': {
1469 | 'geoNameId': 1282588,
1470 | 'alternateNames': ['territoires britanniques de l\'océan indien', 'i-british indian ocean territory', 'britske indickooceanske uzemie', 'britich xin deiy n xo cheiy n ther ri thx ri', 'британско индиско океанска територија', 'britisches territorium im indischen ozean', 'britu indijas okeāna teritorija', 'ဗြိတိသျှ အိန္ဒိယသမုဒ္ဒရာ နယ်မြေ', 'britske uzemie v indickom oceane', 'teritwari y\'inyanja y\'abahinde nyongereza', 'territorio oceanic britanno-indian', 'brytyjskie terytorium oceanu indyjskiego', 'brits indische oceaanterritorium', 'lãnh thổ ấn độ dương thuộc anh', 'indijas okeana britu teritorija', 'ब्रिटिश इंडियन ओशन टेरीटरी', 'பிரிட்டிஷ் இந்தியப் பெருங்கடல் பகுதி', 'indi okeanyndagy britan territorijasy', 'intara y\'ubwongereza yo mu birwa by\'abahindi', 'บริติชอินเดียนโอเชียนเทร์ริทอรี', 'teritöio britannego de loçeano indian', 'indiako ozeanoko britainiar lurraldea', 'tiriogaeth cefnfor india prydain', 'teritori samudra hindia britania', 'indijy furdy britanijy territori', 'የብሪታንያ ሕንድ ውቅያኖስ ግዛት', 'britaniana teritorio en indiana oceano', 'مستعمرههای بریتانیا در اقیانوس هند', 'teritoriul britanic din oceanul indian', 'keeriindi britaani to maayo enndo', 'territorio britanico do oceano indico', 'territorio britannico dell’oceano indiano', 'britiske territorier i indiahavet', 'orílẹ́ède etíkun índíánì ti ìlú bírítísì', 'brittiläinen intian valtameren alue', 'det britiske territoriet i indiahavet', 'territorio britannico delloceano indiano', 'ბრიტანეთის ინდოეთის ოკეანის ტერიტორია', 'yeong-guglyeong indoyang jiyeog', 'ಬ್ರಿಟೀಶ್ ಇಂಡಿಯನ್ ಮಹಾಸಾಗರ ಪ್ರದೇಶ', 'ingiliz hint okyanusu boelgesi', 'potu fonua moana \'initia fakapilitania', 'faridranomasina indiana britanika', 'territorio británico del océano índico', 'tiriad breizhveurat meurvor indez', 'thuoc gia anh tai an gjo duong', 'britaniya-hindistan okeanik territoriyası', 'territori britanic de l\'ocea indic', 'territorio britanico del oceano indico', 'ब्रिटिश हिंद महासागर क्षेत्र', 'брытанская тэрыторыя ў індыйскім акіяне', 'βρετανικό έδαφος ινδικού ωκεανού', 'የብሪታኒያ ህንድ ውቂያኖስ ግዛት', 'britisa indiyana osana teritari', 'brita hindoceana teritorio', '英領インド洋地域', 'ying shu yin du yang ling de', 'aqlym almhyt alhndy albrytany', 'बेलायती हिन्द महासागर क्षेत्र', 'bretsku indiahavsoyggjarnar', 'britans\'ka teritorija v indijs\'komu okeani', 'téritori samudra hindia britania', 'britisa hinda mahasagariya ksetra', 'britanin latta indin okeanekh\'', 'territori britannic en l\'ocean indic', 'britiske omrade i det indiske hav', 'brit indiai-óceáni terület', 'βρετανικά εδάφη ινδικού ωκεανού', 'territoire britannique de l\'océan indien', 'britse gebieden in de indische oceaan', 'indijos vandenyno britu sritis', 'brittiska territoriet i indiska oceanen', 'قلمرو بریتانیا در اقیانوس هند', 'agbègbè òkun índíà brítánì', 'britansko ozemlje v indijskem oceanu', 'eneo la uingereza katika bahari hindi', 'britse indiese oseaan gebied', 'pirittaniya intiyap perunkatal mantalam', 'igirisu lingindo yang de yu', 'territoire britannique de l\'ocean indien', 'territorio británico do océano índico', 'イギリス領インド洋地域', 'հնդկական օվկիանոսի բրիտանական տարածքներ', '英属印度洋领地', 'британская территория в индийском океане', 'teritwari y’inyanja y’abahinde nyongereza', 'britisa bharata mahasagariya ancala', 'vretanika edafi indikou okeanou', 'britisa hindi mahasagara ksetra', 'britis bharatiya osan prantam', 'британин латта индин океанехь', 'ব্ৰিটিশ্ব ইণ্ডিয়ান মহাসাগৰৰ অঞ্চল', 'брытанская тэрыторыя індыйскага акіяну', 'britansko indisko okeanska teritorija', 'indijas okeāna britu teritorija', 'ব্রিটিশ ভারত মহাসাগরীয় অঞ্চল', 'brytanskaa terytorya indyjskaga akianu', 'britanske indijskookeanske teritorije', 'инди океанындагы британ территориясы', 'britanski teritorii v indijskija okean', 'territorio britanico de locian indico', 'הטריטוריה הבריטית באוקיינוס ההודי', 'britanya hint okyanusu toprakları', 'alaqlym albrytany fy almhyt alhndy', 'британска територија индијског океана', 'lutanda lwa angeletele ku mbu wa indiya', 'ying lingindo yang de yu', 'британски територии в индийския океан', 'britanski indijskooceanski teritorij', 'britske indickooceanske uzemi', 'britaniya-hindistan okeanik territoriyasi', 'bresku indlandshafseyjar', 'wilayah inggris di samudra hindia', 'yankin birtaniya na tekun indiya', 'британські території індійського океану', 'territorio britannico dell\'oceano indiano', 'برطانوی بحرہند خطہ', 'britské indickooceánské území', 'io', 'criocha briotanacha an aigein indiagh', 'orileede etikun indiani ti ilu biritisi', 'bryttisca indisca garsecg landscipe', 'british indian ocean territory', 'బ్రిటిష్ భారతీయ ఓషన్ ప్రాంతం', 'britu indijas okeana teritorija', 'britansko indijska okeanska teritorija', 'ബ്രിട്ടീഷ് ഇന്ത്യന് മഹാസമുദ്ര പ്രദേശം', 'teritoryo han britanya ha kalawdan indyano', 'үнді мұхитындағы британия аймағы', 'mabelé ya angɛlɛtɛ́lɛ na mbú ya indiya', 'territori britànic de loceà índic', 'teritoeio britannego de loceano indian', 'brit indiai-oceani teruelet', 'bizinga by\'ecago', 'tiriogaeth brydeinig cefnfor india', 'טריטוריה בריטית באוקיאנוס ההודי', 'إقليم المحيط الهندي البريطاني', 'ბრიტანული ტერიტორია ინდოეთის ოკეანეში', 'det britiske territorium i det indiske ocean', 'බ්රිතාන්ය ඉන්දීය සාගර ප්රාන්තය', 'برطانوی ہندوستانی سمندری خطہ', 'britaniya hind okeanı əraziləri', 'الإقليم البريطاني في المحيط الهندي', 'i̇ngiliz hint okyanusu bölgesi', 'britské územie v indickom oceáne', 'wilayah lautan hindi british', 'britiske område i det indiske hav', 'britanskaja territorija v indijskom okeane', 'wilayah inggreh di samudra hindia', 'britis indiyan mahasagara pradesa', 'britanska teritorija indijskog okeana', 'britské indickooceánske územie', 'wilayah samudra hindia britania', 'brytanskaja tehrytoryja u indyjskim akijane', 'britanya hint okyanusu topraklari', 'territori britanic de locea indic', 'indijos vandenyno britų sritis', 'territori britànic de l\'oceà índic', 'qlmrw brytanya dr aqyanws hnd', 'ਬਰਤਾਨਵੀ ਹਿੰਦ ਮਹਾਂਸਾਗਰ ਰਾਜਖੇਤਰ', 'lanh tho an gjo duong thuoc anh', 'críocha briotanacha an aigéin indiagh', 'britans\'ki teritorii indijs\'kogo okeanu', 'belayati hinda mahasagara ksetra', 'ब्रिटिश हिंद महासागरीय क्षेत्र', 'индийы фурды британийы территори', 'angilɛ ka ɛndu dugukolo', 'sese ti anglee na nguyaemae ti ennde', 'britisa hinda mahasagara ksetra', 'britaintɔwo ƒe india ƒudome nutome', 'sêse tî anglëe na ngûyämä tî ênnde', 'britse indiese oseaangebied', 'britanski teritorij indijskog oceana', 'terytorium brytyjskie oceanu indyjskiego', 'braitisba indiyana mahasagarara ancala', 'britenfo hɔn man wɔ india po no mu', 'territorio britanico de locián indico', 'britanska indookeanska teritorija', '英屬印度洋領地', 'ဗြိတိသျှ အိန္ဒြိယ သမုဒ္ဒရာ ပိုင်နက်', 'briti india ookeani ala', 'ब्रिटीश हिंदी महासागर क्षेत्र', 'thuộc địa anh tại ấn độ dương', 'બ્રિટિશ ઇન્ડિયન ઓશન ટેરિટરી', 'brittiska indiska oceanöarna', 'ବ୍ରିଟିଶ୍ ଭାରତୀୟ ସାମୁଦ୍ରିକ କ୍ଷେତ୍ର', '영국령 인도양 지역', 'британська територія в індійському океані', 'vretaniko edafos indikou okeanou', 'agbegbe okun india britani', 'baratanavi hida mahansagara rajakhetara', 'британска индоокеанска територия', 'brittiska indiska oceanoearna', 'territoires britanniques de l\'ocean indien', 'britanska teritorija u indijskom okeanu', 'wilayah inggréh di samudra hindia', 'pirittis intiyap perunkatal pakuti', 'tiriad meurvor indez breizh-veur', 'британска територија у индијском океану', 'brittilaeinen intian valtameren alue', 'britischs territorium im indischn ozean', 'potu fonua moana ʻinitia fakapilitānia', 'britis bharatiya samudrika ksetra', 'território britânico do oceano índico', 'பிரித்தானிய இந்தியப் பெருங்கடல் மண்டலம்'],
1471 | 'toponym': 'British Indian Ocean Territory',
1472 | 'featureCode': 'PCLD',
1473 | 'countryCode': 'IO',
1474 | 'featureClass': 'A',
1475 | 'hierarchy': {
1476 | 'features': [{
1477 | 'geometry': null,
1478 | 'type': 'Feature',
1479 | 'properties': {
1480 | 'geoNameId': 6255147,
1481 | 'toponym': 'Asia',
1482 | 'hierarchy': {
1483 | 'features': [],
1484 | 'type': 'FeatureCollection'
1485 | },
1486 | 'name': 'Indiana',
1487 | 'locationType': null,
1488 | 'positions': [21],
1489 | 'type': 'location'
1490 | }
1491 | }],
1492 | 'type': 'FeatureCollection'
1493 | },
1494 | 'name': 'Indiana',
1495 | 'locationType': null,
1496 | 'positions': [21],
1497 | 'type': 'location'
1498 | }
1499 | }, {
1500 | 'geometry': {
1501 | 'coordinates': [-51.26047, -22.12587],
1502 | 'type': 'Point'
1503 | },
1504 | 'type': 'Feature',
1505 | 'properties': {
1506 | 'geoNameId': 6322290,
1507 | 'alternateNames': ['indiana'],
1508 | 'toponym': 'Indiana',
1509 | 'featureCode': 'ADM2',
1510 | 'countryCode': 'BR',
1511 | 'featureClass': 'A',
1512 | 'hierarchy': {
1513 | 'features': [{
1514 | 'geometry': null,
1515 | 'type': 'Feature',
1516 | 'properties': {
1517 | 'geoNameId': 3448433,
1518 | 'toponym': 'São Paulo',
1519 | 'hierarchy': {
1520 | 'features': [],
1521 | 'type': 'FeatureCollection'
1522 | },
1523 | 'name': 'Indiana',
1524 | 'locationType': null,
1525 | 'positions': [21],
1526 | 'type': 'location'
1527 | }
1528 | }, {
1529 | 'geometry': null,
1530 | 'type': 'Feature',
1531 | 'properties': {
1532 | 'geoNameId': 3469034,
1533 | 'toponym': 'Brazil',
1534 | 'hierarchy': {
1535 | 'features': [],
1536 | 'type': 'FeatureCollection'
1537 | },
1538 | 'name': 'Indiana',
1539 | 'locationType': null,
1540 | 'positions': [21],
1541 | 'type': 'location'
1542 | }
1543 | }, {
1544 | 'geometry': null,
1545 | 'type': 'Feature',
1546 | 'properties': {
1547 | 'geoNameId': 6255150,
1548 | 'toponym': 'South America',
1549 | 'hierarchy': {
1550 | 'features': [],
1551 | 'type': 'FeatureCollection'
1552 | },
1553 | 'name': 'Indiana',
1554 | 'locationType': null,
1555 | 'positions': [21],
1556 | 'type': 'location'
1557 | }
1558 | }],
1559 | 'type': 'FeatureCollection'
1560 | },
1561 | 'name': 'Indiana',
1562 | 'locationType': null,
1563 | 'positions': [21],
1564 | 'type': 'location'
1565 | }
1566 | }, {
1567 | 'geometry': {
1568 | 'coordinates': [-73.05136, -3.49989],
1569 | 'type': 'Point'
1570 | },
1571 | 'type': 'Feature',
1572 | 'properties': {
1573 | 'geoNameId': 10346348,
1574 | 'alternateNames': ['indiana'],
1575 | 'toponym': 'Indiana',
1576 | 'featureCode': 'PPLA3',
1577 | 'countryCode': 'PE',
1578 | 'featureClass': 'P',
1579 | 'hierarchy': {
1580 | 'features': [{
1581 | 'geometry': null,
1582 | 'type': 'Feature',
1583 | 'properties': {
1584 | 'geoNameId': 3694873,
1585 | 'toponym': 'Provincia de Maynas',
1586 | 'hierarchy': {
1587 | 'features': [],
1588 | 'type': 'FeatureCollection'
1589 | },
1590 | 'name': 'Indiana',
1591 | 'locationType': null,
1592 | 'positions': [21],
1593 | 'type': 'location'
1594 | }
1595 | }, {
1596 | 'geometry': null,
1597 | 'type': 'Feature',
1598 | 'properties': {
1599 | 'geoNameId': 3695238,
1600 | 'toponym': 'Loreto',
1601 | 'hierarchy': {
1602 | 'features': [],
1603 | 'type': 'FeatureCollection'
1604 | },
1605 | 'name': 'Indiana',
1606 | 'locationType': null,
1607 | 'positions': [21],
1608 | 'type': 'location'
1609 | }
1610 | }, {
1611 | 'geometry': null,
1612 | 'type': 'Feature',
1613 | 'properties': {
1614 | 'geoNameId': 3932488,
1615 | 'toponym': 'Peru',
1616 | 'hierarchy': {
1617 | 'features': [],
1618 | 'type': 'FeatureCollection'
1619 | },
1620 | 'name': 'Indiana',
1621 | 'locationType': null,
1622 | 'positions': [21],
1623 | 'type': 'location'
1624 | }
1625 | }, {
1626 | 'geometry': null,
1627 | 'type': 'Feature',
1628 | 'properties': {
1629 | 'geoNameId': 6255150,
1630 | 'toponym': 'South America',
1631 | 'hierarchy': {
1632 | 'features': [],
1633 | 'type': 'FeatureCollection'
1634 | },
1635 | 'name': 'Indiana',
1636 | 'locationType': null,
1637 | 'positions': [21],
1638 | 'type': 'location'
1639 | }
1640 | }],
1641 | 'type': 'FeatureCollection'
1642 | },
1643 | 'name': 'Indiana',
1644 | 'locationType': null,
1645 | 'positions': [21],
1646 | 'type': 'location'
1647 | }
1648 | }, {
1649 | 'geometry': {
1650 | 'coordinates': [-87.85, 15.46667],
1651 | 'type': 'Point'
1652 | },
1653 | 'type': 'Feature',
1654 | 'properties': {
1655 | 'geoNameId': 3608849,
1656 | 'alternateNames': ['indiana'],
1657 | 'toponym': 'Indiana',
1658 | 'featureCode': 'PPL',
1659 | 'countryCode': 'HN',
1660 | 'featureClass': 'P',
1661 | 'hierarchy': {
1662 | 'features': [{
1663 | 'geometry': null,
1664 | 'type': 'Feature',
1665 | 'properties': {
1666 | 'geoNameId': 3613140,
1667 | 'toponym': 'Departamento de Cortés',
1668 | 'hierarchy': {
1669 | 'features': [],
1670 | 'type': 'FeatureCollection'
1671 | },
1672 | 'name': 'Indiana',
1673 | 'locationType': null,
1674 | 'positions': [21],
1675 | 'type': 'location'
1676 | }
1677 | }, {
1678 | 'geometry': null,
1679 | 'type': 'Feature',
1680 | 'properties': {
1681 | 'geoNameId': 3608932,
1682 | 'toponym': 'Honduras',
1683 | 'hierarchy': {
1684 | 'features': [],
1685 | 'type': 'FeatureCollection'
1686 | },
1687 | 'name': 'Indiana',
1688 | 'locationType': null,
1689 | 'positions': [21],
1690 | 'type': 'location'
1691 | }
1692 | }, {
1693 | 'geometry': null,
1694 | 'type': 'Feature',
1695 | 'properties': {
1696 | 'geoNameId': 6255149,
1697 | 'toponym': 'North America',
1698 | 'hierarchy': {
1699 | 'features': [],
1700 | 'type': 'FeatureCollection'
1701 | },
1702 | 'name': 'Indiana',
1703 | 'locationType': null,
1704 | 'positions': [21],
1705 | 'type': 'location'
1706 | }
1707 | }],
1708 | 'type': 'FeatureCollection'
1709 | },
1710 | 'name': 'Indiana',
1711 | 'locationType': null,
1712 | 'positions': [21],
1713 | 'type': 'location'
1714 | }
1715 | }, {
1716 | 'geometry': {
1717 | 'coordinates': [135.43994, -23.3383],
1718 | 'type': 'Point'
1719 | },
1720 | 'type': 'Feature',
1721 | 'properties': {
1722 | 'geoNameId': 8644346,
1723 | 'alternateNames': ['indiana'],
1724 | 'toponym': 'Indiana',
1725 | 'featureCode': 'PPL',
1726 | 'countryCode': 'AU',
1727 | 'featureClass': 'P',
1728 | 'hierarchy': {
1729 | 'features': [{
1730 | 'geometry': null,
1731 | 'type': 'Feature',
1732 | 'properties': {
1733 | 'geoNameId': 7839685,
1734 | 'toponym': 'Central Desert',
1735 | 'hierarchy': {
1736 | 'features': [],
1737 | 'type': 'FeatureCollection'
1738 | },
1739 | 'name': 'Indiana',
1740 | 'locationType': null,
1741 | 'positions': [21],
1742 | 'type': 'location'
1743 | }
1744 | }, {
1745 | 'geometry': null,
1746 | 'type': 'Feature',
1747 | 'properties': {
1748 | 'geoNameId': 2064513,
1749 | 'toponym': 'Northern Territory',
1750 | 'hierarchy': {
1751 | 'features': [],
1752 | 'type': 'FeatureCollection'
1753 | },
1754 | 'name': 'Indiana',
1755 | 'locationType': null,
1756 | 'positions': [21],
1757 | 'type': 'location'
1758 | }
1759 | }, {
1760 | 'geometry': null,
1761 | 'type': 'Feature',
1762 | 'properties': {
1763 | 'geoNameId': 2077456,
1764 | 'toponym': 'Australia',
1765 | 'hierarchy': {
1766 | 'features': [],
1767 | 'type': 'FeatureCollection'
1768 | },
1769 | 'name': 'Indiana',
1770 | 'locationType': null,
1771 | 'positions': [21],
1772 | 'type': 'location'
1773 | }
1774 | }, {
1775 | 'geometry': null,
1776 | 'type': 'Feature',
1777 | 'properties': {
1778 | 'geoNameId': 6255151,
1779 | 'toponym': 'Oceania',
1780 | 'hierarchy': {
1781 | 'features': [],
1782 | 'type': 'FeatureCollection'
1783 | },
1784 | 'name': 'Indiana',
1785 | 'locationType': null,
1786 | 'positions': [21],
1787 | 'type': 'location'
1788 | }
1789 | }],
1790 | 'type': 'FeatureCollection'
1791 | },
1792 | 'name': 'Indiana',
1793 | 'locationType': null,
1794 | 'positions': [21],
1795 | 'type': 'location'
1796 | }
1797 | }, {
1798 | 'geometry': {
1799 | 'coordinates': [-51.25167, -22.17444],
1800 | 'type': 'Point'
1801 | },
1802 | 'type': 'Feature',
1803 | 'properties': {
1804 | 'geoNameId': 3461294,
1805 | 'alternateNames': ['indiana'],
1806 | 'toponym': 'Indiana',
1807 | 'featureCode': 'PPL',
1808 | 'countryCode': 'BR',
1809 | 'featureClass': 'P',
1810 | 'hierarchy': {
1811 | 'features': [{
1812 | 'geometry': null,
1813 | 'type': 'Feature',
1814 | 'properties': {
1815 | 'geoNameId': 6322290,
1816 | 'toponym': 'Indiana',
1817 | 'hierarchy': {
1818 | 'features': [],
1819 | 'type': 'FeatureCollection'
1820 | },
1821 | 'name': 'Indiana',
1822 | 'locationType': null,
1823 | 'positions': [21],
1824 | 'type': 'location'
1825 | }
1826 | }, {
1827 | 'geometry': null,
1828 | 'type': 'Feature',
1829 | 'properties': {
1830 | 'geoNameId': 3448433,
1831 | 'toponym': 'São Paulo',
1832 | 'hierarchy': {
1833 | 'features': [],
1834 | 'type': 'FeatureCollection'
1835 | },
1836 | 'name': 'Indiana',
1837 | 'locationType': null,
1838 | 'positions': [21],
1839 | 'type': 'location'
1840 | }
1841 | }, {
1842 | 'geometry': null,
1843 | 'type': 'Feature',
1844 | 'properties': {
1845 | 'geoNameId': 3469034,
1846 | 'toponym': 'Brazil',
1847 | 'hierarchy': {
1848 | 'features': [],
1849 | 'type': 'FeatureCollection'
1850 | },
1851 | 'name': 'Indiana',
1852 | 'locationType': null,
1853 | 'positions': [21],
1854 | 'type': 'location'
1855 | }
1856 | }, {
1857 | 'geometry': null,
1858 | 'type': 'Feature',
1859 | 'properties': {
1860 | 'geoNameId': 6255150,
1861 | 'toponym': 'South America',
1862 | 'hierarchy': {
1863 | 'features': [],
1864 | 'type': 'FeatureCollection'
1865 | },
1866 | 'name': 'Indiana',
1867 | 'locationType': null,
1868 | 'positions': [21],
1869 | 'type': 'location'
1870 | }
1871 | }],
1872 | 'type': 'FeatureCollection'
1873 | },
1874 | 'name': 'Indiana',
1875 | 'locationType': null,
1876 | 'positions': [21],
1877 | 'type': 'location'
1878 | }
1879 | }, {
1880 | 'geometry': {
1881 | 'coordinates': [121.0773, 16.3332],
1882 | 'type': 'Point'
1883 | },
1884 | 'type': 'Feature',
1885 | 'properties': {
1886 | 'geoNameId': 1710762,
1887 | 'alternateNames': ['indiana'],
1888 | 'toponym': 'Indiana',
1889 | 'featureCode': 'PPL',
1890 | 'countryCode': 'PH',
1891 | 'featureClass': 'P',
1892 | 'hierarchy': {
1893 | 'features': [{
1894 | 'geometry': null,
1895 | 'type': 'Feature',
1896 | 'properties': {
1897 | 'geoNameId': 1697456,
1898 | 'toponym': 'Province of Nueva Vizcaya',
1899 | 'hierarchy': {
1900 | 'features': [],
1901 | 'type': 'FeatureCollection'
1902 | },
1903 | 'name': 'Indiana',
1904 | 'locationType': null,
1905 | 'positions': [21],
1906 | 'type': 'location'
1907 | }
1908 | }, {
1909 | 'geometry': null,
1910 | 'type': 'Feature',
1911 | 'properties': {
1912 | 'geoNameId': 7521297,
1913 | 'toponym': 'Cagayan Valley',
1914 | 'hierarchy': {
1915 | 'features': [],
1916 | 'type': 'FeatureCollection'
1917 | },
1918 | 'name': 'Indiana',
1919 | 'locationType': null,
1920 | 'positions': [21],
1921 | 'type': 'location'
1922 | }
1923 | }, {
1924 | 'geometry': null,
1925 | 'type': 'Feature',
1926 | 'properties': {
1927 | 'geoNameId': 1694008,
1928 | 'toponym': 'Philippines',
1929 | 'hierarchy': {
1930 | 'features': [],
1931 | 'type': 'FeatureCollection'
1932 | },
1933 | 'name': 'Indiana',
1934 | 'locationType': null,
1935 | 'positions': [21],
1936 | 'type': 'location'
1937 | }
1938 | }, {
1939 | 'geometry': null,
1940 | 'type': 'Feature',
1941 | 'properties': {
1942 | 'geoNameId': 6255147,
1943 | 'toponym': 'Asia',
1944 | 'hierarchy': {
1945 | 'features': [],
1946 | 'type': 'FeatureCollection'
1947 | },
1948 | 'name': 'Indiana',
1949 | 'locationType': null,
1950 | 'positions': [21],
1951 | 'type': 'location'
1952 | }
1953 | }],
1954 | 'type': 'FeatureCollection'
1955 | },
1956 | 'name': 'Indiana',
1957 | 'locationType': null,
1958 | 'positions': [21],
1959 | 'type': 'location'
1960 | }
1961 | }, {
1962 | 'geometry': {
1963 | 'coordinates': [-93.0252, 41.20444],
1964 | 'type': 'Point'
1965 | },
1966 | 'type': 'Feature',
1967 | 'properties': {
1968 | 'geoNameId': 4861712,
1969 | 'alternateNames': ['indiana'],
1970 | 'toponym': 'Indiana',
1971 | 'featureCode': 'PPL',
1972 | 'countryCode': 'US',
1973 | 'featureClass': 'P',
1974 | 'hierarchy': {
1975 | 'features': [{
1976 | 'geometry': null,
1977 | 'type': 'Feature',
1978 | 'properties': {
1979 | 'geoNameId': 4866274,
1980 | 'toponym': 'Marion County',
1981 | 'hierarchy': {
1982 | 'features': [],
1983 | 'type': 'FeatureCollection'
1984 | },
1985 | 'name': 'Indiana',
1986 | 'locationType': null,
1987 | 'positions': [21],
1988 | 'type': 'location'
1989 | }
1990 | }, {
1991 | 'geometry': null,
1992 | 'type': 'Feature',
1993 | 'properties': {
1994 | 'geoNameId': 4862182,
1995 | 'toponym': 'Iowa',
1996 | 'hierarchy': {
1997 | 'features': [],
1998 | 'type': 'FeatureCollection'
1999 | },
2000 | 'name': 'Indiana',
2001 | 'locationType': null,
2002 | 'positions': [21],
2003 | 'type': 'location'
2004 | }
2005 | }, {
2006 | 'geometry': null,
2007 | 'type': 'Feature',
2008 | 'properties': {
2009 | 'geoNameId': 6252001,
2010 | 'toponym': 'United States',
2011 | 'hierarchy': {
2012 | 'features': [],
2013 | 'type': 'FeatureCollection'
2014 | },
2015 | 'name': 'Indiana',
2016 | 'locationType': null,
2017 | 'positions': [21],
2018 | 'type': 'location'
2019 | }
2020 | }, {
2021 | 'geometry': null,
2022 | 'type': 'Feature',
2023 | 'properties': {
2024 | 'geoNameId': 6255149,
2025 | 'toponym': 'North America',
2026 | 'hierarchy': {
2027 | 'features': [],
2028 | 'type': 'FeatureCollection'
2029 | },
2030 | 'name': 'Indiana',
2031 | 'locationType': null,
2032 | 'positions': [21],
2033 | 'type': 'location'
2034 | }
2035 | }],
2036 | 'type': 'FeatureCollection'
2037 | },
2038 | 'name': 'Indiana',
2039 | 'locationType': null,
2040 | 'positions': [21],
2041 | 'type': 'location'
2042 | }
2043 | }, {
2044 | 'geometry': {
2045 | 'coordinates': [-58.67773, 6.91336],
2046 | 'type': 'Point'
2047 | },
2048 | 'type': 'Feature',
2049 | 'properties': {
2050 | 'geoNameId': 3378256,
2051 | 'alternateNames': ['indina', 'indiana'],
2052 | 'toponym': 'Indiana',
2053 | 'featureCode': 'LCTY',
2054 | 'countryCode': 'GY',
2055 | 'featureClass': 'L',
2056 | 'hierarchy': {
2057 | 'features': [{
2058 | 'geometry': null,
2059 | 'type': 'Feature',
2060 | 'properties': {
2061 | 'geoNameId': 3376407,
2062 | 'toponym': 'Pomeroon-Supenaam Region',
2063 | 'hierarchy': {
2064 | 'features': [],
2065 | 'type': 'FeatureCollection'
2066 | },
2067 | 'name': 'Indiana',
2068 | 'locationType': null,
2069 | 'positions': [21],
2070 | 'type': 'location'
2071 | }
2072 | }, {
2073 | 'geometry': null,
2074 | 'type': 'Feature',
2075 | 'properties': {
2076 | 'geoNameId': 3378535,
2077 | 'toponym': 'Guyana',
2078 | 'hierarchy': {
2079 | 'features': [],
2080 | 'type': 'FeatureCollection'
2081 | },
2082 | 'name': 'Indiana',
2083 | 'locationType': null,
2084 | 'positions': [21],
2085 | 'type': 'location'
2086 | }
2087 | }, {
2088 | 'geometry': null,
2089 | 'type': 'Feature',
2090 | 'properties': {
2091 | 'geoNameId': 6255150,
2092 | 'toponym': 'South America',
2093 | 'hierarchy': {
2094 | 'features': [],
2095 | 'type': 'FeatureCollection'
2096 | },
2097 | 'name': 'Indiana',
2098 | 'locationType': null,
2099 | 'positions': [21],
2100 | 'type': 'location'
2101 | }
2102 | }],
2103 | 'type': 'FeatureCollection'
2104 | },
2105 | 'name': 'Indiana',
2106 | 'locationType': null,
2107 | 'positions': [21],
2108 | 'type': 'location'
2109 | }
2110 | }, {
2111 | 'geometry': {
2112 | 'coordinates': [-3.69676, 40.24645],
2113 | 'type': 'Point'
2114 | },
2115 | 'type': 'Feature',
2116 | 'properties': {
2117 | 'geoNameId': 9406284,
2118 | 'alternateNames': ['indiana'],
2119 | 'toponym': 'Indiana',
2120 | 'featureCode': 'HTL',
2121 | 'countryCode': 'ES',
2122 | 'featureClass': 'S',
2123 | 'hierarchy': {
2124 | 'features': [{
2125 | 'geometry': null,
2126 | 'type': 'Feature',
2127 | 'properties': {
2128 | 'geoNameId': 6355233,
2129 | 'toponym': 'Provincia de Madrid',
2130 | 'hierarchy': {
2131 | 'features': [],
2132 | 'type': 'FeatureCollection'
2133 | },
2134 | 'name': 'Indiana',
2135 | 'locationType': null,
2136 | 'positions': [21],
2137 | 'type': 'location'
2138 | }
2139 | }, {
2140 | 'geometry': null,
2141 | 'type': 'Feature',
2142 | 'properties': {
2143 | 'geoNameId': 3117732,
2144 | 'toponym': 'Comunidad de Madrid',
2145 | 'hierarchy': {
2146 | 'features': [],
2147 | 'type': 'FeatureCollection'
2148 | },
2149 | 'name': 'Indiana',
2150 | 'locationType': null,
2151 | 'positions': [21],
2152 | 'type': 'location'
2153 | }
2154 | }, {
2155 | 'geometry': null,
2156 | 'type': 'Feature',
2157 | 'properties': {
2158 | 'geoNameId': 2510769,
2159 | 'toponym': 'Spain',
2160 | 'hierarchy': {
2161 | 'features': [],
2162 | 'type': 'FeatureCollection'
2163 | },
2164 | 'name': 'Indiana',
2165 | 'locationType': null,
2166 | 'positions': [21],
2167 | 'type': 'location'
2168 | }
2169 | }, {
2170 | 'geometry': null,
2171 | 'type': 'Feature',
2172 | 'properties': {
2173 | 'geoNameId': 6255148,
2174 | 'toponym': 'Europe',
2175 | 'hierarchy': {
2176 | 'features': [],
2177 | 'type': 'FeatureCollection'
2178 | },
2179 | 'name': 'Indiana',
2180 | 'locationType': null,
2181 | 'positions': [21],
2182 | 'type': 'location'
2183 | }
2184 | }],
2185 | 'type': 'FeatureCollection'
2186 | },
2187 | 'name': 'Indiana',
2188 | 'locationType': null,
2189 | 'positions': [21],
2190 | 'type': 'location'
2191 | }
2192 | }, {
2193 | 'geometry': {
2194 | 'coordinates': [151.28688, -29.9269],
2195 | 'type': 'Point'
2196 | },
2197 | 'type': 'Feature',
2198 | 'properties': {
2199 | 'geoNameId': 8808786,
2200 | 'alternateNames': ['indiana'],
2201 | 'toponym': 'Indiana',
2202 | 'featureCode': 'HMSD',
2203 | 'countryCode': 'AU',
2204 | 'featureClass': 'S',
2205 | 'hierarchy': {
2206 | 'features': [{
2207 | 'geometry': null,
2208 | 'type': 'Feature',
2209 | 'properties': {
2210 | 'geoNameId': 7839726,
2211 | 'toponym': 'Guyra',
2212 | 'hierarchy': {
2213 | 'features': [],
2214 | 'type': 'FeatureCollection'
2215 | },
2216 | 'name': 'Indiana',
2217 | 'locationType': null,
2218 | 'positions': [21],
2219 | 'type': 'location'
2220 | }
2221 | }, {
2222 | 'geometry': null,
2223 | 'type': 'Feature',
2224 | 'properties': {
2225 | 'geoNameId': 2155400,
2226 | 'toponym': 'State of New South Wales',
2227 | 'hierarchy': {
2228 | 'features': [],
2229 | 'type': 'FeatureCollection'
2230 | },
2231 | 'name': 'Indiana',
2232 | 'locationType': null,
2233 | 'positions': [21],
2234 | 'type': 'location'
2235 | }
2236 | }, {
2237 | 'geometry': null,
2238 | 'type': 'Feature',
2239 | 'properties': {
2240 | 'geoNameId': 2077456,
2241 | 'toponym': 'Australia',
2242 | 'hierarchy': {
2243 | 'features': [],
2244 | 'type': 'FeatureCollection'
2245 | },
2246 | 'name': 'Indiana',
2247 | 'locationType': null,
2248 | 'positions': [21],
2249 | 'type': 'location'
2250 | }
2251 | }, {
2252 | 'geometry': null,
2253 | 'type': 'Feature',
2254 | 'properties': {
2255 | 'geoNameId': 6255151,
2256 | 'toponym': 'Oceania',
2257 | 'hierarchy': {
2258 | 'features': [],
2259 | 'type': 'FeatureCollection'
2260 | },
2261 | 'name': 'Indiana',
2262 | 'locationType': null,
2263 | 'positions': [21],
2264 | 'type': 'location'
2265 | }
2266 | }],
2267 | 'type': 'FeatureCollection'
2268 | },
2269 | 'name': 'Indiana',
2270 | 'locationType': null,
2271 | 'positions': [21],
2272 | 'type': 'location'
2273 | }
2274 | }, {
2275 | 'geometry': {
2276 | 'coordinates': [135.43968, -23.3291],
2277 | 'type': 'Point'
2278 | },
2279 | 'type': 'Feature',
2280 | 'properties': {
2281 | 'geoNameId': 8815956,
2282 | 'alternateNames': ['indiana'],
2283 | 'toponym': 'Indiana',
2284 | 'featureCode': 'HMSD',
2285 | 'countryCode': 'AU',
2286 | 'featureClass': 'S',
2287 | 'hierarchy': {
2288 | 'features': [{
2289 | 'geometry': null,
2290 | 'type': 'Feature',
2291 | 'properties': {
2292 | 'geoNameId': 7839685,
2293 | 'toponym': 'Central Desert',
2294 | 'hierarchy': {
2295 | 'features': [],
2296 | 'type': 'FeatureCollection'
2297 | },
2298 | 'name': 'Indiana',
2299 | 'locationType': null,
2300 | 'positions': [21],
2301 | 'type': 'location'
2302 | }
2303 | }, {
2304 | 'geometry': null,
2305 | 'type': 'Feature',
2306 | 'properties': {
2307 | 'geoNameId': 2064513,
2308 | 'toponym': 'Northern Territory',
2309 | 'hierarchy': {
2310 | 'features': [],
2311 | 'type': 'FeatureCollection'
2312 | },
2313 | 'name': 'Indiana',
2314 | 'locationType': null,
2315 | 'positions': [21],
2316 | 'type': 'location'
2317 | }
2318 | }, {
2319 | 'geometry': null,
2320 | 'type': 'Feature',
2321 | 'properties': {
2322 | 'geoNameId': 2077456,
2323 | 'toponym': 'Australia',
2324 | 'hierarchy': {
2325 | 'features': [],
2326 | 'type': 'FeatureCollection'
2327 | },
2328 | 'name': 'Indiana',
2329 | 'locationType': null,
2330 | 'positions': [21],
2331 | 'type': 'location'
2332 | }
2333 | }, {
2334 | 'geometry': null,
2335 | 'type': 'Feature',
2336 | 'properties': {
2337 | 'geoNameId': 6255151,
2338 | 'toponym': 'Oceania',
2339 | 'hierarchy': {
2340 | 'features': [],
2341 | 'type': 'FeatureCollection'
2342 | },
2343 | 'name': 'Indiana',
2344 | 'locationType': null,
2345 | 'positions': [21],
2346 | 'type': 'location'
2347 | }
2348 | }],
2349 | 'type': 'FeatureCollection'
2350 | },
2351 | 'name': 'Indiana',
2352 | 'locationType': null,
2353 | 'positions': [21],
2354 | 'type': 'location'
2355 | }
2356 | }, {
2357 | 'geometry': {
2358 | 'coordinates': [31.22952, 30.03147],
2359 | 'type': 'Point'
2360 | },
2361 | 'type': 'Feature',
2362 | 'properties': {
2363 | 'geoNameId': 9953265,
2364 | 'alternateNames': ['indiana'],
2365 | 'toponym': 'Indiana',
2366 | 'featureCode': 'HTL',
2367 | 'countryCode': 'EG',
2368 | 'featureClass': 'S',
2369 | 'hierarchy': {
2370 | 'features': [{
2371 | 'geometry': null,
2372 | 'type': 'Feature',
2373 | 'properties': {
2374 | 'geoNameId': 360631,
2375 | 'toponym': 'Cairo Governorate',
2376 | 'hierarchy': {
2377 | 'features': [],
2378 | 'type': 'FeatureCollection'
2379 | },
2380 | 'name': 'Indiana',
2381 | 'locationType': null,
2382 | 'positions': [21],
2383 | 'type': 'location'
2384 | }
2385 | }, {
2386 | 'geometry': null,
2387 | 'type': 'Feature',
2388 | 'properties': {
2389 | 'geoNameId': 357994,
2390 | 'toponym': 'Egypt',
2391 | 'hierarchy': {
2392 | 'features': [],
2393 | 'type': 'FeatureCollection'
2394 | },
2395 | 'name': 'Indiana',
2396 | 'locationType': null,
2397 | 'positions': [21],
2398 | 'type': 'location'
2399 | }
2400 | }, {
2401 | 'geometry': null,
2402 | 'type': 'Feature',
2403 | 'properties': {
2404 | 'geoNameId': 6255146,
2405 | 'toponym': 'Africa',
2406 | 'hierarchy': {
2407 | 'features': [],
2408 | 'type': 'FeatureCollection'
2409 | },
2410 | 'name': 'Indiana',
2411 | 'locationType': null,
2412 | 'positions': [21],
2413 | 'type': 'location'
2414 | }
2415 | }],
2416 | 'type': 'FeatureCollection'
2417 | },
2418 | 'name': 'Indiana',
2419 | 'locationType': null,
2420 | 'positions': [21],
2421 | 'type': 'location'
2422 | }
2423 | }],
2424 | 'type': 'FeatureCollection'
2425 | }
2426 | }
2427 | }],
2428 | 'type': 'FeatureCollection'
2429 | };
2430 |
--------------------------------------------------------------------------------
/example/images/circle-grey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZijingPeng/leaflet-tooltip-layout/d9bf9793947b29ea26e5ba5474c4ad8c5d3b176b/example/images/circle-grey.png
--------------------------------------------------------------------------------
/example/images/circle-red.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZijingPeng/leaflet-tooltip-layout/d9bf9793947b29ea26e5ba5474c4ad8c5d3b176b/example/images/circle-red.png
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | leaflet-plugin-tooltip-layout DEMO
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/example/main.js:
--------------------------------------------------------------------------------
1 | import * as L from 'leaflet';
2 | import {
3 | initialize,
4 | resetMarker,
5 | getMarkers,
6 | getLine
7 | } from '../lib';
8 | import {
9 | icon,
10 | iconlarge
11 | } from './Icon';
12 | import testData from './TestData';
13 | import config from './Config';
14 |
15 | // initialize map
16 | const mapContainerElId = 'map-container';
17 | let map = L.map(mapContainerElId, {
18 | zoomSnap: 0.25
19 | }).setView([40.2672, -86.1349], 4);
20 |
21 | // background layer
22 | L.tileLayer(config.mapTileLayerUrlTemplate, {
23 | maxZoom: 18,
24 | id: 'mapbox.streets'
25 | }).addTo(map);
26 |
27 | let markerList = [];
28 |
29 | L.geoJSON(testData, {
30 | pointToLayer: function (feature, latlng) {
31 | let marker = L.marker(latlng, {
32 | icon: icon
33 | });
34 | return marker;
35 | },
36 | onEachFeature(feature, layer) {
37 | feature.properties.alternates.features.forEach((iter, index) => {
38 | let name = iter.properties.name + ' ' + index;
39 | let coord = [iter.geometry.coordinates[1], iter.geometry.coordinates[0]];
40 | let marker = L.marker(coord, {
41 | icon: icon
42 | }).addTo(map);
43 | marker.bindTooltip(name);
44 | resetMarker(marker);
45 | });
46 | }
47 | }).addTo(map);
48 |
49 | // when mouse hover, icon gets larger
50 | function addMarkerHoverEvents() {
51 | function onMarkerMouseover(marker, tooltipDom) {
52 | return function () {
53 | marker.setIcon(iconlarge);
54 | marker._icon.style.zIndex = 999;
55 | tooltipDom.style.zIndex = 999;
56 | tooltipDom.style.border = '2px solid #039BE5';
57 | getLine(marker) && getLine(marker).setStyle({
58 | color: '#039BE5'
59 | });
60 | };
61 | }
62 |
63 | function onMarkerMouseout(marker, tooltipDom) {
64 | return function () {
65 | marker.setIcon(icon);
66 | marker._icon.style.zIndex = '';
67 | tooltipDom.style.zIndex = '';
68 | tooltipDom.style.border = '';
69 | tooltipDom.style.borderColor = '';
70 | getLine(marker) && getLine(marker).setStyle({
71 | color: '#90A4AE'
72 | });
73 | };
74 | }
75 |
76 | var i, marker, tooltip;
77 | var markerList = getMarkers();
78 | for (i = 0; i < markerList.length; i++) {
79 | marker = markerList[i];
80 | tooltip = marker.getTooltip();
81 | marker._icon.addEventListener('mouseover', onMarkerMouseover(marker, tooltip._container));
82 | marker._icon.addEventListener('mouseout', onMarkerMouseout(marker, tooltip._container));
83 | tooltip._container.addEventListener('mouseover', onMarkerMouseover(marker, tooltip._container));
84 | tooltip._container.addEventListener('mouseout', onMarkerMouseout(marker, tooltip._container));
85 | }
86 | }
87 | addMarkerHoverEvents();
88 |
89 | // when trigger HMR, just full reload page because leaflet.js is already init
90 | if (module.hot) {
91 | module.hot.dispose(() => {
92 | location.reload();
93 | });
94 |
95 | module.hot.accept(() => {
96 | location.reload();
97 | });
98 | }
99 |
100 | function onPolylineCreated(ply) {
101 | ply.setStyle({
102 | color: '#90A4AE'
103 | })
104 | }
105 |
106 | // init plugin
107 | initialize(map, onPolylineCreated);
108 |
--------------------------------------------------------------------------------
/example/styles.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | /* translate animation */
7 | .leaflet-tooltip {
8 | transition: transform 0.3s ease-in-out;
9 | }
10 |
11 | /* remove arrow in tooltips */
12 | .leaflet-tooltip-top:before,
13 | .leaflet-tooltip-bottom:before,
14 | .leaflet-tooltip-left:before,
15 | .leaflet-tooltip-right:before {
16 | visibility: hidden;
17 | }
18 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | (function (factory, window) {
2 | if (typeof define === 'function' && define.amd) {
3 | define(['leaflet'], factory);
4 | } else if (typeof exports === 'object') {
5 | module.exports = factory(require('leaflet'));
6 | }
7 | if (typeof window !== 'undefined' && window.L) {
8 | window.L.tooltipLayout = factory(L);
9 | }
10 | })(function (L) {
11 | var TooltipLayout = {};
12 |
13 | // global variables
14 | var map;
15 | var markerList = []; // all markers here
16 | var polylineList = []; // all polylines here
17 |
18 | // events
19 | var _onPolylineCreated = null; // will be called after polyline has been created
20 |
21 | function initialize(leafletMap, onPolylineCreated) {
22 | map = leafletMap;
23 | markerList = [];
24 | polylineList = [];
25 |
26 | //default style
27 | if (onPolylineCreated) {
28 | _onPolylineCreated = onPolylineCreated;
29 | } else {
30 | _onPolylineCreated = (ply) => {
31 | ply.setStyle({
32 | color: '#40809d',
33 | });
34 | };
35 | }
36 |
37 | setRandomPos(map);
38 | layoutByForce();
39 | setEdgePosition();
40 | drawLine(map);
41 | // event registrations
42 | map.on('zoomstart', function() {
43 | removeAllMarkers()
44 | removeAllPolyline(map);
45 | });
46 |
47 | map.on('zoomend', function() {
48 | redrawLines(true);
49 | });
50 |
51 | map.on('dragend', function() {
52 | redrawLines();
53 | });
54 |
55 | map.on('resize', function() {
56 | redrawLines();
57 | });
58 | }
59 |
60 | function redrawLines(maintainAllPolyline) {
61 | if (!maintainAllPolyline) {
62 | removeAllPolyline(map);
63 | }
64 | setRandomPos(map);
65 | layoutByForce();
66 | setEdgePosition();
67 | drawLine(map);
68 | }
69 |
70 | function addMarker(marker) {
71 | markerList.push(marker)
72 | }
73 |
74 | function deleteMarker(marker) {
75 | let i = markerList.indexOf(marker);
76 | if (i !== -1) {
77 | markerList.splice(i, 1)
78 | }
79 | }
80 |
81 | function resetMarker(marker) {
82 | var name = marker.getTooltip().getContent();
83 | var options = marker.getTooltip().options;
84 | marker.unbindTooltip();
85 |
86 | marker.bindTooltip(name, {
87 | pane: options.pane,
88 | offset: options.offset,
89 | className: options.className,
90 | permanent: true,
91 | interactive: true,
92 | direction: 'left',
93 | sticky: 'none',
94 | opacity: options.opacity
95 | });
96 | markerList.push(marker);
97 | }
98 |
99 | function getMarkers() {
100 | return markerList;
101 | }
102 |
103 | function setMarkers(arr) {
104 | markerList = arr;
105 | }
106 |
107 | function getLine(marker) {
108 | return marker.__ply;
109 | }
110 |
111 | function removeAllPolyline(map) {
112 | var i;
113 | for (i = 0; i < polylineList.length; i++) {
114 | map.removeLayer(polylineList[i]);
115 | }
116 | polylineList = [];
117 | }
118 |
119 | function removeAllMarkers() {
120 | getMarkers().forEach((marker) => marker.remove());
121 | markerList = [];
122 | }
123 |
124 | function resetMapContent() {
125 | removeAllMarkers();
126 | removeAllPolyline(map);
127 | }
128 |
129 |
130 |
131 | /**
132 | * Draw lines between markers and tooltips
133 | * @param map leaflet map
134 | */
135 | function drawLine(map) {
136 | removeAllPolyline(map);
137 | for (var i = 0; i < markerList.length; i++) {
138 | var marker = markerList[i];
139 | var markerDom = marker._icon;
140 | var markerPosition = getPosition(markerDom);
141 | var label = marker.getTooltip();
142 |
143 | var labelDom = label._container;
144 | var labelPosition = getPosition(labelDom);
145 |
146 | var x1 = labelPosition.x;
147 | var y1 = labelPosition.y;
148 |
149 | var x = markerPosition.x;
150 | var y = markerPosition.y;
151 |
152 | x1 -= 5;
153 | y1 += 2;
154 | if (x1 - x !== 0 || y1 - y !== 0) {
155 | if (x1 + labelDom.offsetWidth < markerPosition.x) {
156 | x1 += labelDom.offsetWidth;
157 | }
158 | if (y1 + labelDom.offsetHeight < markerPosition.y) {
159 | y1 += labelDom.offsetHeight;
160 | }
161 | var lineDest = L.point(x1, y1);
162 | var destLatLng = map.layerPointToLatLng(lineDest);
163 |
164 | setTimeout(
165 | ((marker, destLatLng) => () => {
166 | let ply = L.polyline([marker.getLatLng(), destLatLng]);
167 | _onPolylineCreated && _onPolylineCreated(ply);
168 | marker.__ply = ply;
169 | polylineList.push(ply);
170 | ply.addTo(map);
171 | })(marker, destLatLng),
172 | 0
173 | );
174 | }
175 | }
176 | }
177 |
178 | function setRandomPos() {
179 | for (var i = 0; i < markerList.length; i++) {
180 | var marker = markerList[i];
181 | var label = marker.getTooltip();
182 | var labelDom = label._container;
183 | var markerDom = marker._icon;
184 | var markerPosition = getPosition(markerDom);
185 | // var angle = Math.floor(Math.random() * 19 + 1) * 2 * Math.PI / 20;
186 | var angle = ((2 * Math.PI) / 6) * i;
187 | var x = markerPosition.x;
188 | var y = markerPosition.y;
189 | var dest = L.point(
190 | Math.ceil(x + 50 * Math.sin(angle)),
191 | Math.ceil(y + 50 * Math.cos(angle))
192 | );
193 | L.DomUtil.setPosition(labelDom, dest);
194 | }
195 | }
196 |
197 | function scaleTo(a, b) {
198 | return L.point(a.x * b.x, a.y * b.y);
199 | }
200 |
201 | function normalize(a) {
202 | var l = a.distanceTo(L.point(0, 0));
203 | if (l === 0) {
204 | return a;
205 | }
206 | return L.point(a.x / l, a.y / l);
207 | }
208 |
209 | function fa(x, k) {
210 | return (x * x) / k;
211 | }
212 |
213 | function fr(x, k) {
214 | return (k * k) / x;
215 | }
216 |
217 | /**
218 | * get position form el.style.transform
219 | */
220 | function getPosition(el) {
221 | // Failsafe to prevent element null
222 | if(el) {
223 | var translateString = el.style.transform
224 | .split('(')[1]
225 | .split(')')[0]
226 | .split(',');
227 | return L.point(parseInt(translateString[0]), parseInt(translateString[1]));
228 | } else {
229 | return L.point(0,0)
230 | }
231 | }
232 |
233 | /**
234 | * t is the temperature in the system
235 | */
236 | function computePositionStep(t) {
237 | var area = (window.innerWidth * window.innerHeight) / 10;
238 | var k = Math.sqrt(area / markerList.length);
239 | var dpos = L.point(0, 0);
240 | var v_pos;
241 | var v;
242 | var i;
243 |
244 | for (i = 0; i < markerList.length; i++) {
245 | v = markerList[i];
246 | // get position of label v
247 | v.disp = L.point(0, 0);
248 | v_pos = getPosition(v.getTooltip()._container);
249 |
250 | // compute gravitational force
251 | for (var j = 0; j < markerList.length; j++) {
252 | var u = markerList[j];
253 | if (i !== j) {
254 | var u_pos = getPosition(u.getTooltip()._container);
255 | dpos = v_pos.subtract(u_pos);
256 | if (dpos !== 0) {
257 | v.disp = v.disp.add(
258 | normalize(dpos).multiplyBy(fr(dpos.distanceTo(L.point(0, 0)), k))
259 | );
260 | }
261 | }
262 | }
263 | }
264 |
265 | // compute force between marker and tooltip
266 | for (i = 0; i < markerList.length; i++) {
267 | v = markerList[i];
268 | v_pos = getPosition(v.getTooltip()._container);
269 | dpos = v_pos.subtract(getPosition(v._icon));
270 | v.disp = v.disp.subtract(
271 | normalize(dpos).multiplyBy(fa(dpos.distanceTo(L.point(0, 0)), k))
272 | );
273 | }
274 |
275 | // calculate layout
276 | for (i = 0; i < markerList.length; i++) {
277 | var disp = markerList[i].disp;
278 | var p = getPosition(markerList[i].getTooltip()._container);
279 | var d = scaleTo(
280 | normalize(disp),
281 | L.point(Math.min(Math.abs(disp.x), t), Math.min(Math.abs(disp.y), t))
282 | );
283 | p = p.add(d);
284 | p = L.point(Math.ceil(p.x), Math.ceil(p.y));
285 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, p);
286 | }
287 | }
288 |
289 | function layoutByForce() {
290 | var start = Math.ceil(window.innerWidth / 10);
291 | var times = 50;
292 | var t;
293 | for (var i = 0; i < times; i += 1) {
294 | t = start * (1 - i / (times - 1));
295 | computePositionStep(t);
296 | }
297 |
298 | for (i = 0; i < markerList.length; i++) {
299 | var p = getPosition(markerList[i].getTooltip()._container);
300 | var width = markerList[i].getTooltip()._container.offsetWidth;
301 | var height = markerList[i].getTooltip()._container.offsetHeight;
302 | p = L.point(Math.ceil(p.x - width / 2), Math.ceil(p.y - height / 2));
303 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, p);
304 | }
305 | }
306 |
307 | function setEdgePosition() {
308 | var bounds = map.getBounds();
309 | var northWest = map.latLngToLayerPoint(bounds.getNorthWest());
310 | var southEast = map.latLngToLayerPoint(bounds.getSouthEast());
311 |
312 | for (let i = 0; i < markerList.length; i++) {
313 | var tooltip = getPosition(markerList[i].getTooltip()._container);
314 | var marker = getPosition(markerList[i]._icon);
315 | var width = markerList[i].getTooltip()._container.offsetWidth;
316 | var height = markerList[i].getTooltip()._container.offsetHeight;
317 |
318 | var isEdge = false;
319 | if (marker.x > northWest.x && tooltip.x < northWest.x) {
320 | tooltip.x = northWest.x;
321 | isEdge = true;
322 | } else if (marker.x < southEast.x && tooltip.x > southEast.x - width) {
323 | tooltip.x = southEast.x - width;
324 | isEdge = true;
325 | }
326 |
327 | if (marker.y > northWest.y && tooltip.y < northWest.y) {
328 | tooltip.y = northWest.y;
329 | isEdge = true;
330 | } else if (marker.y < southEast.y && tooltip.y > southEast.y - height) {
331 | tooltip.y = southEast.y - height;
332 | isEdge = true;
333 | }
334 |
335 | if (!isEdge) {
336 | if (marker.x < northWest.x && tooltip.x > northWest.x - width) {
337 | tooltip.x = northWest.x - width;
338 | } else if (marker.x > southEast.x && tooltip.x < southEast.x) {
339 | tooltip.x = southEast.x;
340 | }
341 |
342 | if (marker.y < northWest.y && tooltip.y > northWest.y - height) {
343 | tooltip.y = northWest.y - height;
344 | } else if (marker.y > southEast.y && tooltip.y < southEast.y) {
345 | tooltip.y = southEast.y;
346 | }
347 | }
348 |
349 | L.DomUtil.setTransform(markerList[i].getTooltip()._container, tooltip);
350 | }
351 | }
352 |
353 | TooltipLayout['initialize'] = initialize;
354 | TooltipLayout['redrawLines'] = redrawLines;
355 | TooltipLayout['resetMarker'] = resetMarker;
356 | TooltipLayout['getMarkers'] = getMarkers;
357 | TooltipLayout['setMarkers'] = setMarkers;
358 | TooltipLayout['addMarker'] = addMarker;
359 | TooltipLayout['deleteMarker'] = deleteMarker;
360 | TooltipLayout['getLine'] = getLine;
361 | TooltipLayout['removeAllPolyline'] = removeAllPolyline;
362 | TooltipLayout['removeAllMarkers'] = removeAllMarkers
363 | TooltipLayout['resetMapContent'] = resetMapContent;
364 |
365 | return TooltipLayout;
366 | }, window);
367 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaflet-tooltip-layout",
3 | "version": "0.1.2",
4 | "description": "Avoid tooltip overlapping and make users find out the relationship between each tooltip and marker easily",
5 | "private": false,
6 | "main": "./lib/index.js",
7 | "scripts": {
8 | "serve": "cross-env NODE_ENV=development parcel --out-dir ./dist/demo/ ./example/index.html",
9 | "build": "npm-run-all -s clean build:demo",
10 | "build:demo": "cross-env NODE_ENV=production parcel build ./example/index.html --out-dir ./dist/demo/ --public-url /overlapping-avoided-tooltip",
11 | "clean": "rimraf ./dist/*"
12 | },
13 | "peerDependencies": {
14 | "leaflet": "^1.3.4"
15 | },
16 | "devDependencies": {
17 | "@types/leaflet": "^1.2.11",
18 | "babel-core": "^6.26.3",
19 | "babel-eslint": "^9.0.0",
20 | "babel-plugin-transform-runtime": "^6.23.0",
21 | "babel-preset-env": "^1.7.0",
22 | "babel-preset-stage-2": "^6.24.1",
23 | "cross-env": "^5.2.0",
24 | "eslint": "^5.6.0",
25 | "npm-run-all": "^4.1.3",
26 | "parcel-bundler": "^1.9.7",
27 | "prettier": "^1.14.2",
28 | "rimraf": "^2.6.2"
29 | },
30 | "dependencies": {
31 | "leaflet": "^1.3.4"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | semi: true
4 | };
5 |
--------------------------------------------------------------------------------