Porównaj commity

...

488 Commity
v3.0.1 ... main

Autor SHA1 Wiadomość Data
Candid Dauth 859f6e7371 Use own Openptmap alternative (#244) 2024-05-31 17:36:13 +02:00
Candid Dauth 6fbda7ff8a
Merge pull request #273 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-05-27 14:03:54 +02:00
gallegonovato a2c6c985a5
Translated using Weblate (Spanish)
Currently translated at 95.4% (42 of 44 strings)

Translation: FacilMap/FacilMap server
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/es/
2024-05-27 13:09:14 +02:00
gallegonovato ea778af1a7
Translated using Weblate (Spanish)
Currently translated at 100.0% (42 of 42 strings)

Translation: FacilMap/FacilMap utils
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-utils/es/
2024-05-27 13:09:14 +02:00
hugoalh 6ad7d29c5b
Added translation using Weblate (Chinese (Traditional)) 2024-05-20 11:19:36 +00:00
Candid Dauth 3718cdbe5b Remove console.log statements from last commit 2024-05-20 13:19:22 +02:00
Candid Dauth 5df3fe99f6 Keep screen on while locate control is active 2024-05-20 12:34:45 +02:00
Candid Dauth c715bc1db0 Fix line disappearing after changing data 2024-05-20 11:35:24 +02:00
Candid Dauth 2386332134
Merge pull request #272 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-05-01 12:10:25 +02:00
Adolfo Jayme Barrientos 7a2415bc7c
Translated using Weblate (Spanish)
Currently translated at 86.4% (623 of 721 strings)

Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/es/
Translation: FacilMap/FacilMap frontend
2024-05-01 11:07:09 +02:00
Candid Dauth e51e8e4e9e
Merge pull request #271 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-30 02:16:52 +02:00
Adolfo Jayme Barrientos 51f7ad29b3
Translated using Weblate (Spanish)
Currently translated at 88.8% (168 of 189 strings)

Translated using Weblate (Spanish)

Currently translated at 75.0% (33 of 44 strings)

Translated using Weblate (Spanish)

Currently translated at 85.8% (619 of 721 strings)

Co-authored-by: Adolfo Jayme Barrientos <fitojb@ubuntu.com>
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/es/
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/es/
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/es/
Translation: FacilMap/FacilMap Leaflet
Translation: FacilMap/FacilMap frontend
Translation: FacilMap/FacilMap server
2024-04-29 22:07:22 +02:00
Roman Deev 48912107c8
Translated using Weblate (Russian)
Currently translated at 55.5% (105 of 189 strings)

Translated using Weblate (Russian)

Currently translated at 6.5% (47 of 721 strings)

Co-authored-by: Roman Deev <roman.deev06@gmail.com>
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/ru/
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/ru/
Translation: FacilMap/FacilMap Leaflet
Translation: FacilMap/FacilMap frontend
2024-04-29 22:07:19 +02:00
Candid Dauth 78af1d76a0
Merge pull request #270 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-27 10:53:19 +02:00
Adolfo Jayme Barrientos 8965fafa04
Translated using Weblate (Spanish)
Currently translated at 87.8% (166 of 189 strings)

Translation: FacilMap/FacilMap Leaflet
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/es/
2024-04-27 05:07:27 +02:00
Adolfo Jayme Barrientos 4306a58298
Translated using Weblate (Spanish)
Currently translated at 70.4% (31 of 44 strings)

Translation: FacilMap/FacilMap server
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/es/
2024-04-27 05:07:26 +02:00
Adolfo Jayme Barrientos 846041999b
Translated using Weblate (Spanish)
Currently translated at 88.0% (37 of 42 strings)

Translation: FacilMap/FacilMap utils
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-utils/es/
2024-04-27 05:07:25 +02:00
Adolfo Jayme Barrientos ae8b78e336
Translated using Weblate (Spanish)
Currently translated at 77.1% (556 of 721 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/es/
2024-04-27 05:07:24 +02:00
Candid Dauth dee17aad75
Translated using Weblate (German)
Currently translated at 100.0% (721 of 721 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/de/
2024-04-27 05:07:22 +02:00
Candid Dauth 8a12ce3984 Do not change zoom level in locate control 2024-04-26 12:35:23 +02:00
Candid Dauth 19a8e9f770 Keep locate control active when zooming 2024-04-26 12:33:39 +02:00
Candid Dauth 4152c667bf Define min/max bounds for bbox updates to avoid constant updates while locate control is active 2024-04-26 12:25:12 +02:00
Candid Dauth 1ae85c2232 Attempt to fix failing test 2024-04-26 05:41:19 +02:00
Candid Dauth e9afb54806 Attempt to fix failing tests 2024-04-26 05:40:21 +02:00
Candid Dauth 469d2ee369 Attempt to fix failing test 2024-04-26 05:32:45 +02:00
Candid Dauth 47ddfa9f21 Fix locate control styles 2024-04-26 05:12:55 +02:00
Candid Dauth 301f0447e5 Enable Spanish translations 2024-04-26 04:16:47 +02:00
Candid Dauth 0661448444
Merge pull request #269 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-26 04:06:07 +02:00
Adolfo Jayme Barrientos 229a5dc551
Translated using Weblate (Spanish)
Currently translated at 67.8% (489 of 721 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/es/
2024-04-26 04:00:30 +02:00
Adolfo Jayme Barrientos 575a7dad25
Translated using Weblate (Spanish)
Currently translated at 68.2% (129 of 189 strings)

Translation: FacilMap/FacilMap Leaflet
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/es/
2024-04-26 04:00:30 +02:00
Adolfo Jayme Barrientos f6d6dd5d5b
Translated using Weblate (Spanish)
Currently translated at 68.1% (30 of 44 strings)

Translation: FacilMap/FacilMap server
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/es/
2024-04-26 04:00:29 +02:00
Adolfo Jayme Barrientos abdb75fa5a
Translated using Weblate (Spanish)
Currently translated at 51.2% (21 of 41 strings)

Translation: FacilMap/FacilMap utils
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-utils/es/
2024-04-26 04:00:29 +02:00
Adolfo Jayme Barrientos 8838865974
Translated using Weblate (Spanish)
Currently translated at 67.7% (488 of 720 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/es/
2024-04-26 04:00:28 +02:00
Adolfo Jayme Barrientos 2632861157
Added translation using Weblate (Spanish) 2024-04-26 04:00:28 +02:00
Adolfo Jayme Barrientos 5291a6e81b
Added translation using Weblate (Spanish) 2024-04-26 04:00:27 +02:00
Adolfo Jayme Barrientos 362da42466
Added translation using Weblate (Spanish) 2024-04-26 04:00:27 +02:00
Adolfo Jayme Barrientos 98798c2019
Added translation using Weblate (Spanish) 2024-04-26 04:00:26 +02:00
Allan Nordhøy b99668d234
Translated using Weblate (Norwegian Bokmål)
Currently translated at 1.5% (3 of 189 strings)

Translation: FacilMap/FacilMap Leaflet
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/nb_NO/
2024-04-26 04:00:26 +02:00
Candid Dauth d3bb95048d Hide narrow attribution in non-narrow mode 2024-04-26 03:59:50 +02:00
Candid Dauth acf717f8e0 Add note to docs that socket v3 is still under development. 2024-04-26 03:58:09 +02:00
Candid Dauth e9f64750f5 Rearrange controls in narrow mode 2024-04-26 03:44:21 +02:00
Candid Dauth e585e5917e Keep toolbox dialogs open on entering/leaving narrow mode 2024-04-24 16:02:36 +02:00
Candid Dauth 9bcb312ac7 Prevent failure by adding empty tests 2024-04-24 13:01:54 +02:00
Candid Dauth 54e41bbe72 Change version to 5.0.0-alpha for now 2024-04-24 12:48:36 +02:00
Candid Dauth 635fa8885f Rename padId to mapId 2024-04-24 12:34:25 +02:00
Candid Dauth 27c089dd69 Upgrade leaflet-draggable-lines 2024-04-23 21:53:12 +02:00
Candid Dauth a90d98bf3b Rename padId to mapId in bookmarks 2024-04-23 21:53:09 +02:00
Candid Dauth b2bbe1c2a2 Rename "Pad" to "Map" in HistoryEntry.type 2024-04-22 16:45:30 +02:00
Candid Dauth 7a5d74dc74 Rename Pads table to Maps 2024-04-22 13:05:17 +02:00
Candid Dauth 7fa2208079 Rename PadNotFoundError to MapNotFoundError 2024-04-21 19:55:09 +02:00
Candid Dauth fe74eab306 Rename pad to map in socket methods 2024-04-21 19:50:44 +02:00
Candid Dauth 24b4c3bf96 Fix error when removing attribution control 2024-04-21 05:03:31 +02:00
Candid Dauth 3f7d64a305 Listen to viewreset event in bbox handler 2024-04-21 05:03:31 +02:00
Candid Dauth 39f3df852f Rename padData/deletePad events to mapData/deleteMap 2024-04-21 05:03:30 +02:00
Candid Dauth daa1257a60 Start renaming "pad" to "map" 2024-04-21 05:03:30 +02:00
Candid Dauth 0592d0bca7 Upgrade facilmap-client-v3 dependency 2024-04-21 05:03:30 +02:00
Candid Dauth db31dc8a2a Support history for socket v2 and add tests 2024-04-21 05:03:29 +02:00
Candid Dauth a547401e08 Import client v4 in integration tests 2024-04-21 05:03:29 +02:00
Candid Dauth 3589ec3336 Rename "symbol" to "marker", introduce v3 socket version 2024-04-21 05:03:29 +02:00
Candid Dauth e080834696 Fix applying hash query on page load (#268) 2024-04-21 04:53:30 +02:00
Candid Dauth c9d20cea88 Improve error stack traces in client 2024-04-19 23:54:13 +02:00
Candid Dauth 78d36ff2fa
Merge pull request #265 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-19 23:53:28 +02:00
Roman Deev 15f2d91d23
Translated using Weblate (Russian)
Currently translated at 38.6% (73 of 189 strings)

Translation: FacilMap/FacilMap Leaflet
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/ru/
2024-04-18 12:10:28 +02:00
Roman Deev 19eda4b29f
Translated using Weblate (Russian)
Currently translated at 21.6% (41 of 189 strings)

Translation: FacilMap/FacilMap Leaflet
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-leaflet/ru/
2024-04-18 12:10:28 +02:00
Roman Deev d9be2030b7
Translated using Weblate (Russian)
Currently translated at 3.6% (26 of 720 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/ru/
2024-04-18 12:10:28 +02:00
Candid Dauth 347edfc861 Make OsmAnd markers and lines transparent 2024-04-18 12:10:14 +02:00
Candid Dauth 79a2c08273 v4.1.1 2024-04-15 04:31:32 +02:00
Candid Dauth b863c84603 Fix type rollup 2024-04-15 04:29:59 +02:00
Candid Dauth 3fba16ee00 Attempt to fix type rollup 2024-04-15 04:01:48 +02:00
Candid Dauth 85704f17a0 Show compact checkbox fields on small screens 2024-04-12 23:35:46 +02:00
Candid Dauth f9c68cc1f5 Add parameters to hide route tab, POI tab and locate control (#251) 2024-04-12 23:04:59 +02:00
Candid Dauth 2ceab20877 Hide routing possibilities if Mapbox/ORS tokens are not set (#233) 2024-04-12 21:49:06 +02:00
Candid Dauth 381d8ee071 Fix example docker-compose file (#264) 2024-04-12 11:44:24 +02:00
Candid Dauth 1075b0730e Add button to move marker to current location 2024-04-12 00:38:27 +02:00
Candid Dauth 43de05bec1 Add "Actions" menu to marker/line info 2024-04-12 00:11:36 +02:00
Candid Dauth 6718c776fe Improve translations 2024-04-11 23:47:35 +02:00
Candid Dauth 2aad260d8c Add option to create marker at current location 2024-04-11 23:47:22 +02:00
Candid Dauth a00190e05e Fix moving marker that is hidden by cluster 2024-04-11 23:13:41 +02:00
Candid Dauth 81cd918afc Pan to location when clicking locate control again 2024-04-11 22:16:22 +02:00
Candid Dauth e1130c0a78 Disable save line button until there are 2 route points 2024-04-11 22:05:10 +02:00
Candid Dauth 8f1688ff8c Fix toast options 2024-04-11 17:24:21 +02:00
Candid Dauth 32568b1682 Clarify port publishing in example docker config (#264) 2024-04-11 10:10:42 +02:00
Candid Dauth 5698d0b8cc v4.1.0 2024-04-10 00:19:11 +02:00
Candid Dauth 5d952d3474 Also clean out directories with yarn clean 2024-04-10 00:00:30 +02:00
Candid Dauth 89071f96c5 Upgrade dependencies 2024-04-09 21:12:34 +02:00
Candid Dauth 39e02dce13 Fix broken translation strings 2024-04-09 18:54:56 +02:00
Candid Dauth 92b5bfdb83 Transmit language to Nominatim 2024-04-09 18:47:32 +02:00
Candid Dauth f2816f4269 Add last missing translations 2024-04-09 17:50:51 +02:00
Candid Dauth f53990bce5 Restructure POI categories, add a few types of "all" (#261) 2024-04-09 16:12:08 +02:00
Candid Dauth 82cfd0f9e4 Make toasts reactive 2024-04-09 15:54:56 +02:00
Candid Dauth fdc541bc92 Add more translations 2024-04-09 14:24:51 +02:00
Candid Dauth b8d7d597df Correct filter spelling mistakes (#242) 2024-04-09 12:33:19 +02:00
Candid Dauth 51a79c7a3b Add more translations 2024-04-09 12:31:07 +02:00
Candid Dauth b43d281134 Fix revalidation on rerender 2024-04-09 12:12:00 +02:00
Candid Dauth 77bdaa9a3a Add more translations 2024-04-09 12:11:53 +02:00
Candid Dauth 56ec7bf7dc Add more translations 2024-04-08 18:19:24 +02:00
Candid Dauth 56090599af Fix failing test 2024-04-08 01:13:29 +02:00
Candid Dauth 51f072524f Translate and fix heightgraph 2024-04-08 01:12:13 +02:00
Candid Dauth 1ebe449286 Add Russian to language picker 2024-04-07 22:40:17 +02:00
Candid Dauth 2949910ce3
Merge pull request #260 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-07 22:27:42 +02:00
Roman Deev 7f9e7378e5
Translated using Weblate (Russian)
Currently translated at 6.6% (24 of 359 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/ru/
2024-04-07 22:25:46 +02:00
Roman Deev 9b51fb9f6d
Added translation using Weblate (Russian) 2024-04-07 22:25:45 +02:00
Candid Dauth f2a687e971 Add more translations 2024-04-07 22:25:22 +02:00
Candid Dauth 72210d9433 Make sure that route has query can be parsed again 2024-04-07 22:24:08 +02:00
Candid Dauth 14c3ab1a4a Add more translations 2024-04-07 13:49:21 +02:00
Candid Dauth 4cb2f52928 Fix route query parsing in other languages 2024-04-07 13:13:01 +02:00
Candid Dauth b9e4fffb0f Add more translations 2024-04-07 01:39:59 +02:00
Candid Dauth 7f103d4a95 Add more translations 2024-04-06 15:32:13 +02:00
Candid Dauth c6a89d111d Add more translations 2024-04-06 13:18:56 +02:00
Candid Dauth 1d82cfa7c3 Convert utils i18n file to weblate format 2024-04-06 01:38:21 +02:00
Candid Dauth 726f49d91c Add more translations 2024-04-06 01:38:06 +02:00
Candid Dauth 1b9d8fa162
Merge pull request #259 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-06 00:12:24 +02:00
Candid Dauth 3aa5d91ce7
Translated using Weblate (English)
Currently translated at 100.0% (248 of 248 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/en/
2024-04-06 00:10:40 +02:00
Allan Nordhøy 44fcb27382
Translated using Weblate (Norwegian Bokmål)
Currently translated at 18.9% (47 of 248 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/nb_NO/
2024-04-06 00:07:13 +02:00
Allan Nordhøy 42b181e597
Translated using Weblate (English)
Currently translated at 100.0% (248 of 248 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/en/
2024-04-06 00:07:12 +02:00
Candid Dauth 667b7036bb Offer new Norwegian translation in the UI 2024-04-06 00:06:20 +02:00
Candid Dauth e5f806c21a
Merge pull request #258 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-05 23:18:07 +02:00
Allan Nordhøy ef563d357b
Added translation using Weblate (Norwegian Bokmål) 2024-04-05 18:32:18 +02:00
Allan Nordhøy c009cd4dcd
Translated using Weblate (Norwegian Bokmål)
Currently translated at 12.1% (4 of 33 strings)

Translation: FacilMap/FacilMap utils
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-utils/nb_NO/
2024-04-05 16:08:03 +02:00
Allan Nordhøy 4db6d44248
Added translation using Weblate (Norwegian Bokmål) 2024-04-05 16:08:03 +02:00
Candid Dauth cc4354877a Add note that translations may be incomplete 2024-04-05 16:07:48 +02:00
Candid Dauth 1de9134b2a Translate history-dialog 2024-04-05 13:48:26 +02:00
Candid Dauth 0e9398bc30 Change "fix" to "fest" in German 2024-04-05 13:47:53 +02:00
Candid Dauth bcdc4c1132 Translate edit-type-dialog to German 2024-04-05 13:28:14 +02:00
Candid Dauth b0426362f6 Avoid HMR error when file has errors 2024-04-05 01:12:48 +02:00
Candid Dauth d60455dd50 Internationalize edit-type-dialog 2024-04-05 01:10:54 +02:00
Candid Dauth 899ce296a9 Add heading to README 2024-04-05 00:14:34 +02:00
Candid Dauth df2dbef085 Document units query parameter for embedding 2024-04-05 00:14:02 +02:00
Candid Dauth 935502b640 Mention Weblate in the docs and README 2024-04-05 00:13:51 +02:00
Candid Dauth e84a5d5663 Adjust i18n file indentation to weblate format 2024-04-04 23:44:34 +02:00
Candid Dauth aea174eb5b
Merge pull request #256 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-04 23:40:18 +02:00
Anonymous c1a3de6015
Translated using Weblate (German)
Currently translated at 100.0% (112 of 112 strings)

Translation: FacilMap/FacilMap frontend
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-frontend/de/
2024-04-04 23:35:03 +02:00
Candid Dauth 3ceb01015c
Merge pull request #255 from weblate/weblate-facilmap-facilmap-frontend
Translations update from Hosted Weblate
2024-04-04 23:32:39 +02:00
Candid Dauth e51378496b
Translated using Weblate (German)
Currently translated at 100.0% (37 of 37 strings)

Translation: FacilMap/FacilMap server
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/de/
2024-04-04 23:30:13 +02:00
Candid Dauth 0a94392fea
Translated using Weblate (English)
Currently translated at 100.0% (37 of 37 strings)

Translation: FacilMap/FacilMap server
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-server/en/
2024-04-04 23:28:47 +02:00
Candid Dauth 8ec7e2b798
Translated using Weblate (German)
Currently translated at 100.0% (33 of 33 strings)

Translation: FacilMap/FacilMap utils
Translate-URL: https://hosted.weblate.org/projects/facilmap/facilmap-utils/de/
2024-04-04 23:15:08 +02:00
Candid Dauth c66a53386c Use JSON i18n files for compatibility with weblate 2024-04-04 22:42:25 +02:00
Candid Dauth 45e4171544 Translate toolbox 2024-04-04 21:52:05 +02:00
Candid Dauth 6ec952ca5a Mention language cookies in privacy docs 2024-04-04 21:39:31 +02:00
Candid Dauth 8384cdd316 Introduce units setting to show distances/elevations in US customary units 2024-04-04 21:10:26 +02:00
Candid Dauth 326258dbd1 Make translations in utils package reactive 2024-04-04 13:43:12 +02:00
Candid Dauth 0878fb3d4b Add user preferences dialog and persist language setting as cookie 2024-04-04 02:38:11 +02:00
Candid Dauth bbbfe83b77 Simplify client-provider toast logic and disable auto-hide for reconnecting toast 2024-04-03 23:49:33 +02:00
Candid Dauth 383864bd08 Do not override custom i18n config if server i18n module is imported later 2024-04-03 21:25:51 +02:00
Candid Dauth bfc1fb3028 Do not show "Unnamed map" in title for non-existing maps 2024-04-03 21:14:14 +02:00
Candid Dauth 99673bdbd7 Internationalize some frontend components 2024-04-03 21:06:47 +02:00
Candid Dauth 09ebec0535 gitignore temporary vite config files 2024-04-03 21:06:18 +02:00
Candid Dauth 010425b2cb Document i18n configuration 2024-04-03 21:05:53 +02:00
Candid Dauth fccb4eb2fa Internationalize server 2024-04-03 14:06:18 +02:00
Candid Dauth f4c6a2f0cd Add first i18n infrastructure and translate utils and about-dialog 2024-03-31 15:51:11 +02:00
Candid Dauth 0ca3fa9356 Add shower POI type 2024-03-30 10:46:14 +01:00
Candid Dauth 64be6aa18f Do not publish test files and tsconfig on NPM 2024-03-29 12:36:08 +01:00
Candid Dauth b842586f40 Fix leaflet and frontend build 2024-03-29 12:28:23 +01:00
Candid Dauth f3a9f6e03b v4.0.0 2024-03-29 02:05:07 +01:00
Candid Dauth 64c9a2fae6 Merge branch 'vue3' 2024-03-29 01:57:19 +01:00
Candid Dauth 9aa954c3c4 Update documentation to reflect new features 2024-03-29 01:44:57 +01:00
Candid Dauth 29ef631fc4 Remove id from update object types 2024-03-29 01:44:39 +01:00
Candid Dauth 326332eedf Enable high accuracy for locate control 2024-03-29 00:20:34 +01:00
Candid Dauth c43878c991 Fix colours for locate control 2024-03-29 00:19:17 +01:00
Candid Dauth 4941d77e98 Validate unique dropdown options 2024-03-28 12:50:45 +01:00
Candid Dauth e2b0f11c92 Add integration tests for dropdown styles 2024-03-28 12:41:17 +01:00
Candid Dauth 6044bf117c Add type style integration tests 2024-03-28 12:22:44 +01:00
Candid Dauth f56f860812 Fix flaky marker tests due to elevation 2024-03-28 12:21:53 +01:00
Candid Dauth 5a407e8395 Enforce unique field names 2024-03-27 22:20:45 +01:00
Candid Dauth 86789a9af8 Add integration tests for type order 2024-03-27 20:45:41 +01:00
Candid Dauth 3c1b59c280 Add tests for rename field 2024-03-27 15:16:58 +01:00
Candid Dauth 0e22ed24b2 Add test for Wikipedia coordinates 2024-03-27 02:11:24 +01:00
Candid Dauth 02d4ab0c42 Add some type tests 2024-03-27 01:02:57 +01:00
Candid Dauth 6fa3502f58 Allow disabling creation of default types 2024-03-26 20:33:26 +01:00
Candid Dauth 13dd7f6afc Allow specifying custom Open Elevation config 2024-03-26 13:46:38 +01:00
Candid Dauth d20356fa6e Fix pipeline script to fail on error 2024-03-26 04:02:44 +01:00
Candid Dauth b9a85a1185 Handle Nominatim errors properly 2024-03-26 03:52:15 +01:00
Candid Dauth 3b2ff2dfc9 Support hemisphere prefixes in coordinates (Park4night uses those) 2024-03-26 03:51:50 +01:00
Candid Dauth 25c79e4a73 Make views reorderable 2024-03-25 16:59:57 +01:00
Candid Dauth 07c059237f Make types reorderable 2024-03-24 21:03:50 +01:00
Candid Dauth 9cdfa4cef9 Add console logs for individual db migrations 2024-03-24 17:25:46 +01:00
Candid Dauth b1414d3428 Add integration tests for line export 2024-03-24 15:42:55 +01:00
Candid Dauth 7ef26a46ab Add florist POI type 2024-03-24 15:42:09 +01:00
Candid Dauth 028cd7216b Improve search and elevation handling
- Move search and elevation functionality to utils (and introduce
  config/fetch adapter)
- Load search results and elevation data directly in frontend where
  possible
- Set marker elevation asynchronously
- Load search results elevation asynchronously
- Fix click marker bugs
- Load click marker reverse geocode info asynchronously (fixes #25)
- Stabilize handling of 504 errors in elevation lookup
- Add migration to add elevation to existing markers
2024-03-24 06:03:21 +01:00
Candid Dauth 2cc372f486 Do not update maxmind db on every start 2024-03-24 00:23:08 +01:00
Candid Dauth 3562065aec Move pad util functions to separate file 2024-03-22 22:51:48 +01:00
Candid Dauth 73bb22b1be Make marker/line click/draw handler react to Enter/Escape 2024-03-22 22:50:43 +01:00
Candid Dauth 33642c546d Fix getLineTemplate() 2024-03-22 22:49:20 +01:00
Candid Dauth 4bfe3ff4c2 Add iframely link tags 2024-03-21 05:46:27 +01:00
Candid Dauth e8798f3cde Attempt to implement oembed API 2024-03-21 04:50:35 +01:00
Candid Dauth b7eaa44324 Fix failing marker tests 2024-03-17 04:52:39 +01:00
Candid Dauth bc4d97d998 Use Open Elevation instead of MapZen to retrieve point elevation (#250) 2024-03-16 06:54:42 +01:00
Candid Dauth db8d7d6080 Fix failing line test 2024-03-16 06:37:41 +01:00
Candid Dauth 3d3110607c Fix failing test 2024-03-16 01:02:32 +01:00
Candid Dauth 21e7aa70e3 Add more marker and line tests 2024-03-16 00:33:46 +01:00
Candid Dauth e7c377105f Add first line tests 2024-03-16 00:10:18 +01:00
Candid Dauth c97e76a6b4 Round route/line distance 2024-03-16 00:09:58 +01:00
Candid Dauth 1b5ad5838d Refactor marker/line style updating 2024-03-16 00:09:58 +01:00
Candid Dauth b42e54245d Add eslint warning for unawaited async functions 2024-03-16 00:09:58 +01:00
Candid Dauth 26753061a7 Properly store absent line extra info as null 2024-03-16 00:09:26 +01:00
Candid Dauth 5071e5b417 Fix diff on failing marker tests 2024-03-15 17:08:45 +01:00
Candid Dauth 16546e1585 Fix setting line points on line creation 2024-03-15 17:08:21 +01:00
Candid Dauth e3660ba870 Upgrade leaflet-highlightable-layers 2024-03-15 17:06:59 +01:00
Candid Dauth bc3cf1098b Add "Abandoned" and "Nudism" POI categories 2024-03-10 15:39:20 +01:00
Candid Dauth de6b56de6e OsmAnd instead of Osmand 2024-03-10 14:52:52 +01:00
Candid Dauth 8d27a1a975 Merge branch 'main' into vue3 2024-03-10 14:44:13 +01:00
Candid Dauth aaf5d132b1 Update README 2024-03-10 14:39:01 +01:00
Candid Dauth 89cd0b4f20 Add GitHub Sponsors link 2024-03-10 13:39:14 +01:00
Candid Dauth 4583476474 Show line description in Osmand, use XML namespace for line style (#246) 2024-03-10 03:54:04 +01:00
Candid Dauth d3e3c513f6 Add option to export GPX files as ZIP, add Osmand line width and colour (#246) 2024-03-10 03:18:33 +01:00
Candid Dauth 2999e4646c Apply workaround for failing test 2024-03-09 05:25:26 +01:00
Candid Dauth d7f3524d91 Fix type errors 2024-03-09 04:46:43 +01:00
Candid Dauth 082fa4379c Upgrade transitive dependencies 2024-03-09 04:38:59 +01:00
Candid Dauth b97e366030 Keep alive export dialog state 2024-03-09 04:33:46 +01:00
Candid Dauth f920856100 Add "copy to clipboard" export method 2024-03-09 04:22:29 +01:00
Candid Dauth 24c5b6d1db Fix cheerio type usage 2024-03-09 03:28:54 +01:00
Candid Dauth e3030a6b55 Use getTabularData() for table export 2024-03-09 03:28:26 +01:00
Candid Dauth 7ab5594144 Support searching for GPS coordinates in degrees 2024-03-09 01:28:26 +01:00
Candid Dauth 81b05e5bbc Add CSV export option 2024-03-07 03:30:24 +01:00
Candid Dauth 03072a29e9 Refine toolbox wording 2024-03-05 06:12:57 +01:00
Candid Dauth 97c5b7506e Fix failing test 2024-03-05 05:42:59 +01:00
Candid Dauth 964d98cd1b Replace export links with export dialog (related to #246) 2024-03-05 04:46:34 +01:00
Candid Dauth 6fdbd995ea Do not use dereferrer for internal links 2024-03-05 04:45:29 +01:00
Candid Dauth 441780beab Allow and respect action/target in ValidatedForm and ModalDialog 2024-03-05 04:45:10 +01:00
Candid Dauth b211800973 Fix lint errors 2024-03-05 01:47:54 +01:00
Candid Dauth f713aceb76 Fix async marker icon typo 2024-03-04 23:49:36 +01:00
Candid Dauth fac5648945 Introduce max width for dropdowns/popovers 2024-03-04 14:11:46 +01:00
Candid Dauth 6fda31ad35 Set max height for popovers 2024-03-04 14:11:32 +01:00
Candid Dauth fba5049c65 Streamline "copy to clipboard" experience 2024-03-04 14:10:47 +01:00
Candid Dauth a9e75cce5e Update glyphicons 2024-03-04 00:21:01 +01:00
Candid Dauth 7a63abc32f Run svgo on glyphicons (in preparation for adding more glyphicons) 2024-03-04 00:15:47 +01:00
Candid Dauth b04a58eedd Do not normalize names for GeoJSON export 2024-03-03 01:27:32 +01:00
Candid Dauth 03dbf5d33e Recalculate filters on type change 2024-03-02 18:20:16 +01:00
Candid Dauth fdd174b21a Fix type error 2024-03-02 15:35:06 +01:00
Candid Dauth b13025c777 Optimize setting line style 2024-03-02 14:13:48 +01:00
Candid Dauth 8d857d4f4e Cache filter results 2024-03-02 13:49:06 +01:00
Candid Dauth 0ef09d539d Do not show focus ring on lines 2024-03-02 13:48:22 +01:00
Candid Dauth 308dfe7dc9 Use protected keyword in some classes 2024-03-02 13:18:48 +01:00
Candid Dauth df37a4fb4a Do not recalculate polyline sections for lines completely within our outside map bbox 2024-03-02 13:12:57 +01:00
Candid Dauth a2d256e307 Prevent hot reload 2024-03-02 04:39:43 +01:00
Candid Dauth 45256247cf Avoid setting unchanged tooltip for lines 2024-03-02 03:46:53 +01:00
Candid Dauth c27a776918 Reset line points in client on zoom change 2024-03-02 03:46:33 +01:00
Candid Dauth 656f408cac Use bbox except for markers also with zoom changes 2024-03-02 02:56:36 +01:00
Candid Dauth 69ef717c44 Avoid unnecessary marker rerenders 2024-03-02 02:56:24 +01:00
Candid Dauth cbf51d148a Add eslint rules against promise misuse 2024-03-02 02:55:43 +01:00
Candid Dauth 9550c91dae Check status code when downloading files 2024-03-02 00:37:29 +01:00
Candid Dauth b7b70b6cf0 Make marker colour/shape Osmand compatible in GPX export (#246) 2024-02-29 02:53:35 +01:00
Candid Dauth bdf6e186a8 Fix updating marker icon 2024-02-27 15:50:47 +01:00
Candid Dauth 29e0942019 Add QR code button to share dialog 2024-02-27 14:57:28 +01:00
Candid Dauth f1e82ab926 Fix applying default colour when creating type 2024-02-26 20:10:58 +01:00
Candid Dauth 2c132c1ad6 Fix legend items when dropdown options fall back to default value 2024-02-26 20:10:34 +01:00
Candid Dauth 38a642450c Allow importing files that only contain types/views 2024-02-26 20:10:16 +01:00
Candid Dauth df78811dd7 Introduce stroke styles for lines (#239) 2024-02-26 17:56:42 +01:00
Candid Dauth d3f482f284 Remove unexpected rejection handler 2024-02-25 23:14:13 +01:00
Candid Dauth 21e22fa91e Clear route points on server startup 2024-02-25 23:14:04 +01:00
Candid Dauth 52e78b0f2e Migrate unexpected null values in type fields 2024-02-25 23:13:33 +01:00
Candid Dauth 8e97093eae Round marker positions in table export 2024-02-25 21:16:11 +01:00
Candid Dauth fa08ff153e Harden UI against very long words 2024-02-25 21:12:50 +01:00
Candid Dauth 94f3755857 Fix error when changing "cluster markers" setting (fixes #164) 2024-02-20 02:11:00 +01:00
Candid Dauth 6185c68d50 Fix legend padding (fixes #158) 2024-02-19 15:13:46 +01:00
Candid Dauth 04ce90720c Add option to include custom CSS (#185 #186) 2024-02-19 14:29:16 +01:00
Candid Dauth 54e5e9f00f Add HIDE_COMMERCIAL_MAP_LINKS config option (#186) 2024-02-19 13:41:23 +01:00
Candid Dauth 8365cf252d Add docker db healthchecks to documentation (#237) 2024-02-19 12:43:24 +01:00
Candid Dauth 5471cb4d7d Hide markers/lines when they are changed and don't match the filter anymore 2024-02-19 12:02:18 +01:00
Candid Dauth 396457aff2 Document filters for checkbox fields (#242) 2024-02-19 11:54:27 +01:00
Candid Dauth c7d52cb1b6 Show marker/line type if there is more than one type (#243) 2024-02-19 11:32:36 +01:00
Candid Dauth 85ccf30960 Create document socket/client API changes 2024-02-18 22:25:51 +01:00
Candid Dauth fd7330c7a6 Document core/extra symbols 2024-02-18 18:41:58 +01:00
Candid Dauth 5d129c2072 Fix service worker 2024-02-18 15:47:49 +01:00
Candid Dauth 40c4be2bba Lazily preload extra icons 2024-02-18 15:47:24 +01:00
Candid Dauth 2f901114c2 Split up icons into core icons and extra icons 2024-02-18 15:03:26 +01:00
Candid Dauth d7d93ae773 Load icons asynchronously 2024-02-18 05:56:50 +01:00
Candid Dauth 2418a5040c Fix handling of fixed type styles in legend 2024-02-18 05:47:45 +01:00
Candid Dauth b8eec23cf5 Fix type error 2024-02-18 04:30:51 +01:00
Candid Dauth d597d546ca Add integration-test-watch script 2024-02-17 15:58:37 +01:00
Candid Dauth 3efcd06e67 Get rid of jquery usage (except tablesorter) 2024-02-17 15:58:21 +01:00
Candid Dauth fe7c8f1a8d Get rid of obfuscation 2024-02-17 15:57:44 +01:00
Candid Dauth 4fc3f51b6c Fix dereferrer 2024-02-17 15:53:18 +01:00
Candid Dauth e1795d1297 Fix error when imported file contains non-string extratags (true for Osmand tracks) 2024-02-17 15:34:02 +01:00
Candid Dauth aa4b82a69f Allow specifying a custom app name, calculate base URL for opensearch.xml 2024-02-17 05:23:34 +01:00
Candid Dauth 485aa98e2b Migrate to Express 5 2024-02-17 04:53:32 +01:00
Candid Dauth f842134d38 Fix legend popover size when legend is scaled 2024-02-16 06:58:41 +01:00
Candid Dauth fb2b631cb5 Add Lima Labs as the default layer 2024-02-16 06:23:35 +01:00
Candid Dauth f4b45cc197 Add integration tests for markers 2024-02-12 00:25:07 +01:00
Candid Dauth e67aceb3ea Fix manifest location 2024-02-11 21:52:33 +01:00
Candid Dauth 1a12e6abea Add healthcheck to docker image and wait for it before running integration tests 2024-01-23 05:01:25 +01:00
Candid Dauth dc3973f0c0 Run tests in GitHub Action 2024-01-22 05:52:41 +01:00
Candid Dauth fad68137a1 Add some first integration tests 2024-01-16 18:59:19 +01:00
Candid Dauth 2e2a363802 Remove pad ID validation from database adapter, it is already part of the zod validators 2024-01-16 18:58:42 +01:00
Candid Dauth a3c7cb3f5b Mention chat in docs 2024-01-16 18:58:27 +01:00
Candid Dauth fe999df48c Move "Privacy" to main section of docs 2024-01-16 18:58:27 +01:00
Candid Dauth 7c04ea3bdd Treat connect error as server error 2024-01-16 18:58:27 +01:00
Candid Dauth 8a3ff49f70 Allow specifying socket options in client 2024-01-16 18:58:27 +01:00
Candid Dauth 401c08c190 Return MultipleEvents results in client 2024-01-16 18:58:27 +01:00
Candid Dauth 112b5ab047 Fix typing of socket 2024-01-16 18:58:27 +01:00
Candid Dauth 9494e4c379 Introduce socket versions 2024-01-16 18:58:27 +01:00
Candid Dauth b61de3e79f Fix project setup 2024-01-16 18:58:27 +01:00
Candid Dauth 6dfbb0c130 Fix test setup 2023-12-30 02:28:45 +01:00
Candid Dauth d7d6bcfed0 Fix eslint 2023-12-30 02:04:03 +01:00
Candid Dauth 6fe73c2356 Upgrade dependencies 2023-12-30 01:00:45 +01:00
Candid Dauth ff94d09df0 Clean up onCleanup functions 2023-12-26 20:00:35 +01:00
Candid Dauth b177f5b043 Fix facilmap-leaflet build 2023-12-26 19:15:36 +01:00
Candid Dauth 73a9fb4d34 Make stacked modal margin smaller on smaller viewports 2023-12-26 19:15:14 +01:00
Candid Dauth e0bb064bc8 Fix legend downscaling 2023-12-26 14:08:55 +01:00
Candid Dauth f1d23def3a Upgrade OSMI icons 2023-12-26 14:08:14 +01:00
Candid Dauth 9635fbdd05 Update download-icons script 2023-12-26 14:04:57 +01:00
Candid Dauth 324dac6401 Upgrade dependencies 2023-12-26 12:24:03 +01:00
Candid Dauth 558067bb69 Fix icons 2023-12-24 18:39:43 +01:00
Candid Dauth 4cf3e2817d Clean up type imports 2023-12-24 13:24:56 +01:00
Candid Dauth ad1901f8a0 Fix build 2023-12-24 12:36:29 +01:00
Candid Dauth 973878593c Attempt removing dev dependencies to reduce docker image size 2023-12-23 23:27:23 +01:00
Candid Dauth 8e5f518fef Build docker image as root to avoid time-consuming chown 2023-12-23 23:04:00 +01:00
Candid Dauth a012e1f0ec Attempt to use TypeScript project references 2023-12-23 21:23:51 +01:00
Candid Dauth bbd7c8ce05 Use type import in client 2023-12-08 20:05:10 +01:00
Candid Dauth fb131f4a8c Revert "Enable GitHub Action cache for docker and yarn"
This reverts commit f9c23366e3.

The build is faster without the cache
2023-12-08 20:04:00 +01:00
Candid Dauth f9c23366e3 Enable GitHub Action cache for docker and yarn 2023-12-08 20:02:55 +01:00
Candid Dauth 3a8fb8bd4c Fix manifest start_url 2023-12-07 19:23:51 +01:00
Candid Dauth cdd17d6945 UI refinements 2023-12-06 01:28:28 +01:00
Candid Dauth 92b54d3b72 Fix GeoLite2 cache dir in Dockerfile 2023-11-16 20:51:41 +01:00
Candid Dauth 409eb86f4d Complete lib/index.ts exports 2023-11-16 20:21:48 +01:00
Candid Dauth 5500153ef6 Fix custom import dialog 2023-11-16 19:43:35 +01:00
Candid Dauth b2169eb968 Fix line elevation plot 2023-11-16 19:43:10 +01:00
Candid Dauth 0afcbcfef9 Fine-tune styles 2023-11-16 19:42:50 +01:00
Candid Dauth 757823fcb5 Update self-hosting docs 2023-11-15 15:03:50 +01:00
Candid Dauth 7977b051e5 Add postgres example to docker-compose.yml 2023-11-15 15:02:38 +01:00
Candid Dauth d8c79e950c Clear "Untitled" names 2023-11-15 14:59:34 +01:00
Candid Dauth e6d612f772 Merge branch 'main' into vue3 2023-11-15 07:30:36 +01:00
Candid Dauth 9e2d259f07 Round integers to avoid error on Postgres 2023-11-15 06:48:26 +01:00
Candid Dauth f456da2e7a Document how to use postgres 2023-11-15 06:27:51 +01:00
Candid Dauth d46c49d88e Install pg to allow using postgres 2023-11-15 06:23:01 +01:00
Candid Dauth ca7ba85ea0
Merge pull request #219 from encrypt94/master
Fix facilmap not working properly with Postgres
2023-11-15 06:10:32 +01:00
Candid Dauth 0efa96f0bd Fix pad name in table export title 2023-11-15 05:20:03 +01:00
Candid Dauth 53accc1157 Update vuepress theme config 2023-11-14 22:03:50 +01:00
Candid Dauth 997509cda0 Fix handling of empty pad/marker/line names in table export 2023-11-14 22:01:39 +01:00
Candid Dauth 304b56d9c6 Fix handling of marker/line default values 2023-11-14 21:55:54 +01:00
Candid Dauth e7d17f2469 Fix modal styles when attributes change 2023-11-14 21:25:31 +01:00
Candid Dauth 8682f2754e Streamline handling of undefined values in field options 2023-11-14 21:24:40 +01:00
Candid Dauth b9ac3b31a2 Fix esrun failure due to fs/promises import (digital-loukoum/esrun#40) 2023-11-14 19:04:11 +01:00
Candid Dauth fdfc443e31 Add global check-types script 2023-11-14 19:03:58 +01:00
Candid Dauth 8c1c36cb80 Fix width picker width 2023-11-14 19:03:44 +01:00
Candid Dauth 7838195e10 Update client docs 2023-11-14 19:03:26 +01:00
Candid Dauth 996ec4516f Make data model more consistent by adding default values and removing null types 2023-11-14 18:58:21 +01:00
Candid Dauth e281c4ee26 Add Ko-fi donation link 2023-11-14 18:42:47 +01:00
Candid Dauth 11d58b3044 Build docker image from vue3 branch for beta deployment 2023-11-14 03:42:28 +01:00
Candid Dauth f4555ee628 WIP finished: Migrated to Vue 3, Bootstrap 5, ESM, Vite (only minor issues remaining) 2023-11-14 03:31:55 +01:00
Candid Dauth 44c16a2f76 WIP 2023-11-12 21:07:18 +01:00
Candid Dauth a7b7693b43 WIP 2023-11-11 08:01:40 +01:00
Candid Dauth 0d4760bf4b WIP 2023-11-09 14:52:02 +01:00
Candid Dauth 1da9f21eac WIP 2023-11-08 21:31:19 +01:00
Candid Dauth 31ca895acc WIP 2023-11-07 02:19:20 +01:00
Candid Dauth b8583ce570 WIP 2023-11-06 03:22:33 +01:00
Candid Dauth 33ebfcdee1 WIP 2023-11-03 04:02:12 +01:00
Candid Dauth bc2c14dd25 WIP 2023-11-02 15:14:05 +01:00
Candid Dauth e12dd1920e WIP 2023-11-01 19:45:16 +01:00
Candid Dauth 6dc6585b24 WIP 2023-10-30 01:14:54 +01:00
encrypt cd748dda34 I forgot to remove some logging 2023-10-25 12:48:23 +02:00
encrypt 5408c8059b Use proper getter to retrieve dialect 2023-10-25 12:48:04 +02:00
encrypt 173e81b154 Fix: makeBboxCondition uses mariadb specific functions, made it works properly when postgres is used 2023-10-25 12:48:01 +02:00
Candid Dauth 4871eea538 WIP 2023-10-25 04:09:33 +02:00
Candid Dauth 41c5b20455 WIP 2023-10-24 02:08:47 +02:00
Candid Dauth a2c7bbac1c WIP 2023-10-23 23:05:19 +02:00
Candid Dauth 869fb9741d Merge branch 'main' into vue3 2023-10-11 15:54:44 +02:00
Candid Dauth b96d64744c Update map tile URLs 2023-10-11 15:39:57 +02:00
Candid Dauth 11c22e008f WIP 2023-10-11 14:18:25 +02:00
Candid Dauth 0243da52ed WIP 2023-10-08 01:24:57 +02:00
Candid Dauth 38df836d40 WIP 2023-10-07 01:13:51 +02:00
Candid Dauth 5a796db1c3 WIP 2023-10-02 15:01:55 +02:00
Candid Dauth 5065ef4df9 WIP 2023-10-02 05:23:22 +02:00
Candid Dauth 196528af3d Merge branch 'main' into vue3 2023-09-25 13:31:34 +02:00
Candid Dauth 69386ea1b5 Fix fatal error when opening collaborative map (#248) 2023-09-24 23:45:23 +02:00
Candid Dauth 78199d9351 WIP 2023-09-24 23:00:38 +02:00
Candid Dauth 650e2c8f02 Merge branch 'main' into vue3 2023-09-23 14:25:55 +02:00
Candid Dauth a87fa429f9 Update outdated links in docs 2023-09-23 05:41:55 +02:00
Candid Dauth aaa301c59d Upgrade vuepress 2023-09-23 05:33:28 +02:00
Candid Dauth effcde8bd7 Adjust OSM object lookup to Nominatim API changes 2023-09-13 15:34:13 +02:00
Candid Dauth 99f23af11a Fix error with search results because of namedetails suddenly being null 2023-09-13 15:15:24 +02:00
Candid Dauth 06493182d4 Fix build setup 2023-09-13 15:15:05 +02:00
Candid Dauth 3ca42f34e5 WIP 2023-09-12 11:15:28 +02:00
Candid Dauth 6b01a2093c Upgrade dependencies, migrate to ESM + Vite for server/types/utils/client 2023-04-07 18:52:04 +02:00
Candid Dauth b83d3ba1dd Migrate to Yarn Berry 2023-04-04 20:56:31 +02:00
Candid Dauth 43dd5b4c05 Upgrade Leaflet plugins 2022-06-14 21:23:19 +02:00
Candid Dauth d67e8d6c53 Remove underscores in overpass keys (fixes #227) 2022-06-14 21:23:19 +02:00
Candid Dauth f55e82ae6b Main branch is now called "main" 2022-05-01 02:48:19 +02:00
Candid Dauth 5ff2d141e5 Add link to Matrix chat room 2022-05-01 02:35:57 +02:00
Candid Dauth 35dfb360a2 Add table tennis overpass preset (#218) 2022-04-21 02:56:44 +02:00
Candid Dauth d5ae145e92 Fix toolbox backdrop on narrow screens 2022-02-18 19:00:29 +01:00
Candid Dauth d50fda00c2 Allow imports of larger files 2022-02-18 18:46:43 +01:00
Candid Dauth 344ba3ec74 Add Liberapay donation option to docs 2022-02-17 03:48:08 +01:00
Candid Dauth 0bf164d917 Fix base layer error message 2021-10-09 20:27:00 +02:00
Candid Dauth 110cb986ab Fix dragging when editing line waypoints 2021-10-09 20:26:23 +02:00
Candid Dauth 964cdb40b2 Handle dependency source maps 2021-10-09 20:23:43 +02:00
Candid Dauth ccb51fa743 Upgrade leaflet-highlightable-layers 2021-08-21 12:10:05 +02:00
Candid Dauth 33068b7a32 Add GitHub Action for publishing to Docker Hub 2021-08-13 21:32:17 +02:00
Candid Dauth 177788658b Add coordinates to POI info and make them copyable 2021-08-12 00:33:44 +02:00
Candid Dauth 5a1040d6fb Implement share dialog (#165) 2021-07-26 11:54:43 +02:00
Candid Dauth a37de7d2d3 v3.4.0 2021-06-27 16:05:18 +02:00
Candid Dauth a7f319c5c7 Fix mobile view 2021-06-27 15:59:44 +02:00
Candid Dauth abf90b9192 Fix error when saving route as line 2021-06-05 01:01:41 +02:00
Candid Dauth d822adf014 Speed up database by using spatial indexes (#167) 2021-05-30 19:23:34 +02:00
Candid Dauth 1726f4dfe5 Make local storage peristent (#171) 2021-05-29 16:57:17 +02:00
Candid Dauth 68b43bec32 Add service worker to allow installing as a PWA 2021-05-29 16:43:46 +02:00
Candid Dauth 12b8e737bd Change migration code into async functions 2021-05-24 04:18:45 +02:00
Candid Dauth 2fac2bd04e v3.3.0 2021-05-23 18:51:55 +02:00
Candid Dauth 681b564251 Update dependencies 2021-05-23 18:42:55 +02:00
Candid Dauth 6c714a8455 Mention POIs in hash docs 2021-05-23 18:03:31 +02:00
Candid Dauth 227bd87599 Improvements to hash handler 2021-05-23 17:26:05 +02:00
Candid Dauth a91709a997 Fix hash handling in Firefox (#154) 2021-05-23 17:25:08 +02:00
Candid Dauth be3f8ee952 Upgrade leaflet-draggable-lines dependency 2021-05-23 05:32:06 +02:00
Candid Dauth 84d01630ab Fix typos in Overpass queries 2021-05-23 01:44:07 +02:00
Candid Dauth 358825a55e Hide loading message when FacilMap is loaded in a background tab 2021-05-21 06:08:56 +02:00
Candid Dauth f4938545ab Finalise POIs, add POIs to views 2021-05-21 04:36:19 +02:00
Candid Dauth 488cc9bfc4 Debounce hash updates 2021-05-20 23:58:28 +02:00
Candid Dauth 72b9f77bb5 Fix overlays in views 2021-05-18 05:01:20 +02:00
Candid Dauth 69f15efc9e Fix HTML escaping of error message in filter dialog 2021-05-18 05:01:20 +02:00
Candid Dauth c5efc2d391 Move POI form into search box tab 2021-05-18 05:01:15 +02:00
Candid Dauth 3195085be1 Fix POI dialog checkboxes 2021-05-18 02:52:55 +02:00
Candid Dauth fca84d778a Only allow search engines to index read-only link again 2021-05-17 19:46:56 +02:00
Candid Dauth deb2a29d1d Clear overpass message when clearing query 2021-05-17 00:14:21 +02:00
Candid Dauth b29dc115df Add selection handler for overpass layer 2021-05-16 01:20:52 +02:00
Candid Dauth 6454f11ac2 Add search field to POI dialog 2021-05-15 22:05:07 +02:00
Candid Dauth 6f787064d6 Basic implementation of POI overlay based on Overpass 2021-05-15 18:26:45 +02:00
Candid Dauth ee571b1ea7 Add link to Google Satellite 2021-05-15 10:43:46 +02:00
Candid Dauth 473c602323 Add User-Agent header to node-fetch calls 2021-05-15 01:25:12 +02:00
Candid Dauth edcfa17c06 Add ts-server script for server development 2021-05-15 01:24:09 +02:00
Candid Dauth e4dddde03d Fix line export error by copying template to dist folder 2021-05-15 01:20:43 +02:00
Candid Dauth 2aace15aa0 Fix table export error (#153) 2021-05-12 03:13:34 +02:00
Candid Dauth ce776a9b21 Fix edit type dropdown/checkbox dialog (#152) 2021-05-11 11:14:35 +02:00
Candid Dauth bb8053c0a2 Load route destination suggestions sooner to avoid delays because of rate limit 2021-05-08 21:43:45 +02:00
Candid Dauth 5aadfc04e0 Get rid of "recommended" route, it is not supported by ORS anymore 2021-05-08 21:33:59 +02:00
Candid Dauth 0f5c0ca5e2 Use node-fetch instead of request 2021-05-08 21:33:50 +02:00
Candid Dauth 12a25cb486 Respect Nominatim rate limit 2021-05-08 20:49:31 +02:00
Candid Dauth 59f6fd3763 Fix display of disconnected message 2021-05-07 08:18:58 +02:00
Candid Dauth 68a97b1a5e v3.2.0 2021-05-07 07:27:51 +02:00
Candid Dauth a9ff318aa5 Properly disable handlers when switching maps 2021-05-07 07:18:03 +02:00
Candid Dauth db6d3fcb76 Don't bundle dependencies in frontend lib 2021-05-07 07:08:52 +02:00
Candid Dauth f3900ac083 Update dependencies 2021-05-07 06:56:31 +02:00
Candid Dauth 10a3356609 Use tabs for indentation everywhere 2021-05-07 05:41:25 +02:00
Candid Dauth a047a55330 Restructure frontend to make it reusable as a component 2021-05-07 04:51:41 +02:00
Candid Dauth cde789cca0 Restructure context handling, introduce FacilMap component 2021-05-07 00:45:37 +02:00
Candid Dauth 8a198e17ff Improve styling of button groups 2021-05-06 03:41:12 +02:00
Candid Dauth 827e6c7afa Add support for bookmarks 2021-05-06 03:40:59 +02:00
Candid Dauth d22931710c Show message after copying to clipboard 2021-05-06 03:00:23 +02:00
Candid Dauth f110cbe374 Set app type to standalone 2021-05-06 02:59:50 +02:00
Candid Dauth 51e6069c7f Improve url encoding of pad IDs 2021-05-06 02:59:33 +02:00
Candid Dauth d9669e4641 Fix line drawing 2021-05-03 03:26:48 +02:00
Candid Dauth fbdea44c5b Fix "undefined" new pad title 2021-05-03 01:25:20 +02:00
Candid Dauth 4903d3039e Fix error when creating database 2021-05-03 00:38:48 +02:00
Candid Dauth fa27bd0445 Make onUpdate cascade explicit for database 2021-05-03 00:38:32 +02:00
Candid Dauth 9a239ae056 Make UTF-8 explicit for database 2021-05-03 00:38:17 +02:00
Candid Dauth 2d67b2cadf Show diff in history when changing pad data 2021-05-02 19:09:42 +02:00
Candid Dauth ec6e4a0076 Move CyclOSM higher up and update description 2021-04-30 19:32:01 +02:00
Candid Dauth 354bb2c502 Remove unused geometryutil dependency 2021-04-30 19:20:53 +02:00
Candid Dauth d20cf1f8a9
Merge pull request #149 from mapamore/master
Add CyclOSM
2021-04-30 19:19:53 +02:00
Win Olario 50eb331f6d
Add CyclOSM to the table 2021-04-27 09:08:40 +08:00
Win Olario f8077991fa
Add entry for CyclOSM layer 2021-04-27 09:03:22 +08:00
Candid Dauth 799c23e61d Normalise manifest filename 2021-04-24 16:49:32 +02:00
Candid Dauth 95e13e7491 Improve documentation front page 2021-04-24 16:18:06 +02:00
Candid Dauth 89bc715fd6 Fix links in documentation 2021-04-24 16:17:12 +02:00
Candid Dauth 0a42f6b502 Finish leaflet components docs 2021-04-18 21:03:56 +02:00
Candid Dauth 8548fdf8a6 Show locate marker above everything else 2021-04-18 13:30:15 +02:00
Candid Dauth 4c5a1230c1 Add more leaflet docs 2021-04-18 13:15:06 +02:00
Candid Dauth 6a426c0a07 Avoid errors when rerendering lines 2021-04-18 13:13:38 +02:00
Candid Dauth 3ecb6f2a6b Allow customising search result style 2021-04-18 02:08:46 +02:00
Candid Dauth df38228aa2 Prevent text selection of map 2021-04-18 00:07:00 +02:00
Candid Dauth ba62fb59e3 Use rounded app icon on Apple 2021-04-17 23:30:16 +02:00
Candid Dauth 0a780f45c3 Start documenting facilmap-leaflet 2021-04-17 19:46:01 +02:00
Candid Dauth 5c739f66e4 Provide a proper way to change the default layers 2021-04-17 19:45:20 +02:00
Candid Dauth 73fc9cd22d Use different dev-server port for each module 2021-04-17 18:44:23 +02:00
Candid Dauth 3056b0d6d1 Change layer structure, allow using layers on multiple maps 2021-04-17 18:43:49 +02:00
Candid Dauth bf6b04ddea Escape square brackets in documentation as a workaround for whxaxes/check-md#4 2021-04-17 15:27:28 +02:00
Candid Dauth e5f92ed235 Overhaul client documentation 2021-04-17 14:44:29 +02:00
Candid Dauth ffac638528 Move documentation build to dist/ 2021-04-17 14:43:38 +02:00
Candid Dauth fc0d86c046 Add documentation contribution docs 2021-04-16 03:44:20 +02:00
Candid Dauth ced3d8d379 Update deps 2021-04-16 03:13:28 +02:00
Candid Dauth dfdd1b328b Add note about permissions to standalone installation docs 2021-04-16 03:13:05 +02:00
Candid Dauth dedbbd693e Fix ORS_TOKEN typo in config.env.example 2021-04-16 03:12:53 +02:00
Candid Dauth 51fd8c4ef3 v3.1.0 2021-04-15 23:31:04 +02:00
Candid Dauth 17a3b981ab Adapt structure to add npm bin for facilmap-server 2021-04-15 23:30:25 +02:00
Candid Dauth 986d62b87f v0.0.0 2021-04-15 23:26:10 +02:00
Candid Dauth 1961a956b8 Update doc structure, get rid of "Administrators" section 2021-04-15 22:02:29 +02:00
Candid Dauth 001979afb8 Make track point handling more reliable
- Add a bbox margin of 20 pixels on each side so that line points reach
  out of view box
- Recall disconnectSegmentsOutsideViewport on each map move
- Render non-routing lines by their route points rather than track
  points (fixes #2)
2021-04-15 17:22:03 +02:00
Candid Dauth c40f3174bc Prevent selecting search results and map objects at the same time using box 2021-04-14 03:53:23 +02:00
Candid Dauth f02a99bcc5 Add spinners to a lot of actions (also fixes #93) 2021-04-14 03:45:05 +02:00
Candid Dauth 7cf01a9abe Disable click handler while drawing box 2021-04-14 02:45:05 +02:00
Candid Dauth 9c20ef6a0d Make multiple result info scrollable 2021-04-14 02:39:03 +02:00
Candid Dauth 02bfdb86a9 Allow selecting multiple objects by drawing a box 2021-04-14 02:33:54 +02:00
Candid Dauth 4a55d37f79 Set bbox when creating map 2021-04-13 15:21:18 +02:00
Candid Dauth 27717e3fc2 Don't use outdated webpack types 2021-04-13 15:21:07 +02:00
Candid Dauth 5930f5e72d Allow deleting multiple objects at once (fixes #84) 2021-04-11 16:27:11 +02:00
Candid Dauth d97c875ab2 Add link to release notes to docs 2021-04-11 16:18:14 +02:00
Candid Dauth 7d8a20c5ae Fix opening custom import dialog when multiple files are open 2021-04-11 04:14:14 +02:00
Candid Dauth 894e51fe04 Improve package files 2021-04-11 04:09:41 +02:00
Candid Dauth 2f920ccc90 Upgrade dependencies 2021-04-11 04:09:32 +02:00
1014 zmienionych plików z 56579 dodań i 33008 usunięć

Wyświetl plik

@ -1,4 +1,8 @@
node_modules
*/node_modules
*/dist
docs
*/out
*/out.node
docs
.github
Dockerfile

Wyświetl plik

@ -1,18 +1,32 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint', 'import'],
extends: ['plugin:import/typescript'],
ignorePatterns: ["**/dist/*", "**/out/*", "**/out.*/*", "**/vite.config.ts.timestamp-*.mjs"],
parserOptions: {
parser: "@typescript-eslint/parser",
project: ["*/tsconfig.json", "*/tsconfig.node.json"],
extraFileExtensions: [".vue"]
},
plugins: ["@typescript-eslint", "import"],
extends: [
"plugin:import/typescript",
"plugin:vue/vue3-essential"
],
overrides: [
{
extends: ["plugin:@typescript-eslint/disable-type-checked"],
files: ["**/*.js", "**/*.cjs"]
}
],
env: {
node: true
},
rules: {
"@typescript-eslint/explicit-module-boundary-types": ["warn", { "allowArgumentsExplicitlyTypedAsAny": true }],
"import/no-unresolved": ["error", { "ignore": [ "geojson" ], "caseSensitive": true }],
"import/no-unresolved": ["error", { "ignore": [ "geojson", "virtual:icons" ], "caseSensitive": true }],
"import/no-extraneous-dependencies": ["error"],
"@typescript-eslint/no-unused-vars": ["warn", { "args": "none" }],
"import/no-named-as-default": ["warn"],
"import/no-named-as-default-member": ["warn"],
"import/no-duplicates": ["warn"],
"import/namespace": ["error"],
"import/default": ["error"],
@ -21,6 +35,11 @@ module.exports = {
"@typescript-eslint/prefer-as-const": ["error"],
"no-restricted-globals": ["error", "$"],
"no-restricted-imports": ["error", "vue/types/umd"],
"vue/multi-word-component-names": ["off"],
"@typescript-eslint/no-base-to-string": ["error"],
"@typescript-eslint/no-misused-promises": ["error", { checksVoidReturn: false }],
"vue/return-in-computed-property": ["off"],
"@typescript-eslint/no-floating-promises": ["error"],
"constructor-super": ["error"],
"for-direction": ["error"],
@ -57,7 +76,6 @@ module.exports = {
"no-obj-calls": ["error"],
"no-octal": ["error"],
"no-prototype-builtins": ["error"],
"no-redeclare": ["error"],
"no-regex-spaces": ["error"],
"no-self-assign": ["error"],
"no-setter-return": ["error"],
@ -75,5 +93,12 @@ module.exports = {
"require-yield": ["error"],
"use-isnan": ["error"],
"valid-typeof": ["error"]
},
"settings": {
"import/resolver": {
"typescript": {
"project": ["tsconfig.json", "*/tsconfig.json"],
}
},
}
};

5
.github/FUNDING.yml vendored 100644
Wyświetl plik

@ -0,0 +1,5 @@
github: [FacilMap]
ko_fi: facilmap
liberapay: facilmap
patreon: facilmap
custom: ["https://www.paypal.com/donate?hosted_button_id=FWR59UXY6HGGS"]

Wyświetl plik

@ -0,0 +1,79 @@
name: Publish Docker image (latest)
on:
push:
branches:
- 'main'
env:
TAG: facilmap/facilmap:latest
jobs:
push_to_registry:
name: Push Docker image to Docker Hub
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Start integration test components in background
run: docker compose -f ./integration-tests/docker-compose.yml up -d --quiet-pull mysql postgres &
-
name: Build docker image
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: |
${{env.TAG}}
facilmap-ci
-
name: Start integration test components
run: |
docker compose -f ./integration-tests/docker-compose.yml up --wait
status="$?"
if (( status != 0 )); then
docker compose -f ./integration-tests/docker-compose.yml logs
exit "$status"
fi
-
name: Run integration tests
run: >-
docker run --rm -u root --add-host host.docker.internal:host-gateway facilmap-ci sh -c "
yarn workspaces focus facilmap-integration-tests &&
FACILMAP_URL=http://host.docker.internal:8080 yarn workspace facilmap-integration-tests run integration-tests &&
FACILMAP_URL=http://host.docker.internal:8081 yarn workspace facilmap-integration-tests run integration-tests
"
-
name: Push docker image
run: docker push "$TAG"
-
name: Build and push (Docs)
id: docker_build_docs
uses: docker/build-push-action@v5
with:
push: true
context: ./docs
tags: facilmap/facilmap-docs:latest

Wyświetl plik

@ -0,0 +1,45 @@
name: Publish Docker image (release)
on:
release:
types: [published]
jobs:
push_to_registry:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Docker meta
id: meta
uses: docker/metadata-action@v3
with:
images: facilmap/facilmap
tags: type=semver,pattern={{major}}.{{minor}}
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

12
.gitignore vendored
Wyświetl plik

@ -4,4 +4,14 @@ node_modules
dist
yarn-error.log
.vscode
config.env
config.env
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
out
out.*
vite.config.ts.timestamp-*.mjs

Wyświetl plik

@ -0,0 +1,11 @@
diff --git a/dist/client.d.ts b/dist/client.d.ts
index f441770687db57140a4c50b4a78a15b696f44177..3dbea34c932e79d6e3dbbf49b5dcafe2fb54cccd 100644
--- a/dist/client.d.ts
+++ b/dist/client.d.ts
@@ -1,5 +1,5 @@
import { Socket as SocketIO } from "socket.io-client";
-import { Bbox, BboxWithZoom, EventHandler, EventName, FindOnMapQuery, FindPadsQuery, FindPadsResult, FindQuery, GetPadQuery, HistoryEntry, ID, Line, LineCreate, LineExportRequest, LineTemplateRequest, LineToRouteCreate, LineUpdate, MapEvents, Marker, MarkerCreate, MarkerUpdate, MultipleEvents, ObjectWithId, PadData, PadDataCreate, PadDataUpdate, PadId, PagedResults, RequestData, RequestName, ResponseData, Route, RouteClear, RouteCreate, RouteExportRequest, RouteInfo, RouteRequest, SearchResult, TrackPoint, Type, TypeCreate, TypeUpdate, View, ViewCreate, ViewUpdate, Writable } from "facilmap-types";
+import { Bbox, BboxWithZoom, EventHandler, EventName, FindOnMapQuery, FindPadsQuery, FindPadsResult, FindQuery, GetPadQuery, HistoryEntry, ID, Line, LineCreate, LineExportRequest, LineTemplateRequest, LineToRouteCreate, LineUpdate, MapEvents, Marker, MarkerCreate, MarkerUpdate, MultipleEvents, ObjectWithId, PadData, PadDataCreate, PadDataUpdate, PadId, PagedResults, RequestData, RequestName, ResponseData, Route, RouteClear, RouteCreate, RouteExportRequest, RouteInfo, RouteRequest, SearchResult, TrackPoint, Type, TypeCreate, TypeUpdate, View, ViewCreate, ViewUpdate, Writable } from "facilmap-types-v3";
export interface ClientEvents<DataType = Record<string, string>> extends MapEvents<DataType> {
connect: [];
disconnect: [string];

Wyświetl plik

@ -0,0 +1,90 @@
diff --git a/dist/facilmap-client.d.ts b/dist/facilmap-client.d.ts
index 80360fe16876fe53bcad94efe2ef607c0be793ee..442708ef08dd539c393924c86f510785697130da 100644
--- a/dist/facilmap-client.d.ts
+++ b/dist/facilmap-client.d.ts
@@ -1,45 +1,45 @@
-import { Bbox } from 'facilmap-types';
-import { BboxWithZoom } from 'facilmap-types';
-import { CRU } from 'facilmap-types';
-import { EventHandler } from 'facilmap-types';
-import { EventName } from 'facilmap-types';
-import { FindOnMapQuery } from 'facilmap-types';
-import { FindPadsQuery } from 'facilmap-types';
-import { FindPadsResult } from 'facilmap-types';
-import { FindQuery } from 'facilmap-types';
-import { GetPadQuery } from 'facilmap-types';
-import { HistoryEntry } from 'facilmap-types';
-import { ID } from 'facilmap-types';
-import { Line } from 'facilmap-types';
-import { LineExportRequest } from 'facilmap-types';
-import { LineTemplate } from 'facilmap-types';
-import { LineTemplateRequest } from 'facilmap-types';
-import { LineToRouteCreate } from 'facilmap-types';
+import { Bbox } from 'facilmap-types-v4';
+import { BboxWithZoom } from 'facilmap-types-v4';
+import { CRU } from 'facilmap-types-v4';
+import { EventHandler } from 'facilmap-types-v4';
+import { EventName } from 'facilmap-types-v4';
+import { FindOnMapQuery } from 'facilmap-types-v4';
+import { FindPadsQuery } from 'facilmap-types-v4';
+import { FindPadsResult } from 'facilmap-types-v4';
+import { FindQuery } from 'facilmap-types-v4';
+import { GetPadQuery } from 'facilmap-types-v4';
+import { HistoryEntry } from 'facilmap-types-v4';
+import { ID } from 'facilmap-types-v4';
+import { Line } from 'facilmap-types-v4';
+import { LineExportRequest } from 'facilmap-types-v4';
+import { LineTemplate } from 'facilmap-types-v4';
+import { LineTemplateRequest } from 'facilmap-types-v4';
+import { LineToRouteCreate } from 'facilmap-types-v4';
import { ManagerOptions } from 'socket.io-client';
-import { Marker } from 'facilmap-types';
-import { MultipleEvents } from 'facilmap-types';
-import { ObjectWithId } from 'facilmap-types';
-import { PadData } from 'facilmap-types';
-import { PadId } from 'facilmap-types';
-import { PagedResults } from 'facilmap-types';
-import { Route } from 'facilmap-types';
-import { RouteClear } from 'facilmap-types';
-import { RouteCreate } from 'facilmap-types';
-import { RouteExportRequest } from 'facilmap-types';
-import { RouteInfo } from 'facilmap-types';
-import { RouteRequest } from 'facilmap-types';
-import { SearchResult } from 'facilmap-types';
-import { SetLanguageRequest } from 'facilmap-types';
-import { SocketEvents } from 'facilmap-types';
+import { Marker } from 'facilmap-types-v4';
+import { MultipleEvents } from 'facilmap-types-v4';
+import { ObjectWithId } from 'facilmap-types-v4';
+import { PadData } from 'facilmap-types-v4';
+import { PadId } from 'facilmap-types-v4';
+import { PagedResults } from 'facilmap-types-v4';
+import { Route } from 'facilmap-types-v4';
+import { RouteClear } from 'facilmap-types-v4';
+import { RouteCreate } from 'facilmap-types-v4';
+import { RouteExportRequest } from 'facilmap-types-v4';
+import { RouteInfo } from 'facilmap-types-v4';
+import { RouteRequest } from 'facilmap-types-v4';
+import { SearchResult } from 'facilmap-types-v4';
+import { SetLanguageRequest } from 'facilmap-types-v4';
+import { SocketEvents } from 'facilmap-types-v4';
import { SocketOptions } from 'socket.io-client';
-import { SocketRequest } from 'facilmap-types';
-import { SocketRequestName } from 'facilmap-types';
-import { SocketResponse } from 'facilmap-types';
-import { SocketVersion } from 'facilmap-types';
-import { TrackPoint } from 'facilmap-types';
-import { Type } from 'facilmap-types';
-import { View } from 'facilmap-types';
-import { Writable } from 'facilmap-types';
+import { SocketRequest } from 'facilmap-types-v4';
+import { SocketRequestName } from 'facilmap-types-v4';
+import { SocketResponse } from 'facilmap-types-v4';
+import { SocketVersion } from 'facilmap-types-v4';
+import { TrackPoint } from 'facilmap-types-v4';
+import { Type } from 'facilmap-types-v4';
+import { View } from 'facilmap-types-v4';
+import { Writable } from 'facilmap-types-v4';
declare class Client {
private socket;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

874
.yarn/releases/yarn-3.6.3.cjs vendored 100755

File diff suppressed because one or more lines are too long

9
.yarnrc.yml 100644
Wyświetl plik

@ -0,0 +1,9 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-3.6.3.cjs

Wyświetl plik

@ -1,26 +1,28 @@
FROM node:15.12-alpine
FROM node:21-alpine
MAINTAINER Candid Dauth <cdauth@cdauth.eu>
CMD yarn run server
CMD yarn run prod-server
EXPOSE 8080
ENV CACHE_DIR=/opt/facilmap/cache
HEALTHCHECK --start-period=60s --start-interval=3s --timeout=5s --retries=1 \
CMD wget -O/dev/null 'http://127.0.0.1:8080/socket.io/?EIO=4&transport=polling' || exit 1
RUN apk add --no-cache yarn
RUN adduser -D -h /opt/facilmap -s /bin/sh facilmap
RUN mkdir /opt/facilmap && adduser -D -H -h /opt/facilmap -s /bin/sh facilmap
WORKDIR /opt/facilmap/server
WORKDIR /opt/facilmap
COPY ./ ../
COPY ./ ./
RUN chown -R facilmap:facilmap /opt/facilmap
USER facilmap
RUN cd .. && yarn install
RUN cd .. && yarn run build
USER root
RUN chown -R root:root /opt/facilmap && chown -R facilmap:facilmap /opt/facilmap/server/cache
RUN yarn install && \
yarn check-types && \
yarn lint && \
yarn test && \
yarn run build:frontend:app && \
yarn run build:server && \
yarn workspaces focus -A --production
RUN mkdir -p "$CACHE_DIR" && chown -R facilmap:facilmap "$CACHE_DIR"
USER facilmap

Wyświetl plik

@ -1,15 +1,22 @@
[FacilMap](https://facilmap.org/) is a privacy-friendly, open-source versatile online map that combines different services based on OpenStreetMap and makes it easy to find places, plan routes and create custom maps full of markers, lines and routes. Features include:
# FacilMap
* Different map styles for roads, topography, cycling, hiking, public transportation, water navigation, ...
* Search for places and show their information (website, opening hours, ...)
* Calculate routes and adjust them by dragging. An elevation profile can be shown.
* Smartphone-friendly interface.
* Create and share custom maps with markers, lines and routes on them.
* Open GPX/KML/OSM/GeoJSON files and save them on a custom map.
* Define custom styles of markers, lines and routes and generate a legend automatically.
* Define custom form fields for the details of markers, lines and routes.
* API to embed custom maps into a website and to modify them programmatically.
* Extensive [documentation](https://docs.facilmap.org/).
[FacilMap](https://facilmap.org/) is a privacy-friendly, open-source versatile online map that combines different services based on OpenStreetMap and makes it easy to find places, plan trips and add markers, lines and routes to custom maps with live collaboration. Features include:
* Choose between different map styles for roads, topography, cycling, hiking, public transportation, water navigation, …
* Search for places and show their information (website, opening hours, …)
* Calculate routes and adjust them by dragging, optionally with an elevation profile and details about road quality
* Show POIs on the map (amenities, attractions, businesses, …)
* Smartphone-friendly interface and Progressive Web App
* Create collaborative maps, add markers, lines and routes and collaborate live through a share link
* View GPX/KML/OSM/GeoJSON files or import them to a collaborative map
* Export collaborative maps to GPX or GeoJSON to import them into OsmAnd or other apps
* Link or embed a read-only or editable version of a collaborative map on your website
* Define different types of markers/lines with custom form fields to be filled out
* Create custom views where markers/lines are shown/hidden based on their form field values
* Define custom styles of markers/lines and routes and generate a legend automatically
* Export the field values of markers/lines as HTML or CSV to import them into a spreadsheet
* Programmatically read and modify collaborative maps through a Socket.io API
* Extensive [user and developer documentation](https://docs.facilmap.org/).
Documentation
=============
@ -18,12 +25,21 @@ For more information, have a look at the [documentation](https://docs.facilmap.o
Quick links:
* [User guide](https://docs.facilmap.org/users/)
* [Embed FacilMap into a website](https://docs.facilmap.org/administrators/embed.html)
* [Run the FacilMap server](https://docs.facilmap.org/administrators/server.html)
* [Embed FacilMap into a website](https://docs.facilmap.org/developers/embed.html)
* [Run the FacilMap server](https://docs.facilmap.org/developers/server/)
* [Use the FacilMap client](https://docs.facilmap.org/developers/client/) to programmatically modify objects on a collaborative map.
* [Dev setup](https://docs.facilmap.org/developers/development/dev-setup.html)
Get help
========
* Have a look at the [documentation](https://docs.facilmap.org/)
* Ask questions [on GitHub](https://github.com/FacilMap/facilmap/discussions)
* Raise bugs and feature requests [on GitHub](https://github.com/FacilMap/facilmap/issues)
* Join the [Matrix chat room](https://matrix.to/#/#facilmap:rankenste.in)
Quick start
===========
@ -35,7 +51,17 @@ Quick start
More details can be found in the [Administrator guide](https://docs.facilmap.org/administrators/server.html#standalone) and the [Developer guide](https://docs.facilmap.org/developers/development/dev-setup.html).
Contribute
==========
* Raise bugs, feature requests and ideas [as issues](https://github.com/FacilMap/facilmap/issues)
* Help translate FacilMap into different languages on [Weblate](https://hosted.weblate.org/projects/facilmap/)
* Create a local [dev setup](https://docs.facilmap.org/developers/development/dev-setup.html) and open pull requests
* [Donate](https://docs.facilmap.org/users/contribute/)
* Spread the word!
Support FacilMap
================
[Financial support](https://docs.facilmap.org/users/contribute/) is always welcome!
I have been working on FacilMap as a hobby since 2009. While I have plenty of ideas and motivation to develop the app further, I have only limited time and resources to do so. By [financially supporting](https://docs.facilmap.org/users/contribute/) FacilMap, you allow me to spend more time implementing new features and to afford the infrastructure that is necessary to keep the app running for a growing number of users. Also, feel free to request the features that you would like to see!

Wyświetl plik

@ -1,9 +1,8 @@
{
"name": "facilmap-client",
"version": "3.0.1",
"version": "5.0.0-alpha",
"description": "A library that acts as a client to FacilMap and makes it possible to retrieve and modify objects on a collaborative map.",
"keywords": [
"webpack",
"maps",
"osm",
"facilmap"
@ -14,7 +13,9 @@
},
"license": "AGPL-3.0",
"author": "Candid Dauth <cdauth@cdauth.eu>",
"main": "./dist/client.js",
"main": "./dist/facilmap-client.mjs",
"type": "module",
"types": "./dist/facilmap-client.d.ts",
"repository": {
"type": "git",
"url": "https://github.com/FacilMap/facilmap.git"
@ -22,26 +23,24 @@
"files": [
"dist",
"src",
"README.md",
"tsconfig.json"
"README.md"
],
"scripts": {
"build": "webpack",
"watch": "webpack --watch",
"clean": "rimraf dist",
"dev-server": "webpack-dev-server --mode development"
"build": "vite build",
"clean": "rimraf dist out out.node",
"dev-server": "vite",
"check-types": "tsc -b --emitDeclarationOnly"
},
"dependencies": {
"facilmap-types": "3.0.1",
"socket.io-client": "^4.0.0"
"facilmap-types": "workspace:^",
"serialize-error": "^11.0.3",
"socket.io-client": "^4.7.5"
},
"devDependencies": {
"@types/geojson": "^7946.0.7",
"rimraf": "^3.0.2",
"ts-loader": "^8.0.12",
"typescript": "^4.1.3",
"webpack": "^5.11.0",
"webpack-bundle-analyzer": "^4.3.0",
"webpack-cli": "^4.2.0"
"@types/geojson": "^7946.0.14",
"rimraf": "^5.0.5",
"typescript": "^5.4.4",
"vite": "^5.2.8",
"vite-plugin-dts": "^3.8.2"
}
}

Wyświetl plik

@ -0,0 +1 @@
**/*.test.ts

Wyświetl plik

@ -1,16 +1,8 @@
import { io, Socket as SocketIO } from "socket.io-client";
import {
Bbox,
BboxWithZoom, EventHandler, EventName, FindOnMapQuery, FindQuery, HistoryEntry, ID, Line, LineCreate,
LineExportRequest, LineTemplateRequest, LineToRouteCreate, LineUpdate, MapEvents, Marker, MarkerCreate, MarkerUpdate, MultipleEvents, ObjectWithId,
PadData, PadDataCreate, PadDataUpdate, PadId, RequestData, RequestName, ResponseData, Route, RouteClear, RouteCreate, RouteExportRequest,
RouteInfo,
RouteRequest,
SearchResult,
TrackPoint, Type, TypeCreate, TypeUpdate, View, ViewCreate, ViewUpdate, Writable
} from "facilmap-types";
import { io, type ManagerOptions, type Socket as SocketIO, type SocketOptions } from "socket.io-client";
import { type Bbox, type BboxWithZoom, type CRU, type EventHandler, type EventName, type FindOnMapQuery, type FindMapsQuery, type FindMapsResult, type FindQuery, type GetMapQuery, type HistoryEntry, type ID, type Line, type LineExportRequest, type LineTemplateRequest, type LineToRouteCreate, type SocketEvents, type Marker, type MultipleEvents, type ObjectWithId, type MapData, type MapId, type PagedResults, type SocketRequest, type SocketRequestName, type SocketResponse, type Route, type RouteClear, type RouteCreate, type RouteExportRequest, type RouteInfo, type RouteRequest, type SearchResult, type SocketVersion, type TrackPoint, type Type, type View, type Writable, type SocketClientToServerEvents, type SocketServerToClientEvents, type LineTemplate, type LinePointsEvent, MapNotFoundError, type SetLanguageRequest } from "facilmap-types";
import { deserializeError, errorConstructors, serializeError } from "serialize-error";
export interface ClientEvents<DataType = Record<string, string>> extends MapEvents<DataType> {
export interface ClientEventsInterface extends SocketEvents<SocketVersion.V3> {
connect: [];
disconnect: [string];
connect_error: [Error];
@ -29,11 +21,13 @@ export interface ClientEvents<DataType = Record<string, string>> extends MapEven
route: [RouteWithTrackPoints];
clearRoute: [RouteClear];
emit: { [eventName in RequestName]: [eventName, RequestData<eventName, DataType>] }[RequestName];
emitResolve: { [eventName in RequestName]: [eventName, ResponseData<eventName, DataType>] }[RequestName];
emitReject: [RequestName, Error];
emit: { [eventName in SocketRequestName<SocketVersion.V3>]: [eventName, SocketRequest<SocketVersion.V3, eventName>] }[SocketRequestName<SocketVersion.V3>];
emitResolve: { [eventName in SocketRequestName<SocketVersion.V3>]: [eventName, SocketResponse<SocketVersion.V3, eventName>] }[SocketRequestName<SocketVersion.V3>];
emitReject: [SocketRequestName<SocketVersion.V3>, Error];
}
export type ClientEvents = Pick<ClientEventsInterface, keyof ClientEventsInterface>; // Workaround for https://github.com/microsoft/TypeScript/issues/15300
const MANAGER_EVENTS: Array<EventName<ClientEvents>> = ['error', 'reconnect', 'reconnect_attempt', 'reconnect_error', 'reconnect_failed'];
export interface TrackPoints {
@ -41,7 +35,7 @@ export interface TrackPoints {
length: number;
}
export interface LineWithTrackPoints<DataType = Record<string, string>> extends Line<DataType> {
export interface LineWithTrackPoints extends Line {
trackPoints: TrackPoints;
}
@ -50,77 +44,119 @@ export interface RouteWithTrackPoints extends Omit<Route, "trackPoints"> {
trackPoints: TrackPoints;
}
export default class Client<DataType = Record<string, string>> {
disconnected: boolean = true;
server!: string;
padId: string | undefined = undefined;
bbox: BboxWithZoom | undefined = undefined;
socket!: SocketIO;
padData: PadData | undefined = undefined;
readonly: boolean | undefined = undefined;
writable: Writable | undefined = undefined;
deleted: boolean = false;
markers: Record<ID, Marker<DataType>> = { };
lines: Record<ID, LineWithTrackPoints<DataType>> = { };
views: Record<ID, View> = { };
types: Record<ID, Type> = { };
history: Record<ID, HistoryEntry> = { };
route: RouteWithTrackPoints | undefined = undefined;
routes: Record<string, RouteWithTrackPoints> = { };
serverError: Error | undefined = undefined;
loading: number = 0;
interface ClientState {
disconnected: boolean;
server: string;
mapId: string | undefined;
bbox: BboxWithZoom | undefined;
readonly: boolean | undefined;
writable: Writable | undefined;
deleted: boolean;
serverError: Error | undefined;
loading: number;
listeningToHistory: boolean;
}
_listeners: {
interface ClientData {
mapData: (MapData & { writable: Writable }) | undefined;
markers: Record<ID, Marker>;
lines: Record<ID, LineWithTrackPoints>;
views: Record<ID, View>;
types: Record<ID, Type>;
history: Record<ID, HistoryEntry>;
route: RouteWithTrackPoints | undefined;
routes: Record<string, RouteWithTrackPoints>;
}
errorConstructors.set("MapNotFoundError", MapNotFoundError as any);
class Client {
private socket: SocketIO<SocketServerToClientEvents<SocketVersion.V3>, SocketClientToServerEvents<SocketVersion.V3>>;
private state: ClientState;
private data: ClientData;
private listeners: {
[E in EventName<ClientEvents>]?: Array<EventHandler<ClientEvents, E>>
} = { };
_listeningToHistory: boolean = false;
constructor(server: string, padId?: string) {
this._init(server, padId);
constructor(server: string, mapId?: string, socketOptions?: Partial<ManagerOptions & SocketOptions>) {
this.state = this._makeReactive({
disconnected: true,
server,
mapId,
bbox: undefined,
readonly: undefined,
writable: undefined,
deleted: false,
serverError: undefined,
loading: 0,
listeningToHistory: false
});
this.data = this._makeReactive({
mapData: undefined,
markers: { },
lines: { },
views: { },
types: { },
history: { },
route: undefined,
routes: { }
});
const serverUrl = typeof location != "undefined" ? new URL(this.state.server, location.href) : new URL(this.state.server);
const socket = io(`${serverUrl.origin}/v3`, {
forceNew: true,
path: serverUrl.pathname.replace(/\/$/, "") + "/socket.io",
...socketOptions
});
this.socket = socket;
for(const i of Object.keys(this._handlers) as EventName<ClientEvents>[]) {
this.on(i, this._handlers[i] as EventHandler<ClientEvents, typeof i>);
}
void Promise.resolve().then(() => {
this._simulateEvent("loadStart");
});
this.once("connect", () => {
this._simulateEvent("loadEnd");
});
}
_set<O, K extends keyof O>(object: O, key: K, value: O[K]): void {
protected _makeReactive<O extends object>(object: O): O {
return object;
}
protected _set<O, K extends keyof O>(object: O, key: K, value: O[K]): void {
object[key] = value;
}
_delete<O>(object: O, key: keyof O): void {
protected _delete<O>(object: O, key: keyof O): void {
delete object[key];
}
_decodeData(data: Record<string, string>): DataType {
protected _decodeData(data: Record<string, string>): Record<string, string> {
const result = Object.create(null);
Object.assign(result, data);
return result;
}
_encodeData(data: DataType): Record<string, string> {
return data as any;
}
_fixRequestObject<T>(requestName: RequestName, obj: T): T {
if (typeof obj != "object" || !(obj as any)?.data || !["addMarker", "editMarker", "addLine", "editLine"].includes(requestName))
return obj;
return {
...obj,
data: this._encodeData((obj as any).data)
};
}
_fixResponseObject<T>(requestName: RequestName, obj: T): T {
private _fixResponseObject<T>(requestName: SocketRequestName<SocketVersion.V3>, obj: T): T {
if (typeof obj != "object" || !(obj as any)?.data || !["getMarker", "addMarker", "editMarker", "deleteMarker", "getLineTemplate", "addLine", "editLine", "deleteLine"].includes(requestName))
return obj;
return {
...obj,
data: this._decodeData((obj as any).data)
};
}
_fixEventObject<T extends any[]>(eventName: EventName<ClientEvents>, obj: T): T {
private _fixEventObject<T extends any[]>(eventName: EventName<ClientEvents>, obj: T): T {
if (typeof obj?.[0] != "object" || !obj?.[0]?.data || !["marker", "line"].includes(eventName))
return obj;
return [
{
...obj[0],
@ -130,65 +166,42 @@ export default class Client<DataType = Record<string, string>> {
] as T;
}
_init(server: string, padId: string | undefined): void {
// Needs to be in a separate method so that we can merge this class with a scope object in the frontend.
this._set(this, 'server', server);
this._set(this, 'padId', padId);
const serverUrl = typeof location != "undefined" ? new URL(server, location.href) : new URL(server);
const socket = io(serverUrl.origin, {
forceNew: true,
path: serverUrl.pathname.replace(/\/$/, "") + "/socket.io"
});
this._set(this, 'socket', socket);
for(const i of Object.keys(this._handlers) as EventName<ClientEvents<DataType>>[]) {
this.on(i, this._handlers[i] as EventHandler<ClientEvents<DataType>, typeof i>);
on<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents, E>): void {
if(!this.listeners[eventName]) {
(MANAGER_EVENTS.includes(eventName) ? this.socket.io as any : this.socket)
.on(eventName, (...[data]: ClientEvents[E]) => { this._simulateEvent(eventName as any, data); });
}
setTimeout(() => {
this._simulateEvent("loadStart");
}, 0);
this.once("connect", () => {
this._simulateEvent("loadEnd");
});
this.listeners[eventName] = [...(this.listeners[eventName] || [] as any), fn];
}
on<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents<DataType>, E>): void {
if(!this._listeners[eventName]) {
(MANAGER_EVENTS.includes(eventName) ? this.socket.io as any : this.socket)
.on(eventName, (...[data]: ClientEvents<DataType>[E]) => { this._simulateEvent(eventName as any, data); });
}
this._set(this._listeners, eventName, [ ...(this._listeners[eventName] || [] as any), fn ]);
}
once<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents<DataType>, E>): void {
once<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents, E>): void {
const handler = ((data: any) => {
this.removeListener(eventName, handler);
(fn as any)(data);
}) as EventHandler<ClientEvents<DataType>, E>;
}) as EventHandler<ClientEvents, E>;
this.on(eventName, handler);
}
}
removeListener<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents<DataType>, E>): void {
const listeners = this._listeners[eventName] as Array<EventHandler<ClientEvents<DataType>, E>> | undefined;
removeListener<E extends EventName<ClientEvents>>(eventName: E, fn: EventHandler<ClientEvents, E>): void {
const listeners = this.listeners[eventName] as Array<EventHandler<ClientEvents, E>> | undefined;
if(listeners) {
this._set(this._listeners, eventName, listeners.filter((listener) => (listener !== fn)) as any);
this.listeners[eventName] = listeners.filter((listener) => (listener !== fn)) as any;
}
}
async _emit<R extends RequestName>(eventName: R, ...[data]: RequestData<R, DataType> extends void ? [ ] : [ RequestData<R, DataType> ]): Promise<ResponseData<R, DataType>> {
private async _emit<R extends SocketRequestName<SocketVersion.V3>>(eventName: R, ...[data]: SocketRequest<SocketVersion.V3, R> extends undefined | null ? [ ] : [ SocketRequest<SocketVersion.V3, R> ]): Promise<SocketResponse<SocketVersion.V3, R>> {
try {
this._simulateEvent("loadStart");
this._simulateEvent("emit", eventName as any, data as any);
const outerError = new Error();
return await new Promise((resolve, reject) => {
this.socket.emit(eventName, this._fixRequestObject(eventName, data), (err: Error, data: ResponseData<R, DataType>) => {
this.socket.emit(eventName as any, data, (err: any, data: SocketResponse<SocketVersion.V3, R>) => {
if(err) {
reject(err);
const cause = deserializeError(err);
reject(deserializeError({ ...serializeError(outerError), message: cause.message, cause }));
this._simulateEvent("emitReject", eventName as any, err);
} else {
const fixedData = this._fixResponseObject(eventName, data);
@ -202,49 +215,49 @@ export default class Client<DataType = Record<string, string>> {
}
}
_handlers: {
[E in EventName<ClientEvents>]?: EventHandler<ClientEvents<DataType>, E>
private _handlers: {
[E in EventName<ClientEvents>]?: EventHandler<ClientEvents, E>
} = {
padData: (data) => {
this._set(this, 'padData', data);
mapData: (data) => {
this._set(this.data, 'mapData', data);
if(data.writable != null) {
this._set(this, 'readonly', data.writable == 0);
this._set(this, 'writable', data.writable);
this._set(this.state, 'readonly', data.writable == 0);
this._set(this.state, 'writable', data.writable);
}
const id = this.writable == 2 ? data.adminId : this.writable == 1 ? data.writeId : data.id;
const id = this.state.writable == 2 ? data.adminId : this.state.writable == 1 ? data.writeId : data.id;
if(id != null)
this._set(this, 'padId', id);
this._set(this.state, 'mapId', id);
},
deletePad: () => {
this._set(this, 'readonly', true);
this._set(this, 'writable', 0);
this._set(this, 'deleted', true);
deleteMap: () => {
this._set(this.state, 'readonly', true);
this._set(this.state, 'writable', 0);
this._set(this.state, 'deleted', true);
},
marker: (data) => {
this._set(this.markers, data.id, data);
this._set(this.data.markers, data.id, data);
},
deleteMarker: (data) => {
this._delete(this.markers, data.id);
this._delete(this.data.markers, data.id);
},
line: (data) => {
this._set(this.lines, data.id, {
this._set(this.data.lines, data.id, {
...data,
trackPoints: this.lines[data.id]?.trackPoints || { length: 0 }
trackPoints: this.data.lines[data.id]?.trackPoints || { length: 0 }
});
},
deleteLine: (data) => {
this._delete(this.lines, data.id);
this._delete(this.data.lines, data.id);
},
linePoints: (data) => {
const line = this.lines[data.id];
const line = this.data.lines[data.id];
if(line == null)
return console.error("Received line points for non-existing line "+data.id+".");
@ -252,16 +265,16 @@ export default class Client<DataType = Record<string, string>> {
},
routePoints: (data) => {
if(!this.route) {
if(!this.data.route) {
console.error("Received route points for non-existing route.");
return;
}
this._set(this.route, 'trackPoints', this._mergeTrackPoints(this.route.trackPoints, data));
this._set(this.data.route, 'trackPoints', this._mergeTrackPoints(this.data.route.trackPoints, data));
},
routePointsWithId: (data) => {
const route = this.routes[data.routeId];
const route = this.data.routes[data.routeId];
if(!route) {
console.error("Received route points for non-existing route.");
return;
@ -271,168 +284,213 @@ export default class Client<DataType = Record<string, string>> {
},
view: (data) => {
this._set(this.views, data.id, data);
this._set(this.data.views, data.id, data);
},
deleteView: (data) => {
this._delete(this.views, data.id);
if (this.padData) {
if(this.padData.defaultViewId == data.id)
this._set(this.padData, 'defaultViewId', null);
this._delete(this.data.views, data.id);
if (this.data.mapData) {
if(this.data.mapData.defaultViewId == data.id)
this._set(this.data.mapData, 'defaultViewId', null);
}
},
type: (data) => {
this._set(this.types, data.id, data);
this._set(this.data.types, data.id, data);
},
deleteType: (data) => {
this._delete(this.types, data.id);
this._delete(this.data.types, data.id);
},
disconnect: () => {
this._set(this, 'disconnected', true);
this._set(this, 'markers', { });
this._set(this, 'lines', { });
this._set(this, 'views', { });
this._set(this, 'history', { });
this._set(this.state, 'disconnected', true);
this._set(this.data, 'markers', { });
this._set(this.data, 'lines', { });
this._set(this.data, 'views', { });
this._set(this.data, 'history', { });
},
connect: () => {
this._set(this, 'disconnected', false); // Otherwise it gets set when padData arrives
this._set(this.state, 'disconnected', false); // Otherwise it gets set when mapData arrives
if(this.padId)
this._setPadId(this.padId);
if(this.state.mapId)
this._setMapId(this.state.mapId).catch(() => undefined);
if(this.bbox)
this.updateBbox(this.bbox);
// TODO: Handle errors
if(this._listeningToHistory) // TODO: Execute after setPadId() returns
if(this.state.bbox)
this.updateBbox(this.state.bbox).catch((err) => { console.error("Error updating bbox.", err); });
if(this.state.listeningToHistory) // TODO: Execute after setMapId() returns
this.listenToHistory().catch(function(err) { console.error("Error listening to history", err); });
if(this.route)
this.setRoute(this.route);
for (const route of Object.values(this.routes))
this.setRoute(route);
if(this.data.route)
this.setRoute(this.data.route).catch((err) => { console.error("Error setting route.", err); });
for (const route of Object.values(this.data.routes))
this.setRoute(route).catch((err) => { console.error("Error setting route.", err); });
},
connect_error: (err) => {
if (!this.socket.active) { // Fatal error, client will not try to reconnect anymore
this._set(this.state, 'serverError', err);
this._simulateEvent("serverError", err);
}
},
history: (data) => {
this._set(this.history, data.id, data);
this._set(this.data.history, data.id, data);
// TODO: Limit to 50 entries
},
loadStart: () => {
this._set(this, 'loading', this.loading + 1);
this._set(this.state, 'loading', this.state.loading + 1);
},
loadEnd: () => {
this._set(this, 'loading', this.loading - 1);
this._set(this.state, 'loading', this.state.loading - 1);
}
};
setPadId(padId: PadId): Promise<void> {
if(this.padId != null)
throw new Error("Pad ID already set.");
async setMapId(mapId: MapId): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
if(this.state.mapId != null)
throw new Error("Map ID already set.");
return this._setPadId(padId);
return await this._setMapId(mapId);
}
updateBbox(bbox: BboxWithZoom): Promise<void> {
this._set(this, 'bbox', bbox);
return this._emit("updateBbox", bbox).then((obj) => {
this._receiveMultiple(obj);
});
async setLanguage(language: SetLanguageRequest): Promise<void> {
await this._emit("setLanguage", language);
}
createPad(data: PadDataCreate): Promise<void> {
return this._emit("createPad", data).then((obj) => {
this._set(this, 'readonly', false);
this._set(this, 'writable', 2);
async updateBbox(bbox: BboxWithZoom): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
const isZoomChange = this.bbox && bbox.zoom !== this.bbox.zoom;
this._receiveMultiple(obj);
});
this._set(this.state, 'bbox', bbox);
const obj = await this._emit("updateBbox", bbox);
if (isZoomChange) {
// Reset line points on zoom change to prevent us from accumulating too many unneeded line points.
// On zoom change the line points are sent from the server without applying the "except" rule for the last bbox,
// so we can be sure that we will receive all line points that are relevant for the new bbox.
obj.linePoints = obj.linePoints || [];
const linePointEventsById = new Map(obj.linePoints.map((e): [number, LinePointsEvent] => [e.id, e])); // Cannot use "as const" due to https://github.com/microsoft/rushstack/issues/3875
for (const lineIdStr of Object.keys(this.data.lines)) {
const lineId = Number(lineIdStr);
const e = linePointEventsById.get(lineId);
if (e) {
e.reset = true;
} else {
obj.linePoints.push({
id: lineId,
trackPoints: [],
reset: true
});
}
}
}
this._receiveMultiple(obj);
return obj;
}
editPad(data: PadDataUpdate): Promise<PadData> {
return this._emit("editPad", data);
async getMap(data: GetMapQuery): Promise<FindMapsResult | null> {
return await this._emit("getMap", data);
}
deletePad(): Promise<void> {
return this._emit("deletePad");
async findMaps(data: FindMapsQuery): Promise<PagedResults<FindMapsResult>> {
return await this._emit("findMaps", data);
}
listenToHistory(): Promise<void> {
return this._emit("listenToHistory").then((obj) => {
this._set(this, '_listeningToHistory', true);
this._receiveMultiple(obj);
});
async createMap(data: MapData<CRU.CREATE>): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
const obj = await this._emit("createMap", data);
this._set(this.state, 'serverError', undefined);
this._set(this.state, 'readonly', false);
this._set(this.state, 'writable', 2);
this._receiveMultiple(obj);
return obj;
}
stopListeningToHistory(): Promise<void> {
this._set(this, '_listeningToHistory', false);
return this._emit("stopListeningToHistory");
async editMap(data: MapData<CRU.UPDATE>): Promise<MapData> {
return await this._emit("editMap", data);
}
revertHistoryEntry(data: ObjectWithId): Promise<void> {
return this._emit("revertHistoryEntry", data).then((obj) => {
this._set(this, 'history', { });
this._receiveMultiple(obj);
});
async deleteMap(): Promise<void> {
await this._emit("deleteMap");
}
async getMarker(data: ObjectWithId): Promise<Marker<DataType>> {
async listenToHistory(): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
const obj = await this._emit("listenToHistory");
this._set(this.state, 'listeningToHistory', true);
this._receiveMultiple(obj);
return obj;
}
async stopListeningToHistory(): Promise<void> {
this._set(this.state, 'listeningToHistory', false);
await this._emit("stopListeningToHistory");
}
async revertHistoryEntry(data: ObjectWithId): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
const obj = await this._emit("revertHistoryEntry", data);
this._set(this.data, 'history', {});
this._receiveMultiple(obj);
return obj;
}
async getMarker(data: ObjectWithId): Promise<Marker> {
const marker = await this._emit("getMarker", data);
this._set(this.markers, marker.id, marker);
this._set(this.data.markers, marker.id, marker);
return marker;
}
async addMarker(data: MarkerCreate<DataType>): Promise<Marker<DataType>> {
async addMarker(data: Marker<CRU.CREATE>): Promise<Marker> {
const marker = await this._emit("addMarker", data);
// If the marker is out of view, we will not recieve it in an event. Add it here manually to make sure that we have it.
this._set(this.markers, marker.id, marker);
this._set(this.data.markers, marker.id, marker);
return marker;
}
editMarker(data: ObjectWithId & MarkerUpdate<DataType>): Promise<Marker<DataType>> {
return this._emit("editMarker", data);
async editMarker(data: Marker<CRU.UPDATE> & { id: ID }): Promise<Marker> {
return await this._emit("editMarker", data);
}
deleteMarker(data: ObjectWithId): Promise<Marker<DataType>> {
return this._emit("deleteMarker", data);
async deleteMarker(data: ObjectWithId): Promise<Marker> {
return await this._emit("deleteMarker", data);
}
getLineTemplate(data: LineTemplateRequest): Promise<Line<DataType>> {
return this._emit("getLineTemplate", data);
async getLineTemplate(data: LineTemplateRequest): Promise<LineTemplate> {
return await this._emit("getLineTemplate", data);
}
addLine(data: LineCreate<DataType>): Promise<Line<DataType>> {
return this._emit("addLine", data);
async addLine(data: Line<CRU.CREATE>): Promise<Line> {
return await this._emit("addLine", data);
}
editLine(data: ObjectWithId & LineUpdate<DataType>): Promise<Line<DataType>> {
return this._emit("editLine", data);
async editLine(data: Line<CRU.UPDATE> & { id: ID }): Promise<Line> {
return await this._emit("editLine", data);
}
deleteLine(data: ObjectWithId): Promise<Line<DataType>> {
return this._emit("deleteLine", data);
async deleteLine(data: ObjectWithId): Promise<Line> {
return await this._emit("deleteLine", data);
}
exportLine(data: LineExportRequest): Promise<string> {
return this._emit("exportLine", data);
async exportLine(data: LineExportRequest): Promise<string> {
return await this._emit("exportLine", data);
}
find(data: FindQuery & { loadUrls?: false }): Promise<SearchResult[]>;
find(data: FindQuery & { loadUrls: true }): Promise<string | SearchResult[]>; // eslint-disable-line no-dupe-class-members
find(data: FindQuery): Promise<string | SearchResult[]> { // eslint-disable-line no-dupe-class-members
return this._emit("find", data);
async find(data: FindQuery & { loadUrls?: false }): Promise<SearchResult[]>;
async find(data: FindQuery & { loadUrls: true }): Promise<string | SearchResult[]>; // eslint-disable-line no-dupe-class-members
async find(data: FindQuery): Promise<string | SearchResult[]> { // eslint-disable-line no-dupe-class-members
return await this._emit("find", data);
}
findOnMap(data: FindOnMapQuery): Promise<ResponseData<'findOnMap'>> {
return this._emit("findOnMap", data);
async findOnMap(data: FindOnMapQuery): Promise<SocketResponse<SocketVersion.V3, 'findOnMap'>> {
return await this._emit("findOnMap", data);
}
getRoute(data: RouteRequest): Promise<RouteInfo> {
return this._emit("getRoute", data);
async getRoute(data: RouteRequest): Promise<RouteInfo> {
return await this._emit("getRoute", data);
}
async setRoute(data: RouteCreate): Promise<RouteWithTrackPoints | undefined> {
@ -447,9 +505,9 @@ export default class Client<DataType = Record<string, string>> {
};
if (data.routeId)
this._set(this.routes, data.routeId, result);
this._set(this.data.routes, data.routeId, result);
else
this._set(this, "route", result);
this._set(this.data, "route", result);
this._simulateEvent("route", result);
return result;
@ -457,13 +515,13 @@ export default class Client<DataType = Record<string, string>> {
async clearRoute(data?: RouteClear): Promise<void> {
if (data?.routeId) {
this._delete(this.routes, data.routeId);
this._delete(this.data.routes, data.routeId);
this._simulateEvent("clearRoute", { routeId: data.routeId });
return this._emit("clearRoute", data);
} else if (this.route) {
this._set(this, 'route', undefined);
await this._emit("clearRoute", data);
} else if (this.data.route) {
this._set(this.data, 'route', undefined);
this._simulateEvent("clearRoute", { routeId: undefined });
return this._emit("clearRoute", data);
await this._emit("clearRoute", data);
}
}
@ -479,44 +537,44 @@ export default class Client<DataType = Record<string, string>> {
};
if (data.routeId)
this._set(this.routes, data.routeId, result);
this._set(this.data.routes, data.routeId, result);
else
this._set(this, "route", result);
this._set(this.data, "route", result);
this._simulateEvent("route", result);
return result;
}
exportRoute(data: RouteExportRequest): Promise<string> {
return this._emit("exportRoute", data);
async exportRoute(data: RouteExportRequest): Promise<string> {
return await this._emit("exportRoute", data);
}
addType(data: TypeCreate): Promise<Type> {
return this._emit("addType", data);
async addType(data: Type<CRU.CREATE>): Promise<Type> {
return await this._emit("addType", data);
}
editType(data: ObjectWithId & TypeUpdate): Promise<Type> {
return this._emit("editType", data);
async editType(data: Type<CRU.UPDATE> & { id: ID }): Promise<Type> {
return await this._emit("editType", data);
}
deleteType(data: ObjectWithId): Promise<Type> {
return this._emit("deleteType", data);
async deleteType(data: ObjectWithId): Promise<Type> {
return await this._emit("deleteType", data);
}
addView(data: ViewCreate): Promise<View> {
return this._emit("addView", data);
async addView(data: View<CRU.CREATE>): Promise<View> {
return await this._emit("addView", data);
}
editView(data: ObjectWithId & ViewUpdate): Promise<View> {
return this._emit("editView", data);
async editView(data: View<CRU.UPDATE> & { id: ID }): Promise<View> {
return await this._emit("editView", data);
}
deleteView(data: ObjectWithId): Promise<View> {
return this._emit("deleteView", data);
async deleteView(data: ObjectWithId): Promise<View> {
return await this._emit("deleteView", data);
}
geoip(): Promise<Bbox | null> {
return this._emit("geoip");
async geoip(): Promise<Bbox | null> {
return await this._emit("geoip");
}
disconnect(): void {
@ -524,38 +582,39 @@ export default class Client<DataType = Record<string, string>> {
this.socket.disconnect();
}
async _setPadId(padId: string): Promise<void> {
this._set(this, 'serverError', undefined);
this._set(this, 'padId', padId);
private async _setMapId(mapId: string): Promise<MultipleEvents<SocketEvents<SocketVersion.V3>>> {
this._set(this.state, 'serverError', undefined);
this._set(this.state, 'mapId', mapId);
try {
const obj = await this._emit("setPadId", padId);
const obj = await this._emit("setMapId", mapId);
this._receiveMultiple(obj);
} catch(err) {
this._set(this, 'serverError', err);
return obj;
} catch(err: any) {
this._set(this.state, 'serverError', err);
this._simulateEvent("serverError", err);
throw err;
}
}
_receiveMultiple(obj?: MultipleEvents<ClientEvents<DataType>>): void {
private _receiveMultiple(obj?: MultipleEvents<ClientEvents>): void {
if (obj) {
for(const i of Object.keys(obj) as EventName<ClientEvents>[])
(obj[i] as Array<ClientEvents<DataType>[typeof i][0]>).forEach((it) => { this._simulateEvent(i, it as any); });
(obj[i] as Array<ClientEvents[typeof i][0]>).forEach((it) => { this._simulateEvent(i, it as any); });
}
}
_simulateEvent<E extends EventName<ClientEvents>>(eventName: E, ...data: ClientEvents<DataType>[E]): void {
private _simulateEvent<E extends EventName<ClientEvents>>(eventName: E, ...data: ClientEvents[E]): void {
const fixedData = this._fixEventObject(eventName, data);
const listeners = this._listeners[eventName] as Array<EventHandler<ClientEvents<DataType>, E>> | undefined;
const listeners = this.listeners[eventName] as Array<EventHandler<ClientEvents, E>> | undefined;
if(listeners) {
listeners.forEach(function(listener: EventHandler<ClientEvents<DataType>, E>) {
listeners.forEach(function(listener: EventHandler<ClientEvents, E>) {
listener(...fixedData);
});
}
}
_mergeTrackPoints(existingTrackPoints: Record<number, TrackPoint> | null, newTrackPoints: TrackPoint[]): TrackPoints {
private _mergeTrackPoints(existingTrackPoints: Record<number, TrackPoint> | null, newTrackPoints: TrackPoint[]): TrackPoints {
const ret = { ...(existingTrackPoints || { }) } as TrackPoints;
for(let i=0; i<newTrackPoints.length; i++) {
@ -570,4 +629,78 @@ export default class Client<DataType = Record<string, string>> {
return ret;
}
get disconnected(): boolean {
return this.state.disconnected;
}
get server(): string {
return this.state.server;
}
get mapId(): string | undefined {
return this.state.mapId;
}
get bbox(): BboxWithZoom | undefined {
return this.state.bbox;
}
get readonly(): boolean | undefined {
return this.state.readonly;
}
get writable(): Writable | undefined {
return this.state.writable;
}
get deleted(): boolean {
return this.state.deleted;
}
get serverError(): Error | undefined {
return this.state.serverError;
}
get loading(): number {
return this.state.loading;
}
get listeningToHistory(): boolean {
return this.state.listeningToHistory;
}
get mapData(): (MapData & { writable: Writable }) | undefined {
return this.data.mapData;
}
get markers(): Record<ID, Marker> {
return this.data.markers;
}
get lines(): Record<ID, LineWithTrackPoints> {
return this.data.lines;
}
get views(): Record<ID, View> {
return this.data.views;
}
get types(): Record<ID, Type> {
return this.data.types;
}
get history(): Record<ID, HistoryEntry> {
return this.data.history;
}
get route(): RouteWithTrackPoints | undefined {
return this.data.route;
}
get routes(): Record<string, RouteWithTrackPoints> {
return this.data.routes;
}
}
export default Client;

Wyświetl plik

@ -0,0 +1,4 @@
{
"extends": "../tsconfig.base.json",
"include": ["src/**/*"]
}

Wyświetl plik

@ -1,14 +1,15 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"target": "es3",
"esModuleInterop": true,
"strict": true,
"sourceMap": true,
"declaration": true,
"outDir": "dist",
"moduleResolution": "node",
"noErrorTruncation": true,
"skipLibCheck": true
"outDir": "./out",
"composite": true,
"paths": {
"facilmap-types": ["../types/src/index.ts"]
}
},
"references": [
{ "path": "./tsconfig.node.json" },
{ "path": "../types/tsconfig.json" }
],
"include": ["src/**/*"]
}

Wyświetl plik

@ -0,0 +1,10 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"outDir": "out.node"
},
"include": [
"vite.config.ts"
]
}

Wyświetl plik

@ -0,0 +1,20 @@
import { defineConfig } from "vite";
import dtsPlugin from "vite-plugin-dts";
export default defineConfig({
plugins: [
dtsPlugin({ rollupTypes: true, tsconfigPath: "./tsconfig.build.json" }),
],
build: {
sourcemap: true,
minify: false,
lib: {
entry: './src/client.ts',
fileName: () => 'facilmap-client.mjs',
formats: ['es']
},
rollupOptions: {
external: (id) => !id.startsWith("./") && !id.startsWith("../") && /* resolved internal modules */ !id.startsWith("/")
}
}
});

Wyświetl plik

@ -1,51 +0,0 @@
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
module.exports = (env, argv) => {
const isDev = argv.mode == "development";
return {
entry: `${__dirname}/src/client.ts`,
output: {
filename: "client.js",
path: __dirname + "/dist/",
library: ["FacilMap", "Client"],
libraryTarget: "umd",
libraryExport: "default"
},
resolve: {
extensions: [ ".js", ".ts" ]
},
mode: isDev ? "development" : "production",
devtool: isDev ? "cheap-eval-source-map" : "source-map",
module: {
rules: [
{
resource: { and: [ /\.ts/, [
__dirname + "/src/"
] ] },
loader: 'ts-loader'
},
{
test: /\.css$/,
use: [ 'style-loader', 'css-loader' ]
}
]
},
externals: {
"socket.io-client": {
commonjs: 'socket.io-client',
commonjs2: 'socket.io-client',
amd: 'socket.io-client',
root: 'io'
}
},
plugins: [
//new BundleAnalyzerPlugin()
],
devServer: {
publicPath: "/dist",
disableHostCheck: true,
injectClient: false // https://github.com/webpack/webpack-dev-server/issues/2484
}
};
};

Wyświetl plik

@ -1,8 +1,9 @@
# Copy this file to config.env when running FacilMap standalone.
# Find all the available configuration options in the documentation: https://docs.facilmap.org/developers/server/config.html
# HTTP requests made by the backend will send this User-Agent header. Please adapt to your URL and e-mail address.
#USER_AGENT=FacilMap (https://facilmap.org/, cdauth@cdauth.eu)
# On which IP the HTTP server will listen. Leave empty to listen to all IPs.
#HOST=
# On which port the HTTP server will listen.
#PORT=8080
@ -17,18 +18,19 @@
# OpenRouteService is used for calculating routes with advanced settings
# Get a token on https://go.openrouteservice.org/
#OSR_TOKEN=
#ORS_TOKEN=
# MapBox is used for calculating routes with simple settings
# Get an API key on https://www.mapbox.com/signup/
#MAPBOX_TOKEN=
# MapZen is used for getting elevation information
# Get an API key on https://mapzen.com/developers/sign_up
#MAPZEN_TOKEN=
# Maxmind configuration. If specified, the maxmind GeoLite2 database will be downloaded
# for Geo IP lookup (to show the initial map state) and kept in memory.
# Sign up here: https://www.maxmind.com/en/geolite2/signup
#MAXMIND_USER_ID=
#MAXMIND_LICENSE_KEY=
#MAXMIND_LICENSE_KEY=
# Lima Labs provides nice double resolution layers.
# If this is defined, they are used as the default layer instead of Mapnik.
# Get an API key here: https://maps.lima-labs.com/
#LIMA_LABS_TOKEN=

Wyświetl plik

@ -1,3 +1,6 @@
# This docker-compose file is for internal testing only.
# Please refer to the documentation on https://docs.facilmap.org/developers/server/docker.html for how to start FacilMap with docker.
version: "2"
services:
facilmap:
@ -6,14 +9,20 @@ services:
ports:
- "127.0.0.1:8080:8080"
environment:
DB_TYPE: mariadb
DB_HOST: mariadb
DB_TYPE: mysql
DB_HOST: mysql
# DB_TYPE: postgres
# DB_HOST: postgres
DB_NAME: facilmap
DB_USER: facilmap
DB_PASSWORD: facilmap
links:
- mysql
depends_on:
mysql:
condition: service_healthy
restart: on-failure
mysql:
image: mysql:5.7
environment:
@ -21,9 +30,26 @@ services:
MYSQL_USER: facilmap
MYSQL_PASSWORD: facilmap
MYSQL_RANDOM_ROOT_PASSWORD: "true"
phpmyadmin:
image: phpmyadmin
links:
- mysql:db
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: mysqladmin ping -h 127.0.0.1 -u $$MYSQL_USER --password=$$MYSQL_PASSWORD
ports:
- 127.0.0.1:8090:80
- "127.0.0.1:40830:3306"
# postgres:
# image: postgis/postgis:16-3.4
# environment:
# POSTGRES_USER: facilmap
# POSTGRES_PASSWORD: facilmap
# POSTGRES_DB: facilmap
# healthcheck:
# test: pg_isready -d $$POSTGRES_DB
# ports:
# - "127.0.0.1:40831:5432"
# phpmyadmin:
# image: phpmyadmin
# links:
# - mysql:db
# ports:
# - 127.0.0.1:8090:80

9
docs/.gitignore vendored 100644
Wyświetl plik

@ -0,0 +1,9 @@
src/.vuepress/.cache
src/.vuepress/.temp
src/.vuepress/*.js.*.mjs
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

7
docs/.yarnrc.yml 100644
Wyświetl plik

@ -0,0 +1,7 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
yarnPath: .yarn/releases/yarn-3.6.3.cjs

Wyświetl plik

@ -3,6 +3,8 @@ MAINTAINER Candid Dauth <cdauth@cdauth.eu>
RUN apk add --no-cache nodejs yarn
RUN echo "ErrorDocument 404 /404.html" >> /usr/local/apache2/conf/httpd.conf
COPY ./ /tmp/facilmap-docs
RUN cd /tmp/facilmap-docs && yarn install && yarn build && mv src/.vuepress/dist/* /usr/local/apache2/htdocs/ && rm -rf /tmp/facilmap-docs
RUN cd /tmp/facilmap-docs && yarn install && yarn build && mv dist/* /usr/local/apache2/htdocs/ && rm -rf /tmp/facilmap-docs

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "facilmap-docs",
"version": "3.0.1",
"version": "5.0.0-alpha",
"description": "Documentation for FacilMap.",
"author": "Candid Dauth <cdauth@cdauth.eu>",
"repository": {
@ -8,19 +8,22 @@
"url": "https://github.com/FacilMap/facilmap.git"
},
"license": "AGPL-3.0",
"type": "module",
"scripts": {
"dev-server": "vuepress dev src",
"build": "vuepress build src",
"deploy": "./deploy.sh",
"clean": "rimraf dist",
"check": "vuepress check-md src"
},
"private": true,
"devDependencies": {
"markdown-it-footnote": "^3.0.2",
"vuepress": "^1.5.3",
"vuepress-plugin-check-md": "^0.0.2"
"@vuepress/plugin-search": "next",
"rimraf": "^5.0.1",
"vuepress": "next",
"vuepress-plugin-check-md": "^0.0.3"
},
"packageManager": "yarn@3.6.3",
"dependencies": {
"qrcode": "^1.4.4",
"vue-qrcode": "^0.3.5"
"qrcode.vue": "^3.4.1"
}
}

Wyświetl plik

@ -0,0 +1,16 @@
import { defineClientConfig } from "@vuepress/client";
import QrcodeVue from "qrcode.vue";
import Screencast from "./components/Screencast.vue";
import Screenshot from "./components/Screenshot.vue";
export default defineClientConfig({
enhance({ app, router, siteData }) {
app.component("qrcode", QrcodeVue);
app.component("Screencast", Screencast);
app.component("Screenshot", Screenshot);
app.config.globalProperties.$resolveLink
router.addRoute({ path: '/users/hash/', redirect: '/users/share/' });
}
});

Wyświetl plik

@ -1,18 +1,32 @@
<script setup lang="ts">
const props = defineProps<{
desktop: string;
mobile: string;
}>();
</script>
<template>
<div>
<video controls style="height: 340px">
<source :src="desktop">
<div class="fm-screencast">
<video controls>
<source :src="props.desktop">
</video>
<video controls style="height: 340px">
<source :src="mobile">
<video controls>
<source :src="props.mobile">
</video>
</div>
</template>
<script>
export default {
props: {
desktop: String,
mobile: String
<style lang="scss">
.fm-screencast {
display: flex;
gap: 6px;
> :nth-child(1) {
width: calc(66.5% - 3px);
}
};
</script>
> :nth-child(2) {
width: calc(33.5% - 3px);
}
}
</style>

Wyświetl plik

@ -1,14 +1,28 @@
<script setup lang="ts">
const props = defineProps<{
desktop: string;
mobile: string;
}>();
</script>
<template>
<div>
<img :src="desktop" style="height: 340px">
<img :src="mobile" style="height: 340px">
<div class="fm-screenshot">
<img :src="props.desktop">
<img :src="props.mobile">
</div>
</template>
<script>
export default {
props: {
desktop: String,
mobile: String
<style lang="scss">
.fm-screenshot {
display: flex;
gap: 6px;
> :nth-child(1) {
width: calc(68.5% - 3px);
}
};
</script>
> :nth-child(2) {
width: calc(31.5% - 3px);
}
}
</style>

Wyświetl plik

@ -1,120 +0,0 @@
const { description } = require('../../package')
module.exports = {
title: 'FacilMap',
description: description,
head: [
['meta', { name: 'theme-color', content: '#3eaf7c' }],
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }]
],
themeConfig: {
repo: '',
editLinks: false,
docsDir: '',
editLinkText: '',
lastUpdated: false,
nav: [
{
text: 'Users',
link: '/users/',
},
{
text: 'Administrators',
link: '/administrators/'
},
{
text: 'Developers',
link: '/developers/'
}
],
sidebar: {
'/users/': [
{
title: "Overview",
collapsable: false,
children: [
"",
"help/",
"contribute/"
]
},
{
title: 'General functions',
collapsable: false,
children: [
"ui/",
"layers/",
"search/",
"route/",
"click-marker/",
"files/",
"locate/",
"hash/",
"app/",
"privacy/"
]
},
{
title: 'Collaborative maps',
collapsable: false,
children: [
"collaborative/",
"markers/",
"lines/",
"types/",
"legend/",
"views/",
"filter/",
"history/",
"export/",
"import/",
"map-settings/"
]
},
],
'/administrators/': [
{
title: 'Administrator guide',
collapsable: false,
children: [
"",
"embed",
"server"
]
}
],
'/developers/': [
"",
{
title: 'Client',
collapsable: false,
children: [
"client/",
"client/properties",
"client/events",
"client/methods",
"client/types"
]
},
{
title: 'Development',
collapsable: false,
children: [
"development/dev-setup.md"
]
}
]
}
},
plugins: [
'@vuepress/plugin-back-to-top',
'@vuepress/plugin-medium-zoom',
'check-md'
],
markdown: {
extendMarkdown: (md) => {
md.use(require("markdown-it-footnote"));
}
}
}

Wyświetl plik

@ -0,0 +1,148 @@
import { description } from "../../package.json";
import { defaultTheme, defineUserConfig } from "vuepress";
import backToTopPlugin from "@vuepress/plugin-back-to-top";
import mediumZoomPlugin from "@vuepress/plugin-medium-zoom";
//import checkMdPluin from "vuepress-plugin-check-md";
import { searchPlugin } from "@vuepress/plugin-search";
export default defineUserConfig({
title: 'FacilMap',
description: description,
head: [
['meta', { name: 'theme-color', content: '#3eaf7c' }], // TODO: Update
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black' }]
],
dest: `${__dirname}/../../dist`,
theme: defaultTheme({
repo: '',
docsDir: '',
editLinkText: '',
lastUpdated: false,
contributors: false,
navbar: [
{
text: 'Users',
link: '/users/',
},
{
text: 'Developers',
link: '/developers/'
}
],
sidebar: {
'/users/': [
{
text: "Overview",
children: [
"/users/",
"/users/help/",
"/users/releases/",
"/users/contribute/",
"/users/privacy/"
]
},
{
text: 'General functions',
children: [
"/users/ui/",
"/users/layers/",
"/users/search/",
"/users/pois/",
"/users/route/",
"/users/click-marker/",
"/users/files/",
"/users/locate/",
"/users/share/",
"/users/app/",
"/users/user-preferences/"
]
},
{
text: 'Collaborative maps',
children: [
"/users/collaborative/",
"/users/markers/",
"/users/lines/",
"/users/multiple/",
"/users/types/",
"/users/legend/",
"/users/views/",
"/users/filter/",
"/users/history/",
"/users/export/",
"/users/import/",
"/users/map-settings/"
]
},
],
'/developers/': [
{
text: "Developer guide",
children: [
"/developers/",
"/developers/embed",
"/developers/i18n"
]
},
{
text: "Server",
children: [
"/developers/server/",
"/developers/server/docker",
"/developers/server/standalone",
"/developers/server/config"
]
},
{
text: 'Client',
children: [
"/developers/client/",
"/developers/client/properties",
"/developers/client/events",
"/developers/client/methods",
"/developers/client/types",
"/developers/client/advanced",
"/developers/client/changelog"
]
},
{
text: "Leaflet components",
children: [
"/developers/leaflet/",
"/developers/leaflet/bbox",
"/developers/leaflet/layers",
"/developers/leaflet/markers",
"/developers/leaflet/lines",
"/developers/leaflet/route",
"/developers/leaflet/search",
"/developers/leaflet/overpass",
"/developers/leaflet/icons",
"/developers/leaflet/hash",
"/developers/leaflet/views",
"/developers/leaflet/filter",
"/developers/leaflet/click-listener"
]
},
{
text: "Frontend",
children: [
"/developers/frontend/",
"/developers/frontend/facilmap"
]
},
{
text: 'Development',
children: [
"/developers/development/dev-setup",
"/developers/development/documentation"
]
}
]
}
}),
plugins: [
//checkMdPlugin,
searchPlugin,
]
});

Wyświetl plik

@ -1,8 +0,0 @@
if (typeof window !== "undefined")
window.global = window;
const qrcode = require("vue-qrcode").default;
export default ({ Vue, options, router, siteData }) => {
Vue.component("qrcode", qrcode);
};

Wyświetl plik

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.1 KiB

Wyświetl plik

@ -0,0 +1,4 @@
// :root {
// --c-brand: hsl(184, 100%, 30%);
// --c-brand-light: hsl(184, 100%, 36%);
// }

Wyświetl plik

@ -1,8 +0,0 @@
/**
* Custom Styles here.
*
* refhttps://v1.vuepress.vuejs.org/config/#index-styl
*/
.home .hero img
max-width 450px!important

Wyświetl plik

@ -1,10 +0,0 @@
/**
* Custom palette here.
*
* refhttps://v1.vuepress.vuejs.org/zh/config/#palette-styl
*/
$accentColor = #3eaf7c
$textColor = #2c3e50
$borderColor = #eaecef
$codeBgColor = #282c34

Wyświetl plik

@ -1,6 +0,0 @@
# Overview
* [Embed FacilMap](./embed) into your website
* [Run your own FacilMap server](./server)
If you are interested in programmatically receiving or modifying objects on a collaborative map or in reusing individual components of the FacilMap UI, check out the [developer guide](../developers/).

Wyświetl plik

@ -1,51 +0,0 @@
# Embed FacilMap
You can embed a map into any website using an iframe:
```html
<iframe style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap"></iframe>
```
If you use a map ID that does not exist yet, the “Create Collaborative Map” dialog will be opened when accessing the
map (unless the `interactive` parameter is `false`).
## Options
You can control the display of different components by using the following query parameters:
* `toolbox`: Show the toolbox (default: `true`)
* `search`: Show the search bar (default: `true`)
* `autofocus`: Autofocus the search field (default: `false`)
* `legend`: Show the legend if available (default: `true`)
* `interactive`: Show certain items (“Create collaborative map”, “Open file”) in the toolbox (default: `false`)
Example:
```html
<iframe style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap?search=false&amp;toolbox=false"></iframe>
```
## Location hash
When a FacilMap is opened directly in the browser, the current view of the map is [added to the location hash](../users/hash/) (the part after the `#` in the URL). This means that users can easily share the current view by copying the URL straight from the address bar of their browser, and reloading the page will not cause the current view to be lost.
FacilMap emits a [cross-origin message](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) every time it updates the location map. You can listen to it to synchronize the location hash of your website with the one of FacilMap by using the following script:
```html
<iframe id="facilmap" style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap"></iframe>
<script>
window.addEventListener("message", function(evt) {
if(evt.data && evt.data.type == "facilmap-hash" && location.hash != "#" + evt.data.hash)
location.replace("#" + evt.data.hash);
});
function handleHashChange() {
var iframe = document.getElementById("facilmap");
iframe.src = iframe.src.replace(/(#.*)?$/, "") + location.hash;
}
window.addEventListener("hashchange", handleHashChange);
if (location.hash)
handleHashChange();
</script>
```

Wyświetl plik

@ -1,88 +0,0 @@
# Run your own server
## Using docker
FacilMap is available as [`facilmap/facilmap`](https://hub.docker.com/r/facilmap/facilmap/) on Docker Hub. Here is
an example `docker-compose.yml`:
```yaml
version: "2"
services:
facilmap:
image: facilmap/facilmap
ports:
- 8080
links:
- db
environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org)
DB_TYPE: mysql
DB_HOST: db
DB_NAME: facilmap
DB_USER: facilmap
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
MAPZEN_TOKEN: # Get an API key on https://mapzen.com/developers/sign_up (needed for elevation info)
MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state)
MAXMIND_LICENSE_KEY:
restart: on-failure
db:
image: mariadb
environment:
MYSQL_DATABASE: facilmap
MYSQL_USER: facilmap
MYSQL_PASSWORD: password
MYSQL_RANDOM_ROOT_PASSWORD: "true"
```
Or the same using `docker create`:
```bash
docker create --link=mysql_mysql_1 -p 8080 --name=facilmap -e "USER_AGENT=My FacilMap (https://facilmap.example.org/, facilmap@example.org)" -e DB_TYPE=mysql -e DB_HOST=mysql_mysql_1 -e DB_NAME=facilmap -e DB_USER=facilmap -e DB_PASSWORD=facilmap -e ORS_TOKEN= -e MAPBOX_TOKEN= -e MAPZEN_TOKEN= --restart on-failure facilmap/facilmap
```
See [below](#config) for the available config options.
Both the FacilMap server and the frontend will be available via HTTP on port `8080`. It is recommended to use a reverse
proxy, such as [`jwilder/nginx-proxy`](https://hub.docker.com/r/jwilder/nginx-proxy), to make it available over HTTPS.
## Standalone
To run the FacilMap server by hand, follow the following steps:
1. Make sure that you have a recent version of [Node.js](https://nodejs.org/), [yarn](https://yarnpkg.com/)
and a database (MariaDB, MySQL, PostgreSQL, SQLite, Microsoft SQL Server) set up. (Note that only MySQL/MariaDB has been tested so far.)
2. Clone the [FacilMap repository](https://github.com/FacilMap/facilmap).
3. Run `yarn install` in the root folder of this repository to install the dependencies.
4. Run `yarn build` to create the JS bundles.
5. Copy `config.env.example` to `config.env` and adjust the configuration (see [below](#config) for the available options).
6. Inside the `server` directory, run `yarn server`. This will automatically set up the database structure and start the server.
## Config
The config can be set either by using environment variables (useful for docker) or by editing `config.env`.
| Variable | Required | Default | Meaning |
|-----------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------|
| `USER_AGENT` | * | | Will be used for all HTTP requests (search, routing, GPX/KML/OSM/GeoJSON files). You better provide your e-mail address in here. |
| `HOST` | | | The ip address to listen on (leave empty to listen on all addresses) |
| `PORT` | | `8080` | The port to listen on. |
| `DB_TYPE` | | `mysql` | The type of database. Either `mysql`, `postgres`, `mariadb`, `sqlite`, or `mssql`. |
| `DB_HOST` | | `localhost` | The host name of the database server. |
| `DB_PORT` | | | The port of the database server (optional). |
| `DB_NAME` | | `facilmap` | The name of the database. |
| `DB_USER` | | `facilmap` | The username to connect to the database with. |
| `DB_PASSWORD` | | `facilmap` | The password to connect to the database with. |
| `ORS_TOKEN` | * | | [OpenRouteService API key](https://go.openrouteservice.org). |
| `MAPBOX_TOKEN` | * | | [Mapbox API key](https://www.mapbox.com/signup/). |
| `MAPZEN_TOKEN` | | | [Mapzen API key](https://mapzen.com/developers/sign_up). |
| `MAXMIND_USER_ID` | | | [MaxMind user ID](https://www.maxmind.com/en/geolite2/signup). |
| `MAXMIND_LICENSE_KEY` | | | MaxMind license key. |
FacilMap makes use of several third-party services that require you to register (for free) and generate an API key:
* Mapbox and OpenRouteService are used for calculating routes. Mapbox is used for basic routes, OpenRouteService is used when custom route mode settings are made. If these API keys are not defined, calculating routes will fail.
* Maxmind provides a free database that maps IP addresses to approximate locations. FacilMap downloads this database to decide the initial map view for users (IP addresses are looked up in FacilMaps copy of the database, on IP addresses are sent to Maxmind). This API key is optional, if it is not set, the default view will be the whole world.
* Mapzen is used to look up the elevation info for search results. The API key is optional, if it is not set, no elevation info will be available for search results.

Wyświetl plik

@ -1,4 +1,27 @@
# Overview
* Use the [FacilMap client](./client/) to programmatically access and modify a collaborative map.
* Read about the [dev setup](./development/dev-setup) to start contributing to the FacilMap code.
## Quick links
* [Embed FacilMap](./embed.md) into any website using an iframe.
* Run your own [FacilMap server](./server/).
* Use the [FacilMap client](./client/) to programmatically access and modify data on a collaborative map.
* Use the [Leaflet components](./leaflet/) to embed certain feature of FacilMap into a Leaflet map.
* Use the [FacilMap frontend](./frontend/) to embed an extended or modified version of FacilMap into a website.
* Read about the [dev setup](./development/dev-setup.md) to start contributing to the FacilMap code.
## Structural overview
FacilMap consists of several layers:
* The **Server** is a Node.js app that stores the data of collaborative maps in a database and runs a [socket.io](https://socket.io/) server to access and modify those maps. It also includes a HTTP server that serves the frontend and the map exports.
* The **Client** is a JavaScript library that provides methods to access the data on collaborative maps by sending requests to the socket.io server.
* The **Leaflet components** are a JavaScript library that provides classes to dynamically show the data received by the Client on a [Leaflet](https://leafletjs.com/) map.
* The **Frontend** is a JavaScript app that provides a complete UI written in [Vue.js](https://vuejs.org/) to create, access and modify collaborative maps. It uses the Client to access those maps and the Leaflet components to render them on a map.
FacilMap is completely written in [TypeScript](https://www.typescriptlang.org/). The code base is split into several NPM modules, each of which can be used independently (although some depend on some others):
* [facilmap-types](https://www.npmjs.com/package/facilmap-types) provides common TypeScript types for map objects and the socket communication and is used by all other modules.
* [facilmap-client](https://www.npmjs.com/package/facilmap-client) contains the [FacilMap client](./client/).
* [facilmap-utils](https://www.npmjs.com/package/facilmap-utils) contains helper methods that are used by facilmap-leaflet, facilmap-frontend and facilmap-server, so they can run both in the browser and in Node.js.
* [facilmap-leaflet](https://www.npmjs.com/package/facilmap-leaflet) contains the [Leaflet components](./leaflet/).
* [facilmap-frontend](https://www.npmjs.com/package/facilmap-frontend) contains the [Frontend](./frontend/).
* [facilmap-server](https://www.npmjs.com/package/facilmap-server) contains the [Server](./server/).

Wyświetl plik

@ -1,18 +1,13 @@
# Overview
The FacilMap client makes a connection to the FacilMap server using [socket.io](http://socket.io/) and
automatically receives updates when markers, lines, the map settings, or any other part of a map is created,
changed or removed. When connecting to the map using the writable ID, it it also makes it possible to make
any modifications to the map that you could make through the web interface, such as creating/changing/removing
markers, lines and views and changing the map settings.
The FacilMap client makes a connection to the FacilMap server using [socket.io](http://socket.io/). The client serves multiple purposes:
* Proxy to third-party services (find, route and geoip)
* Open a specific collaborative map and receive the objects on it
* Modify the objects on a collaborative map (only if opened through its writable or admin ID)
* Be notified live about changes that other people are making to the collaborative map.
One instance of the Client class represents one connection to one specific collaborative map on one specific
FacilMap server. To receive markers and lines, a bbox has to be set, and only the markers and line points within
that bbox will be received. The bbox can be changed (for example, if the user drags the map), which causes objects
from the new bbox to be received. The Client instance will store all objects that it receives in its [properties](./properties).
The socket on the server side maintains different API versions in an attempt to stay backwards compatible with older versions of the client. Have a look at the [./changelog.md](changelog) to find out what has changed when upgrading to a new version of the client.
Note that in the methods of the client, a collaborative map will be referred to as __pad__. This is because the
collaborative part of FacilMap used to be a separate software called FacilPad.
## Setting it up
@ -22,57 +17,105 @@ Install facilmap-client as a dependency using npm or yarn:
npm install -S facilmap-client
```
or load the client directly from facilmap.org (along with socket.io, which is needed by facilmap-client):
or import the client from a CDN (only recommended for test purposes):
```html
<script src="https://unpkg.com/socket.io-client@4"></script>
<script src="https://unpkg.com/facilmap-client@3"></script>
<script type="module">
import Client from "https://esm.sh/facilmap-client";
</script>
```
The client class will be available as the global `FacilMap.Client` variable.
## TypeScript
facilmap-client is fully typed using [TypeScript](https://www.typescriptlang.org/). While facilmap-client can be used in a plain JavaScript app without problems, it is strongly suggested to use TypeScript, as it greatly helps to understand the data types of events, methods and properties and to avoid errors.
## Setting up a connection
## Usage
One instance of the client class represents one connection to one specific collaborative map on one specific FacilMap server. The client instance knows different states:
* No map ID set: Only the find, route and geoip methods are available.
* No map ID set and bbox set: Simplified versions of the track points of active routes are sent according to the bbox.
* Map ID set: All methods are available. Events are received when the map settings, types, views and lines (only metadata, not track points) are created/updated/deleted.
* Map ID and bbox set: All methods are available. In addition to the other events, events are received when markers and lines in the specified bounding box are created/updated/deleted.
It is possible to initialize a client without a map ID and later open a map using [`setMapId`](./methods.md#setmapid-mapid) or [`createMap`](./methods.md#createmap-data). Once a specific map is loaded, it is not possible to close it or switch to another map anymore. To do that, a new client instance has to be created.
The bbox can be updated continuously. In the official FacilMap UI, the bbox is updated every time the user pans the map, causing the server to send the markers within that bbox and a simplified version of the line track points and active routes fit to the bbox and zoom level.
### Open a map
```js
let client = new FacilMap.Client("https://facilmap.org/");
client.setPadId("myMapId").then((padData) => {
console.log(padData);
}).catch((err) => {
console.error(err.stack);
import Client from "facilmap-client";
const client = new Client("https://facilmap.org/");
await client.setMapId("myMapId");
console.log(client.mapData, client.types, client.lines);
```
The client [constructor](./methods.md#constructor-server-mapid) takes the URL where the FacilMap server is running and opens a socket.io connection to the server.
When opening a collaborative map using [`setMapId`](./methods.md#setmapid-mapid), the server sends [events](./events.md) for the map settings, types, views and lines (without track points). The same types of events will be received later if the respective objects are changed while the connection is open. The client has some default listeners registered that will store the data received as events in its [properties](./properties.md). For example, a `mapData` event contains the map settings and is emitted the first time the map ID is set and every time the map settings are changed while the connection is open. The `client.mapData` property always contains the latest state of the map settings.
Note that most methods of the client are asynchronous. Events that the server fires in response to a method call are always fired before the method returns. This is why in the above example, `client.mapData` and the other properties are available right after the `setMapId` call.
### Set a bbox
```js
await client.updateBbox({ top: 53.5566, left: 8.7506, right: 19.8468, bottom: 50.1980, zoom: 8 });
console.log(client.markers, client.lines);
```
Setting the bounding box of the client will cause the server to send events for all the markers within these bounds, and also for any line track points within the bounds, simplified to be appropriate for the specified zoom level. It will also subscribe to any updates to those objects within the bbox.
The bbox can be updated again later to receive the data and change the subscription to objects in that bounding box. (Note that when changing the bounding box, the server will not send events again for objects that were already sent as part of the previous bounding box.)
### Change the map
```js
const newMarker = client.editMarker({ id: 123, title: "New title" });
```
When creating/updating/deleting an object, the data is propagated in multiple ways:
* An event representing the change is fired before the method returns (in the above example, a `marker` event)
* The client property is updated in reaction to the event (in the above example, the updated marker is stored in `client.markers[123]`)
* The method returns the created/updated object.
Note that creating/updating/deleting an object will fail if the operation is not permitted. The above example will fail if the map was opened using its read-only ID.
### Internationalization
Most of the data returned by the client is user-generated and thus not internationalized. There are a few exceptions though, in particular error messages in case someting unexpected happens.
By default, the FacilMap backend detects the user language based on the `Accept-Language` HTTP header. The detected language can be overridden by setting a `lang` cookie or query parameter. In addition, a `units` cookie or query parameter can be set to `metric` or `us_customary`.
For the websocket, the `Accept-Language` header, cookies and query parameters are sent during the socket.io handshake. If you want to force the socket to use a sepcific language, you can pass query parameters through the third parameter of the client constructor:
```js
import Client from "facilmap-client";
const client = new Client("https://facilmap.org/", undefined, {
query: {
lang: "en",
units: "us_customary"
}
});
```
You can also update the internationalization settings for an existing socket connection at any point using [`setLanguage()`](./methods#setlanguage-settings).
## Using it
### Deal with connection problems
A detailed description of all the [properties](./properties), [events](./events), [methods](./methods) and [Types](./types) can be found in the respective sections of the documentation.
## Change detection
When the FacilMap server sends an event to the client that an object has been created, changed or deleted, the client emits the
event and also persists it in its properties. So you have two ways to access the map data: By listening to the map events and
persisting the data somewhere else, or by accessing the properties on the Client object.
If you are using a UI framework that relies on a change detection mechanism (such as Vue.js or Angular), you can override the methods
`_set` and `_delete`. facilmap-client consistently uses these to update any data on its properties.
In Vue.js, it could look like this:
```javascript
let client = new FacilMap.Client("https://facilmap.org/");
client._set = Vue.set;
client._delete = Vue.delete;
```js
const client = new Client("https://facilmap.org/");
client.on("connect", () => {
console.log("connected");
});
client.on("disconnect", () => {
console.log("disconnected");
});
```
In Angular.js, it could look like this:
Constructing the client will attempt to connect to the server. socket.io will retry this until it succeeds. Once the connection is made, a `connect` event is fired.
```javascript
let client = new FacilMap.Client("https://facilmap.org/");
client._set = (object, key, value) => { $rootScope.$apply(() => { object[key] = value; }); };
client._delete = (object, key) => { $rootScope.$apply(() => { delete object[key]; }); };
```
This way your UI framework will detect changes to any properties on the client, and you can reference values like `client.padData.name`,
`client.disconnected` and `client.loading` in your UI components.
If the connection is lost at some point, a `disconnect` event is fired and socket.io will keep trying to connect again. When it succeeds, a `connect` event is fired again. Since the session on the server is lost when disconnecting, the client will automatically set the last map ID, bbox and routes again on reconnection. This means that events for all the map objects are received again.

Wyświetl plik

@ -0,0 +1,121 @@
# Advanced configuration
## Reactivity
When the FacilMap server sends an event to the client that an object has been created, changed or deleted, the client emits the
event and also persists it in its properties. So you have two ways to access the map data: By listening to the map events and
persisting the data somewhere else, or by accessing the properties on the Client object.
If you are using a UI framework that relies on a change detection mechanism (such as Vue.js or Angular), facilmap-client provides
a way to make its properties reactive. Internally, the client maintains a `state` object (for any properties related to the client
itself) and a `data` object (for any received map objects). These two objects are stored as private properties on the client object.
All public properties of the client object are just getters that return the data from the two private objects. The client modifies
these two objects consistently in the following way:
* When the client object is constructed, it constructs the private `state` and `data` objects by calling
`this.object = makeReactive(object)`.
* When the client sets a property inside the `state` and `data` objects, it does so by calling `this._set(this.object, key, value)`.
This includes setting nested properties.
* When the client deletes a property inside the `state` and `data` objects, it does so by calling `this._delete(this.object, key)`.
This includes deleting nested properties.
You can override the `_makeReactive`, `_set` and `_delete` methods to make the private properties (and as a consequence the public
getters) of facilmap-client reactive. This way your UI framework will detect changes to any properties on the client, and you can
reference values like `client.mapData.name`, `client.disconnected` and `client.loading` in your UI components.
Note that client always replaces whole objects rather than updating individual properties. For example, when a new version of the map settings arrives, `client.mapData` is replaced with a new object, or when a new marker arrives, `client.markers[markerId]` is replaced with a new object. This makes deep watches unnecessary in most cases.
### Vue.js 3
```javascript
class ReactiveClient extends Client {
_makeReactive(object) {
return Vue.reactive(object);
}
}
```
### Vue.js 2
```javascript
class ReactiveClient extends Client {
_set(object, key, value) {
Vue.set(object, key, value);
}
_delete(object, key) {
Vue.delete(object, key);
}
}
```
### Angular.js
```javascript
class ReactiveClient extends Client {
_set(object, key, value) {
$rootScope.$apply(() => {
object[key] = value;
});
}
_delete(object, key) {
$rootScope.$apply(() => {
delete object[key];
});
}
}
```
### React
```javascript
class ObservableClient extends Client {
_observers = new Set();
subscribe(callback) {
this._observers.add(callback);
return () => {
this._observers.delete(callback);
};
}
_triggerObservers() {
for (const observer of this._observers) {
observer();
}
}
_set(object, key, value) {
object[key] = value;
this._triggerObservers();
}
_delete(object, key) {
delete object[key];
this._triggerObservers();
}
}
function useClientObserver(client, selector) {
React.useSyncExternalStore(
(callback) => client.subscribe(callback),
() => selector(client)
);
}
const MarkerInfo = ({ client, markerId }) => {
const marker = useClientObserver(client, (client) => client.markers[markerId]);
return (
<div>
Marker name: {marker?.name}
</div>
);
}
```
Keep in mind that Reacts `useSyncExternalStore` will rerender the component if the resulting _object reference_ changes.
This means one the one hand that you cannot use this example implementation on a higher up object of the client (such as
`client` itself or `client.markers`), as their identity never changes, causing your component to never rerender. And on
the other hand that you should avoid using it on objects created in the selector (such as returning
`[client.mapData.id, client.mapData.name]` in order to get multiple values at once), as it will cause your component to
rerender every time the selector is called.

Wyświetl plik

@ -0,0 +1,16 @@
# Changelog
The websocket on the FacilMap server provides different API versions (implemented as socket.io namespaces such as `/` for version 1, `/v2` for version 2, etc.) in order to stay backwards compatible with older clients. Each release of facilmap-client is adapted to a particular API version of the server. When upgrading to a new version of the client, have a look at this page to find out what has changed.
## v5.0.0 (API v3)
_Note: This socket version is still under development and will still change. Do not use it in production yet._
* “symbol” was renamed to “icon” everywhere. This applies to `Marker.symbol`, `Type.defaultSymbol`, `Type.symbolFixed`, `Type.fields[].controlSymbol` and `Type.fields[].options[].symbol`.
* “pad” was renamed “map” everywhere. This applies to the `padData` and `deletePad` socket events and `getPad` (including its `padId` request property), the `findPads`, `createPad`, `editPad`, `deletePad`, `setPadId` client/socket methods, the `PadNotFoundError`, and the `Marker.padId`, `Line.padId`, `Type.padId`, `View.padId` and `HistoryEntry.padId` properties.
## v4.0.0 (API v2)
* Before, creating a map with an empty name resulted in `padData.name` set to `"Unnamed map"`. Now, an empty name will result in `""` and the UI is responsible for displaying that in an appropriate way.
* Before, creating a marker with an empty name resulted in `marker.name` set to `"Untitled marker"`. Now an empty name will result in `""` and the UI is responsible for displaying that in an appropriate way.
* Before, creating a line with an empty name resulted in `line.name` set to `"Untitled line"`. Now an empty name will result in `""` and the UI is responsible for displaying that in an appropriate way.

Wyświetl plik

@ -1,26 +1,36 @@
# Events
Subscribe to events using the [`on(eventName, function)`](./methods#on-eventname-function) method. Example:
The FacilMap server uses events to send information about objects on a collaborative map to the client. The events are fired when the client opens a map or a particular section of a map for the first time, and whenever an object is changed on the map (including when the change is made by the same instance of the client). The client has some listeners already attached to most events and uses them to persist and update the received objects in its [properties](./properties.md).
Note that events are always fired _before_ the method causing them returns. For example, when updating a marker using the `editMarker()` method, a `marker` event with the updated marker is fired first (if the marker is within the current bbox), and only then the method returns the updated marker as well.
Subscribe to events using the [`on(eventName, function)`](./methods.md#on-eventname-function) method. Example:
```js
let client = new FacilMap.Client("https://facilmap.org/", "testPad");
client.on("padData", (padData) => {
document.title = padData.name;
const client = new FacilMap.Client("https://facilmap.org/", "testMap");
client.on("mapData", (mapData) => {
document.title = mapData.name;
});
```
## `connect`, `disconnect`, `connect_error`, `error`, `reconnect`, `reconnect_attempt`, `reconnect_error`, `reconnect_failed`
These events come from socket.io and are [documented there under the section “Events”](http://socket.io/docs/client-api/).
These events come from socket.io and are [documented there](https://socket.io/docs/v4/client-api/#events).
## `padData`
## `mapData`
The settings of the map have changed or are retrieved for the first time.
Note that when this event is fired, the read-only and/or the read-write ID of the map might have changed. The [`padId`](./properties#padid)
Note that when this event is fired, the read-only and/or the read-write ID of the map might have changed. The [`mapId`](./properties.md#mapid)
property is updated automatically.
_Type:_ [padData](./types#paddata)
_Type:_ [MapData](./types.md#mapdata)
## `serverError`
[`setMapId()`](./methods.md#setmapid-mapid) failed and the map could not be opened.
_Type:_ Error
## `deletePad`
@ -30,14 +40,14 @@ The map has been deleted.
An existing marker is retrieved for the first time, has been modified, or a new marker has been created in the current bbox.
_Type:_ [marker](./types#marker)
_Type:_ [Marker](./types.md#marker)
## `deleteMarker`
A marker has been removed. This event is emitted for all markers in the map, even if they are outside of the current bbox
A marker has been removed. This event is emitted for all markers on the map, even if they are outside of the current bbox
(in case that a marker outside of the current bbox is cached).
_Type:_ `{id: "<markerId>"}`
_Type:_ `{ id: number }`
## `line`
@ -45,64 +55,82 @@ An existing line is retrieved for the first time, has been modified, or a new li
objects only contain the line metadata, not its track points (those are handled separately as `linePoints`). This is why
all line objects of the map are sent to the client, regardless of the current bbox.
_Type:_ [line without trackPoints](./types#line)
_Type:_ [Line](./types.md#line) (without trackPoints)
## `deleteLine`
A line has been removed.
_Type:_ `{id: "<lineId>"}`
_Type:_ `{ id: number }`
## `linePoints`
New track points for an existing line are retrieved after a change of bbox (`reset == false`), or the line has been
modified, so the new track points are retrieved (`reset == true`).
_Type:_ {
_Type:_ object with the following properties:
* __id__ (number): The ID of the line that these track points belong to
* __reset__ (boolean): Whether to remove all cached track points for this line (`true`) or to merge these track points
with the cached ones (`false`).
* __trackPoints__ (Array<[trackPoint](./types#trackpoint)>): The track points
}
* __trackPoints__ (Array<[TrackPoint](./types.md#trackpoint)>): The track points
## `view`
A view is retrieved for the first time, has been modified, or a new view has been created.
_Type:_ [view](./types#view)
_Type:_ [View](./types.md#view)
## `deleteView`
A view has been removed.
_Type:_ `{id: "<viewId>"}`
_Type:_ `{ id: number }`
## `type`
A type is retrieved for the first time, has been modified, or a new type has been created.
_Type:_ [type](./types#type)
_Type:_ [Type](./types.md#type)
## `deleteType`
A type has been removed.
_Type:_ `{id: "<typeId>"}`
_Type:_ `{ id: number }`
## `history`
An entry of the modification history is retrieved for the first time, or a new entry has been created due to something
being modified. Note that this event is only fired when the client has subscribed using [`listenToHistory()`](./methods#listentohistory).
_Type:_ [historyEntry](./types#historyentry)
being modified. Note that this event is only fired when the client has subscribed using [`listenToHistory()`](./methods.md#listentohistory).
_Type:_ [historyEntry](./types.md#historyentry)
## `route`
A new route has been set.
_Type:_ [Route](./types.md#route)> with trackpoints for the current bbox. The `routeId` property identifies the route (can be a string or undefined).
## `clearRoute`
A route has been cleared.
_Type:_ `{ routeId: string | undefined }`
## `routePoints`
New track points for the temporary route are retrieved after a change of bbox.
New track points for the default route (route that has been set using [`setRoute()`](./methods.md#setroute-data) without a `routeId`) are retrieved after a change of bbox.
_Type:_ Array<[TrackPoint](./types.md#trackpoint)>
## `routePointsWithId`
New track points for a route with a `routeId` are retrieved after a change of bbox.
_Type:_ object with the following properties:
* **routeId** (string): The `routeId` that was passed when setting the route using [`setRoute()`](./methods.md#setroute-data)
* **trackPoints** (`Array<[trackPoint](./types.md#trackpoint)>`): The additional track points for the route
_Type:_ Array<[trackPoint](./types#trackpoint)>
## `loadStart`, `loadEnd`
This event is fired every time some request is sent to the server and when the response has arrived. It can be used to
@ -118,4 +146,8 @@ client.on("loadEnd", () => {
if(--loading == 0)
hideLoadingIndicator();
});
```
```
## `emit`, `emitResolve`, `emitReject`
`emit` is emitted by the client whenever any request is sent to the server, and `emitResolve` or `emitReject` is emitted when the request is answered. These can be used to hook into the communication between the client and the server. All 3 events are called with two arguments, the first one being the request name and the second one being the request data, response data or error.

Wyświetl plik

@ -1,75 +1,114 @@
# Methods
## `constructor(server, padId)`
## `constructor(server, mapId, socketOptions)`
Connects to the FacilMap server `server` and optionally opens the collaborative map with the ID `padId`. If the pad ID
is not set, it can be set later using [`setPadId(padId)`](#setpadid-padid) or using [`createPad(data)`](#createpad-data).
Connects to the FacilMap server `server` and optionally opens the collaborative map with the ID `mapId`. If the map ID
is not set, it can be set later using [`setMapId(mapId)`](#setmapid-mapid) or using [`createMap(data)`](#createmap-data).
Setting the padId causes the server to send several objects, such as the map settings, all views, all types, and all
lines (just meta data, without line points).
The connection is established in the background, and a `connect` event is fired when it is successful. If a `mapId` is specified, a [`mapData`](./events.md#mapdata) or [`serverError`](./events.md#servererror) event will indicate when the map has been opened successfully or unsuccessfully. Note that you can already call methods immediately after constructing the client, causing them to be delayed until the connection is established.
If the connection to the server breaks down, a `disconnect` event will be emitted and socket.io will attempt to reconnect.
On successful reconnection, a `reconnect` and `connect` event will be fired.
If the connection to the server breaks down, a `disconnect` event will be emitted and socket.io will attempt to reconnect. On successful reconnection, a `reconnect` and `connect` event will be fired. During the interruption, you can still call methods, causing them to be delayed until the connection is reestablished.
* `server` (string): The URL of the FacilMap server, for example `https://facilmap.org/`
* `padId` (string, optional): The ID of the collaborative map to open
* `server` (string): The URL of the FacilMap server, for example `https://facilmap.org/`.
* `mapId` (string, optional): The ID of the collaborative map to open.
* `socketOptions` (object, optional): Any additional [Socket.io client options](https://socket.io/docs/v4/client-options/).
* **Events:** Causes a `connect` event to be fired when the connection is established. If `mapId` is defined, causes events to be fired with the map settings, all views, all types and all lines (without line points) of the map. If the map with `mapId` could not be opened, causes a [`serverError`](./events.md#servererror) event.
## `on(eventName, function)`
Registers a new [event](./events) handler.
Registers a new [event](./events.md) handler.
* `eventName` (string): The name of the event
* `function` (function): The function that should be executed when the event occurs. If the event emits an object,
it will be passed to the function as the first parameter.
* `eventName` (string): The name of the event.
* `function` (function): The function that should be executed when the event occurs. If the event emits an object, it will be passed to the function as the first parameter.
## `removeListener(eventName, function)`
Unregisters an event handler previously assigned using `on(eventName, function)`.
* `eventName` (string): The name of the event
* `function` (function): The function that was passed to `on(eventName, function)` when registering the handler
* `eventName` (string): The name of the event.
* `function` (function): The function that was passed to `on(eventName, function)` when registering the handler.
## `setPadId(padId)`
## `setMapId(mapId)`
Opens the collaborative map with the ID `padId`.
Opens the collaborative map with the ID `mapId`.
This method can only be called once, and only if no `padId` has been passed to the constructor. If you want to open
a different map, you need to create a new instance of the client.
This method can only be called once, and only if no `mapId` was passed to the constructor. If you want to open a different map, you need to create a new instance of the client.
Setting the padId causes the server to send several objects, such as the map settings, all views, and all lines (just
meta data, without line points).
Setting the mapId causes the server to send several objects, such as the map settings, all views, and all lines (just metadata, without line points). Each of these objects is sent as an individual [`event`](./events.md).
* `padId` (string): The ID of the collaborative map to open
* _returns_ (Promise): A promise that resolves when all objects have been received.
* `mapId` (string): The ID of the collaborative map to open. Can be a read-only ID, writable ID or admin ID of a map.
* **Returns:** A promise that is resolved empty when all objects have been received.
* **Events:** Causes events to be fired with the map settings, all views, all types and all lines (without line points) of the map. If the map could not be opened, causes a [`serverError`](./events.md#servererror) event.
* **Availability:** Only available if no map is opened yet on this client instance.
## `setLanguage(settings)`
Updates the language settings for the current socket connection. Usually this only needs to be called if the user changes their internationalization settings and you want to apply the new settings live in the UI. See [Internationalization](./#internationalization) for the details and how to set the language settings when opening a client.
* `settings`: An object with the following properties:
* `lang` (optional): The language, for example `en` or `de`.
* `units` (optional): The units to use, either `metric` or `us_costomary`.
* **Returns:** A promise tat is resolved empty when the settings have been applied.
* **Events:** None.
* **Availability:** Always.
## `updateBbox(bbox)`
Updates the bbox. This will cause all markers and line points within the bbox (except the ones that were already in the
previous bbox, if there was one) to be received.
Updates the bbox. This will cause all markers, line points and route points within the bbox (except the ones that were already in the previous bbox, if there was one) to be received as individual events.
* __bbox__ ([bbox](./types#bbox) with zoom): The bbox that objects should be received for
* _returns_ (Promise): A promise that resolves when all objects have been received.
* `bbox` ([Bbox](./types.md#bbox) with zoom): The bbox that objects should be received for.
* **Returns:** A promise that is resolved empty when all objects have been received.
* **Events:** Causes events to be fired with the markers, line points and route points within the bbox.
* **Availability:** Always.
## `createPad(data)`
## `getMap(data)`
Creates a new collaborative map.
Finds a collaborative map by ID. This can be used to check if a map with a certain ID exists.
* `data` ([padData](./types#paddata)): The data of the new map, including the desired read-only and writable ID
* _returns_ (Promise): A promise that resolves when the map has been created, returning the new padData.
* `data`: An object with the following properties:
* `mapId`: The read-only, writable or admin ID of the map.
* **Returns:** A promise that is resolved with undefined (if no map with that ID exists) or with an object with an `id` (read-only ID), `name` and `description` property.
* **Events:** None.
* **Availability:** Always.
## `editPad(data)`
## `findMaps(data)`
Update the metadata of the current map.
Finds collaborative maps by a search term. Only finds maps that have been made public by setting [`searchEngines`](./types.md#mapdata) to `true`.
* `data` ([padData](./types#paddata)): The data of the map that should be modified. Fields that are not defined will not be
modified. To change the default view, set the `defaultViewId` property. The `defaultView` property is ignored.
* _returns_ (Promise): The new padData.
* `data`: An object with the following properties:
* `query` (string): A search term. `*` can be used as a wildcard and `?` as a single-character wildcard.
* `start`, `limit` (number): If specified, can be used for paging.
* **Returns:**: A promise that is resolved to an object with the following properties:
* `results`: An array of objects with an `id`, `name` and `description` property.
* `totalLength`: The total number of results. If paging is used, this number may be higher than the number of `results` returned.
* **Events:** None.
* **Availability:** Always.
## `deletePad()`
## `createMap(data)`
Creates a new collaborative map and opens it.
* `data` ([mapData](./types.md#mapdata)): The data of the new map, including the desired read-only, writable and admin ID.
* **Returns:** A promise that is resolved with the new mapData when the map has been created.
* **Events:** Causes a [`mapData`](./events.md#mapdata) event and other events for objects that have been created on the map (such as the default Marker and Line types).
* **Availability:** Only if no collaborative map is opened yet.
## `editMap(data)`
Update the map settings of the current map.
* `data` ([MapData](./types.md#mapdata)): The data of the map that should be modified. Fields that are not defined will not be modified. To change the default view, set the `defaultViewId` property. The `defaultView` property is ignored.
* **Returns:** A promise that is resolved with the new mapData.
* **Events:** Causes a [`mapData`](./events.md#mapdata) event.
* **Availability:** Only if a collaborative map is opened through its admin ID.
## `deleteMap()`
Delete the current map irrevocably.
* _returns_ (Promise): An empty promise that resolves when the map has been deleted.
* **Returns:** A promise that is resolved empty when the map has been deleted.
* **Events:** Causes a [`deleteMap`](./events.md#deletemap) event.
* **Availability:** Only if a collaborative map is opened through its admin ID.
## `listenToHistory()`
@ -77,13 +116,17 @@ Start listening to the modification history of the map. Calling this will cause
received (that describe the modification history until now), and new `history` objects will be received every time
something is modified (in addition to the modified object).
* _returns_ (Promise): A promise that resolves when all history objects have been received
* **Returns:** A promise that is resolved empty when all history objects have been received.
* **Events:** Causes multiple [`history`](./events.md#history) events.
* **Availability:** Only if a collaborative map is opened through its admin ID.
## `stopListeningToHistory()`
Stop listening to the modification history of the map.
* _returns_ (Promise): A promise that resolves when the command has completed.
* **Returns:** A promise that is resolved empty when the command has completed.
* **Events:** None.
* **Availability:** Only if a collaborative map is opened through its admin ID and [`listenToHistory()`](#listentohistory) has been called before.
## `revertHistoryEntry(data)`
@ -91,8 +134,10 @@ Undo a modification in the map. When a previously removed object is restored, it
IDs of all other history entries connected to this object are updated as well. This is why reverting a history entry
will cause the whole history to be received again (as if you were calling `listenToHistory()` again).
* `data` (`{id: "<historyEntryId>"}`)): The history object that should be reverted
* _returns_ (Promise): A promise that resolves when the command has completed and all new history objects have been received
* `data` (`{ id: number }`)): The history object that should be reverted.
* **Returns:** A promise that is resolved empty when the command has completed and all new history objects have been received.
* **Events:** Causes multiple [`history`](./events.md#history) events and an event that reverts the change.
* **Availability:** Only if a collaborative map is opened through its admin ID.
## `disconnect()`
@ -100,14 +145,16 @@ Empties all cached objects and disconnects from the server.
## `find(data)`
Search for places.
Search for places. Does not persist anything on the server, simply serves as a proxy to the search service.
* `data` (object): An object with the following properties:
* `query` (string): The query string
* `loadUrls` (boolean): Whether to return the file if `query` is a URL
* `elevation` (boolean): Whether to find out the elevation of the result(s). Will make the search significantly slower.
* _returns_ (Promise<string|Array<[searchResult](./types#searchresult)>>): If `data.query` is a URL to a GPX/KML/OSM/GeoJSON
file, that file as a string, otherwise an array of search results.
* `query` (string): The query string
* `loadUrls` (boolean): Whether to return the file if `query` is a URL
* **Returns:** A promise that is resolved with the following value:
* If `data.query` is a URL to a GPX/KML/OSM/GeoJSON file and `loadUrls` is `true`, a string with the content of the file.
* Otherwise an array of [SearchResults](./types.md#searchresult).
* **Events:** None.
* **Availability:** Always.
## `findOnMap(data)`
@ -115,46 +162,59 @@ Search for markers and lines inside the map.
* `data` (object): An object with the following properties:
* `query` (string): The query string
* _returns_ (Promise<Array<[Marker](./types#marker)|[Line](./types#line)>>) An array of (stripped down) marker and line objects.
The objects only contain the `id`, `name`, `typeId`, `lat`/`lon` (for markers), `left`/`top`/`right`/`bottom` (for
lines) properties, plus an additional `kind` property that is either `"marker"` or `"line"`.
* **Returns:** A promise that is resolved with an array of (stripped down) [Marker](./types.md#marker) and [Line](./types.md#line) objects. The objects only contain the `id`, `name`, `typeId`, `lat`/`lon` (for markers), `left`/`top`/`right`/`bottom` (for lines) properties, plus an additional `kind` property that is either `"marker"` or `"line"`.
* **Events:** None.
* **Availability:** Only when a map is opened.
## `getRoute(data)`
Calculate a route between two or more points.
Calculate a route between two or more points. Does not persist anything on the server, simply serves as a proxy to the routing service.
* `data` (object): An object with the following properties:
* `destinations` (array): An array of at least two route points (objects with a `lat` and `lon` property)
* `mode` (string): `"car"`, `"bicycle"` or `"pedestrian"`
* _returns_ (Promise<[route](./types#route)>)
* `destinations` (array): An array of at least two route points (objects with a `lat` and `lon` property)
* `mode` ([RouteMode](./types.md#routemode)): the route mode
* **Returns:** A promise that is resolved with a [Route](./types.md#route)>.
* **Events:** None.
* **Availability:** Always.
## `setRoute(data)`
Calculate a route between two or more points, but but do not return the track points of the route but cache them on the
server side and send them according to the client bbox. The properties of the route are saved in the [`route`](./properties#route)
property and the trackPoints in `route.trackPoints`. Only one temporary route like this can be set at a time, when
calling `setRoute()` again, the existing route will be modified.
Calculate a route between two or more points, but but do not return the track points of the route but cache them on the server side and send them according to the client bbox. The route is not persisted on a collaborative map, but is temporarily persisted on the server in the scope one particular client connection only. As long as the route is active, the server will send [`routePoints`](./events.md#routepoints) events in response to [`updateBbox()`](#updatebbox-bbox) with the track points of the route simplified according to the bbox. The route will stay active until it is cleared using [`clearRoute()`](#clearroute-data) or the connection is closed.
Multiple routes can be active at the same time. They can be distinguished by their `routeId` property, which is a custom string that you can specify when activating a route. A `routeId` needs to be unique in the scope of this client instance, other clients are not affected by it. For backwards compatibility reasons, `undefined` is an acceptable value for `routeId`, but is considered a unique identifier nonetheless.
Calling `setRoute()` with a `routeId` of a route that is already active will replace that route.
The metadata of a route whose `routeId` is `undefined` is persisted in the [`route`](./properties.md#route) property and its track points in `route.trackPoints`. The metadata of a route whose `routeId` is a string is persisted in the [`routes[routeId]`](./properties.md#routes) property and its track points in `routes[routeId].trackPoints`.
* `data` (object): An object with the following properties:
* `routePoints` (array): An array of at least two route points (objects with a `lat` and `lon` property)
* `mode` (string): `"car"`, `"bicycle"` or `"pedestrian"`
* `elevation` (boolean): `true` to get elevation data for the route
* _returns_ (Promise<[route](./types#route)>)
* `routePoints` (array): An array of at least two route points (objects with a `lat` and `lon` property)
* `mode` ([RouteMode](./types.md#routemode)): the route mode
* `routeId` (string or undefined): the custom `routeId` to identify the route
* **Returns:** A promise that is resolved with a [Route](./types.md#route)> object.
* **Events:** Causes a [`route`](./events.md#route) and a [`routePoints`](./events.md#routepoints) event.
* **Availability:** Always.
## `clearRoute()`
## `clearRoute(data)`
Clear the temporary route set via [`setRoute(data)`](#setroute-data).
Clear a temporary route set via [`setRoute(data)`](#setroute-data).
* _returns_ (Promise)
* `data` (object): An object with the following properties:
* `routeId` (string or undefined): the custom `routeId` to identify the route
* **Returns:** A promise that is resolved empty when the route is cleared.
* **Events:** Causes a [`clearRoute`](./events.md#clearroute) event.
* **Availability:** If a route with the specified `routeId` is active.
## `lineToRoute(data)`
Call [`setRoute()`](#setroute-data) with the parameters of an existing line. Saves time, as the route does not need to be
recalculated.
Call [`setRoute()`](#setroute-data) with the parameters of an existing line. Saves time, as the route does not need to be recalculated. If a route with the same `routeId` is already active, it is replaced.
* `data` (object): An object with the following properties:
* `id` (string): The ID of the line
* _returns_ (Promise<[route](./types#route)>)
* `routeId` (string or undefined): the custom `routeId` to identify the route
* **Returns:** A promise that is resolved with a [Route](./types.md#route)> object.
* **Events:** Causes a [`route`](./events.md#route) and a [`routePoints`](./events.md#routepoints) event.
* **Availability:** Only if a collaborative map is opened.
## `exportRoute(data)`
@ -164,69 +224,84 @@ Export the current route.
* `format` (string): One of the following:
* `gpx-trk`: GPX track (contains the whole course of the route)
* `gpx-rte`: GPX route (contains only the route points, and the navigation device will have to calculate the route)
* _returns_ (Promise&lt;string&gt;)
* `routeId` (string or undefined): the custom `routeId` to identify the route
* **Returns:** A promise that is resolved with a string with the file contents.
* **Events:** None.
* **Availability:** if a route with the specified `routeId` is active.
## `getMarker(data)`
Get the marker with the given ID.
Get the marker with the given ID. This is useful if you want to access a specific marker but it is not loaded as part of the current bbox.
* `data` (object): An object with the following properties:
* `id` (number): The ID of the marker to load
* _returns_ (Promise<[marker](./types#marker)>): The marker
* `id` (number): The ID of the marker to load
* **Returns:** A promise that is resolved with a [Marker](./types.md#marker)>. If the marker is not found, the promise rejects.
* **Events:** None.
* **Availability:** Only if a collaborative map is opened.
## `addMarker(data)`
Create a marker.
* `data` ([marker](./types#marker)): The data of the marker to create. An `id` will be assigned by the server
* _returns_ (Promise<[marker](./types#marker)>): The marker as it is on the server, with an `id` assigned and possibly its
styles modified by the settings of its type.
* `data` ([Marker](./types.md#marker)): The data of the marker to create. An `id` will be assigned by the server.
* **Returns:** A promise that is resolved with a [Marker](./types.md#marker)>, with an `id` assigned and possibly its styles modified by the settings of its type.
* **Events:** May trigger a [`marker`](./events.md#marker) event if the created marker is in the current bbox.
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `editMarker(data)`
Update an existing marker.
* `data` ([marker](./types#marker)). The new marker data. Fields that are not defined will not be unmodified. Only `id` needs
to be defined.
* _returns_ (Promise<[marker](./types#marker)>): The new marker. Might have some styles modified due to the settings of its type
* `data` ([Marker](./types.md#marker)). The new marker data. Fields that are not defined will not be unmodified. Only `id` needs to be defined.
* **Returns:** A promise that is resolved with the updated [Marker](./types.md#marker). Might have some styles modified due to the settings of its type.
* **Events:** May trigger a [`marker`](./events.md#marker) event if the updated marker is in the current bbox.
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `deleteMarker(data)`
Delete an existing marker
* `data` (`{id: <markerId>}`): An object that contains the ID of the marker to be removed
* _returns_ (Promise): A promise that resolves when the operation has completed
* `data` (`{ id: number }`): an object that contains the ID of the marker to be removed
* **Returns:** An promise that is resolved with the deleted [Marker](./types.md#marker) when the operation has completed.
* **Events:** Causes a [`deleteMarker`](./events.md#deletemarker) event.
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `getLineTemplate(data)`
Get a fake line object for a line with the given type. This can be used so that while the user is drawing a new line,
Get a mock line object for a line with the given type. This can be used so that while the user is drawing a new line,
that line already has the right style.
* `data` (`{typeId: <typeId>}`): An object containing the type ID
* _returns_ (Promise<[line](./types#line)>): A fake line object with the styles of this type
* `data` (`{ typeId: number }`): An object containing the type ID
* **Returns:** A promise that is resolved with a mock [Line](./types.md#line) with the styles of this type.
* **Events:** None.
* **Availability:** Only if a collaborative map is opened.
## `addLine(data)`
Create a line.
* `data` ([line](./types#line)): The data of the line to create. An `id` will be assigned by the server
* _returns_ (Promise<[line](./types#line)>): The line as it is on the server, with an `id` assigned and possibly its
styles modified by the settings of its type.
* `data` ([Line](./types.md#line)): The data of the line to create. An `id` will be assigned by the server.
* **Returns:** A promise that is resolved with a [Line](./types.md#line), with an `id` assigned and possibly its styles modified by the settings of its type.
* **Events:** Causes a [`line`](./events.md#line) event and a [`linePoints`](./events.md#linepoints) event (if the line is in the current bbox).
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `editLine(data)`
Update an existing line.
* `data` ([line](./types#line)). The new line data. Fields that are not defined will not be unmodified. Only `id` needs
to be defined.
* _returns_ (Promise<[line](./types#line)>): The new line. Might have some styles modified due to the settings of its type
* `data` ([line](./types.md#line)). The new line data. Fields that are not defined will not be unmodified. Only `id` needs to be defined.
* **Returns:** A promise that is resolved with the update [Line](./types.md#line). Might have some styles modified due to the settings of its type.
* **Events:** Causes a [`line`](./events.md#line) event and possibly a [`linePoints`](./events.md#linepoints) event (if the route mode was changed and the line is in the current bbox).
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `deleteLine(data)`
Delete an existing line
* `data` (`{id: <lineId>}`): An object that contains the ID of the line to be removed
* _returns_ (Promise): A promise that resolves when the operation has completed
* **Returns:** A promise that is resolved with the deleted [Line](./types.md#line) when the operation has completed.
* **Events:** Causes a [`deleteLine`](./events.md#deleteline) event.
* **Availability:** Only if a collaborative map is opened using its writable or admin ID.
## `exportLine(data)`
@ -237,58 +312,68 @@ Export a line.
* `format` (string): One of the following:
* `gpx-trk`: GPX track (contains the whole course of the route)
* `gpx-rte`: GPX route (contains only the route points, and the navigation device will have to calculate the route)
* _returns_ (Promise&lt;string&gt;)
* **Returns:** A promise that is resolved with a string that contains the file.
* **Events:** None.
* **Availability:** If a collaborative map is opened.
## `addType(data)`
Create a type.
* `data` ([type](./types#type)): The data of the type to create. An `id` will be assigned by the server
* _returns_ (Promise<[type](./types#type)>): The type as it is on the server, with an `id` assigned.
* `data` ([Type](./types.md#type)): The data of the type to create. An `id` will be assigned by the server.
* **Returns:** A promise that is resolved with the created [Type](./types.md#type)>, with an `id` assigned.
* **Events:** Causes a [`type`](./events.md#type) event.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `editType(data)`
Update an existing type.
* `data` ([type](./types#type)). The new type data. Fields that are not defined will not be unmodified. Only `id` needs
to be defined.
To rename a field, set the `oldName` property of the field object to the previous name and the `name` property to the
new name. To rename a dropdown entry, set the `oldValue` property to the old value and the `value` property to the new
value.
* _returns_ (Promise<[type](./types#type)>): The new type.
* `data` ([type](./types.md#type)). The new type data. Fields that are not defined will not be unmodified. Only `id` needs to be defined. To rename a field, set the `oldName` property of the field object to the previous name and the `name` property to the new name. To rename a dropdown entry, set the `oldValue` property to the old value and the `value` property to the new value.
* **Returns:** A promise that is resolved with the updated <[Type](./types.md#type)>.
* **Events:** Causes a [`type`](./events.md#type) event. If the update causes the styles of existing markers or lines to change, events for those are triggered as well.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `deleteType(data)`
Delete an existing type
* `data` (`{id: <typeId>}`): An object that contains the ID of the type to be removed
* _returns_ (Promise): A promise that resolves when the operation has completed
* **Returns:** A promise that is resolved with the deleted [Type](./types.md#type) when the operation has completed. If there are any objects on the map that still use this type, the promise rejects.
* **Events:** Causes a [`deleteType`](./events.md#deletetype) event.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `addView(data)`
Create a view.
* `data` ([view](./types#view)): The data of the view to create. An `id` will be assigned by the server
* _returns_ (Promise<[view](./types#view)>): The view as it is on the server, with an `id` assigned.
* `data` ([view](./types.md#view)): The data of the view to create. An `id` will be assigned by the server
* **Returns:** A promise that is resolved with the created <[View](./types.md#view)>), with an `id` assigned.
* **Events:** Causes a [`view`](./events.md#view) event.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `editView(data)`
Update an existing view.
* `data` ([view](./types#view)). The new view data. Fields that are not defined will not be unmodified. Only `id` needs
to be defined.
* _returns_ (Promise<[view](./types#view)>): The new view.
* `data` ([view](./types.md#view)). The new view data. Fields that are not defined will not be unmodified. Only `id` needs to be defined.
* **Returns:** A promise that is resolved with the updated <[View](./types.md#view)>).
* **Events:** Causes a [`view`](./events.md#view) event.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `deleteView(data)`
Delete an existing view
* `data` (`{id: <viewId>}`): An object that contains the ID of the view to be removed
* _returns_ (Promise): A promise that resolves when the operation has completed
* **Returns:** A promise that is resolved when the operation has completed.
* **Events:** Causes a [`deleteView`](./events.md#deleteview) event.
* **Availability:** Only if a collaborative map is opened using its admin link.
## `geoip()`
Returns an approximate location for the IP address of the client.
* _returns_ (Promise<{top,right,bottom,left}>) A promise that resolves to a bounding box that includes the location of
the client
* **Returns:** A promise that is resolved to a [bounding box](./types.md#bbox) (without zoom) that includes the location of the client. If no location can be determined, the promise is resolved with `undefined`.
* **Events:** None.
* **Availability:** Always.

Wyświetl plik

@ -25,25 +25,27 @@ client.on("marker", (marker) => {
});
```
## `padId`
## `mapId`
The ID under which the client is connected to the map. Can be the read-only or a read-write ID of an existing map.
The ID of the collaborative map that the client is connected to. Can be the read-only, writable or admin ID of an existing map.
Note that the ID can be changed in the settings. If in case of a [`padData`](./events#paddata) event, the ID of the pad has
changed, this property is updated automatically.
Note that the ID can be changed in the settings. If in case of a [`mapData`](./events.md#mapdata) event, the ID of the map has changed, this property is updated automatically.
_Set:_ when calling [`setMapId`](./methods.md#setmapid-mapid) and in response to a [`mapData`](./events.md#mapdata) event.\
_Type:_ string
## `readonly`
`true` if the map has been opened using its read-only ID. `false` if the map is writable.
_Set:_ during [`setMapId`](./methods.md#setmapid-mapid).\
_Type:_ boolean
## `writable`
`2` if the map has been opened using its admin ID, `1` if if has been opened using the writable ID, `0` if the map is read-only.
_Set:_ during [`setMapId`](./methods.md#setmapid-mapid).\
_Type:_ number
@ -51,56 +53,82 @@ _Type:_ number
`true` if the map was deleted while this client was connected to it.
## `padData`
_Set:_ in response to a [`deleteMap`](./events.md#deletemap) event.\
_Type:_ boolean
## `mapData`
The current settings of the map. `writeId` and/or `adminId` is null if if has been opened using another ID than the admin ID.
_Type:_ [padData](./types#paddata)
_Set:_ in response to a [`mapData`](./events.md#mapdata) event.\
_Type:_ [MapData](./types.md#mapdata)
## `markers`
All markers that have been retrieved so far.
_Type:_ `{"<marker id>": `[`marker`](./types#marker)`}`
_Set:_ in response to [`marker`](./events.md#marker) and [`deleteMarker`](./events.md#deletemarker) events.\
_Type:_ [<code>{ &#91;markerId: number&#93;: Marker }</code>](./types.md#marker)
## `lines`
All lines and their track points that have been retrieved so far.
All lines of the map along with the track points that have been retrieved so far.
_Type:_ `{"<line id>": `[`line with trackPoints`](./types#line)`}`
_Set:_ in response to [`line`](./events.md#line), [`linePoints`](./events.md#linepoints) and [`deleteLine`](./events.md#deleteline) events.\
_Type:_ [<code>{ &#91;lineId: number&#93;: Line }</code>](./types.md#line) (with track points)
## `views`
All views that have been retrieved so far.
All views of the map.
_Type:_ `{"<view id>": `[`view`](./types#view)`}`
_Set:_ in response to [`view`](./events.md#view) and [`deleteView`](./events.md#deleteview) events.\
_Type:_ [<code>{ &#91;viewId: number&#93;: View }</code>](./types.md#view)
## `types`
All types that have been retrieved so far.
All types of the map.
_Type:_ `{"<type id>": `[`type`](./types#type)`}`
_Set:_ in response to [`type`](./events.md#type) and [`deleteType`](./events.md#deletetype) events.\
_Type:_ [<code>{ &#91;typeId: number&#93;: Type }</code>](./types.md#type)
## `history`
All history entries that have been retrieved so far. Note that you have to subscribe to the history using
[`listenToHistory()`](./methods#listentohistory).
All history entries that have been retrieved so far. Note that you have to subscribe to the history using [`listenToHistory()`](./methods.md#listentohistory).
_Type:_ `{"<entry id>": `[`historyEntry`](./types#historyentry)`}`
_Set:_ in response to [`history`](./events.md#history) events.\
_Type:_ [<code>{ &#91;entryId: number&#93;: HistoryEntry }</code>](./types.md#historyentry)
## `route`
Information about the temporary route set using [`setRoute()`](./methods#setroute-data).
Details and track points (simplified for the current bbox) for the active route set using [`setRoute()`](./methods.md#setroute-data) with `routeId` set to `undefined`, or `undefined` if no such route is active.
_Type:_ [`route`](./types#route)
_Set:_ during [`setRoute()`](./methods.md#setroute-data) and in response to [`routePoints`](./events.md#routepoints) events.\
_Type:_ [`Route`](./types.md#route)
## `routes`
Details and track points (simplified for the current bbox) for the active routes set using [`setRoute()`](./methods.md#setroute-data) with `routeId` set to a string.
_Set:_ during [`setRoute()`](./methods.md#setroute-data) and in response to [`routePoints`](./events.md#routepoints) events.\
_Type:_ [<code>{ &#91;routeId: string&#93;: Route }</code>](./types.md#route)
## `serverError`
If the opening the pad failed ([`setPadId(padId)`](./methods#setpadid-padid) promise got rejected), the error message is stored
in this property.
If the opening the map failed ([`setMapId(mapId)`](./methods.md#setmapid-mapid) promise got rejected), the error message is stored in this property.
_Set:_ in response to a [`serverError`](./events.md#servererror) event (fired during [`setMapId`](./methods.md#setmapid-mapid)).\
_Type:_ Error
## `loading`
A number that indicates how many requests are currently pending. You can use this to show a loading spinner or disable certain
UI elements while the value is greater than 0.
A number that indicates how many requests are currently pending (meaning how many async methods are currently running). You can use this to show a loading spinner or disable certain UI elements while the value is greater than 0.
_Set:_ increased when any method is called and decreased when the method returns.\
_Type:_ `number`
## `disconnected`
`false` in the beginning, changed to `true` as soon as the socket.io connection is made. May be `false` temporarily if the connection is lost.
_Set:_ in reaction to `connect` and `disconnect` events.\
_Type:_ `boolean`

Wyświetl plik

@ -1,6 +1,8 @@
# Types
## bbox
The [facilmap-types](https://www.npmjs.com/package/facilmap-types) package contains TypeScript typings for all the basic types used by the different components of FacilMap. Using TypeScript can be helpful to get some assistance what kind of properties an object provides and expects.
## Bbox
A bounding box that describes which part of the map the user is currently viewing.
@ -8,10 +10,9 @@ A bounding box that describes which part of the map the user is currently viewin
* `bottom` (number, min: -90, max: 90): The latitude of the south end of the box
* `left` (number, min: -180, max: 180): The longitude of the west end of the box
* `right` (number, min: -180, max: 180): The longitude of the east end of the box
* `zoom` (number, min: 1, max: 20): The current zoom level. This is relevant for the density of track points that
should be received.
* `zoom` (number, min: 1, max: 20): The current zoom level. This is relevant for the density of track points that should be received.
## marker
## Marker
* `id` (number): The ID of this marker
* `lat` (number, min: -90, max: 90): The latitude of this marker
@ -19,13 +20,13 @@ A bounding box that describes which part of the map the user is currently viewin
* `name` (string): The name of this marker
* `colour` (string): The colour of this marker as a 6-digit hex value, for example `ff0000`
* `size` (number, min: 15): The height of the marker in pixels
* `symbol` (string): The symbol code for the marker. Default is an empty string.
* `shape` (string): The shape code for the marker. Default is an empty string (equals `"drop"`).
* `elevation` (number): The elevation of this marker in metres (set by the server)
* `icon` (string): The icon name for the marker. Default is an empty string.
* `shape` (string): The shape name for the marker. Default is an empty string (equivalent to `"drop"`).
* `ele` (number or null): The elevation of this marker in metres (set by the server)
* `typeId` (number): The ID of the type of this marker
* `data` (`{"key", "value"}`): The filled out form fields of the marker
* `data` (<code>{ &#91;fieldName: string&#93;: string }</code>): The filled out form fields of the marker. This is a null-prototype object to avoid prototype pollution.
## line
## Line
Each line has `routePoints` and `trackPoints`. The `routePoints` are the start, end and via points that the user created
for that line, the `trackPoints` describe how the line should be drawn. If no routing is used, `routePoints` and
@ -44,18 +45,20 @@ separately through `linePoints` events.
* `mode` (string): The routing mode, an empty string for no routing, or `car`, `bicycle`, `pedestrian`, or `track`
* `colour` (string): The colour of this marker as a 6-digit hex value, for example `0000ff`
* `width` (number, min: 1): The width of the line
* `stroke` (string): The stroke style of the line, an empty string for solid or `dashed` or `dotted`.
* `name` (string): The name of the line
* `distance` (number): The distance of the line in kilometers (set by the server)
* `ascent`, `descent` (number): The total ascent/descent of the line in metres (set by the server)
* `time` (number): The time it takes to travel the route in seconds (only if routing mode is `car`, `bicycle` or `pedestrian`) (set by the server)
* `left`, `top`, `right`, `bottom` (number): The bounding box of the line (set by the server)
* `extraInfo` (<code>{ &#91;type: string&#93;: Array<&#91;startIdx: number, endIdx: number, type: number&#93;>> }</code> or null): Extra details about the route (set by the server). `type` can be for example `steepness`, `surface` or `waytype`. `startIdx` and `endIdx` describe a segment on the trackpoints of the route, the meaning of `type` can be seen in the documentation of [Leaflet.Heightgraph](https://github.com/GIScience/Leaflet.Heightgraph/blob/master/example/mappings.js).
* `typeId` (number): The ID of the type of this line
* `data` (`{"key", "value"}`): The filled out form fields of the line
* `data` (<code>{ &#91;fieldName: string&#93;: string }</code>): The filled out form fields of the line. This is a null-prototype object to avoid prototype pollution.
* `trackPoints`:
* In the `lines` property of the client, an object of the format `{"<idx>": trackPoint}`
* When creating/updating a line with the routing mode `track`, an array of the format `[trackPoint]`
* In the `lines` property of the client, an object of the format [<code>{ &#91;idx: number&#93;: TrackPoint }</code>](#trackpoint)
* When creating/updating a line with the routing mode `track`, an array of the format [`TrackPoint[]`](#trackpoint)
## trackPoint
## TrackPoint
All track points have a `zoom` level and are only received when the zoom level of the current bbox is at least that
level. This makes sure that at a small zoom level, only a low resolution of the line has to be downloaded. When zooming
@ -66,22 +69,23 @@ their `idx` property.
* `lat` (number, min: -90, max: 90): The latitude of this point
* `lon` (number, min: -180, max: 180): The longitude of this point
* `zoom` (number, min: 1, max: 20): The miminum zoom level from which this track point makes sense to show
* `ele` (number): The elevation of this track point in metres (set by the server). Not set for high zoom levels.
* `ele` (number or null): The elevation of this track point in metres (set by the server). Not set for high zoom levels.
## padData
## MapData
* `id` (string): The read-only ID of this map
* `writeId` (string): The read-write ID of this map
* `adminId` (string): The admin ID of this map
* `writeId` (string): The read-write ID of this map (not set when opened through its read-only ID)
* `adminId` (string): The admin ID of this map (not set when opened through its writable or read-only ID)
* `name` (string): The name of this map
* `searchEngines` (boolean): Whether search engines may index the read-only version of this map
* `description` (string): The description for search engines
* `clusterMarkers` (boolean): Whether many markers close to each other should be grouped together
* `legend1`, `legend2` (string): Markdown free text to be shown above and below the legend
* `defaultViewId` (number): The ID of the default view (if any)
* `defaultView` ([view](#view)): A copy of the default view object
* `defaultView` ([view](#view)): A copy of the default view object (set by the server)
* `createDefaultTypes` (boolean): On creation of a map, set this to false to not create one marker and one line type.
## view
## View
* `id` (number): The ID of this view
* `name` (string): The name of this view
@ -90,7 +94,7 @@ their `idx` property.
* `top`, `bottom`, `left`, `right`: The [bbox](#bbox) of this view
* `filter` (string): If set, filter the objects according to this filtrex expression
## historyEntry
## HistoryEntry
* `id` (number): The ID of this history entry
* `time` (Date): The time when the modification was done
@ -100,29 +104,28 @@ their `idx` property.
* `objectBefore` (object): The object before the modification (null if `action` is `create`)
* `objectAfter` (object): The object after the modification (null if `action` is `delete`)
## type
## Type
* `id` (number): The ID of this type
* `name` (string): The name of this type
* `name` (string): The name of this type. Note that the if the name is "Marker" or "Line", the FacilMap UI will translate the name to other languages even though the underlying name is in English.
* `type` (string): `marker` or `line`
* `defaultColour`, `defaultSize`, `defaultSymbol`, `defaultWidth`, `defaultMode` (string/number): Default values for the
* `idx` (number): The sorting position of this type. When a list of types is shown to the user, it must be ordered by this value. If types were deleted or reordered, there may be gaps in the sequence of indexes, but no two types on the same map can ever have the same index. When setting this as part of a type creation/update, other types with a same/higher index will have their index increased to be moved down the list.
* `defaultColour`, `defaultSize`, `defaultIcon`, `defaultShape`, `defaultWidth`, `defaultStroke`, `defaultMode` (string/number): Default values for the
different object properties
* `colourFixed`, `sizeFixed`, `symbolFixed`, `shapeFixed`, `widthFixed`, `modeFixed` (boolean): Whether those values are fixed and
* `colourFixed`, `sizeFixed`, `iconFixed`, `shapeFixed`, `widthFixed`, `strokeFixed`, `modeFixed` (boolean): Whether those values are fixed and
cannot be changed for an individual object
* `fields` ([object]): The form fields for this type. Each field has the following properties:
* `name` (string): The name of the field. This is at the same time the key in the `data` properties of markers and lines
* `oldName` (string): When renaming a field (using [`editType(data)`](./methods#edittype-data)), specify the former name here
* `type` (string): The type of field, one of `textarea`, `dropdown`, `checkbox`, `input`
* `controlColour`, `controlSize`, `controlSymbol`, `controlShape`, `controlWidth` (boolean): If this field is a dropdown, whether
the different options set a specific property on the object
* `default` (string/boolean): The default value of this field
* `options` ([object]): If this field is a dropdown, an array of objects with the following properties:
* `value` (string): The value of this option.
* `oldValue` (string): When renaming a dropdown option (using [`editType(data)`](./methods#edittype-data)), specify the
former value here
* `colour`, `size`, `shape`, `symbol`, `width` (string/number): The property value if this field controls that property
* `name` (string): The name of the field. This is at the same time the key in the `data` properties of markers and lines. Note that the if the name is "Description", the FacilMap UI will translate the name to other languages even though the underlying name is in English.
* `oldName` (string): When renaming a field (using [`editType(data)`](./methods.md#edittype-data)), specify the former name here
* `type` (string): The type of field, one of `textarea`, `dropdown`, `checkbox`, `input`
* `controlColour`, `controlSize`, `controlIcon`, `controlShape`, `controlWidth`, `controlStroke` (boolean): If this field is a dropdown, whether the different options set a specific property on the object
* `default` (string/boolean): The default value of this field
* `options` ([object]): If this field is a dropdown or a checkbox, an array of objects with the following properties. For a checkbox, the array has to have 2 items, the first representing the unchecked and the second the checked state.
* `value` (string): The value of this option.
* `oldValue` (string): When renaming a dropdown option (using [`editType(data)`](./methods.md#edittype-data)), specify the former value here
* `colour`, `size`, `shape`, `icon`, `width`, `stroke` (string/number): The property value if this field controls that property
## searchResult
## SearchResult
* `short_name` (string): Name of the result
* `display_name` (string): Name with address
@ -132,18 +135,40 @@ their `idx` property.
* `zoom` (number): Zoom level at which there is a good view onto the result. Might be null if `boundingbox` is set.
* `extratags` (object): Extra OSM tags that might be useful
* `geojson` (object): GeoJSON if the result has a shape
* `icon` (string): Symbol key of the result
* `icon` (string): Icon key of the result
* `type` (string): Type of the result
* `id` (string): If the result is an OSM object, the ID of the OSM object, prefixed by `n` (node), `w` (way) or `r` (relation)
* `ele` (number): Elevation in meters
## route
## Route
* `routePoints` (array): Array of route points (objects with `lon` and `lat` properties)
* `mode` (string): Route mode: `"car"`, `"bicycle"`, `"pedestrian"` or an empty string `""` for a direct line
* `trackPoints` (array): An array of track points (objects with a `lon`, `lat`, `ele`, `idx` property and also a `zoom`
* `trackPoints` (array): An array of [track points](#trackpoint) (objects with a `lon`, `lat`, `ele`, `idx` property and also a `zoom`
property that indicates from which zoom level the track point should be shown (to avoid an unnecessarily high resolution))
* `distance` (number): The distance of the route in kilometers
* `time` (number): The time it takes to travel the route in seconds
* `ascent` (number): The total meters of climb
* `descent` (number) The total meters of drop
* `descent` (number): The total meters of drop
* `left`, `top`, `right`, `bottom` (number): The bounding box of the line (set by the server)
* `routeId` (string): Some methods allow specifying a custom string here to identify the route. This allows having multiple active routes per connection.
## RouteMode
The route mode is a string that describes for what type of transportation a route should be calculated. The following route modes are available:
* (empty string), aliases `helicopter`, `straight`: Go in a straight line
* `pedestrian`, aliases `foot`, `walk`, `walking`: Go by foot
* `bicycle`, alias `bike`: Go by bicycle
* `car`: Go by car
* `track`: Special route mode for lines, indicates that the line is a track imported from a file, not a calculated route.
To use advanced routing settings, additional keywords can be appended to the route mode, separated by a space:
* `hgv`: HGV (truck) routing (in combination with `car`)
* `road`, `mountain`, `electric`: type of bicycle (in combination with `bicycle`).
* `hiking`, `wheelchair`: type of pedestrian (in combination with `pedestrian`).
* `fastest`, `shortest`: Use this routing preference.
* `details`: Load route details (elevation profile, extra info).
* `highways`, `tollways`, `ferries`, `fords`, `steps`: Avoid these.
* `avoid`: Has no effect, but can be inserted in front of the avoidance types to make the route mode more readable.
An example advanced route mode would be `pedestrian wheelchair details avoid steps fords`. Any unknown words inside the route mode should be ignored (they may be options that used to be available in the past).

Wyświetl plik

@ -1,13 +1,12 @@
# Dev setup
1. Run `yarn install` to install the dependencies
2. Run `yarn build` to build the JS bundles
3. Copy `config.env.example` to `config.env` and adjust the settings
4. Run `yarn server` inside the `server` directory
2. Copy `config.env.example` to `config.env` and adjust the settings
3. Run `yarn dev-server` inside the `server` directory
For developing the frontend/client, FacilMap server can integrate webpack-dev-server. This server will automatically
recompile the frontend files when a file changes, and even delay reloads until the compilation has finished. To run
the dev server, run `yarn dev-server` instead of `yarn server` in the `server` directory. Note that changes in the `client`, `types` or `leaflet` directory still have to be built using `yarn build` in the respective directories for the dev-server to notice them.
This will start the FacilMap server with an integrated Vite dev server that takes care of transpiling the frontend on the fly and also integrating hot module reloading, which can apply Vue component changes without a page reload.
While developing the server, you can also run `yarn server` instead, which will start the server straight from the TypeScript files (which makes it obsolete to run `yarn build` every time before restarting the server) but without transpiling the frontend each time, which makes restarts faster.
To enable debug output of various components, additionally prepend the command by `DEBUG=*`. See the documentation of
[debug](https://github.com/visionmedia/debug). To only enable the debug logging of SQL queries, use `DEBUG=sql`.

Wyświetl plik

@ -0,0 +1,37 @@
# Documentation
The FacilMap documentation is built using [VuePress](https://vuepress.vuejs.org/) from the Markdown files in the [docs](https://github.com/FacilMap/facilmap/tree/main/docs) directory. The [facilmap/facilmap-docs](https://hub.docker.com/r/facilmap/facilmap-docs) Docker image is automatically generated with the latest docs.
## Dev setup
If you want to make some changes to the documentation, first install the necessary dependencies by running `yarn install` in the `docs` directory, and then run `yarn dev-server`. This will start a webserver on http://localhost:8080/ that will show the documentation. Whenever you make changes to the documentation files, the browser will automatically refresh the page and show the latest version.
## Build
Before building the documentation, run `yarn run check` to check that all links in the markdown point to a valid destination.
To build the documentation, run `yarn run build`. This will create static HTML files in `dist` that can be served by a simple HTTP server.
## Embed videos
In the documentation, more complex user interactions are illustrated by screencasts. Since the UI looks somewhat different on big screens than on small screens and works somewhat differently on touch devices than on mouse devices, two recordings of the same interaction are shown next to each other, one representing a desktop computer and one representing a smartphone.
The desktop video should have dimensions of 1024×768 pixels and the smartphone video 320×480 pixels. Both should be recorded with 15 fps and in MP4 format with a sensible framerate (the FFMPEG default bitrate has been used so far, using `ffmpeg -i recording_raw.mp4 recording.mp4`).
The existing recordings were created using [SimpleScreenRecorder](https://www.maartenbaert.be/simplescreenrecorder/). Chromium shows the dimensions of the viewport when the dev tools are open, which makes it easier to adjust it to the right size. For the mobile recordings, Chromium was configured to simulate an iPhone 4.
To embed a video into the documentation, the custom `Screencast` component can be used in Markdown:
```jsx
<Screencast :desktop="recordingMp4" :mobile="recordingMobileMp4"></Screencast>
```
## Embed screenshots
Screenshots should be saved as PNG, with a size of 1024×768 pixels for desktop and 320×480 pixels for mobile. To show the desktop and mobile screenshots next to each other, use the custom `Screenshot` component in Markdown:
```jsx
<Screenshot :desktop="screenshot.png')" :mobile="screenshot-mobile.png')"></Screenshot>
```
## Docker image
To build the docker image, run `docker build -t facilmap-docs .`. To run it, simply run `docker run -d facilmap-docs`. The docker container will expose a HTTP server on port 80.

Wyświetl plik

@ -0,0 +1,66 @@
# Embed FacilMap
You can embed a map into any website using an iframe:
```html
<iframe style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap"></iframe>
```
If you use a map ID that does not exist yet, the “Create Collaborative Map” dialog will be opened when accessing the
map (unless the `interactive` parameter is `false`).
## Options
You can control the display of different components by using the following query parameters:
* `toolbox`: Show the toolbox (default: `true`)
* `search`: Show the search box (default: `true`)
* `route`: Show the route tab in the search box (default: `true`)
* `pois`: Show the POIs tab in the search box (default: `true`)
* `autofocus`: Autofocus the search field (default: `false`)
* `legend`: Show the legend if available (default: `true`)
* `locate`: Show the locate control to zoom to the users location (default: `true`)
* `interactive`: Enable [interactive mode](#interactive-mode) (default: `false`)
* `lang`: Use this display language (for example `en`) by default, instead of the language set by the user in the user preferences dialog or in their browser.
* `units`: Use this type of units (either `metric` or `us_customary`) by default, instead of what the user has configured in the user preferences dialog.
Example:
```html
<iframe style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap?search=false&amp;toolbox=false"></iframe>
```
## Interactive mode
When embedding FacilMap into a website, you may want to disable certain UI interactions that make more sense when FacilMap runs as a standalone app. For example, when you want to embed a specific collaborative map, you may want to disable any interactions that will navigate the user away from the map, such as closing the map or navigating to a bookmark. Disabling interactive mode will hide the following UI interactions:
* “Collaborative map” menu in the toolbox, including bookmarks, the “Open collaborative map” dialog and “Close current map”.
* “Share” dialog.
* “Open file” or dragging a geographic file onto the map.
* “Create collaborative map” dialog when opening a map ID that does not exist.
* “Close map” button when the open map is deleted.
## Location hash
When a FacilMap is opened directly in the browser, the current view of the map is [added to the location hash](../users/share/) (the part after the `#` in the URL). This means that users can easily share the current view by copying the URL straight from the address bar of their browser, and reloading the page will not cause the current view to be lost.
FacilMap emits a [cross-origin message](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) every time it updates the location map. You can listen to it to synchronize the location hash of your website with the one of FacilMap by using the following script:
```html
<iframe id="facilmap" style="height: 500px; width: 100%; border: none;" src="https://facilmap.org/mymap"></iframe>
<script>
window.addEventListener("message", function(evt) {
if(evt.data && evt.data.type == "facilmap-hash" && location.hash != "#" + evt.data.hash)
location.replace("#" + evt.data.hash);
});
function handleHashChange() {
var iframe = document.getElementById("facilmap");
iframe.src = iframe.src.replace(/(#.*)?$/, "") + location.hash;
}
window.addEventListener("hashchange", handleHashChange);
if (location.hash)
handleHashChange();
</script>
```

Wyświetl plik

@ -0,0 +1,59 @@
# Overview
The FacilMap frontend is a [Vue.js](https://vuejs.org/) app that provides the main FacilMap UI. You can use it to integrate a modified or extended version of the FacilMap UI or its individual components into your app. If you just want to embed the whole FacilMap UI without any modifications, it is easier to [embed it as an iframe](../embed.md).
The FacilMap frontend is available as the [facilmap-frontend](https://www.npmjs.com/package/facilmap-frontend) package on NPM.
Right now there is no documentation of the individual UI components, nor are they designed to be reusable or have a stable interface. Use them at your own risk and have a look at the [source code](https://github.com/FacilMap/facilmap/tree/main/frontend/src/lib) to get an idea how to use them.
## Setup
The FacilMap frontend is published as an ES module. It is meant to be used as part of an app that is built using a bundler, rather than importing it directly into a HTML file.
The frontend heavily relies on Vue, Bootstrap and other large libraries. These are not part of the bundle, but are imported using `import` JavaScript statements. This avoids duplicating these dependencies if you also use them elsewhere in your app.
To get FacilMap into your app, install the NPM package using `npm install -S facilmap-frontend` or `yarn add facilmap-frontend` and then import the components that you need.
```javascript
import { FacilMap } from "facilmap-frontend";
```
The FacilMap UI uses a slightly adjusted version of [Bootstrap 5](https://getbootstrap.com/) for styling. To avoid duplication if you want to integrate FacilMap into an app that is already using Bootstrap, the Bootstrap CSS is not part of the main export. If you want to use FacilMaps default Bootstrap styles, import them separately from `facilmap-frontend/bootstrap.css`:
```javascript
import "facilmap-frontend/bootstrap.css";
```
## Structure
The [`<FacilMap>`](./facilmap.md) component renders the whole frontend, including a component that provides a connection to the FacilMap server. If you want to render the whole FacilMap UI, simply render that component rather than rendering all the components of the UI individually.
```vue
<FacilMap
baseUrl="https://facilmap.org/"
serverUrl="https://facilmap.org/"
:mapId="undefined"
></FacilMap>
```
The `<FacilMapContextProvider>` component provides a reactive object that acts as the central hub for all components to provide their public state (for example the facilmap-client object, the current map ID, the current selection, the current map view) and their public API (for example to open a search box tab, to open a map, to set route destinations). All components that need to communicate with each other use this context for that. This means that if you want to render individual UI components, you need to make sure that they are rendered within a `<FacilMapContextProvider>` (or within a `<FacilMap>`, which renders the context provider for you). It also means that if you want to add your own custom UI components, they will benefit greatly from accessing the context.
The context is both injected and exposed by both the `<FacilMap>` and the `<FacilMapContextProvider>` component. To access the injected context, import the `injectContextRequired` function from `facilmap-frontend` and call it in the setup function of your component. For this, the component must be a child of `<FacilMap>` or `<FacilMapContextProvider>`. To access the exposed context, use a ref:
```vue
<script setup lang="ts">
import { FacilMap } from "facilmap-frontend";
const facilMapRef = ref<InstanceType<typeof FacilMap>>();
// Access the context as facilMapRef.value.context
</script>
<template>
<FacilMap ref="facilMapRef"></FacilMap>
</template>
```
For now there is no documentation of the context object. To get an idea of its API, have a look at its [source code](https://github.com/FacilMap/facilmap/blob/main/frontend/src/lib/components/facil-map-context-provider/facil-map-context.ts).
## Styling
All FacilMap components use static class names to make it possible to adjust the styling. FacilMap class names are prefixed with `fm-`.

Wyświetl plik

@ -0,0 +1,66 @@
# FacilMap component
The `FacilMap` component renders a complete FacilMap UI. It can be used like this in a Vue.js app:
```vue
<script setup>
import { FacilMap } from "facilmap-frontend";
</script>
<template>
<FacilMap
baseUrl="https://facilmap.org/"
serverUrl="https://facilmap.org/"
mapId="my-map"
></FacilMap>
</template>
```
In a non-Vue.js app, it can be embedded like this:
```javascript
import { FacilMap } from "facilmap-frontend";
import Vue, { createApp, defineComponent, h } from "vue";
createApp(defineComponent({
setup() {
return () => h(FacilMap, {
baseUrl: "https://facilmap.org/",
serverUrl: "https://facilmap.org/",
mapId: "my-map"
});
}
})).mount(document.getElementById("facilmap")!); // A DOM element that be replaced with the FacilMap component
```
## Props
Note that all of these props are reactive and can be changed while the map is open.
* `baseUrl` (string, required): Collaborative maps should be reachable under `${baseUrl}${mapId}`, while the general map should be available under `${baseUrl}`. For the default FacilMap installation, `baseUrl` would be `https://facilmap.org/`. It needs to end with a slash. It is used to create the map URL for example in the map settings or when switching between different maps (only in interactive mode).
* `serverUrl` (string, required): The URL under which the FacilMap server is running, for example `https://facilmap.org/`. This is invisible to the user.
* `mapId` (string or undefined, required): The ID of the collaborative map that should be opened. If this is undefined, no map is opened. This is reactive, when a new value is passed, a new map is opened. Note that the map ID may change as the map is open, either because the ID of the map is changed in the map settings, or because the user navigates to a different map (only in interactive mode). Use `v-model:mapId` to get a [two-way binding](https://vuejs.org/guide/essentials/forms.html) (or listen to the `update:mapId` event).
* `settings` (object, optional): An object with the following properties:
* `toolbox` (boolean, optional): Whether the toolbox should be shown. Default is `true`.
* `search` (boolean, optional): Whether the search box should be shown. Default is `true`.
* `autofocus` (boolean, optional): Whether the search field should be focused. Default is `false`.
* `legend` (boolean, optional): Whether the legend should be shown (if it is available). Default is `true`.
* `interactive` (boolean, optional): Whether [interactive mode](../embed.md#interactive-mode) should be enabled. Default is `true`.
* `linkLogo` (boolean, optional): If `true`, the FacilMap logo will be a link that opens the map in a new window. Default is `false`.
* `updateHash` (boolean, optional): Whether `location.hash` should be synchonised with the current map view. Default is `false`.
## Events
* `update:mapId`: When the ID of the currently open map is changed, either because the ID of the map was changed in the map settings or because the user navigated to another map. The parameter is a string or undefined (if no map is opened).
* `update:mapName`: When the name of the currently open map is changed, either because it was changed in the map settings or because the user navigated to another map. The parameter is a string or undefined (if no map is opened).
## Slots
* `default`: Components in this slot are rendered directly on top of the map. It makes sense to position them absolutely and choose an appropriate `z-index`.
* `before`, `after`: Components in these slots are rendered above and below the map. Note that on narrow screens (such as smartphones), the search box is also rendered in the `after` slot.
## Styling
By default, the `FacilMap` component renders a flex box with `flex-grow: 1`. To render it at a certain size, wrap it in a flex box of a certain size or override the styles of the `fm-facilmap` class.
The map itself will shrink to make space for the `before` and `after` slots.

Wyświetl plik

@ -0,0 +1,93 @@
# I18n
FacilMap uses [i18next](https://www.i18next.com/) for internationalization throughout the frontend, the server and its libraries. It detects the desired user language like this:
* In the browser, [i18next-browser-languageDetector](https://github.com/i18next/i18next-browser-languageDetector) is used to detect the users language. It looks at the configured browser languages ([`navigator.languages`](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/languages)) and checks for which one a translation exists. The configured language can be overridden by setting a `lang` cookie or appending a `?lang=` query parameter to the URL.
* On the server, when a request is handled through HTTP (including the WebSocket), [i18next-http-middleware](https://www.npmjs.com/package/i18next-http-middleware) is used to detect the users language. It looks at the configured browser languages ([`Accept-Language`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language)) and checks for which one a translation exists. The configured language can be overridden by setting a `lang` cookie or by appending a `?lang=` query parameter to the URL. The server stores the selected language in the [Node.js domain](https://nodejs.org/api/domain.html) that is created for each incoming request, causing all functions triggered (sync or async) from the request to use the language setting of the request.
* On the sever, when a function is called outside of an incoming HTTP request, messages are not internationalized and output in English.
In addition, certain values can be shown in metric units or in US customary units. By default, metric units are used. This can be changed by sending a `units` query parameter or cookie with the value `us_customary`.
Translations are managed on [Weblate](https://hosted.weblate.org/projects/facilmap/), changes there automatically trigger a pull request to the FacilMap repository.
## Use FacilMap in an app not using i18next
When you import any of the FacilMap modules into a JavaScript app that does not use i18next, they will automatically detect the user language and internationalize their output accordingly as described above.
The main instance of i18next is [initialized](https://www.i18next.com/overview/api#init) by FacilMap as soon as the first message is internationalized. This that before calling any of the functions exported by FacilMap, you can still change the i18next configuration.
### Change the language detector
To change the detected user language, you have two options. As mentioned above, both options need to be executed before calling any FacilMap functions that generate internationalized messages.
Option 1 is to set a custom [language detector](https://www.i18next.com/overview/plugins-and-utils#language-detector) using the `setLanguageDetector()` function exported by `facilmap-utils`. This language detector is applied to the `i18next` main instance when it is initialized instead of the default language detector used by FacilMap.
```typescript
import { setLanguageDetector } from "facilmap-utils";
setLanguageDetector(myLanguageDetector);
```
Option 2 is to set a custom [i18next instance](https://www.i18next.com/overview/api#instance-creation) that will be used by FacilMap. In this example, we are disabling the language detector and use a custom i18next instance that always uses German:
```typescript
import { setLanguageDetector, setI18nGetter } from "facilmap-utils";
import { createInstance } from "i18next";
setLanguageDetector(undefined);
setI18nGetter(() => createInstance({ lng: "de" }));
```
### Use the backend language detector
If you are including some of FacilMaps server modules in your own Node.js express server and want messages to be internationalized according to the user language as described above, you need to add FacilMaps `i18nMiddleware` to your express server:
```typescript
import express from "express";
import domainMiddleware from "express-domain-middleware";
import { i18nMiddleware } from "facilmap-server";
const app = express();
app.use(domainMiddleware);
app.use(i18nMiddleware);
```
As seen in the example, `i18nMiddleware` requires [express-domain-middleware](https://www.npmjs.com/package/express-domain-middleware) to be initialized before it.
## Use FacilMap in an app already using i18next
When you use FacilMaps modules in an app that is already using i18next, you may want FacilMap to reuse your existing i18n configuration (such as language detection), or you may want it to use its own configuration independently from yours.
When a FacilMap function internationalizes a message for the first time, it initializes i18next in the following way:
1. It retrieves the i18next instance set through `setI18nGetter()` (exported by `facilmap-utils`). If no instance was set, it defaults to the i18next main instance (`import i18next from "i18next"`).
2. If the instance is not initialized yet (`!i18next.isInitializing && !i18next.isInitialized`), it initializes it with its default configuration (language detector as described above).
3. It adds its resources to the instance under namespaces prefixed by `facilmap-`.
### Reuse your i18next instance for FacilMap
You can make FacilMap reuse your existing i18next instance and configuration in the following way:
* Make sure your i18next instance is initialized before you call any FacilMap functions that need to internationalize messages.
* Call `setI18nGetter()` with a callback that returns your i18next instance (can be skipped if you are using the main instance).
```typescript
import { setLanguageDetector, setI18nGetter } from "facilmap-utils";
import { createInstance } from "i18next";
const i18next = createInstance();
await i18next.init({
lng: "en"
});
setI18nGetter(() => i18next);
```
### Use a separate i18next instance for FacilMap
If you want to make FacilMap use a separate i18next instance, for example because you want to keep your and FacilMaps language detection independent, use the following example:
```typescript
import { setI18nGetter } from "facilmap-utils";
import { createInstance } from "i18next";
setLanguageDetector(undefined);
setI18nGetter(() => createInstance());
```
Because the instance returned by `createInstance()` is not initialized, FacilMap will initialize it using its default configuration.

Wyświetl plik

@ -0,0 +1,54 @@
# Overview
The Leaflet components of FacilMap are classes that you can use to show FacilMap data on a [Leaflet](https://leafletjs.com/) map.
The Leaflet components are only useful in combination with a [FacilMap client](../client/) instance. Most components either require an object created by the client as an argument, or require the client instance itself and render the available map data automatically.
## Components
* [BboxHandler](./bbox.md) automatically calls [updateBbox()](../client/methods.md#updatebbox-bbox) when the position of the map changes.
* [Layers](./layers.md) provides the layers that FacilMap offers by default and helpers to show them.
* [Markers](./markers.md) shows the markers of a collaborative map.
* [Lines](./lines.md) shows the lines of a collaborative map.
* [Route](./route.md) allows showing a calculated route on the map.
* [Search](./search.md) renders search results.
* [Overpass](./overpass.md) shows amenities and POIs in the current map bounds using the Overpass API.
* [Icons](./icons.md) provides methods to draw marker icons and shapes.
* [HashHandler](./hash.md) hooks up the location hash to the current map view.
* [Views](./views.md) allow controlling the map view and handling saved views.
* [Filter](./filter.md) expressions can be used to control which markers/lines are visible on the map.
* [Click listener](./click-listener.md) is a helper to ask the user to click somewhere on the map.
## Setup
The Leaflet components are published on npm as [facilmap-leaflet](https://www.npmjs.com/package/facilmap-leaflet). The recommended way to use them is to install the package using npm or yarn:
```bash
npm install -S facilmap-leaflet
```
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { BboxHandler } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/");
new BboxHandler(map, client).enable();
```
However, a build to use the components directly in the browser is also available. The build contains all the dependencies except Leaflet, including the FacilMap client. The components are available in the global `L.FacilMap` object.
```html
<script src="https://unpkg.com/leaflet@1"></script>
<script src="https://unpkg.com/facilmap-leaflet@3/dist/facilmap-leaflet.full.js"></script>
<script>
const map = L.map('map');
const client = new FacilMap.Client("https://facilmap.org/");
new L.FacilMap.BboxHandler(map, client).enable();
</script>
```
## Example
An example that shows all the Leaflet components in actions can be found in [example.html](https://github.com/FacilMap/facilmap/blob/main/leaflet/example.html) ([demo](https://unpkg.com/facilmap-leaflet/example.html)).

Wyświetl plik

@ -0,0 +1,32 @@
# BboxHandler
The FacilMap client needs to tell the current bounding box and zoom level of the map to the server in order to receive the following information:
* If a collaborative map is open, the markers in the specified bounding box
* If a collaborative map is open, the line points in the specified bounding box (simplified for the specified zoom level)
* If a calculated route is active, the route points in the specified bounding box (simplified for the specified zoom level).
BboxHandler automatically calls [updateBbox()](../client/methods.md#updatebbox-bbox) whenever the position of the map changes, either because the user panned the map or because it was changed programmatically.
## Usage
BboxHandler extends [L.Handler](https://leafletjs.com/reference.html#handler). To use it, pass the map and client objects to the constructor and enable the handler.
```javascript
import { map } from "leaflet";
import Client from "facilmap-client";
import { BboxHandler } from "facilmap-leaflet";
const map = map('map');
const client = new Client("https://facilmap.org/");
new BboxHandler(map, client).enable();
```
## Technical details
The bbox is only sent to the server if one of the following conditions are met (because only in these conditions the bbox is relevant for the server):
* A collaborative map is opened
* A route is active
The bbox will be sent as soon as one of these conditions is fulfilled, and each time the position of the map is changed. The bbox is updated when Leaflet fires a [`moveend`](https://leafletjs.com/reference.html#map-moveend) event, so if the user is panning the map around, the bbox is only updated when they are done, not in the process.
If the position of the map is updated programmatically using [`flyTo()`](https://leafletjs.com/reference.html#map-flyto) (or `flyToBounds()`), the destination bbox is already set when the animation starts, so that the map data can already be loaded while the animation is in progress.

Wyświetl plik

@ -0,0 +1,55 @@
# Click listener
The click listener is a helper that requires the user to click one point on the map. The frontend uses this in the “Add marker” function for example. While the click listener is active, the mouse cursor changes into a crosshair and it is not possible to click any objects on the map anymore, but it is still possible to move the map around.
Calling `addClickListener(map, listener, moveListener)` will enable the click listener. The listener waits for one click and then disables itself again. If you need to listen for multiple clicks in a row, simply enable another click listener when the user clicks. The method returns an object that contains a `cancel()` method, which you can use to manually cancel the handler. `addClickListener` accepts the following arguments:
* `map` ([Map](https://leafletjs.com/reference.html#map)): The Leaflet map
* `listener` (Function): A function that will be called once with the coordinates of the click (an object with a `lat` and `lon` property). Note that if the listener is canceled using the `cancel()` method, the listener is not called.
* `moveListener` (Function, optional): A function that will be called continuously with the mouse coordinates (an object with a `lat` and `lon` property) as the user moves the mouse across the map. Note that on touch devices, `listener` will be called without `moveListener` ever being invoked.
## Interaction event
When a click listener is enabled, the map will fire an `fmInteractionStart` event. When it is finished, the map will fire an `fmInteractionEnd` event. You can listen to these events to disable certain UI actions that themselves need to add a click listener or that otherwise don't make sense while a click listener is active. For example, the FacilMap frontend disables the “Add marker” and “Move marker” actions while a click listener is active, and does fire its own `fmInteractionStart` event when a marker is made draggable as part of a “Move marker” action.
## Example
Here is an example how a click listener could be used to add a marker to the map:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { addClickListener } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
map.on("fmInteractionStart", () => {
document.getElementById("add-marker-button").disabled = true;
});
map.on("fmInteractionEnd", () => {
document.getElementById("add-marker-button").disabled = false;
});
document.getElementById("add-marker-button").addEventListener("click", () => {
const message = showMessage({
body: "Please click on the map to add a marker.",
actions: [
{ label: "Cancel", onClick: () => { clickListener.cancel(); } }
]
});
const clickListener = addClickListener(map, async (point) => {
message.hide();
try {
const marker = await client.addMarker({
lat: point.lat,
lon: point.lon,
typeId: 1
});
} catch (err) {
alert(err);
}
});
});
```

Wyświetl plik

@ -0,0 +1,10 @@
# Filter
Filter expressions in FacilMap can be used to show/hide markers and lines on collaborative maps based on certain criteria. More information can be found in the [User guide](../../users/filter/).
facilmap-leaflet injects the following properties and methods into [Leaflet map](https://leafletjs.com/reference.html#map) objects to use filters:
* `setFmFilter(filter)`: Set the current filter expression. `filter` is a string with the filter expression or `undefined`. An exception is thrown if the filter expression is invalid.
* `fmFilter`: The current filter expression (string or undefined).
* `fmFilterFunc(object, type)`: Returns a boolean that indicates whether the specified object matches the current filter. `object` can be a [Marker](../client/types.md#marker) or [Line](../client/types.md#line), and `type` is its [Type](../client/types.md#type).
When the filter is updated using `setFmFilter()`, the map fires an `fmFilter` event. Other FacilMap Leaflet components ([`MarkersLayer`](./markers.md), [`LinesLayer`](./lines.md), [`HashHandler`](./hash.md)) react to this event and update their state accordingly.

Wyświetl plik

@ -0,0 +1,129 @@
# HashHandler
FacilMap can store the current map view in the location hash. The details can be found in the [User guide](../../users/share/).
`HashHandler` creates a two-way binding between the location hash and the current map view. This means that when the map is loaded and a location hash is set, the map view encoded in the hash is opened. If the location hash changes while the map is open, the new map view is opened. When the map view changes, the location hash is updated to reflect the current map view.
`HashHandler` handles the different details encoded in the location hash in the following way:
* Longitude, latitude, zoom level: automatically synchronised.
* Active layers: automatically synchronised (using the [FacilMap layer helpers](./layers.md)).
* Search term: needs to be synchronised manually (see [below](#search-term)).
* Active filter: automatically synchronised (using the [filter map extension](./filter.md)).
`HashHandler` also has support for saved views. If the position of the map is exactly that of a saved view, the location hash will be set to something like `#q=v123`.
## Usage
`HashHandler` is a [Leaflet handler](https://leafletjs.com/reference.html#handler) whose constructor accepts two arguments, the Leaflet map and a FacilMap client instance. Here is an example how to use it:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { HashHandler } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
const hashHandler = new HashHandler(map, client).enable();
```
## HashHandler and initial view
facilmap-leaflet provides helper methods to set the [initial view](./views.md#initial-view) of a map. However, when opening the map with a specific map view already present in the location hash, loading the initial view would cause unnecessary network requests and jumping of the map if the hash handler would move the map position immediately afterwards.
When the hash handler is enabled for the first time, it checks whether a map view is present in the location hash. If there is, it zooms to that view, otherwise it leaves the map position untouched. This means that if the hash handler is enabled on a map whose position is not initialised yet, the position remains uninitialised if no position is present in the location hash. This can be checked by checking whether `map._loaded` is defined or not, and loading the initial view only if it is not.
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { displayView, getInitialView, HashHandler } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
const hashHandler = new HashHandler(map, client).enable();
if (!map._loaded)
displayView(map, await getInitialView(this.client));
```
## Search term
The FacilMap frontend uses the 5th part (search term) of the location hash to represent the selected map object, the active search term or the active route. If you want to make use of the search term part, you have to set up a two-way binding yourself.
Calling `hashHandler.setQuery(queryObject)` will notify the hash handler about the active search term and cause it to update the location hash. `queryObject` can be `undefined` or an object with the following properties:
* `query` (string): The search term.
* `center` ([LatLng](https://leafletjs.com/reference.html#latlng), optional): The map center if the map were zoomed exactly to the active query.
* `zoom` (number, optional): The zoom level if the map were zoomed exactly to the active query.
If `center` and `zoom` are specified and the map is exactly at that position, the location hash is shortened to `#q=<search term>` (unless a filter or non-default layers are active).
When the hash handler is enabled or the location hash is changed, an `fmQueryChange` event is fired. The event contains the following properties:
* `query`: The search term (may be an empty string or undefined).
* `zoom`: A boolean that indicates whether the map should zoom to the result or not. This is `true` if the location hash is the shortened form `#q=<search term>` and `false` if it is the long form (which has a fixed map position encoded).
Here is an example how the search term could be hooked up to a search form:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { HashHandler, SearchResultsLayer } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/");
const hashHandler = new HashHandler(map, client).enable();
const resultLayer = new SearchResultsLayer().addTo(map);
async function submitSearch(shouldZoom) {
const query = document.getElementById("search-input").value;
if (query.trim() != "") {
try {
resultLayer.setResults(await client.find({ query }));
const bounds = resultLayer.getBounds();
const center = bounds.getCenter();
const zoom = Math.min(15, map.getBoundsZoom(resultLayer.getBounds()));
if (shouldZoom)
map.flyTo(center, zoom);
hashHandler.setQuery({ query, center, zoom })
} catch (err) {
alert(err);
}
} else {
resultsLayer.setResults([]);
hashHandler.setQuery(undefined);
}
}
document.getElementById("search-form").addEventListener("submit", () => {
submitSearch(true);
});
hashHandler.on("fmQueryChange", (event) => {
document.getElementById("search-input").value = event.query || "";
submitSearch(event.zoom);
});
```
## Manual binding
Setting the `simulate` option to `true` will disable the two-way binding between the location hash and the map view. You can use this to set up your own binding using the following features:
* `hashHandler.getHash()` will return a hash string (without leading `#`) representing the current map view.
* `hashHandler.applyHash(hash)` will set the map view to the one represented by the given hash string (without leading `#`).
* The hash handler will emit an `fmHash` event whenever the map view changes. The event object contains a `hash` property containing a hash string (without leading `#`) representing the current map view.
Example setting up a two-way binding between a text field and the map view:
```javascript
const hashHandler = new HashHandler(map, client, { simulate: true }).enable();
hashHandler.on("fmHash", (e) => {
hashInput.value = e.hash;
});
const hashInput = document.getElementById("#hash-input");
hashInput.value = hashHandler.getHash();
hashInput.addEventListener("change", () => {
hashHandler.applyHash(hashInput.value);
});
```
## Options
The third argument to the `HashHandler` constructor can be an object with some of the following properties:
* `simulate`: If this is `true`, the actual location hash will neither be read nor updated. The hash handler will only emit an `fmHash` event with the `hash` property of the event object being the location hash representing the current map view.
* `overpassLayer`: This can be an instance of [OverpassLayer](./overpass.md), causing the overpass query to be persisted in the location hash as well.

Wyświetl plik

@ -0,0 +1,54 @@
# Icons
FacilMap comes with a large selection of icons and marker shapes. The icons come from the following sources:
* All the [Open SVG Map Icons](https://github.com/twain47/Open-SVG-Map-Icons/) (these are the ones used by Nominatim for search results)
* A selection of [Glyphicons](https://getbootstrap.com/docs/3.4/components/#glyphicons-glyphs) from Bootstrap 3.
* A few icons from [Material Design Iconic Font](https://zavoloklom.github.io/material-design-iconic-font/).
* A selection of icons from [Font Awesome](https://fontawesome.com/).
FacilMap uses these icons as part of markers on the map and in regular UI elements such as buttons.
facilmap-leaflet includes all the icons and marker shapes and provides some helper methods to access them in different sizes and styles.
To make the bundle size smaller, the icons are separated into two sets:
* The *core* icons are included in the main facilmap-leaflet bundle. This includes all all icons that are used by FacilMap as UI elements.
* The *extra* icons are included in a separate file. When you call any of the methods below for the first time for an extra icon, this separate file is loaded using an async import. You can also explicitly load the extra icons at any point of time by calling `preloadExtraIcons()`.
## Available icons and shapes
`iconList` and `shapeList` are arrays of strings that contain the names of all the available icons (core and extra) and marker shapes. The `coreIconList` array contains only the names of the core icons.
In addition to the icons listed in `iconList`, any single character can be used as an icon. Single-character icons are rendered in the browser, they dont require loading the extra icons.
## Get an icon
The following methods returns a simple monochrome icon.
* `async getIconCode(colour, size, icon)`: Returns a raw SVG object with the code of the icon as a string.
* `async getIconUrl(colour, size, icon)`: Returns the icon as a `data:` URL (that can be used as the `src` of an `img` for example)
* `async getIconHtml(colour, size, icon)`: Returns the icon as an SVG element source code (as a string) that can be embedded straight into a HTML page.
The following arguments are expected:
* `colour`: Any colour that would be acceptable in SVG, for example `#000000` or `currentColor`.
* `size`: The height/width in pixels that the icon should have (icons are square). For `getIconHtml()`, the size can also be a string (for example `1em`).
* `icon`: Either one of the icon name that is listed in `iconList`, or a single letter, or an empty string or undefined to render the default icon (a dot).
## Get a marker icon
The following methods returns a marker icon with the specified shape and the specified icon inside.
* `async getMarkerCode(colour, height, icon, shape, highlight)`: Returns a raw SVG object with the code of the marker as a string.
* `async getMarkerUrl(colour, height, icon, shape, highlight)`: Returns the marker as a `data:` URL (that can be used as the `src` of an `img` for example)
* `async getMarkerHtml(colour, height, icon, shape, highlight)`: Returns the marker as an SVG element source code (as a string) that can be embedded straight into a HTML page.
* `getMarkerIcon(colour, height, icon, shape, highlight)`: Returns the marker as a [Leaflet Icon](https://leafletjs.com/reference.html#icon) that can be used for Leaflet markers. The anchor point is set correctly. The Icon object is returned synchronously and updates its `src` automatically as soon as it is loaded.
The following arguments are expected:
* `colour`: A colour in hex format, for example `#000000`.
* `height`: The height of the marker in pixels. Different marker shapes have different aspect ratios, so the width will differ depending on which shape is used. Note that the height is approximate, it is scaled down for some shapes with the aim that two markers with different shapes but the same `height` should visually appear roughly the same size.
* `icon`: Either one of the icon name that is listed in `iconList`, or a single letter, or an empty string or undefined to render the default icon (a dot).
* `shape`: A shape name that is listed in `shapeList`, or an empty string or undefined to render the default shape (`drop`).
* `highlight`: If this is set to true, the marker is rendered as highlighted (with an increased border width).
## Get an icon for an OSM element
Calling `getIconForTags(tags)` will make a best attempt to return an icon that is appropriate for a given OSM element. `tags` should be an object that maps OSM tag keys to values. The function returns a string representing the name of an icon. If no fitting icon can be found, an empty string is returned.

Wyświetl plik

@ -0,0 +1,126 @@
# Layers
In Leaflet, a lot of different types of objects are layers internally, for example tile layers, polylines, markers and even tooltips. In the context of FacilMap, there are base layers (tile layers that make up the main map style, only one can be active at a time) and overlays (layers that are shown on top of the base layer). These layers are used in the following way:
* The frontend offers the user to change which base layer and which overlays are visible
* [Saved views](./views.md) contain information about which base layer and which overlays should be visible
* The [location hash](./hash.md) stores which base layer and which overlays are visible.
facilmap-leaflet maintains a list of available base layers and overlays. This list is used by the layer picker in the frontend to show the available layers to the user, but it is also used when for views and the location hash to distinguish which layers on a map are FacilMap layers and which are other types of Leaflet layers.
The methods on this page make it possible to add FacilMaps default selection of layers to a map and to modify that selection.
## Get the available layers
To get the available layers, call `getLayers(map)`. It returns an Object whose `baseLayers` and `overlays` properties contain an object that maps a key to a Leaflet layer. The key is used to identify the map in the location hash or in a saved view. The reason why the map has to be passed as an argument is that a Leaflet layer object can only be used on one map at a time. Internally, `getLayers(map)` persists the list of maps in the `_fmLayers` property of the map.
The following example shows how to add a [Leaflet Layers control](https://leafletjs.com/reference.html#control-layers) to the map that shows all the available layers. Note that the control expects objects mapping the layer name to the layer, while the objects returned by `getLayers(map)` map the layer key to the layer, so a mapping has to be done. FacilMap layers use the `fmName` option to store their display name.
```javascript
import L from "leaflet";
import { getLayers } from "facilmap-leaflet";
const map = L.map('map');
const layers = getLayers(map);
const byName = (layerMap) => Object.fromEntries(Object.entries(layerMap).map(([key, layer]) => [layer.options.fmName || key, layer]));
L.control.layers(byName(layers.baseLayers), byName(layers.overlays)).addTo(map);
```
## Set the layer options
There are some global layer options that change the behaviour of the available layers:
* `limaLabsToken`: A [Lima Labs](https://maps.lima-labs.com/) API token. If defined, the Lima Labs layer will be available and used as the default layer instead of Mapnik. Lima Labs layers are very similar to Mapnik in style, but they are double resolution (so they dont look pixely on high-resolution screens) and have English place names in addition to the local language.
To set the global layer options, use the `setLayerOptions()` function:
```javascript
import { setLayerOptions } from "facilmap-leaflet";
setLayerOptions({
limaLabsToken: "..."
});
```
Note that to avoid unexpected inconsistencies, this should be called before `getLayers()` or any other of functions documented on this page are called.
## Change the available layers
To change the available layers for a particular Leaflet map, set the `_fmLayers` properties of that map.
```javascript
import L from "leaflet";
const map = L.map('map');
map._fmLayers = {
baseLayers: {
test: L.tileLayer("...", { fmName: "Test" })
},
overlays: {
ovr1: L.tileLayer("...", { fmName: "Overlay 1" })
}
};
```
To change the available layers for all maps, use `setLayers()`:
```javascript
import { setLayers } from "facilmap-leaflet";
setLayers(() => ({
baseLayers: {
test: L.tileLayer("...", { fmName: "Test" })
},
overlays: {
ovr1: L.tileLayer("...", { fmName: "Overlay 1" })
},
fallbackLayer: "test"
}));
```
Note that `setLayers()` only affects how the `_fmLayers` property is created, it does not affect the `_fmLayers` properties of existing maps. This means that it has to be called before any part of the code accesses the layers of the map.
The FacilMap frontend uses the `fmName` option to decide under which name to show a layer. The layer key needs to be unique and should by convention be 4 characters long.
If you want to extend the default selection of layers, you can get it using `createDefaultLayers()`:
```javascript
import { createDefaultLayers, setLayers } from "facilmap-leaflet";
setLayers(() => {
const layers = createDefaultLayers();
layers.baseLayers.test = L.tileLayer("...", { fmName: "Test" });
layers.overlays.ovr1: L.tileLayer("...", { fmName: "Overlay 1" });
return layers;
}));
```
## Get the visible layers
To find out which of the FacilMap layers are currently visible, use the `getVisibleLayers(map)` function. It returns an object with the following properties:
* `baseLayer`: A string that contains the layer key of the currently visible base layer
* `overlays`: An array of strings with the layer keys of the currently visible overlays
## Set the visible layers
`setVisibleLayers(map, visibleLayers)` can be used with an object of the same shape as returned by `getVisibleLayers(map)`:
```javascript
import L from "leaflet";
import { setVisibleLayers } from "facilmap-leaflet";
const map = L.map('map');
setVisibleLayers(map, { baseLayer: "Mpnk", overlays: ["Hike", "Rlie"] });
```
If `visibleLayers` is not specified, it defaults to `{ baseLayer: "Mpnk", overlays: [] }`.
The helper functions `setBaseLayer(map, baseLayer)` and `toggleOverlay(map, overlay)` can be used to change the visibility of individual layers:
```javascript
import L from "leaflet";
import { setBaseLayer, toggleOverlay } from "facilmap-leaflet";
const map = L.map('map');
setBaseLayer(map, "Mpnk");
toggleOverlay(map, "Hike");
```
To find out the key of an existing layer, open [https://facilmap.org/](https://facilmap.org/), enable the desired layer and find the layer key in the location hash in the address bar of your browser. For example, when Mapnik and Hiking paths are enabled, the URL will look like this: <code>https://facilmap.org/#9/52.5196/13.4069/<strong>Mpnk-Hike</strong></code>.

Wyświetl plik

@ -0,0 +1,90 @@
# Lines
## Show map lines
`LinesLayer` is a Leaflet layer that will render all the lines on a collaborative map with their appropriate styles (colour and width). To use it, open a connection to a collaborative map using the client and add the layer to the map:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { LinesLayer } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
const linesLayer = new LinesLayer(client).addTo(map);
```
`LinesLayer` also has the following features:
* Lines get a tooltip with their name.
* It automatically reacts to changes. When lines are created, changed or deleted, these changes are reflected on the map. `LinesLayer` can also be added before a collaborative map is opened, and will draw the lines as soon as a map is opened.
* It shows/hides the appropriate lines if a [filter](./filter.md) is set.
## Handle line clicks
`MarkersLayer` emits a `click` event when a line is clicked. To find out which line was clicked, use `event.layer.line`.
```javascript
linesLayer.on("click", (event) => {
console.log(event.layer.line);
});
```
You could for example show the line data or highlight the line in reaction to a click.
## Highlight lines
Lines can be highlighted, which will remove their opacity and raise them above other elements on the map. The frontend uses this to present a line as selected.
To highlight a line, use `linesLayer.highlightLine(id)`, `linesLayer.unhighlightLine(id)` or `linesLayer.setHighlightedLines(ids)`. Lines are identified by their ID, which makes it possible to highlight them even before they are actually loaded (they will be highlighted as soon as they are rendered).
This example will highlight a line on click:
```javascript
linesLayer.on("click", (event) => {
linesLayer.setHighlightedLines(new Set([event.layer.line.id]));
});
```
## Draw a line
Calling `linesLayer.drawLine(lineTemplate)` starts an interaction where the user can draw a line by adding line points through clicks on the map. The user can finish drawing the line by clicking twice on the map, or you can finish the drawing by calling `linesLayer.endDrawLine(true)`, for example in response to clicking a “Finish” button that you show somewhere.
`lineTemplate` should be a line object with basic styling (`colour` and `width` are defined). Calling `linesLayer.endDrawLine(true)` will finish the operation, while `linesLayer.endDrawLine(false)` will cancel the operation. `linesLayer.drawLine(lineTemplate)` returns a promise that is resolved with the line points (array of objects with a `lat` and `lon` property) when the operation is finished, or with `undefined` when the operation is canceled.
Here is an example how drawing a line could look:
```javascript
document.getElementById("draw-line-button").addEventListener("click", async () => {
const message = showMessage({
body: "Click on the map to add line points.",
actions: [
{ text: "Finish", onClick: () => { linesLayer.endDrawLine(true); } },
{ text: "Cancel", onClick: () => { linesLayer.endDrawLine(false); } }
]
});
let routePoints;
try {
routePoints = await linesLayer.drawLine({ colour: "0000ff", width: 7, stroke: "" });
} catch (error) {
alert(error);
} finally {
message.hide();
}
if (routePoints) {
try {
await client.addLine({ typeId: 1, routePoints });
} catch (error) {
alert(error);
}
}
});
```
## Render a single line
The code that styles lines on FacilMap has been moved into an external module called [Leaflet.HighlightableLayers](https://github.com/FacilMap/Leaflet.HighlightableLayers). To draw lines that look like FacilMap lines, use that module.
## Hide a line
Calling `linesLayer.hideLine(lineId)` hides a line until `linesLayer.unhideLine(lineId)` is called. The frontend uses this function to temporarily make a line draggable by calling [client.lineToRoute()](../client/methods.md#linetoroute-data) to convert the line to a route and hiding the line until the dragging has finished.

Wyświetl plik

@ -0,0 +1,76 @@
# Markers
## Show map markers
`MarkersLayer` is a Leaflet layer that will render all the markers on a collaborative map with their appropriate styles (colour, size, icon and shape). To use it, open a connection to a collaborative map using the client and add the layer to the map:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { MarkersLayer } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
const markersLayer = new MarkersLayer(client).addTo(map);
```
`MarkersLayer` also has the following features:
* It clusters markers if this has been enabled in the map settings.
* Markers get a tooltip with their name.
* It automatically reacts to changes. When markers are created, changed or deleted, these changes are reflected on the map. `MarkersLayer` can also be added before a collaborative map is opened, and will draw the markers as soon as a map is opened.
* It shows/hides the appropriate markers if a [filter](./filter.md) is set.
## Handle marker clicks
`MarkersLayer` emits a `click` event when a marker is clicked. To find out which marker was clicked, use `event.layer.marker`.
```javascript
markersLayer.on("click", (event) => {
console.log(event.layer.marker);
});
```
You could for example show the marker data or highlight the marker in reaction to a click.
## Highlight markers
Markers can be highlighted, which will increase their border width, remove their opacity and raise them above other elements on the map. The frontend uses this to present a marker as selected.
To highlight a marker, use `markersLayer.highlightMarker(id)`, `markersLayer.unhighlightMarker(id)` or `markersLayer.setHighlightedMarkers(ids)`. Markers are identified by their ID, which makes it possible to highlight them even before they are actually loaded (they will be highlighted as soon as they are rendered).
This example will highlight a marker on click:
```javascript
markersLayer.on("click", (event) => {
markersLayer.setHighlightedMarkers(new Set([event.layer.marker.id]));
});
```
## Show a particular marker
If you want to make sure that a marker with a particular ID is shown (regardless of whether it is in the current bbox or not), call `markersLayer.showMarker(id)`. This will load the marker from the server if it is not loaded yet. Calling `markersLayer.showMarker(id, true)` will load the marker and fly to it.
## Render a single marker
`MarkerLayer` (in singular, as opposed to `MarkersLayer`) makes it possible to render an individual marker object with its appropriate style. It does not automatically update when the marker changes and can also be used for markers that are not saved on the map.
`MarkerLayer` is based on regular [Leaflet markers](https://leafletjs.com/reference.html#marker), but accepts the following additional options:
* `marker`: A marker object that the marker style will be based on. Only the properties relevant for the style (`colour`, `size`, `icon` and `shape`) need to be set.
* `highlight`: If this is `true`, the marker will be shown with a thicker border.
* `raised`: If this is `true`, the marker will be rendered above other map objects.
```javascript
import L from "leaflet";
import { MarkerLayer } from "facilmap-leaflet";
const map = L.map('map');
new MarkerLayer([52.5295, 13.3840], {
marker: { colour: "00ff00", size: 40, icon: "alert", shape: "pentagon" }
}).addTo(map);
```
## Lock a marker
Locking a marker will disable any position and filter updates for it. It will be shown regardless of whether a filter expression applies to it or not, and updates to its position on the collaborative map will be ignored, although style updates are still applied. The frontend uses this function for its “Move marker” feature. Users wouldnt want the marker to jump to another place or to disappear while they are dragging it.
To lock a marker, call `markersLayer.lockMarker(markerId)`. To unlock it again, call `markersLayer.unlockMarker(markerId)`.

Wyświetl plik

@ -0,0 +1,162 @@
# Overpass
`OverpassLayer` can be used to show amenities or POIs using the [Overpass API](https://overpass-api.de/). In the code, individual amenities/POIs are referred to as “elements”. Elements are rendered on the map as markers, for elements that represent a way or relation in the OpenStreetMap database, the marker is positioned at their geographical centre.
`OverpassLayer` can be used like this:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { OverpassLayer, getOverpassPresets } from "facilmap-leaflet";
const map = L.map('map');
const overpassLayer = new OverpassLayer(getOverpassPresets(["parking"])).addTo(map);
```
Elements are loaded through the Overpass API whenever the bounds of the map change (in reaction to the [`moveend`](https://leafletjs.com/reference.html#map-moveend) event). Some tight limits are applies to that the query to avoid overloading the API and the browser. The result of the load operation and any exceeding of the limits is reported as part of the `loadend` event ([see below](#loading-state)). You might want to listen to those events to inform the user about the load status and ask them to zoom in further.
## Use a preset
Pass an overpass query either as the first argument to the constructor, or set it later by using `overpassLayer.setQuery(query)`. A query can be an array of presets or a string to use a custom query ([see below](#set-a-query)). Passing an empty array, empty string or `undefined` will clear all elements.
facilmap-leaflet provides a list of POI types along with their Overpass queries. The list is taken from [OpenPoiMap](http://openpoimap.org/). Items from this list are referred to as presets. To find out which presets are defined, inspect the `overpassPresets` export or check [its source code](https://github.com/FacilMap/facilmap/blob/master/leaflet/src/overpass/overpass-presets.ts).
`overpassPresets` is an array of categories, each of which contains a list of presets. Each category is an object with the following properties:
* `label`: The name of the category that can be shown to the user.
* `presets`: An array of arrays of presets. Each array of of presets represents a logical group, the UI may render a separator between them. A preset is an object with the following properties:
* `key`: A unique identifier for the preset.
* `query`: The overpass query for the preset.
* `label`: The name of the preset that can be shown to the user.
A preset object can be retrieved directly from `overpassPresets`, or by using the `getOverpassPreset(key)` or `getOverpassPresets(keys)` methods, which resolve preset keys to preset objects. For example, to show parking places and recycling stations, set the query as such:
```javascript
overpassLayer.setQuery(getOverpassPresets(["parking", "recycling"]));
```
To enable the first preset of the first category, use this:
```javascript
overpassLayer.setQuery([overpassPresets[0].presets[0]]);
```
## Set a query
If the Overpass presets dont fit your use case, you can pass a custom overpass query as the first argument to the constructor to `overpassLayer.setQuery(query)`.
`OverpassLayer` expects only the raw query and will add other statements to it as needed. For example, the query `nwr[amenity=parking]` is sent to the Overpass API as something like `[out:json][timeout:1][bbox:52.4323,13.3269,52.4406,13.3681];nwr[amenity=parking];out center 50;`. Note that `OverpassLayer` only renders a marker at the centre of the Overpass element, it does not render any lines or polygons.
See the OpenStreetMap wiki for a reference of [query statements](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#The_Query_Statement) and for an [overview of tags](https://wiki.openstreetmap.org/wiki/Map_features).
Here are some example queries:
* `nwr[amenity=parking]`: Show all parking spaces (nodes, ways and relations).
* `(nwr[amenity=atm];nwr[amenity=bank][atm][atm!=no];)`: Show ATMs.
To create queries based on user input, you may find the `quoteOverpassString(string)` function useful. It returns the string in quotes and with certain characters escaped. You can use `validateQuery(query)` to validate the syntax of a query. It returns a promise that resolves to `undefined` if the query is fine or otherwise a string with an error message.
## Handle errors
The Overpass API will throw an error when the resource limits are exceeded. `OverpassLayer` will emit an `error` event in that case, with the error message in `event.error.message`. Once the map is moved to a position where element can be loaded again, a `clearerror` event is fired.
```javascript
let flag = undefined;
overpassLayer.on("error", (event) => {
if (flag)
flag.close();
if (event.error.message.includes("timed out") || event.error.message.includes("out of memory"))
flag = showFlag("Too many results, please zoom in.");
else
flag = showFlag(event.error.message);
});
overpassLayer.on("clearerror", () => {
flag.close();
});
```
## Handle element click
When an element marker is clicked, `OverpassLayer` emits a `click` event. The element can be accessed as `event.layer._fmOverpassElement`. It is an object with the following properties:
* `type`: The type of the OpenStreetMap object, `node`, `way` or `relation`.
* `id`: The ID of the OpenStreetMap object.
* `lat`, `lon`: The coordinates of the object. For ways and relations, this is the geometric centre of the object.
* `tags`: The tags of the OpenStreetMap object. An object that maps keys to values. May be undefined if the object does not have any tags.
```javascript
overpassLayer.on("click", (event) => {
alert(JSON.stringify(event.layer._fmOverpassElement.tags));
});
```
## Highlight elements
Using `overpassLayer.highlightElement(element)`, `overpassLayer.unhighlightElement(element)` and `overpassLayer.setHighlightedElements(elements)`, you can highlight some elements, causing them to be rendered with an increased outline, no opacity and above other map objects. The frontend uses this to present them as selected.
In this example, an element is highlighted when clicked:
```javascript
overpassLayer.on("click", (event) => {
overpassLayer.setHighlightedElements(new Set([event.layer._fmOverpassElement]));
});
```
## Loading state
When `OverpassLayer` makes a request to the Overpass API, it fires a `loadstart` event. When the request is finished, a `loadend` event is fired. You might want to listen to these events to show a loading indicator to the user. Note that multiple requests might be going on simultaneously if the user moves the map before a pending request has finished, so you need to keep track of the number of times the events are fired.
```javascript
let pending = 0;
overpassLayer.on("loadstart", () => {
if (++pending == 1)
showLoadingSpinner();
});
overpassLayer.on("loadend", () => {
if (--pending == 0)
hideLoadingSpinner();
});
```
The `loadend` event object contains a `status` property that gives details about how the request went. It can have one of the following values:
* `OverpassLoadStatus.COMPLETE`: The elements were successfully loaded and shown.
* `OverpassLoadStatus.INCOMPLETE`: Some elements were loaded and shown, but the number of found elements exceeded the limit specified in the [options](#options). This means that the user needs to zoom in further to see the complete set of elements.
* `OverpassLoadStatus.TIMEOUT`: The request exceeded the timeout specified in the [options](#options), so no elements were retrieved. This usually happens when there are way too many results (because the zoom level is very low). The user needs to zoom in to see any results.
* `OverpassLoadStatus.ERROR`: The overpass API has reported an error and no elements were retrieved. The exact error object is available in the `error` property of the event object.
* `OverpassLoadStatus.ABORTED`: Another request has been started in the meantime (because the user moved the map again while the request was still loading). This result can be ignored (apart from updating the number of pending requests), as another request is in progress (or has already succeeded) that supersedes the current request.
A `clear` event is fired whenever the POIs are cleared (for example if an empty query is applied). If you are persisting the latest load status or present it to the user in some way, you might want to reset that in response to the `clear` event.
Here is an example how the status can be presented to the user:
```javascript
import { OverpassLoadStatus } from "facilmap-leaflet";
// This is just an example, in reality you would use a styled element
const overpassStatus = document.createElement("div");
document.body.appendChild(overpassStatus);
overpassLayer.on("loadend", (event) => {
if (event.status == OverpassLoadStatus.COMPLETE)
overpassStatus.innerText = "";
else if (event.status == OverpassLoadStatus.INCOMPLETE)
overpassStatus.innerText = "Not all POIs are shown because there are too many results. Zoom in to show all results.";
else if (event.status == OverpassLoadStatus.TIMEOUT)
overpassStatus.innerText = "Zoom in to show POIs.";
else if (event.status == OverpassLoadStatus.ERROR)
overpassStatus.innerText = "Error loading POIs: " + event.error.message;
}).on("clear", () => {
overpassStatus.innerText = "";
});
```
## Options
An object with options can be specified as the second parameter to the constructor:
* `markerColour`: The colour to use for the element markers. A 6 character hex code, defaults to `000000`.
* `markerSize`: The size to use for the element markers. A number that specifies the height in pixels, defaults to `35`.
* `markerShape`: The shape to use for the element markers. Defaults to the default shape (drop).
* `timeout`: The number in seconds to use as a timeout for Overpass API requests. Defaults to `1`. If this is exceeded, no elements are shown and the `loadend` event will have a `status` of `OverpassLoadStatus.TIMEOUT`.
* `limit`: The maximum number of elements that should be returned by an Overpass request. Defaults to `50`. If this is reached, the loaded elements are shown, but the `loadend` event will have a `status` of `OverpassLoadStatus.INCOMPLETE`.
## Hash
The current query of an Overpass layer can be persisted in the location hash. To enable this, pass `overpassLayer` in the [options of the hash handler](./hash.md#options):
```javascript
const hashHandler = new HashHandler(map, client, { overpassLayer }).enable();
```
The Overpass layer will be represented in the hash by a dynamic overlay name starting with `o_` for presets and `O_` for a custom query.

Wyświetl plik

@ -0,0 +1,37 @@
# Route
Routes in FacilMap are temporarily stored on the server in the scope of one particular client connection, unrelated to any collaborative map that is opened. This makes it possible that only the track points appropriate for the current bounding box and zoom level have to be received by the client, rather than the whole route in all detail. Multiple routes can be active at the same time by specifying a custom `routeId` when calling [setRoute()](../client/methods.md#setroute-data).
## Show a route
`RouteLayer` renders a route with one particular `routeId` on the map. As long as no route with that `routeId` is active, no route is shown, and as soon as a route is received, it is automatically rendered. An example usage pattern would be that you have one routing form that uses a fixed `routeId`, which would control the active route using [setRoute()](../client/methods.md#setroute-data) and [clearRoute()](../client/methods.md#clearroute-data), and there would be one `RouteLayer` for that `routeId` constantly present on the map which would always show the current state of the route.
The `RouteLayer` constructor accepts the following arguments:
* A client instance
* A `routeId` (string or undefined)
* Layer options (any [Leaflet.HighlightableLayers](https://github.com/FacilMap/Leaflet.HighlightableLayers) can be specified)
Example usage:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { RouteLayer } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
const routeLayer = new RouteLayer(client, "routeForm", { raised: true }).addTo(map);
```
## Make a route draggable
Most of the logic to make routes draggable has been moved into the external module [Leaflet.DraggableLines](https://github.com/FacilMap/Leaflet.DraggableLines).
`RouteDragHandler` is an extended version of `DraggableLines` that automatically updates the route points when the route is dragged, causing it to be recalculated.
Example usage:
```javascript
import { RouteDragHandler } from "facilmap-leaflet";
const routeDragHandler = new RouteDragHandler(map, client).enable();
routeDragHandler.enableForLayer(routeLayer);
```

Wyświetl plik

@ -0,0 +1,61 @@
# Search
## Show search results
`SearchResultsLayer` renders search results as markers, lines and polygons on the map. The constructor accepts the following arguments:
* `results`: An array of search results as returned by [client.find()](../client/methods.md#find-data). You can also leave this `undefined` and add results later using `setResults()`.
* `options`: An optional object containing the following properties:
* `markerColour`: A 6-digit colour to use for search result markers, defaults to `000000`
* `markerSize`: The height of search result markers in pixels, defaults to `35`
* `markerShape`: The shape of search result markers, defaults to `drop`
* `pathOptions`: [Path options](https://leafletjs.com/reference.html#path-option) for lines and polygons.
Note that the marker icon is determined by the type of result.
Example usage:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { SearchResultsLayer } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/");
const resultLayer = new SearchResultsLayer().addTo(map);
document.getElementById("search-form").addEventListener("submit", async () => {
const query = document.getElementById("search-input").value;
if (query.trim() != "") {
try {
resultLayer.setResults(await client.find({ query }));
} catch (err) {
alert(err);
}
} else {
resultsLayer.setResults([]);
}
});
```
## Handle result click
`SearchResultsLayer` fires a `click` event when a result is clicked. To determine which result was clicked, use `event.layer._fmSearchResult`.
Example:
```javascript
resultsLayer.on("click", (event) => {
alert(event.layer._fmSearchResult.display_name);
});
```
## Highlight results
Using `resultsLayer.highlightResult(result)`, `resultsLayer.unhighlightResult(result)` and `resultsLayer.setHighlightedResults(results)`, you can highlight some search results, causing them to be rendered with an increased outline, no opacity and above other map objects. The frontend uses this to present them as selected.
A single search result might consist of multiple geometries, for example a marker and a polygon. Highlighting a search result will highlight all those geometries.
In this example, a search result is highlighted when clicked:
```javascript
resultsLayer.on("click", (event) => {
resultsLayer.setHighlightedResults(new Set([event.layer._fmSearchResult]));
});
```

Wyświetl plik

@ -0,0 +1,45 @@
# Views
Views in FacilMap are a particular bounding box on the map, with a particular set of layers visible and possibly an active filter. Views can be saved as part of collaborative maps. More details can be found in the [User guide](../../users/views/).
A view object represents a view. Its shape is documented in the [Client API](../client/types.md#view). Note that only objects that represent saved views will have an `id`.
## Current view
`getCurrentView(map, { includeFilter })` returns a view object that represents the current view of the map. If `includeFilter` is `true`, the current [filter](./filter.md) is included in the object (if there is one), otherwise it is omitted.
`isAtView(map, view)` returns a boolean that indicates whether the current map view is equal to the view represented by the specified view object. If the `view` object is omitted, the method will indicate whether the current map shows the fallback view (the whole world).
## Show a view
`displayView(map, view)` will animate the map to fly to the specified view and activate the layers and filter specified in the view object. If the `view` object is omitted, the map will show the fallback view (the whole world).
## Initial view
`getInitialView(client)` returns a promise that is resolved with one of the following, in order:
* If a collaborative map is open and a default view is configured, that view is returned.
* Otherwise, an attempt is made to guess the position of the user using [geoip](../client/methods.md#geoip). If a guess could be made, a view object representing the rough area is returned.
* Otherwise, `undefined` is returned.
Here is an example how the initial view could be set:
```javascript
import L from "leaflet";
import Client from "facilmap-client";
import { displayView, getInitialView } from "facilmap-leaflet";
const map = L.map('map');
const client = new Client("https://facilmap.org/", "myMapId");
displayView(map, await getInitialView(this.client));
```
## Overpass
To persist the active query of an [Overpass layer](./overpass.md) as part of a saved view, pass the `overpassLayer` object to the various methods:
* `getCurrentView(map, { overpassLayer })`
* `isAtView(map, view, { overpassLayer })`
* `displayView(map, view, { overpassLayer })`
* `getInitialView(map, view, { overpassLayer })`

Wyświetl plik

@ -0,0 +1,11 @@
# Overview
The FacilMap server is a HTTP server that fulfills the following tasks:
* Serve the frontend under `/` and `/<map ID>`.
* Serve exported collaborative maps under `/<map ID>/<type>`, where `<type>` can be `table`, `gpx` or `geojson`.
* Run a socket.io server under `/socket.io`. The frontend connects to this using the [FacilMap client](../client/) and uses it to get calculated routes, get and update the data on a collaborative map, and receive live updates to a collaborative map, and as a proxy to perform searches.
* Maintain a connection to a database where collaborative map data and calculated routes are stored.
The official FacilMap server is running on [https://facilmap.org/](https://facilmap.org/). If you want, you can run your own FacilMap server using one of these options:
* [Docker](./docker.md) will run the server in an isolated container. It is easer to set up and more secure, but takes more resources.
* Running the server [standalone](./standalone.md) takes less resources, but is less secure and takes more steps to set up.

Wyświetl plik

@ -0,0 +1,48 @@
# Configuration
The config of the FacilMap server can be set either by using environment variables (useful for docker) or by editing `config.env`.
| Variable | Required | Default | Meaning |
|-----------------------|----------|-------------|----------------------------------------------------------------------------------------------------------------------------------|
| `USER_AGENT` | * | | Will be used for all HTTP requests (search, routing, GPX/KML/OSM/GeoJSON files). You better provide your e-mail address in here. |
| `APP_NAME` | | | If specified, will replace “FacilMap” as the name of the app throughout the UI. |
| `TRUST_PROXY` | | | Whether to trust the X-Forwarded-* headers. Can be `true` or a comma-separated list of IP subnets (see the [express documentation](https://expressjs.com/en/guide/behind-proxies.html)). Currently only used to calculate the base URL for the `opensearch.xml` file. |
| `BASE_URL` | | | If `TRUST_PROXY` does not work for your particular setup, you can manually specify the base URL where FacilMap can be publicly reached here. |
| `HOST` | | | The ip address to listen on (leave empty to listen on all addresses) |
| `PORT` | | `8080` | The port to listen on. |
| `DB_TYPE` | | `mysql` | The type of database. Either `mysql`, `postgres`, `mariadb`, `sqlite`, or `mssql`. |
| `DB_HOST` | | `localhost` | The host name of the database server. |
| `DB_PORT` | | | The port of the database server (optional). |
| `DB_NAME` | | `facilmap` | The name of the database. |
| `DB_USER` | | `facilmap` | The username to connect to the database with. |
| `DB_PASSWORD` | | `facilmap` | The password to connect to the database with. |
| `ORS_TOKEN` | | | [OpenRouteService API key](https://openrouteservice.org/). If not specified, advanced routing settings will not be shown. |
| `MAPBOX_TOKEN` | | | [Mapbox API key](https://www.mapbox.com/signup/). If neither this nor `ORS_TOKEN` are specified, the routing tab and any routing options will be hidden. |
| `MAXMIND_USER_ID` | | | [MaxMind user ID](https://www.maxmind.com/en/geolite2/signup). |
| `MAXMIND_LICENSE_KEY` | | | MaxMind license key. |
| `LIMA_LABS_TOKEN` | | | [Lima Labs](https://maps.lima-labs.com/) API key |
| `HIDE_COMMERCIAL_MAP_LINKS` | | | Set to `1` to hide the links to Google/Bing Maps in the “Map style” menu. |
| `CUSTOM_CSS_FILE` | | | The path of a CSS file that should be included ([see more details below](#custom-css-file)). |
| `NOMINATIM_URL` | | `https://nominatim.openstreetmap.org` | The URL to the Nominatim server (used to search for places). |
| `OPEN_ELEVATION_URL` | | `https://api.open-elevation.com` | The URL to the Open Elevation server (used to look up the elevation for markers). |
| `OPEN_ELEVATION_THROTTLE_MS` | | `1000` | The minimum time between two requests to the Open Elevation API. Set to `0` if you are using your own self-hosted instance of Open Elevation. |
| `OPEN_ELEVATION_MAX_BATCH_SIZE` | | `200` | The maximum number of points to resolve in one request through the Open Elevation API. Set this to `1000` if you are using your own self-hosted Open Elevation instance. |
FacilMap makes use of several third-party services that require you to register (for free) and generate an API key:
* Mapbox and OpenRouteService are used for calculating routes. Mapbox is used for basic routes, OpenRouteService is used when custom route mode settings are made. If these API keys are not defined, calculating routes will fail.
* Maxmind provides a free database that maps IP addresses to approximate locations. FacilMap downloads this database to decide the initial map view for users (IP addresses are looked up in FacilMaps copy of the database, on IP addresses are sent to Maxmind). This API key is optional, if it is not set, the default view will be the whole world.
* Lima Labs is used for nicer and higher resolution map tiles than Mapnik. The API key is optional, if it is not set, Mapnik will be the default map style instead.
## Custom CSS file
To include a custom CSS file in the UI, set the `CUSTOM_CSS_FILE` environment variable to the file path.
When running FacilMap with docker, you can mount your CSS file as a volume into the container, for example with the following docker-compose configuration:
```yaml
environment:
CUSTOM_CSS_FILE: /opt/facilmap/custom.css
volumes:
- ./custom.css:/opt/facilmap/custom.css
```
Your custom CSS file will be included in the map UI and in the table export. You can distinguish between the two by using the `html.fm-facilmap-map` and `html.fm-facilmap-table` selectors.

Wyświetl plik

@ -0,0 +1,93 @@
# Docker
[Docker](https://www.docker.com/) is a container management system that is commonly used to run applications on servers. A docker image contains a full Linux system with one particular application installed, and docker runs this in a simulated virtual environment that is isolated from the rest of the server. The main advantages are security (if a hacker gains access to an application that is running inside a docker container is hacked, it is hard to impossible to gain access to the rest of the system from there) and simplicity (applications can be installed, updated and removed without leaving any traces behind using a single command).
This manual assumes that you have docker set up on your system.
The FacilMap server is available as [`facilmap/facilmap`](https://hub.docker.com/r/facilmap/facilmap/) on Docker Hub. The [configuration](./config.md) can be defined using environment variables. The container will expose a HTTP server on port 8080, which you should put behind a reverse proxy such as [nginx-proxy](https://hub.docker.com/r/jwilder/nginx-proxy) or [traefik](https://traefik.io/traefik/) for HTTPS support.
FacilMap needs a database supported by [Sequelize](https://sequelize.org/master/) to run, it is recommended to use MySQL/MariaDB. When creating a MySQL/MariaDB database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database. When using PostgreSQL, the PostGIS extensions must be enabled.
## docker-compose
To run FacilMap with MariaDB using [docker-compose](https://docs.docker.com/compose/), here is an example `docker-compose.yml`:
```yaml
services:
facilmap:
image: facilmap/facilmap
ports:
- 8080:8080
links:
- mariadb
depends_on:
mariadb:
condition: service_healthy
environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org)
TRUST_PROXY: "true"
DB_TYPE: mysql
DB_HOST: mariadb
DB_NAME: facilmap
DB_USER: facilmap
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state)
MAXMIND_LICENSE_KEY:
LIMA_LABS_TOKEN: # Get an API key on https://maps.lima-labs.com/ (optional, needed for double-resolution tiles)
restart: unless-stopped
mariadb:
image: mariadb
environment:
MYSQL_DATABASE: facilmap
MYSQL_USER: facilmap
MYSQL_PASSWORD: password
MYSQL_RANDOM_ROOT_PASSWORD: "true"
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
healthcheck:
test: healthcheck.sh --su-mysql --connect --innodb_initialized
restart: unless-stopped
```
Here is an example with Postgres:
```yaml
services:
facilmap:
image: facilmap/facilmap
ports:
- 8080:8080
links:
- postgres
depends_on:
postgres:
condition: service_healthy
environment:
USER_AGENT: My FacilMap (https://facilmap.example.org/, facilmap@example.org)
TRUST_PROXY: "true"
DB_TYPE: postgres
DB_HOST: db
DB_NAME: facilmap
DB_USER: facilmap
DB_PASSWORD: password
ORS_TOKEN: # Get an API key on https://go.openrouteservice.org/ (needed for routing)
MAPBOX_TOKEN: # Get an API key on https://www.mapbox.com/signup/ (needed for routing)
MAXMIND_USER_ID: # Sign up here https://www.maxmind.com/en/geolite2/signup (needed for geoip lookup to show initial map state)
MAXMIND_LICENSE_KEY:
LIMA_LABS_TOKEN: # Get an API key on https://maps.lima-labs.com/ (optional, needed for double-resolution tiles)
restart: unless-stopped
postgres:
image: postgis/postgis
environment:
POSTGRES_USER: facilmap
POSTGRES_PASSWORD: password
POSTGRES_DB: facilmap
healthcheck:
test: pg_isready -d $$POSTGRES_DB
restart: unless-stopped
```
To start FacilMap, run `docker-compose up -d` in the directory of the `docker-compose.yml` file. To upgrade FacilMap, run `docker-compose pull` and then restart it by running `docker-compose up -d`.
Note that this exposes FacilMap through unencrypted HTTP on port 8080. In a production setup, FacilMap should be served by a reverse proxy that provides HTTPS. Usually, the `ports` directive can be removed then.

Wyświetl plik

@ -0,0 +1,34 @@
# Standalone
The FacilMap server runs on [Node.js](https://nodejs.org/en/). To run the FacilMap server, the following dependencies are needed:
* You need to have a recent version of Node.js and npm installed.
* You need to create a database on one of the systems supported by [Sequelize](https://sequelize.org/master/), it is recommended to use MySQL/MariaDB.
* When creating a MySQL/MariaDB database for FacilMap, make sure to use the `utf8mb4` charset/collation to make sure that characters from all languages can be used on a map. By default, MySQL/MariaDB uses the `latin1` charset, which mostly supports only basic latin characters. When you start the FacilMap server for the first time, the necessary tables are created using the charset of the database.
* When using PostgreSQL, the PostGIS extensions must be enabled.
* It is recommended to run FacilMap as an unprivileged user.
## Run the latest release
A bundled version of the FacilMap server is published on NPM as [facilmap-server](https://www.npmjs.com/package/facilmap-server). To run it, run the following steps:
1. If you dont have a global NPM prefix set up yet, run `npm config set prefix ~/.local`. This will install npm packages into `~/.local/bin`, rather than trying to install them into `/usr/local/bin`.
2. Install facilmap-server by running `npm install -g facilmap-server`
3. Create a `config.env` file based on [`config.env.example`](https://github.com/FacilMap/facilmap/blob/main/config.env.example) and to adjust the [configuration](./config.md).
4. Start the FacilMap server by running `~/.local/bin/facilmap-server dotenv_config_path=config.env`.
FacilMap will need write access to the directory `~/.local/lib/node_modules/.cache/facilmap-server` (or specify another directory in the `CACHE_DIR` environment variable). All other files and directories can be read-only. To harden the FacilMap installation, make the whole installation folder owned by root, but create the cache directory and make it owned by the facilmap user.
## Run the development version
To run the latest state from the [FacilMap repository](https://github.com/FacilMap/facilmap), run the following steps:
1. Make sure that you have a recent version of [Node.js](https://nodejs.org/), [yarn](https://yarnpkg.com/)
and a database (MariaDB, MySQL, PostgreSQL, SQLite, Microsoft SQL Server) set up. (Note that only MySQL/MariaDB has been tested so far.)
2. Clone the [FacilMap repository](https://github.com/FacilMap/facilmap).
3. Run `yarn install` in the root folder of this repository to install the dependencies.
4. Run `yarn build` to create the JS bundles.
5. Copy `config.env.example` to `config.env` and adjust the [configuration](./config.md).
6. Inside the `server` directory, run `yarn server`. This will automatically set up the database structure and start the server.
You can also run `yarn dev-server`, which will automatically rebuild the frontend bundle when the code is changed. See [dev setup](../development/dev-setup.md) for more information.

Wyświetl plik

@ -1,15 +1,11 @@
---
home: true
heroImage: https://v1.vuepress.vuejs.org/hero.png
tagline:
actionText: User guide →
actionLink: /users/
#features:
#- title: Feature 1 Title
# details: Feature 1 Description
#- title: Feature 2 Title
# details: Feature 2 Description
#- title: Feature 3 Title
# details: Feature 3 Description
#footer: Made by with ❤️
---
heroImage: ./logo_f.svg
tagline:
actions:
- text: User guide →
link: /users/
- text: Developer guide →
link: /developers/
type: secondary
---

Wyświetl plik

@ -5,18 +5,21 @@ FacilMap is a privacy-friendly, open-source versatile online map that combines d
* ([Overview over the UI elements](./ui/))
* [Show different map styles](./layers/), for example maps optimized for driving, cycling, hiking or showing the topography or public transportation networks.
* [Search for places](./search/)
* [Show amenities and POIs](./pois/)
* [Calculate a route](./route/), optionally showing the elevation profile.
* [Find out what is at a particular point on the map](./click-marker/)
* [Open geographic files](./files/), for example GPX, KML or GeoJSON files
* [Show your location on the map](./locate/)
* [Share a link](./hash/) to a particular view of the map.
* [Share a link](./share/) to a particular view of the map.
* Add FacilMap as an [app](./app/) to your device.
* Change the language settings in the [user preferences](./user-preferences/).
* FacilMap is [privacy-friendly](./privacy/) and does not track you.
In addition, FacilMap allows you to create collaborative maps, where you and others can add markers, draw lines, save routes and import GPX/KML/GeoJSON files, which will be saved under a custom URL.
* [Creating collaborative maps](./collaborative/)
* [Add markers](./markers/)
* [Add lines/routes](./lines/)
* [Handle multiple objects](./multiple/), for example to delete multiple markers/lines at once.
* By default, markers/lines/routes have a name and a description. You can [define additional fields](./types/) that contain more detailed information and can automatically influence the style of the marker/line/route.
* [Add a legend](./legend/) that shows which colour/style belongs to which type of marker/line.
* [Saved views](./views/) reference a particular area on the map with certain settings. You can change the default view of a collaborative map or save additional views as a shortcut for users to navigate to a certain area on the map.
@ -26,4 +29,4 @@ In addition, FacilMap allows you to create collaborative maps, where you and oth
* [Import](./import/) GPX, KML or GeoJSON files. Importing a GeoJSON file that was exported by FacilMap makes it possible to copy parts of one map onto another.
* Adjust the [map settings](./map-settings/). This is also where your can [delete the map](./map-settings/#delete-the-map).
If you are interested in embedding FacilMap into your website or even running your own FacilMap server, check out the [Administrator guide](../administrators/).
If you are interested in embedding FacilMap into your website or even running your own FacilMap server, check out the [Developer guide](../developers/).

Wyświetl plik

@ -1,3 +1,8 @@
<script setup lang="ts">
import markerMp4 from "@source/users/click-marker/marker.mp4";
import markerMobileMp4 from "@source/users/click-marker/marker-mobile.mp4";
</script>
# Map point information
By pressing a specific point on the map and holding the mouse/finger for a second, you can find out more information about that point. A temporary marker appears on the map and a [search box](../ui/#search-box) tab with information about the place is opened.
@ -8,11 +13,11 @@ The marker and search box tab will disappear again when you click somewhere else
At the bottom of the search box tab, you will find a “Use as” button to [use the map point as a route destination](../route/#use-map-points-as-destinations). If a collaborative map is open, you can use the “Add to map” button to add the point as a marker to the map (which will automatically prefill some fields).
<Screencast :desktop="require('./marker.mp4')" :mobile="require('./marker-mobile.mp4')"></Screencast>
<Screencast :desktop="markerMp4" :mobile="markerMobileMp4"></Screencast>
## Share a link
FacilMap continuously updates the location hash (the part after the `#` in the address bar of your browser) to represent what you currently view on the map. More information can be found under [Share a link](../hash/).
FacilMap continuously updates the location hash (the part after the `#` in the address bar of your browser) to represent what you currently view on the map. More information can be found under [Share a link](../share/).
When you long-click the map to open the map point information, this is also persisted in the location hash. This means that you can use the URL straight from the browser address bar to share a link to a specific map point.

Wyświetl plik

@ -6,9 +6,9 @@ While using a collaborative map, users can still use all of the non-collaborativ
When changing something on the map, there is no need to “Save” the map. Changes are applied immediately.
## Start a collaborative map
## Create a map
To start a collaborative map, click the “Start collaborative map” button in the [toolbox](../ui/#toolbox).
To create a collaborative map, click “Collaborative maps” and then “Create a new map” in the [toolbox](../ui/#toolbox).
![](./save.png)
@ -33,9 +33,23 @@ When creating a collaborative map, some random characters are proposed for the d
| Change [map settings](../map-settings/) | ✘ | ✘ | ✔ |
| [Delete map](../map-settings/#delete-the-map) | ✘ | ✘ | ✔ |
## Exit a collaborative map
## Open an existing map
If you want to go back to the basic FacilMap, click on “Tools” in the [toolbox](../ui/#toolbox) and then “Exit collaborative map”.
If someone has shared a link to a collaborative map with you, you can simply open that link in your browser to open the map. Alternatively, click “Collaborative maps” and then “Open an existing map” (or, if you are already viewing another map, “Open another map”) in the [toolbox](../ui/#toolbox). A dialog will open where you can paste the link into the text field on top and click “Open” to open the map.
In the bottom section of the dialog you can search for existing collaborative maps that people have [made public](../map-settings/#search-engines). Simply type in a search term and click the search icon to search for maps. You can use `*` as a wildcard and `?` as a single-character wildcard.
## Close a map
If you want to close a collaborative map and go back to the basic FacilMap, click “Collaborative maps” and then “Close …” (where “…” is the name of the open map) in the [toolbox](../ui/#toolbox).
## Bookmark a map
While you can simply add a bookmark in your browser to remember the link to a specific map, FacilMap also brings its own bookmark function.
To bookmark a particular map, open that map and then click “Collaborative maps” and then “Bookmark …” (where “…” is the name of the open map) in the [toolbox](../ui/#toolbox). This will add the map as a new item to the “Collaborative maps” menu. Bookmarks are stored in your browser, so they are not visible to anyone else. When bookmarking a map for the first time, your browser might ask you to allow FacilMap to store data in the persistent storage. Allowing this will make sure that your bookmarks are not removed when your system runs low in disk space or when you clear your browser history.
To reorder, rename or remove bookmarks, click “Manage bookmarks” (only visible if you have any bookmarks).
## Delete a collaborative map

Wyświetl plik

@ -8,17 +8,26 @@ You are welcome to contribute code, improve the documentation or raise ideas on
Financial contributions are very much appreciated and can be sent through the following channels:
<div style="display: flex; flex-wrap: wrap">
<a href="https://www.paypal.com/donate?hosted_button_id=FWR59UXY6HGGS" target="_blank" style="display: flex; flex-direction: column; align-items: center">
<ClientOnly><qrcode value="https://www.paypal.com/donate?hosted_button_id=FWR59UXY6HGGS" :width="164"></qrcode></ClientOnly>
PayPal
</a>
<a href="https://www.patreon.com/facilmap" target="_blank" style="display: flex; flex-direction: column; align-items: center">
<ClientOnly><qrcode value="https://www.patreon.com/facilmap" :width="164"></qrcode></ClientOnly>
Patreon
</a>
<a href="bitcoin:1PEfenaGXC9qNGQSuL5o6f6doZMYXRFiCv" style="display: flex; flex-direction: column; align-items: center">
<ClientOnly><qrcode value="bitcoin:1PEfenaGXC9qNGQSuL5o6f6doZMYXRFiCv" :width="164"></qrcode></ClientOnly>
Bitcoin
</a>
</div>
<template v-for="{ width, gap, links } in [{
width: 740,
gap: 20,
links: {
'GitHub': 'https://github.com/sponsors/FacilMap',
'Liberapay': 'https://liberapay.com/facilmap/',
'Ko-fi': 'https://ko-fi.com/facilmap',
'PayPal': 'https://www.paypal.com/donate?hosted_button_id=FWR59UXY6HGGS',
'Patreon': 'https://www.patreon.com/facilmap',
'Bitcoin': 'bitcoin:1PEfenaGXC9qNGQSuL5o6f6doZMYXRFiCv'
}
}]">
<template v-for="size in [(width - gap * (Object.keys(links).length - 1)) / Object.keys(links).length]">
<div style="display: flex; flex-wrap: wrap" :style="{ gap: `${gap}px` }">
<template v-for="(url, label) in links">
<a :href="url" target="_blank" style="display: flex; flex-direction: column; align-items: center">
<qrcode :value="url" :size="size" level="M" render-as="svg" style="margin-bottom: 5px"></qrcode>
{{label}}
</a>
</template>
</div>
</template>
</template>

Wyświetl plik

@ -2,26 +2,39 @@
A map can be exported as a file, in order to use it in another application or to create a backup.
To export a map, in the [toolbox](../ui/#toolbox), click “Tools” and then one of the “Export” options. Note that when a [filter](../filter/) is active, only the objects that match the filter are exported.
To export a map, in the [toolbox](../ui/#toolbox), click “Tools” and then “Export”. This opens a dialog where you can configure in what format to export the map.
The exports are available under their own URL. In the context menu (right click) of the export links, you can copy the URL to use it elsewhere.
## Formats
## GeoJSON
### GPX
When exporting a map as GeoJSON, all markers and lines (or, if a [filter](../filter/) is active, those that match the filter) including their data fields, all [saved views](../views/), and all [types](../types/) that represent the exported markers/lines are exported. This means that if no filter is active, this is suitable to make a backup of the whole map (apart from the [map settings](../map-settings/) and the [edit history](../history/)). Such a file can also be [imported](../import/) again into a map to restore the backup.
GPX is a format that is understood by many geographic apps and navigation devices. Exporting a map as GPX will export all markers and lines on the map.
## GPX
When exporting to GPX, one of three “Route type” options needs to be selected:
* **Track points:** Lines that are calculated routes will be exported as GPX tracks. This means that the whole course of the route is saved in the file.
* **Track points, one file per line (ZIP file):** Like “Track points”, but rather than creating one GPX file containing all markers/lines of the map, a ZIP file will be generated that contains one GPX file with all markers and a folder with one GPX file per line. This is useful for importing the file into OsmAnd, which only supports one track per file.
* **Route points:** Lines that are calculated routes will be exported as GPX routes. This means that only the route destinations are saved in the file, and the app or device that opens the file is responsible for calculating the best route.
GPX is a format that is understood by many geographic apps and navigation devices. Exporting a map as GPX will export all markers and lines on the map (or, if a [filter](../filter/) is active, those that match the filter). There are two options:
* **Export as GPX (tracks):** Lines that are calculated routes will be exported as GPX tracks. This means that the whole course of the route is saved in the file.
* **Export as GPX (routes):** Lines that are calculated routes will be exported as GPX routes. This means that only the route destinations are saved in the file, and the app or device that opens the file is responsible for calculating the best route.
The marker/line description and any [custom fields](../types/) will be saved in the description of the GPX objects. The marker/line styles are not exported, with the exception of some basic style settings supported by OsmAnd.
The marker/line description and any [custom fields](../types/) will be saved in the description of the GPX objects.
### GeoJSON
## Table
When exporting a map as GeoJSON, all markers and lines including their data fields, all [saved views](../views/), and all [types](../types/) that represent the exported markers/lines are exported. This means that if no filter is active, this is suitable to make a backup of the whole map (apart from the [map settings](../map-settings/) and the [edit history](../history/)). Such a file can also be [imported](../import/) again into a map to restore the backup.
The table export is a static HTML export of all the markers and lines (or, if a [filter](../filter/) is active, those that match the filter) in table form. All the field values of the markers/lines are shown, along with some metadata (name, coordinates, distance, time).
### HTML
A separate table for each [type](../types/) is shown. Individual types can be hidden by clicking the down arrow next to their heading.
The HTML export will render a web page that contains a table for each [type](../types/) that lists each marker/line of that type along with all field values and some metadata (name, coordinates, distance, time). Types can be shown/hidden by clicking on the down arrow next to their heading, and the table can be sorted by an individual data attribute by clicking on its column header.
Table columns can be sorted by clicking on their header.
For HTML exports, there additional “Copy to clipboard” export method is available. This will copy the table for a single type into the clipboard. Such a table can be pasted directly into a spreadsheet application such as EtherCalc or LibreOffice Calc.
### CSV
CSV files can be opened by most spreadsheet applications. A CSV export will contain all the markers/lines of a single type, along with their field values and some metadata (name, coordinates, distance, time). Note that CSV only supports plain text, so any rich text formatting will be lost.
## Generate a link
By default, the export dialog will create a file and download or open it. By selecting “Generate link” as the export method, you can copy a URL to the exported file instead. This URL will always generate the file with the lastest map data according to the export settings that you have defined, so you can use it to link to the export from a website or a browser bookmark or to integrate the export with another tool that should periodically create copies of your map data.
## Apply a filter
If you have an active [filter](../filter/), the export dialog will show an additional “Apply filter” option. When this option is enabled, the exported file will only contain the map objects that match the filter.

Wyświetl plik

@ -1,3 +1,8 @@
<script setup lang="ts">
import detailsMp4 from "@source/users/files/details.mp4";
import detailsMobileMp4 from "@source/users/files/details-mobile.mp4";
</script>
# Open geographic files
With FacilMap you can open geographic files (such as GPX or KML) exported by other apps/devices or by FacilMap itself.
@ -30,7 +35,7 @@ The search box tab shows a list of all geographic objects in the opened file. Cl
By clicking the arrow on the right side of an object in the list, you can show some details about this object. You also have the option there to [use it as a route destination](../route/#use-map-points-as-destinations) (only for markers) or to add them to the map (only for [collaborative maps](../collaborative/)). To get back from the details, click on the blue arrow on the left of the heading.
<Screencast :desktop="require('./details.mp4')" :mobile="require('./details-mobile.mp4')"></Screencast>
<Screencast :desktop="detailsMp4" :mobile="detailsMobileMp4"></Screencast>
## Open an OpenStreetMap object

Wyświetl plik

@ -1,6 +1,6 @@
# Filters
Filters provide a way to temporarily show/hide certain markers/lines based on user-defined criteria. Filters are only affect what you currently see on the map, they are not persisted and do not affect what other people see on the map. Filters can be persisted as part of [saved views](../views/).
Filters provide a way to temporarily show/hide certain markers/lines based on user-defined criteria. Filters only affect what you currently see on the map, they are not persisted and do not affect what other people see on the map. Filters can be persisted as part of [saved views](../views/).
If you simply want to show/hide objects of a certain type, clicking items in the [legend](../legend/) will toggle the visibility of those types by automatically applying a filter expression.
@ -16,6 +16,8 @@ Field values are available as `data`. By default, markers and objects have only
Be aware that in filter expression, comparison is case sensitive. So the above expression would not match an object whose status is “done”. For case-insensitive comparison, compare the lower-case values: `lower(data.Status) == "done"`.
Checkbox field values are internally represented by the values `0` (unchecked) and `1` (checked). For example, to show only values where the checkbox field “Confirmed” is checked, use `data.Confirmed == 1`.
The regular expression operator `~=` allows for more advanced text matching. For example, to show all objects whose name contains “Untitled”, use `lower(name) ~= "untitled"`. Regular expressions allow to define very complex criteria what the text should look like. There are plenty of tutorials online how to use regular expressions, for example [RegexOne](https://regexone.com/).
### Filter by type
@ -24,6 +26,6 @@ To show only objects of a particular [type](../types/), use `typeId`. You can fi
## Share a link
FacilMap continuously updates the [location hash](../hash/) with your current view on the map. Your current filter expression is also saved in the location hash.
FacilMap continuously updates the [location hash](../share/) with your current view on the map. Your current filter expression is also saved in the location hash.
If you want to share a link to a particular section of the map with a filter expression applied, simply apply the filter and then copy the URL from the address bar of your browser. An example of such a link would be `https://facilmap.org/my-map#6/51.910/8.789/Mpnk//typeId%3D%3D123`.

Wyświetl plik

@ -1,42 +0,0 @@
# Share a link
When you open `https://facilmap.org/`, the URL in the address bar of your browser will change to something like `https://facilmap.org/#8/52.462/13.491/MSfR`. The part starting with the `#` is called the *location hash* and is continuously updated by FacilMap to represent your current view of the map.
This means that if you want to share a link to a specific view of the map, you can simply copy the URL from the address bar of your browser.
The following information is stored in the location hash:
* The coordinates of the centre of the part of the map that you are currently viewing
* The current zoom level
* The current [map style](../layers/) (base layer and overlays)
* One of the following (depending which is active):
* The selected [search](../search/) result, or the search term if none is selected
* The [route](../route/)
* The [map point](../click-marker/)
* The selected [marker](../markers/) or [line](../lines/) (on [collaborative maps](../collaborative/))
* The active [view](../views/) (on [collaborative maps](../collaborative/))
* The active [filter](../filter/) (on [collaborative maps](../collaborative/))
## Technical details
The location can have one of the following formats:
* Short format: `#q=<search term>`
* Long format: `#<zoom>/<latitude>/<longitude>/<layers>/<search term>/<filter>`. If the filter and/or search term is empty, the trailing slashes are omitted.
The short format is used if the current map view equals the default view for the search. This means that the map is currently zoomed to the exact location where it would zoom to when opening the result of the search term, and no additional layers or filters are active.
The different components of the long format have the following meaning:
* **zoom:** The zoom level of the map, minimum `0`, maximum `18` (might be higher or lower depending on the layers)
* **latitude:** The latitude of the center of the map, minimum `-90`, maximum `90`
* **longitude:** The longitude of the center of the map
* **layers:** Each layer is identified by a four-character key. If overlays are enabled, they are appended with a `-`. For example `Mpnk` for only Mapnik or `Mpnk-Rlie-grid` for Mapnik, Relief and Graticule.
* **search term:** Can be one of the following:
* The name of a place. Will show all results for this search term.
* The ID of a specific search result in the form `n123`, `w123`, `r123` (representing its corresponding OpenStreetMap node, way or relation ID)
* A [route query](../route/#use-a-route-query), for example `Berlin to Hamburg`
* [Coordinates](../search/#search-for-coordinates), for example `52.51704,13.38886` or `geo:52.51704,13.38886?z=11`
* The URL of a [geographic file](../files/)
* The ID of a [marker](../markers/) or [line](../lines/) in the form `m123` or `l123` (on [collaborative maps](../collaborative/))
* The ID of a [view](../views/) in the form `v123` (on [collaborative maps](../collaborative/)). This only works in the short format of the location hash.
* The ID of an OpenStreetMap object in the form `node 123`, `way 123`, `relation 123` or `trace 123`.
* Anything else that can be typed into the [search form](../search/).
* **filter:** The currently applied [filter](../filter/) (on [collaborative maps](../collaborative/))

Wyświetl plik

@ -1,5 +1,7 @@
# Getting help
If you have a question that is not answered by this documentation, feel free to to open a [discussion on GitHub](https://github.com/FacilMap/facilmap/discussions).
If you have a question that is not answered by this documentation, feel free to to open a [discussion on GitHub](https://github.com/FacilMap/facilmap/discussions) or join the [Matrix chat room](https://matrix.to/#/#facilmap:rankenste.in).
If you have found an error or have a suggestion how FacilMap or this documentation could be improved, please raise an [issue on GitHub](https://github.com/FacilMap/facilmap/issues).
If you have found an error or have a suggestion how FacilMap or this documentation could be improved, please raise an [issue on GitHub](https://github.com/FacilMap/facilmap/issues).
If you have found a translation mistake or would like to add a missing translation, you can contribute directly on [Weblate](https://hosted.weblate.org/projects/facilmap/).

Wyświetl plik

@ -1,3 +1,10 @@
<script setup lang="ts">
import customImportMp4 from "@source/users/import/custom-import.mp4";
import customImportMobileMp4 from "@source/users/import/custom-import-mobile.mp4";
import typesMp4 from "@source/users/import/types.mp4";
import typesMobileMp4 from "@source/users/import/types-mobile.mp4";
</script>
# Import geographic files
FacilMap allows you to [open geographic files](../files/) such as GeoJSON, GPX and KML. When opening a file on a collaborative map, some or all of the objects that are part of the file can be imported to be persisted on the map.
@ -18,11 +25,11 @@ When [opening a file](../files/) created by FacilMap, FacilMap will offer some a
The saved views in the file will be shown at the top of the file objects and the types at the bottom. You can import them into the map by simply clicking the + on the right. The + will only be shown if an identical view/type does not exist yet on the map. Note that importing views/types will always add new ones rather than updating existing ones, so you might end up with two types called “Marker” and two types called “Line”, which you will have to resolve by hand. If you are importing into an empty map, you might want to [delete the existing types](../types/#delete-a-type) before the import to avoid confusion.
<Screencast :desktop="require('./types.mp4')" :mobile="require('./types-mobile.mp4')"></Screencast>
<Screencast :desktop="typesMp4" :mobile="typesMobileMp4"></Screencast>
To import all the markers/lines, click “Select all” at the bottom and then “Add selected items to map” and “Custom type mapping…”. This will open a dialog where you can define in detail which objects should be imported as what. If you have already import the types, you can choose the existing types, otherwise an option to import them is offered.
<Screencast :desktop="require('./custom-import.mp4')" :mobile="require('./custom-import-mobile.mp4')"></Screencast>
<Screencast :desktop="customImportMp4" :mobile="customImportMobileMp4"></Screencast>
The recommended workflow to import an exported map onto a new empty map is:
1. Delete the default “Marker” and “Line” types on the map

Wyświetl plik

@ -1,3 +1,8 @@
<script setup lang="ts">
import layersMp4 from "@source/users/layers/layers.mp4";
import layersMobileMp4 from "@source/users/layers/layers-mobile.mp4";
</script>
# Map styles (layers)
FacilMap embeds the map styles of different OpenStreetMap-based services. Different styles present geographic features in a different way and are useful for different purposes.
@ -7,7 +12,7 @@ You can switch the style of the map by clicking on the “Map style” item in t
2. The second section are so-called “overlays”. These are shown on top of the base layer to show additional information on the map. Multiple overlays can be shown at the same time.
3. The third section contains some links to open the current map area on another map service (such as Google Maps). Clicking these links will open a new browser tab.
<Screencast :desktop="require('./layers.mp4')" :mobile="require('./layers-mobile.mp4')"></Screencast>
<Screencast :desktop="layersMp4" :mobile="layersMobileMp4"></Screencast>
## Base layers
@ -15,10 +20,12 @@ Base layers set the main style of the map. Only one base layer can be shown at t
| Base layer | Source | Purpose |
|------------|--------|---------|
| Lima Labs | [Lima Labs](https://maps.lima-labs.com/) | Good for a general geographic overview. Has a focus on car infrastructure and political borders. Supports high resolution displays. |
| Mapnik | [OpenStreetMap](https://www.openstreetmap.org/) | Good for a general geographic overview. Has a focus on car infrastructure and political borders. |
| TopPlus | [German state](https://www.bkg.bund.de/SharedDocs/Produktinformationen/BKG/DE/P-2017/170922-TopPlus-Web-Open.html) | Good for a general geographic overview, with more details than Mapnik. Has a focus on car infrastructure, political borders and topography. Unfortunately all labels are in German. |
| TopPlus | [German state](https://gdz.bkg.bund.de/index.php/default/wms-topplusopen-wms-topplus-open.html) | Good for a general geographic overview, with more details than Mapnik. Has a focus on car infrastructure, political borders and topography. Unfortunately all labels are in German. |
| Map1.eu | [Map1.eu](https://www.map1.eu/) | Has a focus on cities/towns, car infrastructure and political borders. Aims to have the same level of detail as a paper map. Only available for Europe. |
| OpenTopoMap | [OpenTopoMap](https://opentopomap.org/) | Strong focus on topography, but also shows car and train infrastructure. Shows contour lines at higher zoom levels. |
| CyclOSM | [CyclOSM](https://cyclomap.org/) | Strong focus on bicycle infrastructure and sign-posted bicycle routes. |
| OpenCycleMap | [OpenCycleMap](https://www.opencyclemap.org/) | Strong focus on sign-posted bicycle routes. At higher zoom levels, details about cycling infrastructure are shown. |
| Hike & Bike Map | [Hike & Bike Map](https://hikebikemap.org/) | Emphasizes hiking trails and off-road tracks. |
| Mapnik Water | [FreieTonne](https://www.freietonne.de/) | Similar to Mapnik, but emphasizes lakes, rivers and the sea. |
@ -40,4 +47,4 @@ Overlays are additional details that are shown on top of a base layer. Multiple
| Graticule | FacilMap/[Leaflet.AutoGraticule](https://github.com/FacilMap/Leaflet.AutoGraticule) | Adds a grid of longitude and latitude coordinates to the map. |
| Sea marks | [FreieTonne](https://www.freietonne.de/) | Shows details about light houses, weirs, buoys and many other features relevant for nautical navigation. |
If you know of a free service that provides an overlay and think it would be a useful addition to FacilMap, please raise an [issue on GitHub](https://github.com/FacilMap/facilmap/issues).
If you know of a free service that provides an overlay and think it would be a useful addition to FacilMap, please raise an [issue on GitHub](https://github.com/FacilMap/facilmap/issues).

Wyświetl plik

@ -1,3 +1,14 @@
<script setup lang="ts">
import checkboxMp4 from "@source/users/legend/checkbox.mp4";
import checkboxMobileMp4 from "@source/users/legend/checkbox-mobile.mp4";
import fieldsMp4 from "@source/users/legend/fields.mp4";
import fieldsMobileMp4 from "@source/users/legend/fields-mobile.mp4";
import filterMp4 from "@source/users/legend/filter.mp4";
import filterMobileMp4 from "@source/users/legend/filter-mobile.mp4";
import typeMp4 from "@source/users/legend/type.mp4";
import typeMobileMp4 from "@source/users/legend/type-mobile.mp4";
</script>
# Legend
A legend describes the meaning of the different styles of items. FacilMap shows the legend as a box in the bottom right of the screen on big screens and as a [search box](../ui/#search-box) tab on small screens. The legend is shown if [custom legend text](#show-custom-text) is defined or if at least one type is configured to be [shown in the legend](#show-custom-types).
@ -16,15 +27,15 @@ When editing a [type](../types/), checking “Show in legend” will add an item
If the type has any style properties that are set to “Fixed”, the legend item will represent this style. Otherwise, a generic style is chosen. For example, if a marker type is configured to have its colour fixed as yellow, its shape fixed as a circle and its icon fixed as a heart, the legend item will show a yellow circle marker with a heart. But if a marker has none of its style fixed, the legend item will show a rainbox drop marker without an icon.
<Screencast :desktop="require('./type.mp4')" :mobile="require('./type-mobile.mp4')"></Screencast>
<Screencast :desktop="typeMp4" :mobile="typeMobileMp4"></Screencast>
If a type has dropdown or checkbox fields that [control the style](../types/#styles-based-on-field-values), additional legend items are shown for each dropdown option / checkbox state that indicate its style.
<Screencast :desktop="require('./fields.mp4')" :mobile="require('./fields-mobile.mp4')"></Screencast>
<Screencast :desktop="fieldsMp4" :mobile="fieldsMobileMp4"></Screencast>
For a checkbox, two items are shown that represent the checked and unchecked state and by default have the label “~~Field name~~” (strikethrough) and “Field name”. You can change the labels by editing the checkbox field.
<Screencast :desktop="require('./checkbox.mp4')" :mobile="require('./checkbox-mobile.mp4')"></Screencast>
<Screencast :desktop="checkboxMp4" :mobile="checkboxMobileMp4"></Screencast>
## Using the legend
@ -32,4 +43,4 @@ Sometimes icons on legend items can be small to see. By hovering over a legend i
You can click individual items or headings in the legend to hide/show items of this type on the map. Internally, this sets a [filter](../filter/), so it is not persisted on the map and does not affect what other users are seeing, it only temporarily adjusts what you see on the map (unless you persist it as part of a [saved view](../views/)).
<Screencast :desktop="require('./filter.mp4')" :mobile="require('./filter-mobile.mp4')"></Screencast>
<Screencast :desktop="filterMp4" :mobile="filterMobileMp4"></Screencast>

Wyświetl plik

@ -1,6 +1,23 @@
<script setup lang="ts">
import addResultMp4 from "@source/users/lines/add-result.mp4";
import addResultMobileMp4 from "@source/users/lines/add-result-mobile.mp4";
import addResultsMp4 from "@source/users/lines/add-results.mp4";
import addResultsMobileMp4 from "@source/users/lines/add-results-mobile.mp4";
import addRouteMp4 from "@source/users/lines/add-route.mp4";
import addRouteMobileMp4 from "@source/users/lines/add-route-mobile.mp4";
import dragMp4 from "@source/users/lines/drag.mp4";
import dragMobileMp4 from "@source/users/lines/drag-mobile.mp4";
import drawMp4 from "@source/users/lines/draw.mp4";
import drawMobileMp4 from "@source/users/lines/draw-mobile.mp4";
import editDetailsMp4 from "@source/users/lines/edit-details.mp4";
import editDetailsMobileMp4 from "@source/users/lines/edit-details-mobile.mp4";
import removeMp4 from "@source/users/lines/remove.mp4";
import removeMobileMp4 from "@source/users/lines/remove-mobile.mp4";
</script>
# Lines
A line is a connection of two or more points on the map and has a name, a certain style (width, colour) and some data (such as a description). It can be a straight line or a calculated route, depending on its route mode. When you add a line to a map, it is permanently saved there and visible for anyone who is viewing the map.
A line is a connection of two or more points on the map and has a name, a certain style (width, colour, stroke) and some data (such as a description). It can be a straight line or a calculated route, depending on its route mode. When you add a line to a map, it is permanently saved there and visible for anyone who is viewing the map.
By default, a collaborative map has one type of line called “Line” that has a description field and whose style can be configured freely. Other types of lines with fixed styles and additional fields can be defined using [custom types](../types/). For simplicity, the descriptions on this page are assuming that you are working with the default “Line” type.
@ -10,7 +27,7 @@ One way to add a line to a map is to draw it on the map. In the [toolbox](../ui/
When you draw lines, they will always connect their line points using straight lines. You can, however, choose a route mode later in the [line details](#edit-line-details). This means that the line points that you added will become route destinations.
<Screencast :desktop="require('./draw.mp4')" :mobile="require('./draw-mobile.mp4')"></Screencast>
<Screencast :desktop="drawMp4" :mobile="drawMobileMp4"></Screencast>
After saving the line, it will be called “Untitled line”, will have the default style and no description. As a next step, you might want to [edit the line details](#edit-line-details) to give it a name and change its style.
@ -20,7 +37,7 @@ Another way to add a line to the map is to calculate a [route](../route/) first
On the map, the route will still look the same, but it is now saved as a line, as you can see from the fact that a new search box tab appeared for the line details, and the line is not draggable anymore.
<Screencast :desktop="require('./add-route.mp4')" :mobile="require('./add-route-mobile.mp4')"></Screencast>
<Screencast :desktop="addRouteMp4" :mobile="addRouteMobileMp4"></Screencast>
## Add a search result as a line
@ -28,11 +45,11 @@ Some [search results](../search/) are line or polygons (for example when the res
In the search box tab of the search result, click on “Add to map” and then “Line”. The line will be created with the name of the result. If the line is a [custom type](../types/), a mapping from the result info to the line data is attempted, for example if the line has a field “Address”, it will be filled with the address of the result.
<Screencast :desktop="require('./add-result.mp4')" :mobile="require('./add-result-mobile.mp4')"></Screencast>
<Screencast :desktop="addResultMp4" :mobile="addResultMobileMp4"></Screencast>
You can also add multiple results at once. In the search form, select multiple items by using the “Select all” button or clicking individual items while holding the Ctrl or Shift key. Then click “Add selected items to map” and then “Line/polygon items as Line”. (Those selected results that are markers and not lines/polygons will not be added.)
You can also add multiple results at once. In the search form, select multiple items by using the “Select all” button or clicking individual items while holding the Ctrl key. Then click “Add selected items to map” and then “Line/polygon items as Line”. (Those selected results that are markers and not lines/polygons will not be added.)
<Screencast :desktop="require('./add-results.mp4')" :mobile="require('./add-results-mobile.mp4')"></Screencast>
<Screencast :desktop="addResultsMp4" :mobile="addResultsMobileMp4"></Screencast>
## Show line details
@ -45,20 +62,23 @@ To edit the details of a line, select the line and then click “Edit data” in
* **Routing mode:** The route mode of the line. By default, “straight line” is selected, but you can select something else to make the line points into route destinations. More information about route modes can be found under [routes](../route/#route-modes).
* **Colour:** The colour of the line.
* **Width:** The width of the line (in pixels). Use the + and &minus; buttons to change the value.
* **Stroke:** The stroke style of the line (solid, dashed or dotted).
* **Description:** The description of the line. Will be shown in the line details. You can format the text using [Markdown](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
Click “Save” to save your changes.
<Screencast :desktop="require('./edit-details.mp4')" :mobile="require('./edit-details-mobile.mp4')"></Screencast>
<Screencast :desktop="editDetailsMp4" :mobile="editDetailsMobileMp4"></Screencast>
## Edit line points
To change the course or the position of a line, select the line and then click “Edit waypoints” in its [search box](../ui/#search-box) tab. A message will appear on the top right of the screen and the tab will turn into a search form and the line will become draggable. You can now change the line in the same way that you would [change a route](../route/#drag-a-route), by changing the route destinations in the form or by dragging the line. When you are finished, click the “Finish” button in the message.
<Screencast :desktop="require('./drag.mp4')" :mobile="require('./drag-mobile.mp4')"></Screencast>
<Screencast :desktop="dragMp4" :mobile="dragMobileMp4"></Screencast>
## Remove a line
To remove a line from the map, select the line, click “Remove” and confirm the alert box. Note that removed lines will remain in the [edit history](../history/) of the map and can be seen and restored there by admins.
<Screencast :desktop="require('./remove.mp4')" :mobile="require('./remove-mobile.mp4')"></Screencast>
<Screencast :desktop="removeMp4" :mobile="removeMobileMp4"></Screencast>
You can also remove multiple lines at once by [handling multiple objects](../multiple/).

Wyświetl plik

@ -1,3 +1,8 @@
<script setup lang="ts">
import locateMp4 from "@source/users/locate/locate.mp4";
import locateMobileMp4 from "@source/users/locate/locate-mobile.mp4";
</script>
# Show your location
By clicking the crosshair icon on the top left of the screen (under the zoom buttons), FacilMap will attempt to zoom to your location on the map.
@ -9,8 +14,10 @@ The cross-hair icon will be shown in one of these colours:
* **Orange:** Your location is shown on the map and the map will follow you as your location changes.
* **Blue:** Your location is shown on the map, but the map does not follow you.
Clicking the cross-hair icon will enable the orange mode. As soon as you move the map by hand, it will switch to blue mode. To go back to orange mode, first go back to black mode by clicking the icon once and then go to orange mode by clicking it again.
Clicking the cross-hair icon will enable the orange mode. As soon as you move the map by hand, it will switch to blue mode. To go back to orange mode, click the icon again.
While your location is visible on the map, FacilMap will try to prevent your screen from turning off, which would be disruptive during live navigation. It will depend on your browser and on your operating system whether this works and whether your browser will ask you for permission for this first.
Your location will be indicated as a blue circle with a blue dot at the center. Your location may be anywhere within the circle, it is not necessarily at its centre. If the circle is very small, it means that your browser could determine your location very accurately. If the circle is very big, it means that your browser had trouble to make an accurate guess about your location, and you may be anywhere within the circle.
<Screencast :desktop="require('./locate.mp4')" :mobile="require('./locate-mobile.mp4')"></Screencast>
<Screencast :desktop="locateMp4" :mobile="locateMobileMp4"></Screencast>

Wyświetl plik

@ -18,6 +18,8 @@ The map name is shown as the title of your browser tab and window. It will also
If you check the “Accessible for search engines” checkbox, search engines like Google or Duckduckgo will be allowed to list the read-only version of the map. Note that FacilMap itself does not report these maps to the search engines, but when they find them through a link on a public website, it is this setting that allows them to add the map to their results.
Enabling this will also make the map available in FacilMaps list of public maps in the [open map dialog](../collaborative/#open-an-existing-map).
When this is enabled, an additional field “Short description” is shown. Search engines will list the map with the [map name](#map-name) and this description.
## Cluster markers

Wyświetl plik

@ -1,3 +1,16 @@
<script setup lang="ts">
import addMp4 from "@source/users/markers/add.mp4";
import addMobileMp4 from "@source/users/markers/add-mobile.mp4";
import addResultMp4 from "@source/users/markers/add-result.mp4";
import addResultMobileMp4 from "@source/users/markers/add-result-mobile.mp4";
import addResultsMp4 from "@source/users/markers/add-results.mp4";
import addResultsMobileMp4 from "@source/users/markers/add-results-mobile.mp4";
import moveMp4 from "@source/users/markers/move.mp4";
import moveMobileMp4 from "@source/users/markers/move-mobile.mp4";
import removeMp4 from "@source/users/markers/remove.mp4";
import removeMobileMp4 from "@source/users/markers/remove-mobile.mp4";
</script>
# Markers
A marker is a point on the map that has a name, a certain style (shape, icon, size, colour) and some data (such as a description). When you add a marker to a map, it is permanently saved there and visible for anyone who is viewing the map.
@ -8,21 +21,21 @@ By default, a collaborative map has one type of marker called “Marker” that
To add a marker, click on “Add” in the [toolbox](../ui/#toolbox) and then “Marker”. A message will appear that asks you to click on the map to add a marker, along with a “Cancel” button to cancel the operation. Once you click somewhere on the map, a marker is added there and a [search box](../ui/#search-box) tab opens with information about the marker.
<Screencast :desktop="require('./add.mp4')" :mobile="require('./add-mobile.mp4')"></Screencast>
<Screencast :desktop="addMp4" :mobile="addMobileMp4"></Screencast>
The new marker is called “Untitled marker”, has the default style and no description. As a next step, you might want to [edit the marker details](#edit-marker-details) to give it a name and change its style.
## Add a search result as a marker
An alternative way to add a marker is to add a [search result](../search/), a marker from an [opened geographic file](../files/) or a [map point](../click-marker/) as a marker to the collaborative map.
An alternative way to add a marker is to add a [search result](../search/), a [POI](../pois/), a marker from an [opened geographic file](../files/) or a [map point](../click-marker/) as a marker to the collaborative map.
In the search box tab of that object, click on “Add to map” and then “Marker”. The marker will be created with the name of the result. If the marker is a [custom type](../types/), a mapping from the result info to the marker data is attempted, for example if the marker has a field “Address”, it will be filled with the address of the result.
<Screencast :desktop="require('./add-result.mp4')" :mobile="require('./add-result-mobile.mp4')"></Screencast>
<Screencast :desktop="addResultMp4" :mobile="addResultMobileMp4"></Screencast>
You can also add multiple results at once. In the search form, select multiple items by using the “Select all” button or clicking individual items while holding the Ctrl or Shift key. Then click “Add selected items to map” and then “Marker items as Marker”. (In case of an opened geographic file, some of the selected items may be polygons or polylines, these will not be added, as they are not “Marker items”.)
You can also add multiple results at once. In the search form, select multiple items by using the “Select all” button or clicking individual items while holding the Ctrl key. Then click “Add selected items to map” and then “Marker items as Marker”. (In case of an opened geographic file, some of the selected items may be polygons or polylines, these will not be added, as they are not “Marker items”.)
<Screencast :desktop="require('./add-results.mp4')" :mobile="require('./add-results-mobile.mp4')"></Screencast>
<Screencast :desktop="addResultsMp4" :mobile="addResultsMobileMp4"></Screencast>
## Show marker details
@ -44,10 +57,12 @@ Click “Save” to save your changes.
To change the position of a marker, select the marker and then click “Move” in its [search box](../ui/#search-box) tab. A message will appear at the top right of the screen. Now drag the marker to the desired position, and once you are done, click “Finish” in the message. To keep it at its old position, cancel the operation by clicking “Cancel”.
<Screencast :desktop="require('./move.mp4')" :mobile="require('./move-mobile.mp4')"></Screencast>
<Screencast :desktop="moveMp4" :mobile="moveMobileMp4"></Screencast>
## Remove a marker
To remove a marker from the map, select the marker, click “Remove” and confirm the alert box. Note that removed markers will remain in the [edit history](../history/) of the map and can be seen and restored there by admins.
<Screencast :desktop="require('./remove.mp4')" :mobile="require('./remove-mobile.mp4')"></Screencast>
<Screencast :desktop="removeMp4" :mobile="removeMobileMp4"></Screencast>
You can also remove multiple markers at once by [handling multiple objects](../multiple/).

Wyświetl plik

@ -0,0 +1,17 @@
# Handle multiple objects
You can select multiple markers and lines at once and perform batch operations on them.
## Select multiple objects
To select multiple objects, click them on the map while holding the Ctrl key on your keyboard. A new tab will appear in the [search box](../ui/#search-box) that lists all the selected objects. To exclude an object from the selection again, Ctrl+Click the item on the map or in the list of objects again. Note that clicking an object without holding Ctrl will clear the selection and select only that object.
Alternatively, you can hold Shift and draw a box on the map using your mouse. All objects that are inside the box will be selected. If you already have some items selected, holding Shift+Ctrl while drawing the box will add the objects in the box to the existing selection rather than resetting the existing selection.
**Note:** At the moment it is not possible yet to select multiple objects on devices without a keyboard (such as smartphones). This will be implemented soon.
## Remove multiple objects
To remove multiple objects at once, select them and then click “Remove” in the bottom of the search box tab and confirm the alert box.
Note that removed objects will remain in the [edit history](../history/) for admins to see and restore.

Wyświetl plik

@ -0,0 +1,56 @@
<script setup lang="ts">
import metadataMp4 from "@source/users/pois/metadata.mp4";
import metadataMobileMp4 from "@source/users/pois/metadata-mobile.mp4";
import poisMp4 from "@source/users/pois/pois.mp4";
import poisMobileMp4 from "@source/users/pois/pois-mobile.mp4";
import zoomMp4 from "@source/users/pois/zoom.mp4";
import zoomMobileMp4 from "@source/users/pois/zoom-mobile.mp4";
</script>
# POIs
Using the “POIs” menu in the [search box](../ui/#search-box), you can find amenities and points of interest (POIs) on the map. You can use it for example to show all post boxes, parking places, supermarkets or bus stops in an area.
Choose a category of POIs and then check some of the POI types to show them on the map. Since the list of available POI types is quite long, you can type something into the filter field on top to show only those checkboxes that contain that phrase. For example, if you type “parking” into the filter field, only the “Bicycle parking” and “Parking” checkboxes are shown.
When one or more checkboxes are checked, the POIs of this type are shown as black square markers on the map. Only the POIs in the current map view are loaded, so only when you move to a particular area on the map, the POIs for that area are loaded. A maximum of 50 POIs are loaded. If the current area of the map contains more than 50 POIs of the selected type, a yellow message will be shown on the top of the map (see more details [below](#zoom-limitations)). In that case none or only some of the available POIs are shown. Zoom in further to get a complete overview of POIs.
<Screencast :desktop="poisMp4" :mobile="poisMobileMp4"></Screencast>
## Show details
Clicking on a POI marker will open an additional search box tab with some metadata, such as opening hours, access restrictions and contact details. The metadata tab can be closed by clicking on the X or clicking somewhere else on the map.
<Screencast :desktop="metadataMp4" :mobile="metadataMobileMp4"></Screencast>
## Zoom limitations
To avoid overloading the server or your browser, a maximum of 50 POIs are shown on the map, and the server may take a maximum of 2 seconds to load them. When the limit is exceeded, a yellow message is shown on the top of the map. There is no fixed minimum zoom level, it depends on the density of POIs in the current map area. The yellow message may say one of the following things:
* “Zoom in to show POIs.” – This is shown when you are zoomed out so far that the server took more than 2 seconds to look for POIs. To avoid overloading the server, the request was canceled and no POIs are shown.
* “Not all POIs are shown because there are too many results. Zoom in to show all results.” – This means that the server found more than 50 POIs. To save resources, only the first 50 POIs found are shown on the map. This means that the results shown are not all that exist. To get a complete overview of the existing POIs, zoom in further.
<Screencast :desktop="zoomMp4" :mobile="zoomMobileMp4"></Screencast>
## Share a link
When you enable POIs on the map, the types of POIs that you checked are stored in the [location hash](../share/). To share a link to a specific part of the map with specific types of POIs shown, simply navigate the map to that view and copy the URL from the address bar of your browser.
On collaborative maps, POI types can also be saved and shared as part of [saved views](../views/).
## Custom query
FacilMap uses the [Overpass API](https://overpass-api.de/) to query the OpenStreetMap database for POIs. If the available types of POIs do not fulfill your needs, you can click the “Custom query” button at the bottom of the POI tab and type a custom query into the text field. The query will be applied automatically, there is no save button or something like that.
The custom query field only expects a [query statement](https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL#The_Query_Statement), without any other Overpass commands. FacilMap will automatically enrich the query so that it searches only in the current map area and there is a limit of 50 results. The results will always be shown as markers. If a query returns OpenStreetMap ways or relations, the marker will be rendered at their geometric centre.
The Overpass API queries the OpenStreetMap database for objects based on their tags. An overview over the most common tags can be found [on the OpenStreetMap wiki](https://wiki.openstreetmap.org/wiki/Map_features).
Here are some example queries:
* `nwr[amenity=parking]`: Parking places.
* `(nwr[amenity=toilets]; -nwr[amenity=toilets][fee][fee!=no];)`: Toilets without a fee.
* `(nwr[amenity=post_box]; nwr[amenity=post_office];)`: Post boxes and post offices.
* `nwr[amenity=drinking_water][drinking_water=yes]`: Water fountains that are explicity marked as drinkable.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -6,17 +6,19 @@ FacilMap is a combination of services provided by FacilMap itself and third-part
## FacilMap itself
The following data is *sent* to the FacilMap server but is *not persisted* there:
The following data is *processed* but *not persisted* by the FacilMap server:
* Your **IP address**: When you open FacilMap, a connection to the server is made, which inevitably reveils your IP address to the server. FacilMap uses your IP address to guess your location using the [MaxMind GeoLite2](https://dev.maxmind.com/geoip/geoip2/geolite2/) database (to decide the initial map view), but it uses a local copy of that database rather than sending your IP address to MaxMind.
* Your **map position**: When you have a [route](../route/) or a [collaborative map](../collaborative/) open, the current map position is sent to the server every time you move/zoom the map. (In response, the server will send any map objects in your current map view.)
* Your **search term**: When you [search](../search/) for a place, open a [map point](../click-marker/) or calculate a [route](../route/), the search term is sent to the FacilMap server and relayed to third-party services from there.
* Your **language settings**: If you have set custom language and/or unit user preferences, these are sent to the server on each request. Otherwise, the language preferences that your browser sends with each request are used.
The following data is sent to the FacilMap server and *persisted* there:
The following data is *processed* and *persisted* by the FacilMap server:
* When you calculate a [route](../route/), the details about this route are stored on the server until you close the route (or your browser window) again.
* Any [markers](../markers/), [lines](../lines), [types](../types), [views](../views) that you add and any [map settings](../map-settings/) that you make on a [collaborative map](../collaborative/) is stored on the server. Deleting such objects will still keep a copy in the [change history](../history/). The only way to delete the data is to [delete the collaborative map](../map-settings/#delete-the-map).
The following data is persisted in your browser:
* If you change the [zoom settings](../search/#zoom-settings), these are persisted in the local storage of your browser.
* If you add [bookmarks](../collaborative/#bookmark-a-map), these are persisted in the local storage of your browser.
* If you change the language or unit user preferences, these are persisted as a cookie in your browser.
## Layers
@ -29,7 +31,7 @@ FacilMap does not have any control over what these providers do with your data,
## Search/route
When you make a [search](../search/), calculate a [route](../route/) or open a [map point](../click-marker/), the search is relayed through the FacilMap server to the third-party provider. This means that only the search terms itself are sent to the provider, but your IP address or any cookies are not reveiled to the third party. The following providers are used:
When you make a [search](../search/), calculate a [route](../route/) or open a [map point](../click-marker/), a third-party provider is used, so the search terms, your IP address and any cookies set by the provider may be transmitted. The following providers are used:
* [Nominatim](https://nominatim.openstreetmap.org/) to resolve search terms or locations
* [Mapbox](https://www.mapbox.com/) for routes with a simple [route mode](../route/#route-modes)
* [OpenRouteService](https://openrouteservice.org/) for routes with an advanced [route mode](../route/#route-modes)

Some files were not shown because too many files have changed in this diff Show More