@tomtom-org/maps-sdk-plugin-flyover 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.txt +37 -461
- package/dist/index.d.ts +1 -1
- package/dist/index.es.js +1 -1
- package/dist/index.es.js.map +1 -1
- package/package.json +6 -7
package/LICENSE.txt
CHANGED
|
@@ -1,511 +1,87 @@
|
|
|
1
|
-
TOMTOM MAPS SDK FOR JAVASCRIPT LICENSE AGREEMENT (“AGREEMENT”)
|
|
1
|
+
TOMTOM MAPS SDK FOR JAVASCRIPT LICENSE AGREEMENT (“AGREEMENT”)
|
|
2
2
|
|
|
3
|
-
Last update:
|
|
3
|
+
Last update: September 2026
|
|
4
4
|
|
|
5
|
+
By downloading or accessing the “TomTom Maps SDK for JavaScript” (the “SDK”), you are entering into a binding agreement with TomTom Global Content B.V. (“TomTom”). The SDK comprises all software, binaries and source code made available by TomTom for it, whether through the TomTom Developer Portal or public code repositories (e.g. GitHub). This Agreement with TomTom consists of the terms and conditions below together with the Data Processing Schedule. You should not use the SDK if you cannot agree to both.
|
|
5
6
|
|
|
7
|
+
You may not use the SDK or accept this Agreement if you are barred from receiving it under the laws of the European Union or of any other country, including the country in which you are resident or from which you use the SDK. If you accept this Agreement on behalf of your employer or another entity, you represent and warrant that you have full legal authority to bind that employer or entity. If you do not have that authority, you may not accept this Agreement or use the SDK on their behalf.
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
You may not use the TomTom Maps SDK for JavaScript and may not accept the Agreement if you are a person barred from receiving the TomTom Maps SDK for JavaScript under the laws of the European Union or other countries, including the country in which you are resident or from which you use the TomTom Maps SDK for JavaScript. If you agree to be bound by the Agreement on behalf of your employer or other entity, you represent and warrant that you have full legal authority to bind your employer or such entity to the Agreement. If you do not have the requisite authority, you may not accept the Agreement or use the TomTom Maps SDK for JavaScript on behalf of your employer or other entity.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
1. GRANT OF LICENSE: Subject to and conditional upon your compliance at all times with this Agreement, TomTom grants to you a limited, non-exclusive, non-transferable and non-sublicensable, revocable, royalty-free license during the Term (as defined in Clause 9) to use the TomTom Maps SDK for JavaScript in connection with the development of web based applications or mobile applications to be used in connection with the TomTom APIs and, in addition, third party online APIs (“Permitted Applications”). For the avoidance of doubt, you must use the TomTom APIs in connection with the TomTom Maps SDK for JavaScript. Any such Permitted Application and use of the TomTom APIs shall be subject to TomTom’s standard terms of use applicable to the TomTom APIs as made available on the TomTom developer portal or any other specific written agreement that you and TomTom have executed regarding the use of the TomTom APIs.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
2. UPDATES AND CHANGES TO THE TOMTOM MAPS SDK FOR JAVASCRIPT AND THIS AGREEMENT: TomTom reserves the right, with or without notice and at its sole discretion, to discontinue, update, modify, upgrade and supplement any of the TomTom Maps SDK for JavaScript or any features within the TomTom Maps SDK for JavaScript and to modify this Agreement. You agree that future versions of the TomTom Maps SDK for JavaScript may be incompatible with applications that were developed on previous versions of the TomTom Maps SDK for JavaScript.
|
|
18
|
-
|
|
9
|
+
1. GRANT OF LICENSE: Subject to and conditional upon your compliance at all times with this Agreement, TomTom grants you a limited, non-exclusive, non-transferable, non-sublicensable, revocable, royalty-free license during the Term (as defined in Clause 9) to use the SDK to develop web or mobile applications that are used in connection with the TomTom APIs and, in addition, third party online APIs (“Permitted Applications”). For the avoidance of doubt, you must use the TomTom APIs in connection with the SDK. Any Permitted Application and any use of the TomTom APIs is subject to TomTom’s standard terms of use applicable to the TomTom APIs as made available on the TomTom Developer Portal, or to any other specific written agreement executed between you and TomTom regarding the use of the TomTom APIs.
|
|
19
10
|
|
|
11
|
+
2. UPDATES AND CHANGES TO THE SDK AND THIS AGREEMENT: TomTom reserves the right, with or without notice and at its sole discretion, to discontinue, update, modify, upgrade and supplement the SDK or any of its features, and to modify this Agreement. You agree that future versions of the SDK may be incompatible with applications developed on previous versions.
|
|
20
12
|
|
|
21
13
|
3. YOUR FURTHER OBLIGATIONS:
|
|
22
14
|
|
|
15
|
+
3.1. You agree to use the SDK only for purposes permitted by (a) this Agreement and (b) any applicable law, regulation or generally accepted practice or guideline in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries).
|
|
23
16
|
|
|
17
|
+
3.2. Other than as permitted by applicable legislation, you are prohibited from copying (except for backup purposes), modifying, adapting, (re)distributing, renting, lending, publicly presenting, decompiling, reverse engineering, disassembling or creating derivative works of the SDK or any part of it. Notwithstanding the foregoing, where the SDK is hosted on a public repository (e.g. GitHub, which allows forking under its terms of service), you may: (i) fork the repository solely for the purpose of submitting a pull request to TomTom, provided that such fork remains hosted within the platform and is not used as a basis for a separate or competing product or service; and (ii) download a local copy of the source code solely for the purpose of debugging your Permitted Application, provided that such copy is not shared or redistributed. The availability of the source code in a public repository does not constitute “Open-Source” licensing and grants you no rights under any Open Source Initiative (OSI) approved license. Neither that availability, nor any rights granted by the platform’s terms of service with respect to viewing or forking within the platform, shall be construed as a grant of rights to modify, fork or redistribute the SDK beyond those expressly granted in Clause 1 and this Clause 3.2.
|
|
24
18
|
|
|
25
|
-
3.
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
3.2. Other than as permitted by applicable legislation, you are prohibited from copying (except for backup purposes), modifying, adapting, (re)distributing, renting, lending, publicly presenting, decompiling, reverse engineering, disassembling, or creating derivative works of the TomTom Maps SDK for JavaScript or any part of the TomTom Maps SDK for JavaScript. Notwithstanding the foregoing, if the TomTom Maps SDK is hosted on a public repository (e.g. GitHub, which allows forking under its terms of service), you may: (i) fork the repository solely for the purpose of submitting a Pull Request to TomTom, provided that such fork remains hosted within the platform and may not be used as a basis for a separate or competing product or service; and (ii) download a local copy of the source code solely for the purpose of debugging your Permitted Application, provided that such copy is not shared or redistributed. The availability of the source code in a public repository, and any rights granted by that platform's terms of service with respect to viewing or forking within the platform, do not constitute — and shall not be construed as — a grant of rights to modify, fork, or redistribute.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
3.3. You may not use the TomTom Maps SDK for JavaScript in connection with any illegal, fraudulent, dishonest or unethical activity or behaviour, specially to create, develop, or help others create or develop a product, service, or library that competes, directly or indirectly, with the TomTom Maps SDK or the TomTom APIs.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
3.4. You shall be permitted to display TomTom APIs results (“Results”) on third party content or map data provided that nothing shall entitle you to add or incorporate Results into the OSM or any other open source database. You shall also be permitted to display Results from third party content or map data on a TomTom map as a separate layer.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
3.5. The TomTom Maps SDK for JavaScript may include data which TomTom licenses from third parties or open-source software. You shall comply with all requirements and restrictions which such third parties may require TomTom to impose on you (and as may be updated and amended by TomTom from time to time). It shall be your responsibility to check, review and comply with any updated or amended terms that would apply to the TomTom Maps SDK for JavaScript licensed to you under this Agreement. For the avoidance of doubt, any such software is made available to you under the terms and conditions of the applicable licenses. In the event of a conflict between this Agreement and an Open-Source license governing a specific dependency, the Open Source license shall prevail solely with respect to that dependency.
|
|
19
|
+
3.3. You may not use the SDK in connection with any illegal, fraudulent, dishonest or unethical activity or behaviour, and in particular not to create or develop, or help others create or develop, a product, service or library that competes, directly or indirectly, with the SDK or the TomTom APIs.
|
|
42
20
|
|
|
21
|
+
3.4. You are permitted to display TomTom APIs results (“Results”) on third party content or map data, and to display results from third party content or map data on a TomTom map as a separate layer. Nothing herein entitles you to add or incorporate Results into OSM or any other open source database.
|
|
43
22
|
|
|
23
|
+
3.5. The SDK may include data or software that TomTom licenses from third parties, including open-source software. Such components are made available to you under the terms and conditions of their applicable licenses, and you shall comply with all requirements and restrictions that those third parties may require TomTom to impose on you (as may be updated and amended by TomTom from time to time). It is your responsibility to check, review and comply with those terms as they apply to the SDK licensed to you under this Agreement. The applicable third party and open-source notices are set out in the accompanying THIRD_PARTY.txt file. In the event of a conflict between this Agreement and an open-source license governing a specific dependency, that open-source license shall prevail solely with respect to that dependency.
|
|
44
24
|
|
|
45
|
-
4. PRIVACY: In order to improve the TomTom Developer Portal and
|
|
46
|
-
|
|
47
|
-
|
|
25
|
+
4. PRIVACY: In order to improve the TomTom Developer Portal and the SDK, TomTom may collect certain usage related information from the software, including but not limited to general SDK and product identifiers, version numbers, and information on which tools and/or services in the SDK are used and how you use them. In addition, TomTom may collect your unique identifier and associated IP address for legitimate business reasons (such as, without limitation, the processing of invoices). This data is retained for a maximum period of 120 days and is maintained in accordance with our Privacy Policy.
|
|
48
26
|
|
|
49
27
|
5. INTELLECTUAL PROPERTY RIGHTS AND COPYRIGHT NOTICES:
|
|
50
28
|
|
|
29
|
+
5.1. All intellectual property rights related to the SDK and to the TomTom APIs are owned by TomTom and/or its licensors and suppliers. TomTom obtains no intellectual property rights under this Agreement in or to any Permitted Application that you develop using the SDK and the TomTom APIs.
|
|
51
30
|
|
|
52
|
-
|
|
53
|
-
5.1. All intellectual property rights related to the TomTom Maps SDK for JavaScript and to the TomTom APIs are owned by TomTom and/or its licensors and suppliers. However, TomTom agrees that it obtains no intellectual property rights under this Agreement in or to any Permitted Application that you develop using the TomTom Maps SDK for JavaScript and the TomTom APIs.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
5.2. Your use of any trade names, logos, trademarks, service marks and other marks of TomTom is subject to TomTom’s prior written approval. Any goodwill arising out of the use thereof shall inure to the benefit of TomTom. In addition, you agree not to remove, obscure, mask or change any logo and/or copyright notice placed on or automatically generated by the TomTom Maps SDK for JavaScript or the TomTom APIs.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
31
|
+
5.2. Your use of any trade names, logos, trademarks, service marks and other marks of TomTom is subject to TomTom’s prior written approval, and any goodwill arising out of such use shall inure to the benefit of TomTom. You also agree not to remove, obscure, mask or change any logo and/or copyright notice placed on, or automatically generated by, the SDK or the TomTom APIs.
|
|
62
32
|
|
|
63
33
|
6. WARRANTIES:
|
|
64
34
|
|
|
35
|
+
6.1. YOU ACKNOWLEDGE THAT THE SDK HAS NOT BEEN DEVELOPED TO MEET YOUR INDIVIDUAL REQUIREMENTS AND THAT IT IS THEREFORE YOUR RESPONSIBILITY TO ENSURE THAT ITS FACILITIES AND FUNCTIONS MEET YOUR REQUIREMENTS. TOMTOM DOES NOT AND CANNOT WARRANT THAT THE SDK OPERATES UNINTERRUPTED OR ERROR-FREE, OR THAT IT IS CAPABLE OF INTEROPERATING WITH ANY OTHER SYSTEM, DEVICE OR PRODUCT (EITHER SOFTWARE OR HARDWARE).
|
|
65
36
|
|
|
37
|
+
6.2. YOUR USE OF THE SDK AND OF ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH ITS USE IS AT YOUR OWN DISCRETION AND RISK, AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICES, OR ANY LOSS OF DATA, THAT RESULTS FROM SUCH USE.
|
|
66
38
|
|
|
67
|
-
6.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
6.2. YOUR USE OF THE TOMTOM MAPS SDK FOR JAVASCRIPT AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE TOMTOM MAPS SDK FOR JAVASCRIPT IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICES OR ANY LOSS OF DATA THAT RESULTS FROM SUCH USE.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
6.3. WITHOUT LIMITING CLAUSES 6.1 AND 6.2 ABOVE, THE TOMTOM MAPS SDK FOR JAVASCRIPT IS PROVIDED ON AN “AS IS” AND WITH “ALL FAULTS BASIS” AND TOMTOM EXPRESSLY DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, SATISFACTORY QUALITY, ACCURACY, TITLE AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
6.4. THE PROVISIONS OF THIS CLAUSE DO NOT AFFECT ANY OF YOUR LEGAL RIGHTS UNDER APPLICABLE MANDATORY NATIONAL LEGISLATION, INCLUDING BUT NOT LIMITED TO ANY MANDATORY STATUTORY WARRANTIES. IF ANY PART OF THIS LIMITED WARRANTY IS HELD TO BE INVALID OR UNENFORCEABLE, THE REMAINDER OF THE LIMITED WARRANTY SHALL NONETHELESS REMAIN IN FULL FORCE AND EFFECT.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
39
|
+
6.3. WITHOUT LIMITING CLAUSES 6.1 AND 6.2 ABOVE, THE SDK IS PROVIDED ON AN “AS IS” AND “ALL FAULTS” BASIS AND TOMTOM EXPRESSLY DISCLAIMS ALL OTHER WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, SATISFACTORY QUALITY, ACCURACY, TITLE AND FITNESS FOR A PARTICULAR PURPOSE.
|
|
83
40
|
|
|
41
|
+
6.4. THIS CLAUSE 6 DOES NOT AFFECT ANY OF YOUR LEGAL RIGHTS UNDER APPLICABLE MANDATORY NATIONAL LEGISLATION, INCLUDING BUT NOT LIMITED TO ANY MANDATORY STATUTORY WARRANTIES.
|
|
84
42
|
|
|
85
43
|
7. LIMITATION OF LIABILITY:
|
|
86
44
|
|
|
45
|
+
7.1. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, NEITHER TOMTOM NOR ITS LICENSORS, SUPPLIERS, OFFICERS, DIRECTORS OR EMPLOYEES SHALL BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY DAMAGES, WHETHER DIRECT, SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL (INCLUDING BUT NOT LIMITED TO DAMAGES FOR THE INABILITY TO USE OR ACCESS THE SDK OR THE TOMTOM APIS, LOSS OF DATA, LOSS OF BUSINESS, LOSS OF PROFITS, BUSINESS INTERRUPTION OR THE LIKE) ARISING OUT OF OR RELATED TO THE USE OF, THE INABILITY TO USE OR ACCESS, OR OTHERWISE THE PERFORMANCE OF THE SDK, EVEN IF TOMTOM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
87
46
|
|
|
47
|
+
7.2. TO THE EXTENT TOMTOM IS NOT PERMITTED UNDER APPLICABLE LAW TO EXCLUDE ITS LIABILITY PURSUANT TO CLAUSE 7.1 ABOVE, TOMTOM’S TOTAL AGGREGATE LIABILITY TO YOU SHALL BE LIMITED TO EUR 100.00.
|
|
88
48
|
|
|
89
|
-
7.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
7.2. TO THE EXTENT TOMTOM IS NOT PERMITTED UNDER APPLICABLE LAW TO EXCLUDE ITS LIABILITY PURSUANT TO CLAUSE 7.1 ABOVE, TOMTOM’S TOTAL AGGREGATE LIABILITY TO YOU SHALL BE LIMITED TO THE AMOUNT OF EUR100.00.
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
7.3. THE ABOVE LIMITATIONS OF LIABILITY UNDER THIS CLAUSE 7 DO NOT: (I) AFFECT YOUR RIGHTS UNDER ANY APPLICABLE MANDATORY NATIONAL LEGISLATION; (II) EXCLUDE OR LIMIT ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
8. IPR INDMENITY: To the maximum extent permitted by law, you agree to defend, indemnify and hold TomTom and its respective directors, officers, employees, affiliates, agents and suppliers harmless from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorney’s fees) arising out of or accruing from (a) your use of the TomTom Maps SDK for JavaScript, (b) any application you develop on the TomTom Maps SDK for JavaScript that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this Agreement.
|
|
104
|
-
|
|
49
|
+
7.3. THE LIMITATIONS OF LIABILITY UNDER THIS CLAUSE 7 DO NOT: (I) AFFECT YOUR RIGHTS UNDER ANY APPLICABLE MANDATORY NATIONAL LEGISLATION; OR (II) EXCLUDE OR LIMIT ANY LIABILITY THAT CANNOT BE EXCLUDED OR LIMITED UNDER APPLICABLE LAW.
|
|
105
50
|
|
|
51
|
+
8. IPR INDEMNITY: To the maximum extent permitted by law, you agree to defend, indemnify and hold TomTom and its respective directors, officers, employees, affiliates, agents and suppliers harmless from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorney’s fees) arising out of or accruing from (a) your use of the SDK, (b) any application you develop on the SDK that infringes any copyright, trademark, trade secret, trade dress, patent or other intellectual property right of any person, or that defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you with this Agreement.
|
|
106
52
|
|
|
107
53
|
9. TERM AND TERMINATION AND DIRECT ENFORCEMENT:
|
|
108
54
|
|
|
55
|
+
9.1. This Agreement begins on either (a) the date you demonstrate your acceptance of its terms and conditions or (b) the date you first use the SDK, and continues until terminated (jointly the “Term”) by either TomTom or you in accordance with this Agreement. The provisions of this Agreement which by their nature are intended to survive termination will remain in effect after termination, and you agree that TomTom may directly enforce these provisions against you.
|
|
109
56
|
|
|
57
|
+
9.2. Without prejudice to any other rights, TomTom may immediately terminate this Agreement if (i) you fail to comply with any of its terms and conditions, (ii) the agreement governing the use of the TomTom APIs between you and TomTom has terminated, or (iii) TomTom decides to no longer provide the SDK, or certain parts of it, either entirely or in territories related to your use.
|
|
110
58
|
|
|
111
|
-
9.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
9.2. Without prejudice to any other rights, TomTom may immediately terminate this Agreement if (i) you fail to comply with any of its terms and conditions, or (ii) the agreement governing the use of TomTom APIs between you and TomTom you has terminated, or (iii) TomTom decides to no longer provide the TomTom Maps SDK for JavaScript or certain parts of the TomTom Maps SDK for JavaScript either entirely or in certain territories related to your use.
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
9.3. You may terminate this Agreement by ceasing your use of the TomTom Maps SDK for JavaScript at any time.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
59
|
+
9.3. You may terminate this Agreement at any time by ceasing your use of the SDK.
|
|
124
60
|
|
|
125
61
|
10. GOVERNING LAW AND DISPUTE RESOLUTION:
|
|
126
62
|
|
|
63
|
+
10.1. This Agreement, and any dispute arising under or in connection with it (including any non-contractual claims and disputes), shall be governed by and construed in accordance with the laws of the Netherlands. The applicability of the United Nations Convention on Contracts for the International Sale of Goods is explicitly excluded.
|
|
127
64
|
|
|
65
|
+
10.2. If you, your employer or the entity on whose behalf you act is incorporated in the European Union, you and TomTom hereby irrevocably submit to the exclusive jurisdiction of the courts of Amsterdam, the Netherlands, in respect of any such dispute.
|
|
128
66
|
|
|
129
|
-
10.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
10.2. If you, your employer or the entity on whose behalf you act is incorporated outside of European Union, then this Agreement and any dispute arising under or in connection with it, including any non-contractual claims and disputes, shall be governed by and construed in accordance with the laws of the Netherlands. All disputes arising in connection with the Agreement, including any non-contractual claims or disputes, shall be settled in accordance with the Arbitration Rules of the Netherlands Arbitration Institute. The arbitral tribunal shall be composed of three (3) arbitrators. The place or arbitration shall be Amsterdam, and the language of the arbitration shall be English. Consolidation of the arbitral proceedings with other arbitral proceedings, as provided for in Article 1046 of the Dutch Code of Civil Procedure and Article 39 of the Arbitration Rules of the Netherlands Arbitration Institute, is excluded.
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
10.3. Notwithstanding this, TomTom shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
67
|
+
10.3. If you, your employer or the entity on whose behalf you act is incorporated outside the European Union, all such disputes shall be settled in accordance with the Arbitration Rules of the Netherlands Arbitration Institute. The arbitral tribunal shall be composed of three (3) arbitrators, the place of arbitration shall be Amsterdam and the language of the arbitration shall be English. Consolidation of the arbitral proceedings with other arbitral proceedings, as provided for in Article 1046 of the Dutch Code of Civil Procedure and Article 39 of the Arbitration Rules of the Netherlands Arbitration Institute, is excluded.
|
|
141
68
|
|
|
69
|
+
10.4. Notwithstanding Clauses 10.2 and 10.3, TomTom remains entitled to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
|
|
142
70
|
|
|
143
71
|
11. OTHER LEGAL TERMS:
|
|
144
72
|
|
|
73
|
+
11.1. No failure or delay by TomTom in exercising any right or remedy provided under this Agreement or by law constitutes a waiver of that or any other right or remedy, nor does it preclude or restrict the further exercise of that or any other right or remedy. No single or partial exercise of such right or remedy precludes or restricts its further exercise.
|
|
145
74
|
|
|
75
|
+
11.2. If any provision of this Agreement is held invalid or unenforceable, for any reason or to any extent, that does not affect the validity or enforceability of the remaining provisions, and the provision concerned shall be enforced to the extent permitted by law.
|
|
146
76
|
|
|
147
|
-
11.
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
11.2. Should for any reason, or to any extent, any provision of this Agreement be held invalid or unenforceable, such invalidity or enforceability shall not affect or render invalid or unenforceable the remaining provisions of this Agreement and the application of that provision shall be enforced to the extent permitted by law.
|
|
152
|
-
|
|
77
|
+
11.3. The rights granted to you in this Agreement may not be assigned or transferred by you without TomTom’s prior written approval.
|
|
153
78
|
|
|
79
|
+
11.4. The TomTom APIs and the SDK are provided as “Commercial Computer Software” or “restricted computer software”. Use, duplication or disclosure by the U.S. Government or a U.S. Government subcontractor is subject to the restrictions set forth in 48 C.F.R. Section 12.212 or 48 C.F.R. 227.2702, as applicable, or successor provisions. In respect of the Map API and Search API, the manufacturer is TomTom Global Content B.V.
|
|
154
80
|
|
|
155
|
-
11.
|
|
81
|
+
11.5. By submitting any suggestion, bug fix or code contribution (“Contribution”) to TomTom (e.g. via a GitHub Pull Request), you grant TomTom a perpetual, irrevocable, worldwide, royalty-free and non-exclusive license to use, reproduce, modify and distribute the Contribution as part of the SDK. You represent that you have the legal right to grant this license and that the Contribution does not infringe any third-party intellectual property rights.
|
|
156
82
|
|
|
83
|
+
THIRD PARTY SOFTWARE NOTICES
|
|
157
84
|
|
|
85
|
+
As referred to in Clause 3.5, the third party and open-source components included in or required by this package are listed, together with their applicable license notices, in the THIRD_PARTY.txt file distributed alongside this LICENSE.txt. For the SDK bundles themselves, that file is also published at:
|
|
158
86
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
11.5. The availability of the TomTom Maps SDK source code in a public repository does not constitute 'Open-Source' licensing. This Agreement does not grant you any rights under any Open-Source Initiative (OSI) approved license. Your rights are strictly limited to those expressly granted in Clause 1."
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
11.6 By submitting any suggestion, bug fix, or code contribution (“Contribution”) to TomTom (e.g., via a GitHub Pull Request), you hereby grant TomTom a perpetual, irrevocable, worldwide, royalty-free, and non-exclusive license to use, reproduce, modify, and distribute the Contribution as part of the TomTom Maps SDK. You represent that you have the legal right to grant this license and that the Contribution does not infringe any third-party intellectual property rights.
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
Name: zod
|
|
174
|
-
Version: 4.1.12
|
|
175
|
-
License: MIT
|
|
176
|
-
Private: false
|
|
177
|
-
Description: TypeScript-first schema declaration and validation library with static type inference
|
|
178
|
-
Repository: git+https://github.com/colinhacks/zod.git
|
|
179
|
-
Homepage: https://zod.dev
|
|
180
|
-
Author: Colin McDonnell <zod@colinhacks.com>
|
|
181
|
-
License Copyright:
|
|
182
|
-
===
|
|
183
|
-
|
|
184
|
-
MIT License
|
|
185
|
-
|
|
186
|
-
Copyright (c) 2025 Colin McDonnell
|
|
187
|
-
|
|
188
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
189
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
190
|
-
in the Software without restriction, including without limitation the rights
|
|
191
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
192
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
193
|
-
furnished to do so, subject to the following conditions:
|
|
194
|
-
|
|
195
|
-
The above copyright notice and this permission notice shall be included in all
|
|
196
|
-
copies or substantial portions of the Software.
|
|
197
|
-
|
|
198
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
199
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
200
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
201
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
202
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
203
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
204
|
-
SOFTWARE.
|
|
205
|
-
|
|
206
|
-
---
|
|
207
|
-
|
|
208
|
-
Name: lodash-es
|
|
209
|
-
Version: 4.17.21
|
|
210
|
-
License: MIT
|
|
211
|
-
Private: false
|
|
212
|
-
Description: Lodash exported as ES modules.
|
|
213
|
-
Repository: undefined
|
|
214
|
-
Homepage: https://lodash.com/custom-builds
|
|
215
|
-
Author: John-David Dalton <john.david.dalton@gmail.com>
|
|
216
|
-
Contributors:
|
|
217
|
-
John-David Dalton <john.david.dalton@gmail.com>
|
|
218
|
-
Mathias Bynens <mathias@qiwi.be>
|
|
219
|
-
License Copyright:
|
|
220
|
-
===
|
|
221
|
-
|
|
222
|
-
Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
|
|
223
|
-
|
|
224
|
-
Based on Underscore.js, copyright Jeremy Ashkenas,
|
|
225
|
-
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
|
226
|
-
|
|
227
|
-
This software consists of voluntary contributions made by many
|
|
228
|
-
individuals. For exact contribution history, see the revision history
|
|
229
|
-
available at https://github.com/lodash/lodash
|
|
230
|
-
|
|
231
|
-
The following license applies to all parts of this software except as
|
|
232
|
-
documented below:
|
|
233
|
-
|
|
234
|
-
====
|
|
235
|
-
|
|
236
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
|
237
|
-
a copy of this software and associated documentation files (the
|
|
238
|
-
"Software"), to deal in the Software without restriction, including
|
|
239
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
|
240
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
|
241
|
-
permit persons to whom the Software is furnished to do so, subject to
|
|
242
|
-
the following conditions:
|
|
243
|
-
|
|
244
|
-
The above copyright notice and this permission notice shall be
|
|
245
|
-
included in all copies or substantial portions of the Software.
|
|
246
|
-
|
|
247
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
248
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
249
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
250
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
251
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
252
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
253
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
254
|
-
|
|
255
|
-
====
|
|
256
|
-
|
|
257
|
-
Copyright and related rights for sample code are waived via CC0. Sample
|
|
258
|
-
code is defined as all source code displayed within the prose of the
|
|
259
|
-
documentation.
|
|
260
|
-
|
|
261
|
-
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
|
262
|
-
|
|
263
|
-
====
|
|
264
|
-
|
|
265
|
-
Files located in the node_modules and vendor directories are externally
|
|
266
|
-
maintained libraries used by this software which have their own
|
|
267
|
-
licenses; we recommend you read them, as their terms may differ from the
|
|
268
|
-
terms above.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
Name: @turf/helpers
|
|
277
|
-
Version: 7.3.0
|
|
278
|
-
License: MIT
|
|
279
|
-
Private: false
|
|
280
|
-
Description: Provides helper functions to create GeoJSON features, like points, lines, or areas on a map.
|
|
281
|
-
Repository: git://github.com/Turfjs/turf.git
|
|
282
|
-
Homepage: https://github.com/Turfjs/turf
|
|
283
|
-
Author: Turf Authors
|
|
284
|
-
Contributors:
|
|
285
|
-
Tom MacWright <@tmcw>
|
|
286
|
-
Stefano Borghi <@stebogit>
|
|
287
|
-
Denis Carriere <@DenisCarriere>
|
|
288
|
-
William Nordmann <@wnordmann>
|
|
289
|
-
License Copyright:
|
|
290
|
-
===
|
|
291
|
-
|
|
292
|
-
The MIT License (MIT)
|
|
293
|
-
|
|
294
|
-
Copyright (c) 2017 TurfJS
|
|
295
|
-
|
|
296
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
297
|
-
this software and associated documentation files (the "Software"), to deal in
|
|
298
|
-
the Software without restriction, including without limitation the rights to
|
|
299
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
300
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
301
|
-
subject to the following conditions:
|
|
302
|
-
|
|
303
|
-
The above copyright notice and this permission notice shall be included in all
|
|
304
|
-
copies or substantial portions of the Software.
|
|
305
|
-
|
|
306
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
307
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
308
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
309
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
310
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
311
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
312
|
-
|
|
313
|
-
---
|
|
314
|
-
|
|
315
|
-
Name: @turf/invariant
|
|
316
|
-
Version: 7.3.0
|
|
317
|
-
License: MIT
|
|
318
|
-
Private: false
|
|
319
|
-
Description: Lightweight utility for input validation and data extraction in Turf.js. Ensures GeoJSON inputs are in the correct format and extracts specific components like coordinates or geometries.
|
|
320
|
-
Repository: git://github.com/Turfjs/turf.git
|
|
321
|
-
Homepage: https://github.com/Turfjs/turf
|
|
322
|
-
Author: Turf Authors
|
|
323
|
-
Contributors:
|
|
324
|
-
Tom MacWright <@tmcw>
|
|
325
|
-
Denis Carriere <@DenisCarriere>
|
|
326
|
-
License Copyright:
|
|
327
|
-
===
|
|
328
|
-
|
|
329
|
-
The MIT License (MIT)
|
|
330
|
-
|
|
331
|
-
Copyright (c) 2017 TurfJS
|
|
332
|
-
|
|
333
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
334
|
-
this software and associated documentation files (the "Software"), to deal in
|
|
335
|
-
the Software without restriction, including without limitation the rights to
|
|
336
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
337
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
338
|
-
subject to the following conditions:
|
|
339
|
-
|
|
340
|
-
The above copyright notice and this permission notice shall be included in all
|
|
341
|
-
copies or substantial portions of the Software.
|
|
342
|
-
|
|
343
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
344
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
345
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
346
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
347
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
348
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
349
|
-
|
|
350
|
-
---
|
|
351
|
-
|
|
352
|
-
Name: @turf/bearing
|
|
353
|
-
Version: 7.3.0
|
|
354
|
-
License: MIT
|
|
355
|
-
Private: false
|
|
356
|
-
Description: Takes two points and finds the geographic bearing between them.
|
|
357
|
-
Repository: git://github.com/Turfjs/turf.git
|
|
358
|
-
Homepage: https://github.com/Turfjs/turf
|
|
359
|
-
Author: Turf Authors
|
|
360
|
-
License Copyright:
|
|
361
|
-
===
|
|
362
|
-
|
|
363
|
-
The MIT License (MIT)
|
|
364
|
-
|
|
365
|
-
Copyright (c) 2017 TurfJS
|
|
366
|
-
|
|
367
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
368
|
-
this software and associated documentation files (the "Software"), to deal in
|
|
369
|
-
the Software without restriction, including without limitation the rights to
|
|
370
|
-
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
|
371
|
-
the Software, and to permit persons to whom the Software is furnished to do so,
|
|
372
|
-
subject to the following conditions:
|
|
373
|
-
|
|
374
|
-
The above copyright notice and this permission notice shall be included in all
|
|
375
|
-
copies or substantial portions of the Software.
|
|
376
|
-
|
|
377
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
378
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
|
379
|
-
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
|
380
|
-
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
381
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
382
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
383
|
-
|
|
384
|
-
---
|
|
385
|
-
|
|
386
|
-
Name: maplibre-gl
|
|
387
|
-
Version: 5.12.0
|
|
388
|
-
License: BSD-3-Clause
|
|
389
|
-
Private: false
|
|
390
|
-
Description: BSD licensed community fork of mapbox-gl, a WebGL interactive maps library
|
|
391
|
-
Repository: git://github.com/maplibre/maplibre-gl-js.git
|
|
392
|
-
Homepage: https://maplibre.org/
|
|
393
|
-
License Copyright:
|
|
394
|
-
===
|
|
395
|
-
|
|
396
|
-
Copyright (c) 2023, MapLibre contributors
|
|
397
|
-
|
|
398
|
-
All rights reserved.
|
|
399
|
-
|
|
400
|
-
Redistribution and use in source and binary forms, with or without modification,
|
|
401
|
-
are permitted provided that the following conditions are met:
|
|
402
|
-
|
|
403
|
-
* Redistributions of source code must retain the above copyright notice,
|
|
404
|
-
this list of conditions and the following disclaimer.
|
|
405
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
406
|
-
this list of conditions and the following disclaimer in the documentation
|
|
407
|
-
and/or other materials provided with the distribution.
|
|
408
|
-
* Neither the name of MapLibre GL JS nor the names of its contributors
|
|
409
|
-
may be used to endorse or promote products derived from this software
|
|
410
|
-
without specific prior written permission.
|
|
411
|
-
|
|
412
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
413
|
-
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
414
|
-
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
415
|
-
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
416
|
-
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
417
|
-
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
418
|
-
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
419
|
-
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
420
|
-
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
421
|
-
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
422
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
-------------------------------------------------------------------------------
|
|
426
|
-
|
|
427
|
-
Contains code from mapbox-gl-js v1.13 and earlier
|
|
428
|
-
|
|
429
|
-
Version v1.13 of mapbox-gl-js and earlier are licensed under a BSD-3-Clause license
|
|
430
|
-
|
|
431
|
-
Copyright (c) 2020, Mapbox
|
|
432
|
-
Redistribution and use in source and binary forms, with or without modification,
|
|
433
|
-
are permitted provided that the following conditions are met:
|
|
434
|
-
|
|
435
|
-
* Redistributions of source code must retain the above copyright notice,
|
|
436
|
-
this list of conditions and the following disclaimer.
|
|
437
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
438
|
-
this list of conditions and the following disclaimer in the documentation
|
|
439
|
-
and/or other materials provided with the distribution.
|
|
440
|
-
* Neither the name of Mapbox GL JS nor the names of its contributors
|
|
441
|
-
may be used to endorse or promote products derived from this software
|
|
442
|
-
without specific prior written permission.
|
|
443
|
-
|
|
444
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
445
|
-
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
446
|
-
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
447
|
-
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
448
|
-
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
449
|
-
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
450
|
-
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
451
|
-
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
452
|
-
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
453
|
-
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
|
|
454
|
-
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
-------------------------------------------------------------------------------
|
|
458
|
-
|
|
459
|
-
Contains code from glfx.js
|
|
460
|
-
|
|
461
|
-
Copyright (C) 2011 by Evan Wallace
|
|
462
|
-
|
|
463
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
464
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
465
|
-
in the Software without restriction, including without limitation the rights
|
|
466
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
467
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
468
|
-
furnished to do so, subject to the following conditions:
|
|
469
|
-
|
|
470
|
-
The above copyright notice and this permission notice shall be included in
|
|
471
|
-
all copies or substantial portions of the Software.
|
|
472
|
-
|
|
473
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
474
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
475
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
476
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
477
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
478
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
479
|
-
THE SOFTWARE.
|
|
480
|
-
|
|
481
|
-
--------------------------------------------------------------------------------
|
|
482
|
-
|
|
483
|
-
Contains a portion of d3-color https://github.com/d3/d3-color
|
|
484
|
-
|
|
485
|
-
Copyright 2010-2016 Mike Bostock
|
|
486
|
-
All rights reserved.
|
|
487
|
-
|
|
488
|
-
Redistribution and use in source and binary forms, with or without modification,
|
|
489
|
-
are permitted provided that the following conditions are met:
|
|
490
|
-
|
|
491
|
-
* Redistributions of source code must retain the above copyright notice, this
|
|
492
|
-
list of conditions and the following disclaimer.
|
|
493
|
-
|
|
494
|
-
* Redistributions in binary form must reproduce the above copyright notice,
|
|
495
|
-
this list of conditions and the following disclaimer in the documentation
|
|
496
|
-
and/or other materials provided with the distribution.
|
|
497
|
-
|
|
498
|
-
* Neither the name of the author nor the names of contributors may be used to
|
|
499
|
-
endorse or promote products derived from this software without specific prior
|
|
500
|
-
written permission.
|
|
501
|
-
|
|
502
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
503
|
-
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
504
|
-
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
505
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
506
|
-
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
507
|
-
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
508
|
-
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
509
|
-
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
510
|
-
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
511
|
-
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
87
|
+
https://github.com/tomtom-international/maps-sdk-js/blob/main/THIRD_PARTY.txt
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export declare const anchorForPitch: (pitchDegrees: number) => FlyoverAnchor;
|
|
|
27
27
|
* The image that rides the route, drawn pointing up: the flight turns it to face the road.
|
|
28
28
|
*
|
|
29
29
|
* - `arrow` — the plugin's own arrow, recoloured with the SDK's SVG icon options. Its fill defaults
|
|
30
|
-
* to the SDK's default route outline colour, not a theme's accent, and its outline to white.
|
|
30
|
+
* to `#105287`, the SDK's default route outline colour, not a theme's accent, and its outline to white.
|
|
31
31
|
* - `custom` — yours: a URL, raw SVG markup, a data URI or a loaded `<img>`, as for any SDK custom
|
|
32
32
|
* image. A URL on another origin needs CORS, since the image becomes a WebGL texture.
|
|
33
33
|
*
|
package/dist/index.es.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{calculateProgressAtRoutePoint as e,generateId as t,getCoordinateAtRouteProgress as r}from"@tomtom-org/maps-sdk/core";import{ROUTE_LINE_OUTLINE_COLOR as i}from"@tomtom-org/maps-sdk/map";import{MercatorCoordinate as a}from"maplibre-gl";var n=6371008.8,o={centimeters:637100880,centimetres:637100880,cm:637100880,degrees:360/(2*Math.PI),deg:360/(2*Math.PI),feet:20902260.511392,ft:20902260.511392,inches:39.37*n,in:39.37*n,kilometers:6371.0088,kilometres:6371.0088,km:6371.0088,meters:n,metres:n,m:n,miles:3958.761333810546,mi:3958.761333810546,millimeters:6371008800,millimetres:6371008800,mm:6371008800,nauticalmiles:n/1852,nmi:n/1852,radians:1,rad:1,yards:6967335.223679999,yd:6967335.223679999};function s(e,t,r={}){if(!e)throw new Error("coordinates is required");if(!Array.isArray(e))throw new Error("coordinates must be an Array");if(e.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!c(e[0])||!c(e[1]))throw new Error("coordinates must contain numbers");return function(e,t,r={}){const i={type:"Feature"};return(0===r.id||r.id)&&(i.id=r.id),r.bbox&&(i.bbox=r.bbox),i.properties=t||{},i.geometry=e,i}({type:"Point",coordinates:e},t,r)}function h(e){return e%(2*Math.PI)*180/Math.PI}function m(e){return e%360*Math.PI/180}function c(e){return!isNaN(e)&&null!==e&&!Array.isArray(e)}function d(e){if(!e)throw new Error("coord is required");if(!Array.isArray(e)){if("Feature"===e.type&&null!==e.geometry&&"Point"===e.geometry.type)return[...e.geometry.coordinates];if("Point"===e.type)return[...e.coordinates]}if(Array.isArray(e)&&e.length>=2&&!Array.isArray(e[0])&&!Array.isArray(e[1]))return[...e];throw new Error("coord must be GeoJSON Point or an Array of numbers")}function u(e,t,r={}){if(!0===r.final)return function(e,t){let r=u(t,e);return r=(r+180)%360,r>180?r-360:r}(e,t);const i=d(e),a=d(t),n=m(i[0]),o=m(a[0]),s=m(i[1]),c=m(a[1]),g=Math.sin(o-n)*Math.cos(c),l=Math.cos(s)*Math.sin(c)-Math.sin(s)*Math.cos(c)*Math.cos(o-n);return h(Math.atan2(g,l))}function g(e,t,r,i={}){const a=d(e),n=m(a[0]),c=m(a[1]),u=m(r),g=function(e,t="kilometers"){const r=o[t];if(!r)throw new Error(t+" units is invalid");return e/r}(t,i.units),l=Math.asin(Math.sin(c)*Math.cos(g)+Math.cos(c)*Math.sin(g)*Math.cos(u)),p=h(n+Math.atan2(Math.sin(u)*Math.sin(g)*Math.cos(c),Math.cos(g)-Math.sin(c)*Math.sin(l))),v=h(l);return void 0!==a[2]?s([p,v,a[2]],i.properties):s([p,v],i.properties)}var l=(e,t)=>function(e){let t=e%360;return t<0&&(t+=360),t}(u(e,t)),p=(e,t)=>(t-e+540)%360-180,v=(e,t,r)=>Math.min(Math.max(e,t),r),M=e=>({value:e,velocity:0}),f=1e-6,y=(e,t,r,i,a=Number.POSITIVE_INFINITY)=>{const n=4/i,o=2*a/n,s=v(e.value-t,-o,o);if(Math.abs(s)<f&&Math.abs(e.velocity)<f)return M(t);const h=(e.velocity+n*s)*r,m=Math.exp(-n*r);return{value:e.value-s+(s+h)*m,velocity:(e.velocity-n*h)*m}},A=(e,t,r,i,a)=>{const n=y(e,e.value+p(e.value,t),r,i,a);return{value:(n.value+360)%360,velocity:n.velocity}},w=e=>v(e,.05,.95),T=e=>({x:.5,y:.5+v(e,0,60)/60*.16666666666666663}),E=(e,t)=>{const r=w(e.x)-.5,i=w(e.y)-.5,a=(e,t)=>Math.max(2*e*t,0);return{left:a(r,t.width),right:a(-r,t.width),top:a(i,t.height),bottom:a(-i,t.height)}},b=40075016.686/512,x=(e,t)=>Math.log2(b*Math.cos(m(t))/e),_=({viewportHeight:e,fieldOfViewDegrees:t})=>.5*e/Math.tan(m(t)/2),I=(e,t)=>_(t)*((e,t)=>b*Math.cos(m(t))/2**e)(e,t.latitudeDegrees),R=(e,t)=>{const r=document.createElementNS("http://www.w3.org/2000/svg",e);for(const[i,a]of Object.entries(t))r.setAttribute(i,a);return r},L=e=>`data:image/svg+xml;charset=utf-8,${encodeURIComponent(e)}`,P=(e,t)=>{const r="arrow"===e.type?L((({fillColor:e=i,outlineColor:t="white",outlineOpacity:r=1},a)=>{const n=R("svg",{viewBox:"0 0 24 24",width:String(a),height:String(a)});return n.append(R("path",{d:"M12 3l8 18-8-4-8 4z",fill:e,stroke:t,"stroke-opacity":String(r),"stroke-width":"3","stroke-linejoin":"round","paint-order":"stroke"})),(new XMLSerializer).serializeToString(n)})(e,t)):e.image;if("string"!=typeof r)return r;const a=new Image;return a.crossOrigin="anonymous",a.src=r.includes("<svg")?L(r):r,a},k=(e,t,r)=>{const i=e.createShader(t);if(!i)throw new Error("The fly-over marker could not create a shader");return e.shaderSource(i,r),e.compileShader(i),i},D=class{map;id=`tomtom-flyover-marker-${t()}`;type="custom";renderingMode="2d";config={};placed;raster;gl;drawing;removed=!1;stopRestoring;constructor(e,t){this.map=e,this.stopRestoring=e.addStyleChangeHandler({onStyleChanged:()=>this.install()}),this.installWhenReady(),this.apply(t)}apply(e){this.config=e;const t=e.size??44;var r;(async(e,t)=>{const r=P(e,t);await r.decode();const i=document.createElement("canvas");i.width=t,i.height=t;const a=t/Math.max(r.naturalWidth,r.naturalHeight,1),[n,o]=[r.naturalWidth*a,r.naturalHeight*a];return i.getContext("2d")?.drawImage(r,(t-n)/2,(t-o)/2,n,o),i})(e.image??{type:"arrow"},(r=t,Math.min(2**Math.ceil(Math.log2(Math.max(r*devicePixelRatio,1))),512))).then(t=>{this.config===e&&(this.raster=t,this.releaseTexture(),this.map.mapLibreMap.triggerRepaint())}).catch(e=>console.warn("The fly-over marker image could not be loaded",e)),this.map.mapLibreMap.triggerRepaint()}place(e,t){this.placed={position:e,heading:t},this.map.mapLibreMap.triggerRepaint()}remove(){this.removed=!0,this.stopRestoring(),this.map.mapLibreMap.getLayer(this.id)&&this.map.mapLibreMap.removeLayer(this.id)}onAdd(e,t){this.gl=t,this.drawing=(e=>{const t=e.createProgram(),r=e.createBuffer();if(!t||!r)throw new Error("The fly-over marker could not create its program");return e.attachShader(t,k(e,e.VERTEX_SHADER,"\nuniform mat4 u_matrix;\nuniform vec2 u_right;\nuniform vec2 u_forward;\nattribute vec2 a_corner;\nvarying vec2 v_uv;\nvoid main() {\n v_uv = vec2(a_corner.x, -a_corner.y) * 0.5 + 0.5;\n gl_Position = u_matrix * vec4(u_right * a_corner.x + u_forward * a_corner.y, 0.0, 1.0);\n}")),e.attachShader(t,k(e,e.FRAGMENT_SHADER,"\nprecision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_uv;\nvoid main() {\n gl_FragColor = texture2D(u_image, v_uv);\n}")),e.linkProgram(t),e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),e.STATIC_DRAW),{program:t,corners:r,texture:void 0,uniforms:{matrix:e.getUniformLocation(t,"u_matrix"),right:e.getUniformLocation(t,"u_right"),forward:e.getUniformLocation(t,"u_forward"),image:e.getUniformLocation(t,"u_image")},corner:e.getAttribLocation(t,"a_corner")}})(t)}onRemove(e,t){this.releaseTexture(),this.drawing&&(t.deleteProgram(this.drawing.program),t.deleteBuffer(this.drawing.corners)),this.drawing=void 0,this.gl=void 0}render(e,t){const{drawing:r,placed:i,raster:n}=this;if(!r||!i||!n||!1===this.config.visible)return;r.texture??=((e,t)=>{const r=e.createTexture()??void 0;return e.bindTexture(e.TEXTURE_2D,r??null),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,t),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),e.generateMipmap(e.TEXTURE_2D),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),r})(e,n);const o=this.map.mapLibreMap,[s,h]=i.position,c=o.queryTerrainElevation([s,h])??0,d=a.fromLngLat([s,h],c),u=(this.config.size??44)/2*(1/(512*2**o.getZoom()));const g=m(i.heading),[l,p]=[Math.sin(g)*u,Math.cos(g)*u];e.useProgram(r.program),e.uniformMatrix4fv(r.uniforms.matrix,!1,((e,{x:t,y:r,z:i})=>{const a=Float32Array.from(e);for(let n=0;n<4;n++)a[12+n]=e[n]*t+e[4+n]*r+e[8+n]*i+e[12+n];return a})(t.defaultProjectionData.mainMatrix,d)),e.uniform2f(r.uniforms.right,p,l),e.uniform2f(r.uniforms.forward,l,-p),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,r.texture??null),e.uniform1i(r.uniforms.image,0),e.bindBuffer(e.ARRAY_BUFFER,r.corners),e.enableVertexAttribArray(r.corner),e.vertexAttribPointer(r.corner,2,e.FLOAT,!1,0,0),e.disable(e.DEPTH_TEST),e.enable(e.BLEND),e.blendFunc(e.ONE,e.ONE_MINUS_SRC_ALPHA),e.drawArrays(e.TRIANGLE_STRIP,0,4)}releaseTexture(){this.drawing?.texture&&this.gl?.deleteTexture(this.drawing.texture),this.drawing&&(this.drawing.texture=void 0)}installWhenReady(){this.map.mapReady?this.install():this.map.mapLibreMap.once("styledata",()=>this.installWhenReady())}install(){this.removed||this.map.mapLibreMap.getLayer(this.id)||this.map.mapLibreMap.addLayer(this)}},H={speedKMH:200,pitch:70,aim:{mode:"instruction",fallbackMetersAhead:250},zoom:{mode:"instructions",instructionsAhead:2}},S=2,C=2,F=3,U=1.5,z=1.5,N=.6,X=.5,O=15,G=e=>v(e,11,15),B=class{map;route;camera;onProgress;marker;animationFrame;lastFrameTime=0;traveledDistanceInMeters=0;speedKMH=M(0);motion;target;framedZoom;secondsSinceSurvey=0;viewport={width:1,height:1};flying=!1;pageCenterClampedToGround=!0;pagePadding;constructor(e,t){this.map=e,this.route=t.route,this.camera={...H,...t.camera},this.onProgress=t.onProgress??(()=>{});const r=e.mapLibreMap,i=this.targetAnchor();this.motion={bearing:M(r.getBearing()),zoom:M(r.getZoom()),pitch:M(r.getPitch()),anchorX:M(i.x),anchorY:M(i.y),elevation:M(r.getCenterElevation()),markerHeading:M(0)},this.target={bearing:this.motion.bearing.value,zoom:this.motion.zoom.value,elevation:this.motion.elevation.value,markerHeading:0},this.framedZoom=this.target.zoom,this.readViewport(),r.on("resize",this.readViewport),this.marker=new D(e,t.marker??{}),this.placeMarkerOutright()}get running(){return void 0!==this.animationFrame}start(){if(this.running)return;const e=this.map.mapLibreMap;this.pageCenterClampedToGround=e.getCenterClampedToGround(),e.setCenterClampedToGround(!1),this.speedKMH=M(0),this.lastFrameTime=performance.now(),this.animationFrame=requestAnimationFrame(this.step)}stop(){void 0!==this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=void 0,this.map.mapLibreMap.setCenterClampedToGround(this.pageCenterClampedToGround))}setRoute(e){this.route=e,this.traveledDistanceInMeters=0,this.flying=!1,this.running?this.placeCamera():this.placeMarkerOutright()}setCamera(e){this.camera={...this.camera,...e},this.flying&&!this.running&&this.placeCamera()}getCamera(){return{...this.camera}}setMarker(e){this.marker.apply(e)}seek(e){this.traveledDistanceInMeters=this.routeLengthInMeters()*v(e,0,1),this.placeCamera()}remove(){this.stop(),this.marker.remove();const e=this.map.mapLibreMap;e.off("resize",this.readViewport),this.pagePadding&&e.setPadding(this.pagePadding)}readViewport=()=>{const e=this.map.mapLibreMap.getCanvas();this.viewport={width:Math.max(e.clientWidth,1),height:Math.max(e.clientHeight,1)}};routeLengthInMeters(){return this.route.properties.summary.lengthInMeters}step=e=>{const t=v((e-this.lastFrameTime)/1e3,0,.25);this.lastFrameTime=e;const r=this.speedKMH.value;this.speedKMH=y(this.speedKMH,this.camera.speedKMH,t,S);const i=(r+this.speedKMH.value)/2/3.6;this.traveledDistanceInMeters=this.wrapped(this.traveledDistanceInMeters+i*t),this.placeCamera(t),this.animationFrame=requestAnimationFrame(this.step)};coordinateAt(e){return r(this.route,{traveledDistanceInMeters:e})}positionAt(e){return this.coordinateAt(this.wrapped(this.traveledDistanceInMeters+e))?.position}wrapped(e){const t=this.routeLengthInMeters();return t>0?(e%t+t)%t:0}roadHeading(){const[e,t]=[-15,O].map(e=>this.coordinateAt(this.traveledDistanceInMeters+e)?.position);return e&&t?l(e,t):void 0}instructions(){return this.route.properties.guidance?.instructions??[]}instructionAhead(e=0){const t=this.instructions();if(!t.length)return;const r=t.findIndex(e=>e.routeOffsetInMeters>this.traveledDistanceInMeters);return t[Math.min((-1===r?0:r)+e,t.length-1)]}pointIndexHere(){let t=0,r=this.route.geometry.coordinates.length-1;for(;t<r;){const i=Math.floor((t+r)/2),a=e(this.route,i);void 0!==a&&a.distanceInMeters<this.traveledDistanceInMeters?t=i+1:r=i}return t}aimPosition(){const e=this.camera.aim;switch(e.mode){case"distance":return this.positionAt(e.metersAhead);case"points":{const t=this.route.geometry.coordinates;return t[(this.pointIndexHere()+e.pointsAhead)%t.length]}case"instruction":{const t=this.instructionAhead(),r=t&&this.wrapped(t.routeOffsetInMeters-this.traveledDistanceInMeters);return void 0===r?this.positionAt(e.fallbackMetersAhead):this.positionAt(r)}}}modeZoom(e){const t=this.camera.zoom;if("fixed"===t.mode)return t.zoom;if("speed"===t.mode){const e=Math.log2(Math.max(this.camera.speedKMH,1)/90);return G(14-1*e)}const r=this.instructionAhead(Math.max(t.instructionsAhead,1)-1);if(!r)return G(14);const i=this.wrapped(r.routeOffsetInMeters-this.traveledDistanceInMeters)*Math.cos(m(Math.min(this.camera.pitch,85)));return G(((e,t,r)=>x(Math.max(e,1)/Math.max(t,1),r))(i,.6*this.viewport.height,e))}clearanceZoom(e,t,r){const i=m(Math.min(this.camera.pitch,85)),a=I(this.motion.zoom.value,r)*Math.sin(i)/1e3,n=this.motion.bearing.value+180;let o=200;for(let s=0;s<4;s++){const r=s/4,i=g(e,a*(1-r),n).geometry.coordinates,h=this.groundAt(i);void 0!==h&&(o=Math.max(o,200+(h-t)/(1-r)))}return((e,t)=>x(Math.max(e,1)/_(t),t.latitudeDegrees))(o/Math.cos(i),r)}groundAt([e,t]){return this.map.mapLibreMap.queryTerrainElevation([e,t])??void 0}survey(e){const t={latitudeDegrees:e[1],viewportHeight:this.viewport.height,fieldOfViewDegrees:this.map.mapLibreMap.getVerticalFieldOfView()},r=this.aimPosition();r&&(this.target.bearing=((e,t,r)=>Math.abs(p(e,t))>r?t:e)(this.target.bearing,l(e,r),2.5)),this.target.markerHeading=this.roadHeading()??this.target.markerHeading;const i=this.modeZoom(t.latitudeDegrees),a="instructions"===this.camera.zoom.mode?.2:0;Math.abs(i-this.framedZoom)>a&&(this.framedZoom=i);const n=this.groundAt(e);void 0===n?(this.map.mapLibreMap.getTerrain()||(this.target.elevation=0),this.target.zoom=this.framedZoom):(this.target.elevation=n,this.target.zoom=Math.min(this.framedZoom,this.clearanceZoom(e,n,t)))}targetAnchor(){return this.camera.anchor??T(this.camera.pitch)}ease(e){const{motion:t,target:r}=this,i=this.targetAnchor(),a=(t,r,i)=>y(t,r,e,i);this.motion={bearing:A(t.bearing,r.bearing,e,C,22),markerHeading:A(t.markerHeading,r.markerHeading,e,X),zoom:a(t.zoom,r.zoom,F),elevation:a(t.elevation,r.elevation,N),pitch:a(t.pitch,this.camera.pitch,U),anchorX:a(t.anchorX,i.x,z),anchorY:a(t.anchorY,i.y,z)}}settle(){const e=this.targetAnchor();this.motion={bearing:M(this.target.bearing),markerHeading:M(this.target.markerHeading),zoom:M(this.target.zoom),elevation:M(this.target.elevation),pitch:M(this.camera.pitch),anchorX:M(e.x),anchorY:M(e.y)}}placeMarkerOutright(){const e=this.positionAt(0);this.target.markerHeading=this.roadHeading()??this.target.markerHeading,this.motion.markerHeading=M(this.target.markerHeading),e&&this.marker.place(e,this.target.markerHeading)}placeCamera(e){const t=this.coordinateAt(this.traveledDistanceInMeters);if(!t)return;const r=void 0!==e&&this.flying;this.secondsSinceSurvey+=e??0;const i=!r||this.secondsSinceSurvey>=.2;i&&(this.secondsSinceSurvey=0,this.survey(t.position)),r?this.ease(e):this.settle(),this.flying=!0;const[a,n]=t.position,{bearing:o,zoom:s,pitch:h,elevation:m,anchorX:c,anchorY:d,markerHeading:u}=this.motion,g=this.map.mapLibreMap;this.pagePadding??=g.getPadding(),g.jumpTo({center:[a,n],elevation:m.value,bearing:o.value,pitch:h.value,zoom:s.value,padding:E({x:c.value,y:d.value},this.viewport)}),this.marker.place(t.position,u.value),i&&this.onProgress({traveledDistanceInMeters:t.distanceInMeters,traveledTimeInSeconds:t.travelTimeInSeconds})}};export{B as RouteFlyover,T as anchorForPitch};
|
|
1
|
+
import{calculateProgressAtRoutePoint as e,generateId as t,getCoordinateAtRouteProgress as r}from"@tomtom-org/maps-sdk/core";import{MercatorCoordinate as i}from"maplibre-gl";var a=6371008.8,n={centimeters:637100880,centimetres:637100880,cm:637100880,degrees:360/(2*Math.PI),deg:360/(2*Math.PI),feet:20902260.511392,ft:20902260.511392,inches:39.37*a,in:39.37*a,kilometers:6371.0088,kilometres:6371.0088,km:6371.0088,meters:a,metres:a,m:a,miles:3958.761333810546,mi:3958.761333810546,millimeters:6371008800,millimetres:6371008800,mm:6371008800,nauticalmiles:a/1852,nmi:a/1852,radians:1,rad:1,yards:6967335.223679999,yd:6967335.223679999};function o(e,t,r={}){if(!e)throw new Error("coordinates is required");if(!Array.isArray(e))throw new Error("coordinates must be an Array");if(e.length<2)throw new Error("coordinates must be at least 2 numbers long");if(!m(e[0])||!m(e[1]))throw new Error("coordinates must contain numbers");return function(e,t,r={}){const i={type:"Feature"};return(0===r.id||r.id)&&(i.id=r.id),r.bbox&&(i.bbox=r.bbox),i.properties=t||{},i.geometry=e,i}({type:"Point",coordinates:e},t,r)}function s(e){return e%(2*Math.PI)*180/Math.PI}function h(e){return e%360*Math.PI/180}function m(e){return!isNaN(e)&&null!==e&&!Array.isArray(e)}function c(e){if(!e)throw new Error("coord is required");if(!Array.isArray(e)){if("Feature"===e.type&&null!==e.geometry&&"Point"===e.geometry.type)return[...e.geometry.coordinates];if("Point"===e.type)return[...e.coordinates]}if(Array.isArray(e)&&e.length>=2&&!Array.isArray(e[0])&&!Array.isArray(e[1]))return[...e];throw new Error("coord must be GeoJSON Point or an Array of numbers")}function d(e,t,r={}){if(!0===r.final)return function(e,t){let r=d(t,e);return r=(r+180)%360,r>180?r-360:r}(e,t);const i=c(e),a=c(t),n=h(i[0]),o=h(a[0]),m=h(i[1]),u=h(a[1]),g=Math.sin(o-n)*Math.cos(u),l=Math.cos(m)*Math.sin(u)-Math.sin(m)*Math.cos(u)*Math.cos(o-n);return s(Math.atan2(g,l))}function u(e,t,r,i={}){const a=c(e),m=h(a[0]),d=h(a[1]),u=h(r),g=function(e,t="kilometers"){const r=n[t];if(!r)throw new Error(t+" units is invalid");return e/r}(t,i.units),l=Math.asin(Math.sin(d)*Math.cos(g)+Math.cos(d)*Math.sin(g)*Math.cos(u)),p=s(m+Math.atan2(Math.sin(u)*Math.sin(g)*Math.cos(d),Math.cos(g)-Math.sin(d)*Math.sin(l))),v=s(l);return void 0!==a[2]?o([p,v,a[2]],i.properties):o([p,v],i.properties)}var g=(e,t)=>function(e){let t=e%360;return t<0&&(t+=360),t}(d(e,t)),l=(e,t)=>(t-e+540)%360-180,p=(e,t,r)=>Math.min(Math.max(e,t),r),v=e=>({value:e,velocity:0}),M=1e-6,f=(e,t,r,i,a=Number.POSITIVE_INFINITY)=>{const n=4/i,o=2*a/n,s=p(e.value-t,-o,o);if(Math.abs(s)<M&&Math.abs(e.velocity)<M)return v(t);const h=(e.velocity+n*s)*r,m=Math.exp(-n*r);return{value:e.value-s+(s+h)*m,velocity:(e.velocity-n*h)*m}},y=(e,t,r,i,a)=>{const n=f(e,e.value+l(e.value,t),r,i,a);return{value:(n.value+360)%360,velocity:n.velocity}},A=e=>p(e,.05,.95),w=e=>({x:.5,y:.5+p(e,0,60)/60*.16666666666666663}),T=(e,t)=>{const r=A(e.x)-.5,i=A(e.y)-.5,a=(e,t)=>Math.max(2*e*t,0);return{left:a(r,t.width),right:a(-r,t.width),top:a(i,t.height),bottom:a(-i,t.height)}},E=40075016.686/512,b=(e,t)=>Math.log2(E*Math.cos(h(t))/e),x=({viewportHeight:e,fieldOfViewDegrees:t})=>.5*e/Math.tan(h(t)/2),_=(e,t)=>x(t)*((e,t)=>E*Math.cos(h(t))/2**e)(e,t.latitudeDegrees),I=(e,t)=>{const r=document.createElementNS("http://www.w3.org/2000/svg",e);for(const[i,a]of Object.entries(t))r.setAttribute(i,a);return r},R=e=>`data:image/svg+xml;charset=utf-8,${encodeURIComponent(e)}`,L=(e,t)=>{const r="arrow"===e.type?R((({fillColor:e="#105287",outlineColor:t="white",outlineOpacity:r=1},i)=>{const a=I("svg",{viewBox:"0 0 24 24",width:String(i),height:String(i)});return a.append(I("path",{d:"M12 3l8 18-8-4-8 4z",fill:e,stroke:t,"stroke-opacity":String(r),"stroke-width":"3","stroke-linejoin":"round","paint-order":"stroke"})),(new XMLSerializer).serializeToString(a)})(e,t)):e.image;if("string"!=typeof r)return r;const i=new Image;return i.crossOrigin="anonymous",i.src=r.includes("<svg")?R(r):r,i},P=(e,t,r)=>{const i=e.createShader(t);if(!i)throw new Error("The fly-over marker could not create a shader");return e.shaderSource(i,r),e.compileShader(i),i},k=class{map;id=`tomtom-flyover-marker-${t()}`;type="custom";renderingMode="2d";config={};placed;raster;gl;drawing;removed=!1;stopRestoring;constructor(e,t){this.map=e,this.stopRestoring=e.addStyleChangeHandler({onStyleChanged:()=>this.install()}),this.installWhenReady(),this.apply(t)}apply(e){this.config=e;const t=e.size??44;var r;(async(e,t)=>{const r=L(e,t);await r.decode();const i=document.createElement("canvas");i.width=t,i.height=t;const a=t/Math.max(r.naturalWidth,r.naturalHeight,1),[n,o]=[r.naturalWidth*a,r.naturalHeight*a];return i.getContext("2d")?.drawImage(r,(t-n)/2,(t-o)/2,n,o),i})(e.image??{type:"arrow"},(r=t,Math.min(2**Math.ceil(Math.log2(Math.max(r*devicePixelRatio,1))),512))).then(t=>{this.config===e&&(this.raster=t,this.releaseTexture(),this.map.mapLibreMap.triggerRepaint())}).catch(e=>console.warn("The fly-over marker image could not be loaded",e)),this.map.mapLibreMap.triggerRepaint()}place(e,t){this.placed={position:e,heading:t},this.map.mapLibreMap.triggerRepaint()}remove(){this.removed=!0,this.stopRestoring(),this.map.mapLibreMap.getLayer(this.id)&&this.map.mapLibreMap.removeLayer(this.id)}onAdd(e,t){this.gl=t,this.drawing=(e=>{const t=e.createProgram(),r=e.createBuffer();if(!t||!r)throw new Error("The fly-over marker could not create its program");return e.attachShader(t,P(e,e.VERTEX_SHADER,"\nuniform mat4 u_matrix;\nuniform vec2 u_right;\nuniform vec2 u_forward;\nattribute vec2 a_corner;\nvarying vec2 v_uv;\nvoid main() {\n v_uv = vec2(a_corner.x, -a_corner.y) * 0.5 + 0.5;\n gl_Position = u_matrix * vec4(u_right * a_corner.x + u_forward * a_corner.y, 0.0, 1.0);\n}")),e.attachShader(t,P(e,e.FRAGMENT_SHADER,"\nprecision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_uv;\nvoid main() {\n gl_FragColor = texture2D(u_image, v_uv);\n}")),e.linkProgram(t),e.bindBuffer(e.ARRAY_BUFFER,r),e.bufferData(e.ARRAY_BUFFER,new Float32Array([-1,-1,1,-1,-1,1,1,1]),e.STATIC_DRAW),{program:t,corners:r,texture:void 0,uniforms:{matrix:e.getUniformLocation(t,"u_matrix"),right:e.getUniformLocation(t,"u_right"),forward:e.getUniformLocation(t,"u_forward"),image:e.getUniformLocation(t,"u_image")},corner:e.getAttribLocation(t,"a_corner")}})(t)}onRemove(e,t){this.releaseTexture(),this.drawing&&(t.deleteProgram(this.drawing.program),t.deleteBuffer(this.drawing.corners)),this.drawing=void 0,this.gl=void 0}render(e,t){const{drawing:r,placed:a,raster:n}=this;if(!r||!a||!n||!1===this.config.visible)return;r.texture??=((e,t)=>{const r=e.createTexture()??void 0;return e.bindTexture(e.TEXTURE_2D,r??null),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!0),e.texImage2D(e.TEXTURE_2D,0,e.RGBA,e.RGBA,e.UNSIGNED_BYTE,t),e.pixelStorei(e.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),e.generateMipmap(e.TEXTURE_2D),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.LINEAR_MIPMAP_LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.LINEAR),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),r})(e,n);const o=this.map.mapLibreMap,[s,m]=a.position,c=o.queryTerrainElevation([s,m])??0,d=i.fromLngLat([s,m],c),u=(this.config.size??44)/2*(1/(512*2**o.getZoom()));const g=h(a.heading),[l,p]=[Math.sin(g)*u,Math.cos(g)*u];e.useProgram(r.program),e.uniformMatrix4fv(r.uniforms.matrix,!1,((e,{x:t,y:r,z:i})=>{const a=Float32Array.from(e);for(let n=0;n<4;n++)a[12+n]=e[n]*t+e[4+n]*r+e[8+n]*i+e[12+n];return a})(t.defaultProjectionData.mainMatrix,d)),e.uniform2f(r.uniforms.right,p,l),e.uniform2f(r.uniforms.forward,l,-p),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,r.texture??null),e.uniform1i(r.uniforms.image,0),e.bindBuffer(e.ARRAY_BUFFER,r.corners),e.enableVertexAttribArray(r.corner),e.vertexAttribPointer(r.corner,2,e.FLOAT,!1,0,0),e.disable(e.DEPTH_TEST),e.enable(e.BLEND),e.blendFunc(e.ONE,e.ONE_MINUS_SRC_ALPHA),e.drawArrays(e.TRIANGLE_STRIP,0,4)}releaseTexture(){this.drawing?.texture&&this.gl?.deleteTexture(this.drawing.texture),this.drawing&&(this.drawing.texture=void 0)}installWhenReady(){this.map.mapReady?this.install():this.map.mapLibreMap.once("styledata",()=>this.installWhenReady())}install(){this.removed||this.map.mapLibreMap.getLayer(this.id)||this.map.mapLibreMap.addLayer(this)}},D={speedKMH:200,pitch:70,aim:{mode:"instruction",fallbackMetersAhead:250},zoom:{mode:"instructions",instructionsAhead:2}},H=2,S=2,C=3,F=1.5,U=1.5,z=.6,N=.5,X=15,O=e=>p(e,11,15),G=class{map;route;camera;onProgress;marker;animationFrame;lastFrameTime=0;traveledDistanceInMeters=0;speedKMH=v(0);motion;target;framedZoom;secondsSinceSurvey=0;viewport={width:1,height:1};flying=!1;pageCenterClampedToGround=!0;pagePadding;constructor(e,t){this.map=e,this.route=t.route,this.camera={...D,...t.camera},this.onProgress=t.onProgress??(()=>{});const r=e.mapLibreMap,i=this.targetAnchor();this.motion={bearing:v(r.getBearing()),zoom:v(r.getZoom()),pitch:v(r.getPitch()),anchorX:v(i.x),anchorY:v(i.y),elevation:v(r.getCenterElevation()),markerHeading:v(0)},this.target={bearing:this.motion.bearing.value,zoom:this.motion.zoom.value,elevation:this.motion.elevation.value,markerHeading:0},this.framedZoom=this.target.zoom,this.readViewport(),r.on("resize",this.readViewport),this.marker=new k(e,t.marker??{}),this.placeMarkerOutright()}get running(){return void 0!==this.animationFrame}start(){if(this.running)return;const e=this.map.mapLibreMap;this.pageCenterClampedToGround=e.getCenterClampedToGround(),e.setCenterClampedToGround(!1),this.speedKMH=v(0),this.lastFrameTime=performance.now(),this.animationFrame=requestAnimationFrame(this.step)}stop(){void 0!==this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=void 0,this.map.mapLibreMap.setCenterClampedToGround(this.pageCenterClampedToGround))}setRoute(e){this.route=e,this.traveledDistanceInMeters=0,this.flying=!1,this.running?this.placeCamera():this.placeMarkerOutright()}setCamera(e){this.camera={...this.camera,...e},this.flying&&!this.running&&this.placeCamera()}getCamera(){return{...this.camera}}setMarker(e){this.marker.apply(e)}seek(e){this.traveledDistanceInMeters=this.routeLengthInMeters()*p(e,0,1),this.placeCamera()}remove(){this.stop(),this.marker.remove();const e=this.map.mapLibreMap;e.off("resize",this.readViewport),this.pagePadding&&e.setPadding(this.pagePadding)}readViewport=()=>{const e=this.map.mapLibreMap.getCanvas();this.viewport={width:Math.max(e.clientWidth,1),height:Math.max(e.clientHeight,1)}};routeLengthInMeters(){return this.route.properties.summary.lengthInMeters}step=e=>{const t=p((e-this.lastFrameTime)/1e3,0,.25);this.lastFrameTime=e;const r=this.speedKMH.value;this.speedKMH=f(this.speedKMH,this.camera.speedKMH,t,H);const i=(r+this.speedKMH.value)/2/3.6;this.traveledDistanceInMeters=this.wrapped(this.traveledDistanceInMeters+i*t),this.placeCamera(t),this.animationFrame=requestAnimationFrame(this.step)};coordinateAt(e){return r(this.route,{traveledDistanceInMeters:e})}positionAt(e){return this.coordinateAt(this.wrapped(this.traveledDistanceInMeters+e))?.position}wrapped(e){const t=this.routeLengthInMeters();return t>0?(e%t+t)%t:0}roadHeading(){const[e,t]=[-15,X].map(e=>this.coordinateAt(this.traveledDistanceInMeters+e)?.position);return e&&t?g(e,t):void 0}instructions(){return this.route.properties.guidance?.instructions??[]}instructionAhead(e=0){const t=this.instructions();if(!t.length)return;const r=t.findIndex(e=>e.routeOffsetInMeters>this.traveledDistanceInMeters);return t[Math.min((-1===r?0:r)+e,t.length-1)]}pointIndexHere(){let t=0,r=this.route.geometry.coordinates.length-1;for(;t<r;){const i=Math.floor((t+r)/2),a=e(this.route,i);void 0!==a&&a.distanceInMeters<this.traveledDistanceInMeters?t=i+1:r=i}return t}aimPosition(){const e=this.camera.aim;switch(e.mode){case"distance":return this.positionAt(e.metersAhead);case"points":{const t=this.route.geometry.coordinates;return t[(this.pointIndexHere()+e.pointsAhead)%t.length]}case"instruction":{const t=this.instructionAhead(),r=t&&this.wrapped(t.routeOffsetInMeters-this.traveledDistanceInMeters);return void 0===r?this.positionAt(e.fallbackMetersAhead):this.positionAt(r)}}}modeZoom(e){const t=this.camera.zoom;if("fixed"===t.mode)return t.zoom;if("speed"===t.mode){const e=Math.log2(Math.max(this.camera.speedKMH,1)/90);return O(14-1*e)}const r=this.instructionAhead(Math.max(t.instructionsAhead,1)-1);if(!r)return O(14);const i=this.wrapped(r.routeOffsetInMeters-this.traveledDistanceInMeters)*Math.cos(h(Math.min(this.camera.pitch,85)));return O(((e,t,r)=>b(Math.max(e,1)/Math.max(t,1),r))(i,.6*this.viewport.height,e))}clearanceZoom(e,t,r){const i=h(Math.min(this.camera.pitch,85)),a=_(this.motion.zoom.value,r)*Math.sin(i)/1e3,n=this.motion.bearing.value+180;let o=200;for(let s=0;s<4;s++){const r=s/4,i=u(e,a*(1-r),n).geometry.coordinates,h=this.groundAt(i);void 0!==h&&(o=Math.max(o,200+(h-t)/(1-r)))}return((e,t)=>b(Math.max(e,1)/x(t),t.latitudeDegrees))(o/Math.cos(i),r)}groundAt([e,t]){return this.map.mapLibreMap.queryTerrainElevation([e,t])??void 0}survey(e){const t={latitudeDegrees:e[1],viewportHeight:this.viewport.height,fieldOfViewDegrees:this.map.mapLibreMap.getVerticalFieldOfView()},r=this.aimPosition();r&&(this.target.bearing=((e,t,r)=>Math.abs(l(e,t))>r?t:e)(this.target.bearing,g(e,r),2.5)),this.target.markerHeading=this.roadHeading()??this.target.markerHeading;const i=this.modeZoom(t.latitudeDegrees),a="instructions"===this.camera.zoom.mode?.2:0;Math.abs(i-this.framedZoom)>a&&(this.framedZoom=i);const n=this.groundAt(e);void 0===n?(this.map.mapLibreMap.getTerrain()||(this.target.elevation=0),this.target.zoom=this.framedZoom):(this.target.elevation=n,this.target.zoom=Math.min(this.framedZoom,this.clearanceZoom(e,n,t)))}targetAnchor(){return this.camera.anchor??w(this.camera.pitch)}ease(e){const{motion:t,target:r}=this,i=this.targetAnchor(),a=(t,r,i)=>f(t,r,e,i);this.motion={bearing:y(t.bearing,r.bearing,e,S,22),markerHeading:y(t.markerHeading,r.markerHeading,e,N),zoom:a(t.zoom,r.zoom,C),elevation:a(t.elevation,r.elevation,z),pitch:a(t.pitch,this.camera.pitch,F),anchorX:a(t.anchorX,i.x,U),anchorY:a(t.anchorY,i.y,U)}}settle(){const e=this.targetAnchor();this.motion={bearing:v(this.target.bearing),markerHeading:v(this.target.markerHeading),zoom:v(this.target.zoom),elevation:v(this.target.elevation),pitch:v(this.camera.pitch),anchorX:v(e.x),anchorY:v(e.y)}}placeMarkerOutright(){const e=this.positionAt(0);this.target.markerHeading=this.roadHeading()??this.target.markerHeading,this.motion.markerHeading=v(this.target.markerHeading),e&&this.marker.place(e,this.target.markerHeading)}placeCamera(e){const t=this.coordinateAt(this.traveledDistanceInMeters);if(!t)return;const r=void 0!==e&&this.flying;this.secondsSinceSurvey+=e??0;const i=!r||this.secondsSinceSurvey>=.2;i&&(this.secondsSinceSurvey=0,this.survey(t.position)),r?this.ease(e):this.settle(),this.flying=!0;const[a,n]=t.position,{bearing:o,zoom:s,pitch:h,elevation:m,anchorX:c,anchorY:d,markerHeading:u}=this.motion,g=this.map.mapLibreMap;this.pagePadding??=g.getPadding(),g.jumpTo({center:[a,n],elevation:m.value,bearing:o.value,pitch:h.value,zoom:s.value,padding:T({x:c.value,y:d.value},this.viewport)}),this.marker.place(t.position,u.value),i&&this.onProgress({traveledDistanceInMeters:t.distanceInMeters,traveledTimeInSeconds:t.travelTimeInSeconds})}};export{G as RouteFlyover,w as anchorForPitch};
|
|
2
2
|
//# sourceMappingURL=index.es.js.map
|
package/dist/index.es.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.es.js","names":["calculateProgressAtRoutePoint","generateId","getCoordinateAtRouteProgress","ROUTE_LINE_OUTLINE_COLOR","MercatorCoordinate","earthRadius","factors","centimeters","centimetres","cm","degrees","Math","PI","deg","feet","ft","inches","in","kilometers","kilometres","km","meters","metres","m","miles","mi","millimeters","millimetres","mm","nauticalmiles","nmi","radians","rad","yards","yd","point","coordinates","properties","options","Error","Array","isArray","length","isNumber","geom","feat","type","id","bbox","geometry","feature","radiansToDegrees","degreesToRadians","num","isNaN","getCoord","coord","bearing","start","end","final","bear","calculateFinalBearing","coordinates1","coordinates2","lon1","lon2","lat1","lat2","a","sin","cos","b","atan2","destination","origin","distance","longitude1","latitude1","bearingRad","units","factor","lengthToRadians","latitude2","asin","lng","lat","headingBetween","from","to","angle","bearingToAzimuth","shortestTurn","clamp","value","min","max","springAt","velocity","AT_REST","springTowards","spring","target","elapsedSeconds","settleSeconds","maxVelocity","Number","POSITIVE_INFINITY","rate","reach","offset","abs","drift","decay","exp","springTowardsBearing","next","clampFraction","anchorForPitch","pitchDegrees","x","y","anchorPadding","anchor","viewport","fromCentreX","fromCentreY","pad","fraction","extent","left","width","right","top","height","bottom","EQUATOR_METERS_PER_PIXEL","zoomForMetersPerPixel","latitudeDegrees","log2","cameraDistanceInPixels","viewportHeight","fieldOfViewDegrees","tan","cameraDistanceInMeters","zoom","lens","metersPerPixel","svgElement","name","attributes","element","document","createElementNS","attribute","Object","entries","setAttribute","svgDataUri","markup","encodeURIComponent","imageElement","image","side","source","fillColor","outlineColor","outlineOpacity","svg","viewBox","String","append","d","fill","stroke","XMLSerializer","serializeToString","arrowMarkup","Image","crossOrigin","src","includes","compile","gl","shader","createShader","shaderSource","compileShader","PositionMarker","map","renderingMode","config","placed","raster","drawing","removed","stopRestoring","constructor","this","addStyleChangeHandler","onStyleChanged","install","installWhenReady","apply","size","sizeInPixels","async","decode","canvas","createElement","scale","naturalWidth","naturalHeight","getContext","drawImage","rasterise","ceil","devicePixelRatio","then","releaseTexture","mapLibreMap","triggerRepaint","catch","error","console","warn","place","position","heading","remove","getLayer","removeLayer","onAdd","_map","program","createProgram","corners","createBuffer","attachShader","VERTEX_SHADER","FRAGMENT_SHADER","linkProgram","bindBuffer","ARRAY_BUFFER","bufferData","Float32Array","STATIC_DRAW","texture","uniforms","matrix","getUniformLocation","forward","corner","getAttribLocation","createDrawing","onRemove","deleteProgram","deleteBuffer","render","visible","createTexture","bindTexture","TEXTURE_2D","pixelStorei","UNPACK_PREMULTIPLY_ALPHA_WEBGL","texImage2D","RGBA","UNSIGNED_BYTE","generateMipmap","texParameteri","TEXTURE_MIN_FILTER","LINEAR_MIPMAP_LINEAR","TEXTURE_MAG_FILTER","LINEAR","TEXTURE_WRAP_S","CLAMP_TO_EDGE","TEXTURE_WRAP_T","uploadTexture","longitude","latitude","ground","queryTerrainElevation","centre","fromLngLat","half","getZoom","sine","cosine","useProgram","uniformMatrix4fv","projection","z","row","matrixAt","defaultProjectionData","mainMatrix","uniform2f","activeTexture","TEXTURE0","uniform1i","enableVertexAttribArray","vertexAttribPointer","FLOAT","disable","DEPTH_TEST","enable","BLEND","blendFunc","ONE","ONE_MINUS_SRC_ALPHA","drawArrays","TRIANGLE_STRIP","deleteTexture","mapReady","once","addLayer","DEFAULT_CAMERA","speedKMH","pitch","aim","mode","fallbackMetersAhead","instructionsAhead","SETTLE_SECONDS","HEADING_SPAN_METERS","clampZoom","RouteFlyover","route","camera","onProgress","marker","animationFrame","lastFrameTime","traveledDistanceInMeters","motion","framedZoom","secondsSinceSurvey","flying","pageCenterClampedToGround","pagePadding","targetAnchor","getBearing","getPitch","anchorX","anchorY","elevation","getCenterElevation","markerHeading","readViewport","on","placeMarkerOutright","running","getCenterClampedToGround","setCenterClampedToGround","performance","now","requestAnimationFrame","step","stop","cancelAnimationFrame","setRoute","placeCamera","setCamera","getCamera","setMarker","seek","routeLengthInMeters","off","setPadding","getCanvas","clientWidth","clientHeight","summary","lengthInMeters","frameTime","previousSpeedKMH","metersPerSecond","wrapped","coordinateAt","positionAt","offsetInMeters","distanceInMeters","routeLength","roadHeading","behind","ahead","instructions","guidance","instructionAhead","skip","findIndex","instruction","routeOffsetInMeters","pointIndexHere","low","high","middle","floor","progress","aimPosition","metersAhead","pointsAhead","modeZoom","doublings","foreshortened","pixels","zoomFitting","clearanceZoom","here","road","trailingInKilometers","sample","share","groundAt","zoomForCameraDistance","survey","getVerticalFieldOfView","steering","deadbandDegrees","steadyBearing","framed","deadband","getTerrain","ease","towards","settle","smooth","surveying","getPadding","jumpTo","center","padding","traveledTimeInSeconds","travelTimeInSeconds"],"sources":["../../../node_modules/.pnpm/@turf+helpers@7.4.0/node_modules/@turf/helpers/dist/esm/index.js","../../../node_modules/.pnpm/@turf+invariant@7.4.0/node_modules/@turf/invariant/dist/esm/index.js","../../../node_modules/.pnpm/@turf+bearing@7.4.0/node_modules/@turf/bearing/dist/esm/index.js","../../../node_modules/.pnpm/@turf+destination@7.4.0/node_modules/@turf/destination/dist/esm/index.js","../src/geodesy.ts","../src/framing.ts","../src/marker.ts","../src/RouteFlyover.ts"],"sourcesContent":["// index.ts\nvar earthRadius = 63710088e-1;\nvar factors = {\n centimeters: earthRadius * 100,\n centimetres: earthRadius * 100,\n cm: earthRadius * 100,\n degrees: 360 / (2 * Math.PI),\n deg: 360 / (2 * Math.PI),\n feet: earthRadius * 3.28084,\n ft: earthRadius * 3.28084,\n inches: earthRadius * 39.37,\n in: earthRadius * 39.37,\n kilometers: earthRadius / 1e3,\n kilometres: earthRadius / 1e3,\n km: earthRadius / 1e3,\n meters: earthRadius,\n metres: earthRadius,\n m: earthRadius,\n miles: earthRadius / 1609.344,\n mi: earthRadius / 1609.344,\n millimeters: earthRadius * 1e3,\n millimetres: earthRadius * 1e3,\n mm: earthRadius * 1e3,\n nauticalmiles: earthRadius / 1852,\n nmi: earthRadius / 1852,\n radians: 1,\n rad: 1,\n yards: earthRadius * 1.0936,\n yd: earthRadius * 1.0936\n};\nvar areaFactors = {\n acres: 247105e-9,\n ac: 247105e-9,\n centimeters: 1e4,\n centimetres: 1e4,\n cm: 1e4,\n feet: 10.763910417,\n ft: 10.763910417,\n hectares: 1e-4,\n ha: 1e-4,\n inches: 1550.003100006,\n in: 1550.003100006,\n kilometers: 1e-6,\n kilometres: 1e-6,\n km: 1e-6,\n meters: 1,\n metres: 1,\n m: 1,\n miles: 386e-9,\n mi: 386e-9,\n nauticalmiles: 29155334959812285e-23,\n nmi: 29155334959812285e-23,\n millimeters: 1e6,\n millimetres: 1e6,\n mm: 1e6,\n yards: 1.195990046,\n yd: 1.195990046\n};\nfunction feature(geom, properties, options = {}) {\n const feat = { type: \"Feature\" };\n if (options.id === 0 || options.id) {\n feat.id = options.id;\n }\n if (options.bbox) {\n feat.bbox = options.bbox;\n }\n feat.properties = properties || {};\n feat.geometry = geom;\n return feat;\n}\nfunction geometry(type, coordinates, _options = {}) {\n switch (type) {\n case \"Point\":\n return point(coordinates).geometry;\n case \"LineString\":\n return lineString(coordinates).geometry;\n case \"Polygon\":\n return polygon(coordinates).geometry;\n case \"MultiPoint\":\n return multiPoint(coordinates).geometry;\n case \"MultiLineString\":\n return multiLineString(coordinates).geometry;\n case \"MultiPolygon\":\n return multiPolygon(coordinates).geometry;\n default:\n throw new Error(type + \" is invalid\");\n }\n}\nfunction point(coordinates, properties, options = {}) {\n if (!coordinates) {\n throw new Error(\"coordinates is required\");\n }\n if (!Array.isArray(coordinates)) {\n throw new Error(\"coordinates must be an Array\");\n }\n if (coordinates.length < 2) {\n throw new Error(\"coordinates must be at least 2 numbers long\");\n }\n if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {\n throw new Error(\"coordinates must contain numbers\");\n }\n const geom = {\n type: \"Point\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction points(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return point(coords, properties);\n }),\n options\n );\n}\nfunction polygon(coordinates, properties, options = {}) {\n for (const ring of coordinates) {\n if (ring.length < 4) {\n throw new Error(\n \"Each LinearRing of a Polygon must have 4 or more Positions.\"\n );\n }\n if (ring[ring.length - 1].length !== ring[0].length) {\n throw new Error(\"First and last Position are not equivalent.\");\n }\n for (let j = 0; j < ring[ring.length - 1].length; j++) {\n if (ring[ring.length - 1][j] !== ring[0][j]) {\n throw new Error(\"First and last Position are not equivalent.\");\n }\n }\n }\n const geom = {\n type: \"Polygon\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction polygons(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return polygon(coords, properties);\n }),\n options\n );\n}\nfunction lineString(coordinates, properties, options = {}) {\n if (coordinates.length < 2) {\n throw new Error(\"coordinates must be an array of two or more positions\");\n }\n const geom = {\n type: \"LineString\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction lineStrings(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return lineString(coords, properties);\n }),\n options\n );\n}\nfunction featureCollection(features, options = {}) {\n const fc = { type: \"FeatureCollection\" };\n if (options.id) {\n fc.id = options.id;\n }\n if (options.bbox) {\n fc.bbox = options.bbox;\n }\n fc.features = features;\n return fc;\n}\nfunction multiLineString(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiLineString\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction multiPoint(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiPoint\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction multiPolygon(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiPolygon\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction geometryCollection(geometries, properties, options = {}) {\n const geom = {\n type: \"GeometryCollection\",\n geometries\n };\n return feature(geom, properties, options);\n}\nfunction round(num, precision = 0) {\n if (precision && !(precision >= 0)) {\n throw new Error(\"precision must be a positive number\");\n }\n const multiplier = Math.pow(10, precision || 0);\n return Math.round(num * multiplier) / multiplier;\n}\nfunction radiansToLength(radians, units = \"kilometers\") {\n const factor = factors[units];\n if (!factor) {\n throw new Error(units + \" units is invalid\");\n }\n return radians * factor;\n}\nfunction lengthToRadians(distance, units = \"kilometers\") {\n const factor = factors[units];\n if (!factor) {\n throw new Error(units + \" units is invalid\");\n }\n return distance / factor;\n}\nfunction lengthToDegrees(distance, units) {\n return radiansToDegrees(lengthToRadians(distance, units));\n}\nfunction bearingToAzimuth(bearing) {\n let angle = bearing % 360;\n if (angle < 0) {\n angle += 360;\n }\n return angle;\n}\nfunction azimuthToBearing(angle) {\n angle = angle % 360;\n if (angle > 180) {\n return angle - 360;\n } else if (angle < -180) {\n return angle + 360;\n }\n return angle;\n}\nfunction radiansToDegrees(radians) {\n const normalisedRadians = radians % (2 * Math.PI);\n return normalisedRadians * 180 / Math.PI;\n}\nfunction degreesToRadians(degrees) {\n const normalisedDegrees = degrees % 360;\n return normalisedDegrees * Math.PI / 180;\n}\nfunction convertLength(length, originalUnit = \"kilometers\", finalUnit = \"kilometers\") {\n if (!(length >= 0)) {\n throw new Error(\"length must be a positive number\");\n }\n return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);\n}\nfunction convertArea(area, originalUnit = \"meters\", finalUnit = \"kilometers\") {\n if (!(area >= 0)) {\n throw new Error(\"area must be a positive number\");\n }\n const startFactor = areaFactors[originalUnit];\n if (!startFactor) {\n throw new Error(\"invalid original units\");\n }\n const finalFactor = areaFactors[finalUnit];\n if (!finalFactor) {\n throw new Error(\"invalid final units\");\n }\n return area / startFactor * finalFactor;\n}\nfunction isNumber(num) {\n return !isNaN(num) && num !== null && !Array.isArray(num);\n}\nfunction isObject(input) {\n return input !== null && typeof input === \"object\" && !Array.isArray(input);\n}\nfunction removeBbox(geojson) {\n delete geojson.bbox;\n if (geojson.type === \"Feature\") {\n if (geojson.geometry) removeBbox(geojson.geometry);\n } else if (geojson.type === \"FeatureCollection\") {\n geojson.features.forEach(removeBbox);\n } else if (geojson.type === \"GeometryCollection\") {\n geojson.geometries.forEach(removeBbox);\n }\n}\nfunction validateBBox(bbox) {\n if (!bbox) {\n throw new Error(\"bbox is required\");\n }\n if (!Array.isArray(bbox)) {\n throw new Error(\"bbox must be an Array\");\n }\n if (bbox.length !== 4 && bbox.length !== 6) {\n throw new Error(\"bbox must be an Array of 4 or 6 numbers\");\n }\n bbox.forEach((num) => {\n if (!isNumber(num)) {\n throw new Error(\"bbox must only contain numbers\");\n }\n });\n}\nfunction validateId(id) {\n if (!id) {\n throw new Error(\"id is required\");\n }\n if ([\"string\", \"number\"].indexOf(typeof id) === -1) {\n throw new Error(\"id must be a number or a string\");\n }\n}\nexport {\n areaFactors,\n azimuthToBearing,\n bearingToAzimuth,\n convertArea,\n convertLength,\n degreesToRadians,\n earthRadius,\n factors,\n feature,\n featureCollection,\n geometry,\n geometryCollection,\n isNumber,\n isObject,\n lengthToDegrees,\n lengthToRadians,\n lineString,\n lineStrings,\n multiLineString,\n multiPoint,\n multiPolygon,\n point,\n points,\n polygon,\n polygons,\n radiansToDegrees,\n radiansToLength,\n removeBbox,\n round,\n validateBBox,\n validateId\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport { isNumber } from \"@turf/helpers\";\nfunction getCoord(coord) {\n if (!coord) {\n throw new Error(\"coord is required\");\n }\n if (!Array.isArray(coord)) {\n if (coord.type === \"Feature\" && coord.geometry !== null && coord.geometry.type === \"Point\") {\n return [...coord.geometry.coordinates];\n }\n if (coord.type === \"Point\") {\n return [...coord.coordinates];\n }\n }\n if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {\n return [...coord];\n }\n throw new Error(\"coord must be GeoJSON Point or an Array of numbers\");\n}\nfunction getCoords(coords) {\n if (Array.isArray(coords)) {\n return coords;\n }\n if (coords.type === \"Feature\") {\n if (coords.geometry !== null) {\n return coords.geometry.coordinates;\n }\n } else {\n if (coords.coordinates) {\n return coords.coordinates;\n }\n }\n throw new Error(\n \"coords must be GeoJSON Feature, Geometry Object or an Array\"\n );\n}\nfunction containsNumber(coordinates) {\n if (coordinates.length > 1 && isNumber(coordinates[0]) && isNumber(coordinates[1])) {\n return true;\n }\n if (Array.isArray(coordinates[0]) && coordinates[0].length) {\n return containsNumber(coordinates[0]);\n }\n throw new Error(\"coordinates must only contain numbers\");\n}\nfunction geojsonType(value, type, name) {\n if (!type || !name) {\n throw new Error(\"type and name required\");\n }\n if (!value || value.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + value.type\n );\n }\n}\nfunction featureOf(feature, type, name) {\n if (!feature) {\n throw new Error(\"No feature passed\");\n }\n if (!name) {\n throw new Error(\".featureOf() requires a name\");\n }\n if (!feature || feature.type !== \"Feature\" || !feature.geometry) {\n throw new Error(\n \"Invalid input to \" + name + \", Feature with geometry required\"\n );\n }\n if (!feature.geometry || feature.geometry.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + feature.geometry.type\n );\n }\n}\nfunction collectionOf(featureCollection, type, name) {\n if (!featureCollection) {\n throw new Error(\"No featureCollection passed\");\n }\n if (!name) {\n throw new Error(\".collectionOf() requires a name\");\n }\n if (!featureCollection || featureCollection.type !== \"FeatureCollection\") {\n throw new Error(\n \"Invalid input to \" + name + \", FeatureCollection required\"\n );\n }\n for (const feature of featureCollection.features) {\n if (!feature || feature.type !== \"Feature\" || !feature.geometry) {\n throw new Error(\n \"Invalid input to \" + name + \", Feature with geometry required\"\n );\n }\n if (!feature.geometry || feature.geometry.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + feature.geometry.type\n );\n }\n }\n}\nfunction getGeom(geojson) {\n if (geojson.type === \"Feature\") {\n return geojson.geometry;\n }\n return geojson;\n}\nfunction getType(geojson, _name) {\n if (geojson.type === \"FeatureCollection\") {\n return \"FeatureCollection\";\n }\n if (geojson.type === \"GeometryCollection\") {\n return \"GeometryCollection\";\n }\n if (geojson.type === \"Feature\" && geojson.geometry !== null) {\n return geojson.geometry.type;\n }\n return geojson.type;\n}\nexport {\n collectionOf,\n containsNumber,\n featureOf,\n geojsonType,\n getCoord,\n getCoords,\n getGeom,\n getType\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport { degreesToRadians, radiansToDegrees } from \"@turf/helpers\";\nimport { getCoord } from \"@turf/invariant\";\nfunction bearing(start, end, options = {}) {\n if (options.final === true) {\n return calculateFinalBearing(start, end);\n }\n const coordinates1 = getCoord(start);\n const coordinates2 = getCoord(end);\n const lon1 = degreesToRadians(coordinates1[0]);\n const lon2 = degreesToRadians(coordinates2[0]);\n const lat1 = degreesToRadians(coordinates1[1]);\n const lat2 = degreesToRadians(coordinates2[1]);\n const a = Math.sin(lon2 - lon1) * Math.cos(lat2);\n const b = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);\n return radiansToDegrees(Math.atan2(a, b));\n}\nfunction calculateFinalBearing(start, end) {\n let bear = bearing(end, start);\n bear = (bear + 180) % 360;\n return bear > 180 ? bear - 360 : bear;\n}\nvar index_default = bearing;\nexport {\n bearing,\n index_default as default\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport {\n degreesToRadians,\n lengthToRadians,\n point,\n radiansToDegrees\n} from \"@turf/helpers\";\nimport { getCoord } from \"@turf/invariant\";\nfunction destination(origin, distance, bearing, options = {}) {\n const coordinates1 = getCoord(origin);\n const longitude1 = degreesToRadians(coordinates1[0]);\n const latitude1 = degreesToRadians(coordinates1[1]);\n const bearingRad = degreesToRadians(bearing);\n const radians = lengthToRadians(distance, options.units);\n const latitude2 = Math.asin(\n Math.sin(latitude1) * Math.cos(radians) + Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearingRad)\n );\n const longitude2 = longitude1 + Math.atan2(\n Math.sin(bearingRad) * Math.sin(radians) * Math.cos(latitude1),\n Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2)\n );\n const lng = radiansToDegrees(longitude2);\n const lat = radiansToDegrees(latitude2);\n if (coordinates1[2] !== void 0) {\n return point([lng, lat, coordinates1[2]], options.properties);\n }\n return point([lng, lat], options.properties);\n}\nvar index_default = destination;\nexport {\n index_default as default,\n destination\n};\n//# sourceMappingURL=index.js.map","/**\n * The arithmetic the camera runs on: headings, and the spring that carries every value it eases.\n *\n * @module\n * @ignore\n */\nimport { bearing, bearingToAzimuth } from '@turf/turf';\nimport type { Position } from 'geojson';\n\n/**\n * Initial bearing from one coordinate to the other, in degrees clockwise from north within 0..360 —\n * the direction of travel, which is where the camera and the marker face.\n * @ignore\n */\nexport const headingBetween = (from: Position, to: Position): number => bearingToAzimuth(bearing(from, to));\n\n/**\n * The shorter way round between two bearings, so a flight crossing north turns by a few degrees\n * rather than the 350 the raw difference reports.\n * @ignore\n */\nexport const shortestTurn = (from: number, to: number): number => ((to - from + 540) % 360) - 180;\n\n/**\n * The heading to steer for: the road's, once it has moved off the one already being steered for by\n * more than `deadbandDegrees` — and otherwise the one already being steered for, unchanged.\n *\n * A route's vertices are metres apart, so the direction to a point a few hundred metres ahead\n * wanders by a degree or two as the flight crosses them. That is the line's sampling rather than\n * the road's shape, and a camera that answers it is a camera that never quite settles.\n * @ignore\n */\nexport const steadyBearing = (steering: number, road: number, deadbandDegrees: number): number =>\n Math.abs(shortestTurn(steering, road)) > deadbandDegrees ? road : steering;\n\n/** `value` held within `min`..`max`. @ignore */\nexport const clamp = (value: number, min: number, max: number): number => Math.min(Math.max(value, min), max);\n\n/**\n * A value in motion: where it is, and how fast it is getting there, in its own unit per second.\n * @ignore\n */\nexport type Spring = { value: number; velocity: number };\n\n/** @ignore */\nexport const springAt = (value: number): Spring => ({ value, velocity: 0 });\n\n// The spring's rate against the time it is given to settle: at this one, `settleSeconds` closes\n// 91% of a step, and twice that 99.7%.\nconst SETTLE_RATE = 4;\n\n// How close to its target, and how slow, a spring has to be to count as there. It only ever\n// approaches its target, and a value that changes by amounts nothing can show still costs the map\n// a new camera every frame.\nconst AT_REST = 1e-6;\n\n/**\n * `spring` carried `elapsedSeconds` towards `target`, critically damped: no overshoot, and a new\n * target bends the motion instead of kicking its velocity. Solved exactly, so for a target that\n * holds still, two half steps land where one whole one does, at any frame rate.\n *\n * `maxVelocity` caps the chase: the target is held no further off than the spring closes at that speed.\n * @ignore\n */\nexport const springTowards = (\n spring: Spring,\n target: number,\n elapsedSeconds: number,\n settleSeconds: number,\n maxVelocity = Number.POSITIVE_INFINITY,\n): Spring => {\n const rate = SETTLE_RATE / settleSeconds;\n const reach = (2 * maxVelocity) / rate;\n const offset = clamp(spring.value - target, -reach, reach);\n if (Math.abs(offset) < AT_REST && Math.abs(spring.velocity) < AT_REST) return springAt(target);\n\n const drift = (spring.velocity + rate * offset) * elapsedSeconds;\n const decay = Math.exp(-rate * elapsedSeconds);\n return {\n value: spring.value - offset + (offset + drift) * decay,\n velocity: (spring.velocity - rate * drift) * decay,\n };\n};\n\n/**\n * {@link springTowards} for a bearing: it takes the short way round and stays within 0..360.\n * @ignore\n */\nexport const springTowardsBearing = (\n spring: Spring,\n target: number,\n elapsedSeconds: number,\n settleSeconds: number,\n maxVelocity?: number,\n): Spring => {\n const next = springTowards(\n spring,\n spring.value + shortestTurn(spring.value, target),\n elapsedSeconds,\n settleSeconds,\n maxVelocity,\n );\n return { value: (next.value + 360) % 360, velocity: next.velocity };\n};\n","/**\n * Where the followed position sits on screen, and the zooms that fit a stretch of road into the\n * frame or lift the camera to a given height.\n *\n * @module\n */\nimport { degreesToRadians } from '@turf/turf';\nimport type { PaddingOptions } from 'maplibre-gl';\nimport { clamp } from './geodesy';\n\n/**\n * Where the position being followed sits in the view, as fractions of the viewport: `{ x: 0.5, y:\n * 0.5 }` is the middle of the screen, `y: 0.67` two thirds of the way down.\n *\n * @group Flyover\n */\nexport type FlyoverAnchor = { x: number; y: number };\n\n// How far down the frame a fully tilted camera puts the position it follows. Two thirds: the road\n// under the camera belongs in the lower third of a picture that is otherwise all of what lies\n// ahead, which is where a driver's own view of it sits.\nconst TILTED_ANCHOR_Y = 2 / 3;\n\n// The pitch at which the anchor has slid all the way to `TILTED_ANCHOR_Y`; beyond it, no further.\nconst FULLY_TILTED_DEGREES = 60;\n\n// How near the edge of the frame an anchor may put the position. A padding as deep as the viewport\n// leaves the camera nothing to centre in, and the projection stops meaning anything before that.\nconst ANCHOR_LIMIT = 0.05;\n\nconst clampFraction = (value: number): number => clamp(value, ANCHOR_LIMIT, 1 - ANCHOR_LIMIT);\n\n/**\n * Where a camera at this pitch puts the position it follows, absent an anchor of your own.\n *\n * A level map is a plan, so the position belongs in the middle. Tilt the camera and the picture\n * becomes mostly what lies *ahead*, so the position slides down with the pitch, reaching the lower\n * third at 60° — and a top-down pass stays centred without switching modes.\n *\n * @group Flyover\n */\nexport const anchorForPitch = (pitchDegrees: number): FlyoverAnchor => {\n const tilt = clamp(pitchDegrees, 0, FULLY_TILTED_DEGREES) / FULLY_TILTED_DEGREES;\n return { x: 0.5, y: 0.5 + (TILTED_ANCHOR_Y - 0.5) * tilt };\n};\n\n/**\n * The viewport padding that lands the followed position on `anchor`.\n *\n * MapLibre centres in the rectangle the padding leaves, so an anchor is padding on the side it sits\n * away from: two thirds of the way down a 600px view is 200px off the top. One side only, since\n * padding both would shrink the rectangle and take the zoom with it.\n * @ignore\n */\nexport const anchorPadding = (\n anchor: FlyoverAnchor,\n viewport: { width: number; height: number },\n): Required<PaddingOptions> => {\n const fromCentreX = clampFraction(anchor.x) - 0.5;\n const fromCentreY = clampFraction(anchor.y) - 0.5;\n // Not rounded to whole pixels: an anchor eased from one place to another would move the whole\n // view in one-pixel steps.\n const pad = (fraction: number, extent: number) => Math.max(2 * fraction * extent, 0);\n return {\n left: pad(fromCentreX, viewport.width),\n right: pad(-fromCentreX, viewport.width),\n top: pad(fromCentreY, viewport.height),\n bottom: pad(-fromCentreY, viewport.height),\n };\n};\n\n// MapLibre's world at zoom 0, in pixels: one 512-pixel tile.\nconst WORLD_PIXELS_AT_ZOOM_0 = 512;\n\n// Metres per pixel at zoom 0 on the equator: the Earth's circumference over that world, which is\n// what turns a ground distance into a zoom and back.\nconst EQUATOR_METERS_PER_PIXEL = 40_075_016.686 / WORLD_PIXELS_AT_ZOOM_0;\n\n/**\n * How much of MapLibre's mercator square — 1 unit across the world — a CSS pixel spans at `zoom`.\n * @ignore\n */\nexport const mercatorUnitsPerPixel = (zoom: number): number => 1 / (WORLD_PIXELS_AT_ZOOM_0 * 2 ** zoom);\n\nconst metersPerPixel = (zoom: number, latitudeDegrees: number): number =>\n (EQUATOR_METERS_PER_PIXEL * Math.cos(degreesToRadians(latitudeDegrees))) / 2 ** zoom;\n\nconst zoomForMetersPerPixel = (meters: number, latitudeDegrees: number): number =>\n Math.log2((EQUATOR_METERS_PER_PIXEL * Math.cos(degreesToRadians(latitudeDegrees))) / meters);\n\n/**\n * The zoom at which `distanceInMeters` of ground spans `pixels` of screen at `latitudeDegrees`.\n * @ignore\n */\nexport const zoomFitting = (distanceInMeters: number, pixels: number, latitudeDegrees: number): number =>\n zoomForMetersPerPixel(Math.max(distanceInMeters, 1) / Math.max(pixels, 1), latitudeDegrees);\n\n/**\n * What decides how far the camera is from the point it looks at, besides the zoom: MapLibre sets\n * that distance from the viewport's height and vertical field of view.\n * @ignore\n */\nexport type CameraLens = { latitudeDegrees: number; viewportHeight: number; fieldOfViewDegrees: number };\n\n// The camera's distance from the centre, in pixels: MapLibre's `cameraToCenterDistance`.\nconst cameraDistanceInPixels = ({ viewportHeight, fieldOfViewDegrees }: CameraLens): number =>\n (0.5 * viewportHeight) / Math.tan(degreesToRadians(fieldOfViewDegrees) / 2);\n\n/**\n * How far the camera is from the point it looks at, in metres, at `zoom`.\n * @ignore\n */\nexport const cameraDistanceInMeters = (zoom: number, lens: CameraLens): number =>\n cameraDistanceInPixels(lens) * metersPerPixel(zoom, lens.latitudeDegrees);\n\n/**\n * The zoom that puts the camera `distanceInMeters` from the point it looks at.\n * @ignore\n */\nexport const zoomForCameraDistance = (distanceInMeters: number, lens: CameraLens): number =>\n zoomForMetersPerPixel(Math.max(distanceInMeters, 1) / cameraDistanceInPixels(lens), lens.latitudeDegrees);\n","/**\n * What rides the route at the followed position: a MapLibre custom layer drawing one textured quad.\n *\n * - **In the map's own frame**, so map effects — depth of field included — and captures take it along.\n * - **Placed as each frame is drawn**, on the terrain under its route point. A symbol layer's source\n * goes through the worker and lands a frame or more behind the camera, which reads as wobble.\n *\n * @module\n */\nimport { generateId } from '@tomtom-org/maps-sdk/core';\nimport type { CustomImage, SVGIconStyleOptions, TomTomMap } from '@tomtom-org/maps-sdk/map';\nimport { ROUTE_LINE_OUTLINE_COLOR } from '@tomtom-org/maps-sdk/map';\nimport { degreesToRadians } from '@turf/turf';\nimport type { Position } from 'geojson';\nimport { type CustomLayerInterface, type CustomRenderMethodInput, MercatorCoordinate } from 'maplibre-gl';\nimport { mercatorUnitsPerPixel } from './framing';\n\n/**\n * The image that rides the route, drawn pointing up: the flight turns it to face the road.\n *\n * - `arrow` — the plugin's own arrow, recoloured with the SDK's SVG icon options. Its fill defaults\n * to the SDK's default route outline colour, not a theme's accent, and its outline to white.\n * - `custom` — yours: a URL, raw SVG markup, a data URI or a loaded `<img>`, as for any SDK custom\n * image. A URL on another origin needs CORS, since the image becomes a WebGL texture.\n *\n * @group Flyover\n */\nexport type FlyoverMarkerImage =\n | ({ type: 'arrow' } & SVGIconStyleOptions)\n | { type: 'custom'; image: NonNullable<CustomImage['image']> };\n\n/**\n * The marker that rides the route at the followed position, lying on the map and facing the road.\n *\n * @group Flyover\n */\nexport type FlyoverMarkerConfig = {\n /** @default true */\n visible?: boolean;\n /** @default `{ type: 'arrow' }` */\n image?: FlyoverMarkerImage;\n /** Width and height on screen, in pixels, before the pitch foreshortens it. @default 44 */\n size?: number;\n};\n\n// Lying on the map, the marker is foreshortened by the pitch: this is half as tall at 60°.\nconst DEFAULT_SIZE = 44;\n\n// The texture's side: a power of two, so it can be mipmapped — a quad seen at a steep pitch is\n// squeezed several times over, and without mipmaps it shimmers — and no more than a display needs.\nconst textureSize = (sizeInPixels: number): number =>\n Math.min(2 ** Math.ceil(Math.log2(Math.max(sizeInPixels * devicePixelRatio, 1))), 512);\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\nconst svgElement = <K extends 'svg' | 'path'>(name: K, attributes: Record<string, string>) => {\n const element = document.createElementNS(SVG_NAMESPACE, name);\n for (const [attribute, value] of Object.entries(attributes)) element.setAttribute(attribute, value);\n return element;\n};\n\nconst svgDataUri = (markup: string): string => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;\n\n// One path, pointing up in a 24-unit box: its stroke is the outline, painted under the fill, so\n// only the outer half of it shows.\nconst arrowMarkup = (\n { fillColor = ROUTE_LINE_OUTLINE_COLOR, outlineColor = 'white', outlineOpacity = 1 }: SVGIconStyleOptions,\n side: number,\n): string => {\n const svg = svgElement('svg', { viewBox: '0 0 24 24', width: String(side), height: String(side) });\n svg.append(\n svgElement('path', {\n d: 'M12 3l8 18-8-4-8 4z',\n fill: fillColor,\n stroke: outlineColor,\n 'stroke-opacity': String(outlineOpacity),\n 'stroke-width': '3',\n 'stroke-linejoin': 'round',\n 'paint-order': 'stroke',\n }),\n );\n return new XMLSerializer().serializeToString(svg);\n};\n\n/**\n * The image to rasterise: the arrow drawn at `side` pixels, or yours. Raw SVG markup is told apart\n * from a URL the way the SDK's own custom images are.\n * @ignore\n */\nexport const imageElement = (image: FlyoverMarkerImage, side: number): HTMLImageElement => {\n const source = image.type === 'arrow' ? svgDataUri(arrowMarkup(image, side)) : image.image;\n if (typeof source !== 'string') return source;\n\n const element = new Image();\n element.crossOrigin = 'anonymous';\n element.src = source.includes('<svg') ? svgDataUri(source) : source;\n return element;\n};\n\n// The image drawn into a square of `side` pixels, centred and scaled to fit: the texture's source.\nconst rasterise = async (image: FlyoverMarkerImage, side: number): Promise<HTMLCanvasElement> => {\n const element = imageElement(image, side);\n await element.decode();\n const canvas = document.createElement('canvas');\n canvas.width = side;\n canvas.height = side;\n const scale = side / Math.max(element.naturalWidth, element.naturalHeight, 1);\n const [width, height] = [element.naturalWidth * scale, element.naturalHeight * scale];\n canvas.getContext('2d')?.drawImage(element, (side - width) / 2, (side - height) / 2, width, height);\n return canvas;\n};\n\n// A quad of corners at ±1, spanned by the two axes the flight turns to face the road.\nconst VERTEX_SHADER = `\nuniform mat4 u_matrix;\nuniform vec2 u_right;\nuniform vec2 u_forward;\nattribute vec2 a_corner;\nvarying vec2 v_uv;\nvoid main() {\n v_uv = vec2(a_corner.x, -a_corner.y) * 0.5 + 0.5;\n gl_Position = u_matrix * vec4(u_right * a_corner.x + u_forward * a_corner.y, 0.0, 1.0);\n}`;\n\nconst FRAGMENT_SHADER = `\nprecision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_uv;\nvoid main() {\n gl_FragColor = texture2D(u_image, v_uv);\n}`;\n\ntype GL = WebGLRenderingContext | WebGL2RenderingContext;\n\ntype Drawing = {\n program: WebGLProgram;\n corners: WebGLBuffer;\n texture: WebGLTexture | undefined;\n uniforms: Record<'matrix' | 'right' | 'forward' | 'image', WebGLUniformLocation | null>;\n corner: number;\n};\n\nconst compile = (gl: GL, type: number, source: string): WebGLShader => {\n const shader = gl.createShader(type);\n if (!shader) throw new Error('The fly-over marker could not create a shader');\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n return shader;\n};\n\nconst createDrawing = (gl: GL): Drawing => {\n const program = gl.createProgram();\n const corners = gl.createBuffer();\n if (!program || !corners) throw new Error('The fly-over marker could not create its program');\n\n gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERTEX_SHADER));\n gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER));\n gl.linkProgram(program);\n gl.bindBuffer(gl.ARRAY_BUFFER, corners);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n return {\n program,\n corners,\n texture: undefined,\n uniforms: {\n matrix: gl.getUniformLocation(program, 'u_matrix'),\n right: gl.getUniformLocation(program, 'u_right'),\n forward: gl.getUniformLocation(program, 'u_forward'),\n image: gl.getUniformLocation(program, 'u_image'),\n },\n corner: gl.getAttribLocation(program, 'a_corner'),\n };\n};\n\nconst uploadTexture = (gl: GL, raster: HTMLCanvasElement): WebGLTexture | undefined => {\n const texture = gl.createTexture() ?? undefined;\n gl.bindTexture(gl.TEXTURE_2D, texture ?? null);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, raster);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n return texture;\n};\n\n// The map's matrix with the marker's centre folded in, in double precision: the quad's own corners\n// are then a few millionths of the world across, which single precision holds exactly where a\n// centre near 0.5 would not.\nconst matrixAt = (projection: ArrayLike<number>, { x, y, z }: MercatorCoordinate): Float32Array => {\n const matrix = Float32Array.from(projection);\n for (let row = 0; row < 4; row++) {\n matrix[12 + row] =\n projection[row] * x + projection[4 + row] * y + projection[8 + row] * z + projection[12 + row];\n }\n return matrix;\n};\n\n/**\n * The marker's layer, which re-adds itself after a style change and remembers where it was last\n * put so a new image lands in its place.\n * @ignore\n */\nexport class PositionMarker implements CustomLayerInterface {\n readonly id = `tomtom-flyover-marker-${generateId()}`;\n readonly type = 'custom';\n readonly renderingMode = '2d';\n\n private config: FlyoverMarkerConfig = {};\n private placed: { position: Position; heading: number } | undefined;\n private raster: HTMLCanvasElement | undefined;\n private gl: GL | undefined;\n private drawing: Drawing | undefined;\n private removed = false;\n private readonly stopRestoring: () => void;\n\n constructor(\n private readonly map: TomTomMap,\n config: FlyoverMarkerConfig,\n ) {\n this.stopRestoring = map.addStyleChangeHandler({ onStyleChanged: () => this.install() });\n this.installWhenReady();\n this.apply(config);\n }\n\n apply(config: FlyoverMarkerConfig): void {\n this.config = config;\n const size = config.size ?? DEFAULT_SIZE;\n rasterise(config.image ?? { type: 'arrow' }, textureSize(size))\n .then((raster) => {\n // A later image may have been asked for while this one loaded.\n if (this.config !== config) return;\n\n this.raster = raster;\n this.releaseTexture();\n this.map.mapLibreMap.triggerRepaint();\n })\n .catch((error) => console.warn('The fly-over marker image could not be loaded', error));\n this.map.mapLibreMap.triggerRepaint();\n }\n\n /** Puts the marker at a position on the route, facing `heading`. */\n place(position: Position, heading: number): void {\n this.placed = { position, heading };\n this.map.mapLibreMap.triggerRepaint();\n }\n\n remove(): void {\n this.removed = true;\n this.stopRestoring();\n if (this.map.mapLibreMap.getLayer(this.id)) this.map.mapLibreMap.removeLayer(this.id);\n }\n\n onAdd(_map: unknown, gl: GL): void {\n this.gl = gl;\n this.drawing = createDrawing(gl);\n }\n\n onRemove(_map: unknown, gl: GL): void {\n this.releaseTexture();\n if (this.drawing) {\n gl.deleteProgram(this.drawing.program);\n gl.deleteBuffer(this.drawing.corners);\n }\n this.drawing = undefined;\n this.gl = undefined;\n }\n\n render(gl: GL, options: CustomRenderMethodInput): void {\n const { drawing, placed, raster } = this;\n if (!drawing || !placed || !raster || this.config.visible === false) return;\n\n drawing.texture ??= uploadTexture(gl, raster);\n const mapLibreMap = this.map.mapLibreMap;\n const [longitude, latitude] = placed.position;\n // The ground under the route point, read as the frame is drawn: whatever the camera is\n // doing, the marker is on the road.\n const ground = mapLibreMap.queryTerrainElevation([longitude, latitude]) ?? 0;\n const centre = MercatorCoordinate.fromLngLat([longitude, latitude], ground);\n const half = ((this.config.size ?? DEFAULT_SIZE) / 2) * mercatorUnitsPerPixel(mapLibreMap.getZoom());\n // Mercator y runs south, so the road's heading points up the square at -cos.\n const heading = degreesToRadians(placed.heading);\n const [sine, cosine] = [Math.sin(heading) * half, Math.cos(heading) * half];\n\n gl.useProgram(drawing.program);\n gl.uniformMatrix4fv(drawing.uniforms.matrix, false, matrixAt(options.defaultProjectionData.mainMatrix, centre));\n gl.uniform2f(drawing.uniforms.right, cosine, sine);\n gl.uniform2f(drawing.uniforms.forward, sine, -cosine);\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, drawing.texture ?? null);\n gl.uniform1i(drawing.uniforms.image, 0);\n gl.bindBuffer(gl.ARRAY_BUFFER, drawing.corners);\n gl.enableVertexAttribArray(drawing.corner);\n gl.vertexAttribPointer(drawing.corner, 2, gl.FLOAT, false, 0, 0);\n // Always on top of the ground it lies on, and blended as the premultiplied texture it is.\n gl.disable(gl.DEPTH_TEST);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n\n private releaseTexture(): void {\n if (this.drawing?.texture) this.gl?.deleteTexture(this.drawing.texture);\n if (this.drawing) this.drawing.texture = undefined;\n }\n\n private installWhenReady(): void {\n if (this.map.mapReady) {\n this.install();\n } else {\n this.map.mapLibreMap.once('styledata', () => this.installWhenReady());\n }\n }\n\n private install(): void {\n if (!this.removed && !this.map.mapLibreMap.getLayer(this.id)) this.map.mapLibreMap.addLayer(this);\n }\n}\n","import type { Route } from '@tomtom-org/maps-sdk/core';\nimport { calculateProgressAtRoutePoint, getCoordinateAtRouteProgress } from '@tomtom-org/maps-sdk/core';\nimport type { TomTomMap } from '@tomtom-org/maps-sdk/map';\nimport { degreesToRadians, destination } from '@turf/turf';\nimport type { Position } from 'geojson';\nimport type { PaddingOptions } from 'maplibre-gl';\nimport {\n anchorForPitch,\n anchorPadding,\n type CameraLens,\n cameraDistanceInMeters,\n type FlyoverAnchor,\n zoomFitting,\n zoomForCameraDistance,\n} from './framing';\nimport {\n clamp,\n headingBetween,\n type Spring,\n springAt,\n springTowards,\n springTowardsBearing,\n steadyBearing,\n} from './geodesy';\nimport { type FlyoverMarkerConfig, PositionMarker } from './marker';\n\n/**\n * What the camera points at, which is what decides how early it turns into a bend.\n *\n * - `distance` — a fixed distance down the road. Predictable, and blind to what the road does.\n * - `points` — a number of the route's own vertices. The line is denser through bends, so the aim\n * shortens exactly where the road is turning.\n * - `instruction` — the next guidance instruction, so the camera looks at the junction it is\n * heading for. Needs a route asked for with `guidance`; without instructions it aims by distance.\n *\n * @group Flyover\n */\nexport type FlyoverAim =\n | { mode: 'distance'; metersAhead: number }\n | { mode: 'points'; pointsAhead: number }\n | { mode: 'instruction'; fallbackMetersAhead: number };\n\n/**\n * How high the camera rides. Whatever the mode, it pulls back further where terrain would otherwise\n * come between the camera and the road.\n *\n * - `fixed` — one MapLibre zoom, whatever the road does.\n * - `instructions` — framed on the junctions ahead: the zoom that fits the next N instructions (one\n * at least) in the view, so a run of hairpins pulls back and a straight closes in.\n * - `speed` — tied to the ground speed, the way a driver's eyes lift with it.\n *\n * @group Flyover\n */\nexport type FlyoverZoom =\n | { mode: 'fixed'; zoom: number }\n | { mode: 'instructions'; instructionsAhead: number }\n | { mode: 'speed' };\n\n/**\n * How the camera flies.\n *\n * @group Flyover\n */\nexport type FlyoverCamera = {\n /** Ground speed of the camera, in kilometres per hour. @default 200 */\n speedKMH: number;\n /** Camera tilt, in degrees; the map's `maxPitch` is the ceiling. @default 70 */\n pitch: number;\n /** What the camera aims at down the route. @default `{ mode: 'instruction', fallbackMetersAhead: 250 }` */\n aim: FlyoverAim;\n /** What sets the camera's height. @default `{ mode: 'instructions', instructionsAhead: 2 }` */\n zoom: FlyoverZoom;\n /**\n * Where the followed position sits on screen. Omitted, it follows the pitch — see\n * {@link anchorForPitch}.\n */\n anchor?: FlyoverAnchor;\n};\n\n/**\n * How far along the route the flight has got, reported as it advances.\n *\n * @group Flyover\n */\nexport type FlyoverProgress = {\n traveledDistanceInMeters: number;\n traveledTimeInSeconds: number;\n};\n\n/**\n * @group Flyover\n */\nexport type RouteFlyoverOptions = {\n /** The route to fly. Replace it later with {@link RouteFlyover.setRoute}. */\n route: Route;\n /** Overrides for the camera; anything left out takes the default on {@link FlyoverCamera}. */\n camera?: Partial<FlyoverCamera>;\n /** The marker riding the route at the followed position; an arrow by default. */\n marker?: FlyoverMarkerConfig;\n /** Called with the flight's progress, once per survey rather than once per frame. */\n onProgress?: (progress: FlyoverProgress) => void;\n};\n\nconst DEFAULT_CAMERA: FlyoverCamera = {\n speedKMH: 200,\n pitch: 70,\n aim: { mode: 'instruction', fallbackMetersAhead: 250 },\n zoom: { mode: 'instructions', instructionsAhead: 2 },\n};\n\n/**\n * How long each eased value takes to settle onto a new target, in seconds — see\n * {@link springTowards}. The altitude is the quickest, so what the camera looks at stays on the road.\n */\nconst SETTLE_SECONDS = {\n speed: 2,\n bearing: 2,\n zoom: 3,\n pitch: 1.5,\n anchor: 1.5,\n elevation: 0.6,\n markerHeading: 0.5,\n} as const;\n\n/**\n * How far the road's direction has to move before the camera adopts a new heading, in degrees (see\n * {@link steadyBearing}), and the fastest it may then turn, in degrees per second. The rate cap\n * covers a genuine change of target, such as the next junction becoming the current one.\n */\nconst BEARING_DEADBAND_DEGREES = 2.5;\nconst MAX_TURN_DEGREES_PER_SECOND = 22;\n\n// How much the framing has to want before the zoom follows it. A junction the camera is closing on\n// asks for a little more zoom every survey, and a map that re-tiles for every tenth of a level\n// spends the flight loading tiles it is about to leave.\nconst ZOOM_DEADBAND = 0.2;\n\n/**\n * How often the camera works out *where it is going*, in seconds — as opposed to where it is,\n * which it reads every frame.\n *\n * The route lookups behind the aim, the framing, the viewport and the terrain clearance all live on\n * this tick: their answers feed targets the camera eases toward anyway, and a target recomputed\n * five times a second cannot jitter at sixty.\n */\nconst SURVEY_INTERVAL_SECONDS = 0.2;\n\n/**\n * The longest frame the flight will act on: a backgrounded tab hands back one frame worth many\n * seconds, and without this the camera teleports down the route on return.\n *\n * Loose enough not to punish a slow renderer, which is the other thing a long frame means. Any\n * frame over the cap advances the flight by less than its own time, so a clamp at a tenth of a\n * second would fly a machine rendering at 5fps down the route at half speed.\n */\nconst MAX_FRAME_SECONDS = 0.25;\n\n// How high the camera keeps above the ground under it. Along its line of sight the margin narrows\n// to nothing at the road, which is where the camera is looking.\nconst CLEARANCE_METERS = 200;\n// Samples of the ground from under the camera towards the road, the road itself excluded.\nconst CLEARANCE_SAMPLES = 4;\n// The steepest pitch the camera's geometry is worked out for: towards 90° the camera needs an\n// unbounded distance to gain any height, and sees the road ahead foreshortened to nothing.\nconst MAX_WORKED_PITCH_DEGREES = 85;\n\n// Either side of the followed position, the stretch of road the marker takes its heading from:\n// long enough to span a vertex or two, short enough to still be the road under it.\nconst HEADING_SPAN_METERS = 15;\n\n// The zooms the automatic modes may choose between: closer than this the road fills the screen,\n// further out it stops reading as a flight.\nconst MIN_ZOOM = 11;\nconst MAX_ZOOM = 15;\n\n// What the framing modes fit their target into: a share of the viewport, since a junction pinned\n// to the very edge of the screen is one the camera is already passing.\nconst FRAMED_SHARE_OF_VIEWPORT = 0.6;\n\n// The speed the `speed` mode holds `MAX_ZOOM - 1` at, and how far it pulls back per doubling of it:\n// twice the speed needs about twice the road in shot, which is one zoom level.\nconst SPEED_REFERENCE_KMH = 90;\nconst ZOOM_PER_SPEED_DOUBLING = 1;\n\nconst clampZoom = (zoom: number): number => clamp(zoom, MIN_ZOOM, MAX_ZOOM);\n\n/** The values the camera eases, each towards the target the last survey or setting gave it. */\ntype Motion = Record<'bearing' | 'zoom' | 'pitch' | 'anchorX' | 'anchorY' | 'elevation' | 'markerHeading', Spring>;\n\n/**\n * A camera that flies a route on a loop: it advances a distance along the line every frame, reads\n * the coordinate the route has at that distance, and points itself at where it will be next.\n *\n * @remarks\n * The route is the only geometry it needs — no path of camera keyframes to author, and no\n * animation to re-time when the route changes. What it adds on top of following the line:\n *\n * - **it aims ahead** rather than at its own position, by distance, by route vertices or at the\n * next guidance instruction, so the view turns into a bend before the camera reaches it;\n * - **it frames** what is coming: the zoom that fits the next junctions, or one tied to the speed;\n * - **it clears the ground**, pulling back where terrain would come between the camera and the road;\n * - **it parks the position where you want it on screen** — see {@link FlyoverAnchor} — and marks\n * it with an arrow, or an image of your own, facing the road;\n * - **it moves like a camera**: every value it changes eases on a critically damped spring, so a new\n * heading, zoom or setting bends the motion rather than kicking it, and a start ramps the speed.\n *\n * While it runs, the flight owns the altitude of the point the camera looks at — the ground under\n * the followed position — so it takes MapLibre off clamping the centre to the ground, and gives\n * that back when it stops.\n *\n * @example\n * ```typescript\n * import { RouteFlyover } from '@tomtom-org/maps-sdk-plugin-flyover';\n *\n * const [route] = (await calculateRoute({ locations, guidance: { type: 'coded' } })).features;\n * const flyover = new RouteFlyover(map, { route, camera: { speedKMH: 300, pitch: 76 } });\n * flyover.start();\n * ```\n *\n * @group Flyover\n */\nexport class RouteFlyover {\n private route: Route;\n private camera: FlyoverCamera;\n private readonly onProgress: (progress: FlyoverProgress) => void;\n private readonly marker: PositionMarker;\n\n private animationFrame: number | undefined;\n private lastFrameTime = 0;\n private traveledDistanceInMeters = 0;\n private speedKMH = springAt(0);\n private motion: Motion;\n // What the last survey decided the camera should become; the flight is the motion closing on it.\n private readonly target: Record<'bearing' | 'zoom' | 'elevation' | 'markerHeading', number>;\n // The zoom the framing mode last asked for, before the terrain has its say.\n private framedZoom: number;\n private secondsSinceSurvey = 0;\n // Read when the map resizes rather than in flight: asking an element for its size mid-flight is\n // a layout the browser has to do before it can answer.\n private viewport = { width: 1, height: 1 };\n // Whether the camera is on the route at all: until it is, the page owns the view.\n private flying = false;\n // The page's own camera settings, handed back when the flight lets go of them.\n private pageCenterClampedToGround = true;\n private pagePadding: PaddingOptions | undefined;\n\n constructor(\n private readonly map: TomTomMap,\n options: RouteFlyoverOptions,\n ) {\n this.route = options.route;\n this.camera = { ...DEFAULT_CAMERA, ...options.camera };\n this.onProgress = options.onProgress ?? (() => {});\n const mapLibreMap = map.mapLibreMap;\n const anchor = this.targetAnchor();\n this.motion = {\n bearing: springAt(mapLibreMap.getBearing()),\n zoom: springAt(mapLibreMap.getZoom()),\n pitch: springAt(mapLibreMap.getPitch()),\n anchorX: springAt(anchor.x),\n anchorY: springAt(anchor.y),\n elevation: springAt(mapLibreMap.getCenterElevation()),\n markerHeading: springAt(0),\n };\n this.target = {\n bearing: this.motion.bearing.value,\n zoom: this.motion.zoom.value,\n elevation: this.motion.elevation.value,\n markerHeading: 0,\n };\n this.framedZoom = this.target.zoom;\n this.readViewport();\n mapLibreMap.on('resize', this.readViewport);\n this.marker = new PositionMarker(map, options.marker ?? {});\n this.placeMarkerOutright();\n }\n\n /** Whether the flight is advancing. */\n get running(): boolean {\n return this.animationFrame !== undefined;\n }\n\n /** Starts, or resumes, the flight, ramping up to its speed. */\n start(): void {\n if (this.running) return;\n\n const mapLibreMap = this.map.mapLibreMap;\n this.pageCenterClampedToGround = mapLibreMap.getCenterClampedToGround();\n mapLibreMap.setCenterClampedToGround(false);\n this.speedKMH = springAt(0);\n this.lastFrameTime = performance.now();\n this.animationFrame = requestAnimationFrame(this.step);\n }\n\n /**\n * Stops the flight where it is; {@link start} resumes from there. The centre clamp goes back to\n * what the map had before {@link start}.\n */\n stop(): void {\n if (this.animationFrame === undefined) return;\n\n cancelAnimationFrame(this.animationFrame);\n this.animationFrame = undefined;\n this.map.mapLibreMap.setCenterClampedToGround(this.pageCenterClampedToGround);\n }\n\n /** Puts a new route under the flight, from its departure point. */\n setRoute(route: Route): void {\n this.route = route;\n this.traveledDistanceInMeters = 0;\n this.flying = false;\n if (this.running) {\n this.placeCamera();\n } else {\n this.placeMarkerOutright();\n }\n }\n\n /** Applies camera settings — speed, pitch, aim, framing, anchor — paused or under way. */\n setCamera(camera: Partial<FlyoverCamera>): void {\n this.camera = { ...this.camera, ...camera };\n // Before the first frame the page still owns the view — that is how a whole route can be\n // shown before anyone flies it — so this only reframes a flight already on the line.\n if (this.flying && !this.running) this.placeCamera();\n }\n\n /** The camera the flight is using, including the defaults it was not given. */\n getCamera(): FlyoverCamera {\n return { ...this.camera };\n }\n\n /** Replaces the marker riding the route, or hides it with `visible: false`. */\n setMarker(marker: FlyoverMarkerConfig): void {\n this.marker.apply(marker);\n }\n\n /** Moves the flight to a point along the route, as a share of its length within 0..1. */\n seek(fraction: number): void {\n this.traveledDistanceInMeters = this.routeLengthInMeters() * clamp(fraction, 0, 1);\n this.placeCamera();\n }\n\n /**\n * Stops the flight and gives the map back: the marker leaves, and the padding returns to what\n * the map had before the flight first placed the camera.\n */\n remove(): void {\n this.stop();\n this.marker.remove();\n const mapLibreMap = this.map.mapLibreMap;\n mapLibreMap.off('resize', this.readViewport);\n if (this.pagePadding) mapLibreMap.setPadding(this.pagePadding);\n }\n\n private readonly readViewport = (): void => {\n const canvas = this.map.mapLibreMap.getCanvas();\n this.viewport = { width: Math.max(canvas.clientWidth, 1), height: Math.max(canvas.clientHeight, 1) };\n };\n\n private routeLengthInMeters(): number {\n return this.route.properties.summary.lengthInMeters;\n }\n\n private readonly step = (frameTime: number): void => {\n // A frame's time is its vsync, which can precede the `performance.now()` that `start` read.\n const elapsedSeconds = clamp((frameTime - this.lastFrameTime) / 1000, 0, MAX_FRAME_SECONDS);\n this.lastFrameTime = frameTime;\n\n const previousSpeedKMH = this.speedKMH.value;\n this.speedKMH = springTowards(this.speedKMH, this.camera.speedKMH, elapsedSeconds, SETTLE_SECONDS.speed);\n const metersPerSecond = (previousSpeedKMH + this.speedKMH.value) / 2 / 3.6;\n // Wrapping rather than stopping is what makes the flight endless: the arrival is also the\n // departure, and a loop over a round trip never lands.\n this.traveledDistanceInMeters = this.wrapped(this.traveledDistanceInMeters + metersPerSecond * elapsedSeconds);\n\n this.placeCamera(elapsedSeconds);\n this.animationFrame = requestAnimationFrame(this.step);\n };\n\n private coordinateAt(traveledDistanceInMeters: number) {\n return getCoordinateAtRouteProgress(this.route, { traveledDistanceInMeters });\n }\n\n // The position on the route a given distance from where the flight currently is, wrapped into\n // the route the way the flight itself wraps.\n private positionAt(offsetInMeters: number): Position | undefined {\n return this.coordinateAt(this.wrapped(this.traveledDistanceInMeters + offsetInMeters))?.position;\n }\n\n private wrapped(distanceInMeters: number): number {\n const routeLength = this.routeLengthInMeters();\n return routeLength > 0 ? ((distanceInMeters % routeLength) + routeLength) % routeLength : 0;\n }\n\n // The direction of the road under the followed position, which the marker faces. Not wrapped:\n // at the departure, the road behind is the departure itself rather than the far end of the loop.\n private roadHeading(): number | undefined {\n const [behind, ahead] = [-HEADING_SPAN_METERS, HEADING_SPAN_METERS].map(\n (offsetInMeters) => this.coordinateAt(this.traveledDistanceInMeters + offsetInMeters)?.position,\n );\n return behind && ahead ? headingBetween(behind, ahead) : undefined;\n }\n\n private instructions() {\n return this.route.properties.guidance?.instructions ?? [];\n }\n\n /**\n * The instruction the flight is heading for — the first one still ahead of it, and the first of\n * all once the loop has passed the last. `undefined` when the route carries no guidance.\n */\n private instructionAhead(skip = 0) {\n const instructions = this.instructions();\n if (!instructions.length) return undefined;\n\n const next = instructions.findIndex(\n (instruction) => instruction.routeOffsetInMeters > this.traveledDistanceInMeters,\n );\n const from = next === -1 ? 0 : next;\n return instructions[Math.min(from + skip, instructions.length - 1)];\n }\n\n // The route vertex the flight is standing on, found by bisecting the progress the route\n // carries per point rather than by measuring the line again.\n private pointIndexHere(): number {\n let low = 0;\n let high = this.route.geometry.coordinates.length - 1;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n const progress = calculateProgressAtRoutePoint(this.route, middle);\n if (progress !== undefined && progress.distanceInMeters < this.traveledDistanceInMeters) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n }\n\n /** Where the camera looks: the point the current {@link FlyoverAim} picks out. */\n private aimPosition(): Position | undefined {\n const aim = this.camera.aim;\n switch (aim.mode) {\n case 'distance':\n return this.positionAt(aim.metersAhead);\n case 'points': {\n const coordinates = this.route.geometry.coordinates;\n // Wrapped, so the aim crosses the loop's seam rather than pinning to the last vertex.\n return coordinates[(this.pointIndexHere() + aim.pointsAhead) % coordinates.length];\n }\n case 'instruction': {\n // Through the instruction's distance along the route rather than its own\n // `maneuverPoint`: the flight already speaks in distances, and the junction the\n // router names sits a few metres off the sampled line.\n const instruction = this.instructionAhead();\n const ahead =\n instruction && this.wrapped(instruction.routeOffsetInMeters - this.traveledDistanceInMeters);\n return ahead === undefined ? this.positionAt(aim.fallbackMetersAhead) : this.positionAt(ahead);\n }\n }\n }\n\n /** The zoom the current {@link FlyoverZoom} asks for at this point of the route. */\n private modeZoom(latitudeDegrees: number): number {\n const zoom = this.camera.zoom;\n if (zoom.mode === 'fixed') return zoom.zoom;\n\n if (zoom.mode === 'speed') {\n const doublings = Math.log2(Math.max(this.camera.speedKMH, 1) / SPEED_REFERENCE_KMH);\n return clampZoom(MAX_ZOOM - 1 - doublings * ZOOM_PER_SPEED_DOUBLING);\n }\n\n // Framed on the junctions ahead: the road still to come before the Nth of them, fitted into\n // the share of the viewport a flight wants it to occupy. A tilted camera sees that road\n // foreshortened, by the cosine of the pitch.\n const target = this.instructionAhead(Math.max(zoom.instructionsAhead, 1) - 1);\n if (!target) return clampZoom(MAX_ZOOM - 1);\n\n const ahead = this.wrapped(target.routeOffsetInMeters - this.traveledDistanceInMeters);\n const foreshortened = ahead * Math.cos(degreesToRadians(Math.min(this.camera.pitch, MAX_WORKED_PITCH_DEGREES)));\n return clampZoom(zoomFitting(foreshortened, this.viewport.height * FRAMED_SHARE_OF_VIEWPORT, latitudeDegrees));\n }\n\n /**\n * The closest zoom at which nothing between the camera and the road stands higher than the line\n * of sight.\n *\n * The camera trails the road along the bearing, so its ground track is sampled from under the\n * camera towards the followed position. A sample a share `t` of the way along needs the camera\n * `(ground − road) / (1 − t)` higher than the road, on top of a clearance that narrows the same\n * way — which puts the camera itself `CLEARANCE_METERS` over the ground beneath it.\n */\n private clearanceZoom(here: Position, road: number, lens: CameraLens): number {\n const pitch = degreesToRadians(Math.min(this.camera.pitch, MAX_WORKED_PITCH_DEGREES));\n const trailingInKilometers = (cameraDistanceInMeters(this.motion.zoom.value, lens) * Math.sin(pitch)) / 1000;\n const behind = this.motion.bearing.value + 180;\n let height = CLEARANCE_METERS;\n for (let sample = 0; sample < CLEARANCE_SAMPLES; sample++) {\n const share = sample / CLEARANCE_SAMPLES;\n const point = destination(here, trailingInKilometers * (1 - share), behind).geometry.coordinates;\n const ground = this.groundAt(point);\n if (ground !== undefined) height = Math.max(height, CLEARANCE_METERS + (ground - road) / (1 - share));\n }\n return zoomForCameraDistance(height / Math.cos(pitch), lens);\n }\n\n // Terrain elevations come back exaggerated, as the rendered mesh is, and `null` wherever the map\n // has no terrain or a tile has yet to load.\n private groundAt([longitude, latitude]: Position): number | undefined {\n return this.map.mapLibreMap.queryTerrainElevation([longitude, latitude]) ?? undefined;\n }\n\n /**\n * Where the camera is going: the heading the road asks for, the zoom the framing and the\n * terrain allow, and the ground the followed position is on.\n *\n * Everything in here costs a walk of the route or a question to the terrain, and none of it is\n * worth asking every frame — see {@link SURVEY_INTERVAL_SECONDS}.\n */\n private survey(here: Position): void {\n const lens: CameraLens = {\n latitudeDegrees: here[1],\n viewportHeight: this.viewport.height,\n fieldOfViewDegrees: this.map.mapLibreMap.getVerticalFieldOfView(),\n };\n\n const ahead = this.aimPosition();\n if (ahead) {\n this.target.bearing = steadyBearing(\n this.target.bearing,\n headingBetween(here, ahead),\n BEARING_DEADBAND_DEGREES,\n );\n }\n this.target.markerHeading = this.roadHeading() ?? this.target.markerHeading;\n\n // The deadband belongs to the framing mode alone, whose target creeps up every survey as\n // the junction it frames comes closer. The other two follow a caller's own figure, and a\n // value someone chose has to land whatever its size.\n const framed = this.modeZoom(lens.latitudeDegrees);\n const deadband = this.camera.zoom.mode === 'instructions' ? ZOOM_DEADBAND : 0;\n if (Math.abs(framed - this.framedZoom) > deadband) this.framedZoom = framed;\n\n const road = this.groundAt(here);\n if (road === undefined) {\n // A map without terrain is flat at sea level; a tile yet to load keeps the last height.\n if (!this.map.mapLibreMap.getTerrain()) this.target.elevation = 0;\n\n this.target.zoom = this.framedZoom;\n } else {\n this.target.elevation = road;\n this.target.zoom = Math.min(this.framedZoom, this.clearanceZoom(here, road, lens));\n }\n }\n\n private targetAnchor(): FlyoverAnchor {\n return this.camera.anchor ?? anchorForPitch(this.camera.pitch);\n }\n\n // A frame's step of every value towards its target, with the turn additionally capped so no\n // second can swing the view further than a passenger's head would.\n private ease(elapsedSeconds: number): void {\n const { motion, target } = this;\n const anchor = this.targetAnchor();\n const towards = (spring: Spring, value: number, settleSeconds: number) =>\n springTowards(spring, value, elapsedSeconds, settleSeconds);\n this.motion = {\n bearing: springTowardsBearing(\n motion.bearing,\n target.bearing,\n elapsedSeconds,\n SETTLE_SECONDS.bearing,\n MAX_TURN_DEGREES_PER_SECOND,\n ),\n markerHeading: springTowardsBearing(\n motion.markerHeading,\n target.markerHeading,\n elapsedSeconds,\n SETTLE_SECONDS.markerHeading,\n ),\n zoom: towards(motion.zoom, target.zoom, SETTLE_SECONDS.zoom),\n elevation: towards(motion.elevation, target.elevation, SETTLE_SECONDS.elevation),\n pitch: towards(motion.pitch, this.camera.pitch, SETTLE_SECONDS.pitch),\n anchorX: towards(motion.anchorX, anchor.x, SETTLE_SECONDS.anchor),\n anchorY: towards(motion.anchorY, anchor.y, SETTLE_SECONDS.anchor),\n };\n }\n\n // Every value put at its target outright, at rest: there is no motion to be smooth about.\n private settle(): void {\n const anchor = this.targetAnchor();\n this.motion = {\n bearing: springAt(this.target.bearing),\n markerHeading: springAt(this.target.markerHeading),\n zoom: springAt(this.target.zoom),\n elevation: springAt(this.target.elevation),\n pitch: springAt(this.camera.pitch),\n anchorX: springAt(anchor.x),\n anchorY: springAt(anchor.y),\n };\n }\n\n // Where the page can see the flight will start, before the camera is on the route.\n private placeMarkerOutright(): void {\n const here = this.positionAt(0);\n this.target.markerHeading = this.roadHeading() ?? this.target.markerHeading;\n this.motion.markerHeading = springAt(this.target.markerHeading);\n if (here) this.marker.place(here, this.target.markerHeading);\n }\n\n /**\n * One frame: where the flight has got to, and a step of the camera towards what the last survey\n * decided. Without `elapsedSeconds` — a seek, a new route, a changed setting — the camera is\n * surveyed and placed at its targets outright.\n */\n private placeCamera(elapsedSeconds?: number): void {\n const here = this.coordinateAt(this.traveledDistanceInMeters);\n if (!here) return;\n\n const smooth = elapsedSeconds !== undefined && this.flying;\n this.secondsSinceSurvey += elapsedSeconds ?? 0;\n const surveying = !smooth || this.secondsSinceSurvey >= SURVEY_INTERVAL_SECONDS;\n if (surveying) {\n this.secondsSinceSurvey = 0;\n this.survey(here.position);\n }\n if (smooth) {\n this.ease(elapsedSeconds);\n } else {\n this.settle();\n }\n this.flying = true;\n\n const [longitude, latitude] = here.position;\n const { bearing, zoom, pitch, elevation, anchorX, anchorY, markerHeading } = this.motion;\n const mapLibreMap = this.map.mapLibreMap;\n this.pagePadding ??= mapLibreMap.getPadding();\n // The centre is the followed position on the ground, so the anchor puts the road itself on\n // screen where it asked — not a point above it.\n mapLibreMap.jumpTo({\n center: [longitude, latitude],\n elevation: elevation.value,\n bearing: bearing.value,\n pitch: pitch.value,\n zoom: zoom.value,\n padding: anchorPadding({ x: anchorX.value, y: anchorY.value }, this.viewport),\n });\n this.marker.place(here.position, markerHeading.value);\n\n if (surveying) {\n this.onProgress({\n traveledDistanceInMeters: here.distanceInMeters,\n traveledTimeInSeconds: here.travelTimeInSeconds,\n });\n }\n }\n}\n"],"x_google_ignoreList":[0,1,2,3],"mappings":"iPACA,IAAIK,EAAc,UACdC,EAAU,CACZC,YAAaF,UACbG,YAAaH,UACbI,GAAIJ,UACJK,QAAS,KAAO,EAAIC,KAAKC,IACzBC,IAAK,KAAO,EAAIF,KAAKC,IACrBE,KAAMT,gBACNU,GAAIV,gBACJW,OAAsB,MAAdX,EACRY,GAAkB,MAAdZ,EACJa,WAAYb,UACZc,WAAYd,UACZe,GAAIf,UACJgB,OAAQhB,EACRiB,OAAQjB,EACRkB,EAAGlB,EACHmB,MAAOnB,kBACPoB,GAAIpB,kBACJqB,YAAarB,WACbsB,YAAatB,WACbuB,GAAIvB,WACJwB,cAAexB,EAAc,KAC7ByB,IAAKzB,EAAc,KACnB0B,QAAS,EACTC,IAAK,EACLC,MAAO5B,kBACP6B,GAAI7B,mBA4DN,SAAS8B,EAAMC,EAAaC,EAAYC,EAAU,CAAC,GACjD,IAAKF,EACH,MAAM,IAAIG,MAAM,2BAElB,IAAKC,MAAMC,QAAQL,GACjB,MAAM,IAAIG,MAAM,gCAElB,GAAIH,EAAYM,OAAS,EACvB,MAAM,IAAIH,MAAM,+CAElB,IAAKI,EAASP,EAAY,MAAQO,EAASP,EAAY,IACrD,MAAM,IAAIG,MAAM,oCAMlB,OA/CF,SAAiBK,EAAMP,EAAYC,EAAU,CAAC,GAC5C,MAAMO,EAAO,CAAEC,KAAM,WASrB,OARmB,IAAfR,EAAQS,IAAYT,EAAQS,MAC9BF,EAAKE,GAAKT,EAAQS,IAEhBT,EAAQU,OACVH,EAAKG,KAAOV,EAAQU,MAEtBH,EAAKR,WAAaA,GAAc,CAAC,EACjCQ,EAAKI,SAAWL,EACTC,CACT,CAoCSK,CAAQ,CAHbJ,KAAM,QACNV,eAEmBC,EAAYC,EACnC,CAwIA,SAASa,EAAiBpB,GAExB,OAD0BA,GAAW,EAAIpB,KAAKC,IACnB,IAAMD,KAAKC,EACxC,CACA,SAASwC,EAAiB1C,GAExB,OAD0BA,EAAU,IACTC,KAAKC,GAAK,GACvC,CAqBA,SAAS+B,EAASU,GAChB,OAAQC,MAAMD,IAAgB,OAARA,IAAiBb,MAAMC,QAAQY,EACvD,CC9QA,SAASE,EAASC,GAChB,IAAKA,EACH,MAAM,IAAIjB,MAAM,qBAElB,IAAKC,MAAMC,QAAQe,GAAQ,CACzB,GAAmB,YAAfA,EAAMV,MAAyC,OAAnBU,EAAMP,UAA6C,UAAxBO,EAAMP,SAASH,KACxE,MAAO,IAAIU,EAAMP,SAASb,aAE5B,GAAmB,UAAfoB,EAAMV,KACR,MAAO,IAAIU,EAAMpB,YAErB,CACA,GAAII,MAAMC,QAAQe,IAAUA,EAAMd,QAAU,IAAMF,MAAMC,QAAQe,EAAM,MAAQhB,MAAMC,QAAQe,EAAM,IAChG,MAAO,IAAIA,GAEb,MAAM,IAAIjB,MAAM,qDAClB,CCfA,SAASkB,EAAQC,EAAOC,EAAKrB,EAAU,CAAC,GACtC,IAAsB,IAAlBA,EAAQsB,MACV,OAYJ,SAA+BF,EAAOC,GACpC,IAAIE,EAAOJ,EAAQE,EAAKD,GAExB,OADAG,GAAQA,EAAO,KAAO,IACfA,EAAO,IAAMA,EAAO,IAAMA,CACnC,CAhBWC,CAAsBJ,EAAOC,GAEtC,MAAMI,EAAeR,EAASG,GACxBM,EAAeT,EAASI,GACxBM,EAAOb,EAAiBW,EAAa,IACrCG,EAAOd,EAAiBY,EAAa,IACrCG,EAAOf,EAAiBW,EAAa,IACrCK,EAAOhB,EAAiBY,EAAa,IACrCK,EAAI1D,KAAK2D,IAAIJ,EAAOD,GAAQtD,KAAK4D,IAAIH,GACrCI,EAAI7D,KAAK4D,IAAIJ,GAAQxD,KAAK2D,IAAIF,GAAQzD,KAAK2D,IAAIH,GAAQxD,KAAK4D,IAAIH,GAAQzD,KAAK4D,IAAIL,EAAOD,GAC9F,OAAOd,EAAiBxC,KAAK8D,MAAMJ,EAAGG,GACxC,CCRA,SAASE,EAAYC,EAAQC,EAAUnB,EAASnB,EAAU,CAAC,GACzD,MAAMyB,EAAeR,EAASoB,GACxBE,EAAazB,EAAiBW,EAAa,IAC3Ce,EAAY1B,EAAiBW,EAAa,IAC1CgB,EAAa3B,EAAiBK,GAC9B1B,EH2MR,SAAyB6C,EAAUI,EAAQ,cACzC,MAAMC,EAAS3E,EAAQ0E,GACvB,IAAKC,EACH,MAAM,IAAI1C,MAAMyC,EAAQ,qBAE1B,OAAOJ,EAAWK,CACpB,CGjNkBC,CAAgBN,EAAUtC,EAAQ0C,OAC5CG,EAAYxE,KAAKyE,KACrBzE,KAAK2D,IAAIQ,GAAanE,KAAK4D,IAAIxC,GAAWpB,KAAK4D,IAAIO,GAAanE,KAAK2D,IAAIvC,GAAWpB,KAAK4D,IAAIQ,IAMzFM,EAAMlC,EAJO0B,EAAalE,KAAK8D,MACnC9D,KAAK2D,IAAIS,GAAcpE,KAAK2D,IAAIvC,GAAWpB,KAAK4D,IAAIO,GACpDnE,KAAK4D,IAAIxC,GAAWpB,KAAK2D,IAAIQ,GAAanE,KAAK2D,IAAIa,KAG/CG,EAAMnC,EAAiBgC,GAC7B,YAAwB,IAApBpB,EAAa,GACR5B,EAAM,CAACkD,EAAKC,EAAKvB,EAAa,IAAKzB,EAAQD,YAE7CF,EAAM,CAACkD,EAAKC,GAAMhD,EAAQD,WACnC,CCbA,IAAakD,EAAA,CAAkBC,EAAgBC,IJoN/C,SAA0BhC,GACxB,IAAIiC,EAAQjC,EAAU,IAItB,OAHIiC,EAAQ,IACVA,GAAS,KAEJA,CACT,CI1NwEC,CAAiBlC,EAAQ+B,EAAMC,IAO1FG,EAAA,CAAgBJ,EAAcC,KAAyBA,EAAKD,EAAO,KAAO,IAAO,IAejFK,EAAA,CAASC,EAAeC,EAAaC,IAAwBrF,KAAKoF,IAAIpF,KAAKqF,IAAIF,EAAOC,GAAMC,GAS5FC,EAAYH,IAAA,CAA6BA,QAAOI,SAAU,IASjEC,EAAU,KAUHC,EAAA,CACTC,EACAC,EACAC,EACAC,EACAC,EAAcC,OAAOC,qBAErB,MAAMC,EAtBU,EAsBWJ,EACrBK,EAAS,EAAIJ,EAAeG,EAC5BE,EAASjB,EAAMQ,EAAOP,MAAQQ,GAASO,EAAOA,GACpD,GAAIlG,KAAKoG,IAAID,GAAUX,GAAWxF,KAAKoG,IAAIV,EAAOH,UAAYC,EAAS,OAAOF,EAASK,GAEvF,MAAMU,GAASX,EAAOH,SAAWU,EAAOE,GAAUP,EAC5CU,EAAQtG,KAAKuG,KAAKN,EAAOL,GAC/B,MAAO,CACHT,MAAOO,EAAOP,MAAQgB,GAAUA,EAASE,GAASC,EAClDf,UAAWG,EAAOH,SAAWU,EAAOI,GAASC,IAQxCE,EAAA,CACTd,EACAC,EACAC,EACAC,EACAC,KAEA,MAAMW,EAAOhB,EACTC,EACAA,EAAOP,MAAQF,EAAaS,EAAOP,MAAOQ,GAC1CC,EACAC,EACAC,GAEJ,MAAO,CAAEX,OAAQsB,EAAKtB,MAAQ,KAAO,IAAKI,SAAUkB,EAAKlB,WCxEvDmB,EAAiBvB,GAA0BD,EAAMC,EAFlC,IAEuD,KAW/DwB,EAAkBC,IAEpB,CAAEC,EAAG,GAAKC,EAAG,GADP5B,EAAM0B,EAAc,EAlBR,OAmBE,qBAWlBG,EAAA,CACTC,EACAC,KAEA,MAAMC,EAAcR,EAAcM,EAAOH,GAAK,GACxCM,EAAcT,EAAcM,EAAOF,GAAK,GAGxCM,EAAA,CAAOC,EAAkBC,IAAmBtH,KAAKqF,IAAI,EAAIgC,EAAWC,EAAQ,GAClF,MAAO,CACHC,KAAMH,EAAIF,EAAaD,EAASO,OAChCC,MAAOL,GAAKF,EAAaD,EAASO,OAClCE,IAAKN,EAAID,EAAaF,EAASU,QAC/BC,OAAQR,GAAKD,EAAaF,EAASU,UASrCE,EAA2B,aAJF,IAezBC,EAAA,CAAyBpH,EAAgBqH,IAC3C/H,KAAKgI,KAAMH,EAA2B7H,KAAK4D,IAAInB,EAAiBsF,IAAqBrH,GAiBnFuH,EAAA,EAA4BC,iBAAgBC,wBAC7C,GAAMD,EAAkBlI,KAAKoI,IAAI3F,EAAiB0F,GAAsB,GAMhEE,EAAA,CAA0BC,EAAcC,IACjDN,EAAuBM,GA7BrB,EAAkBD,EAAcP,IACjCF,EAA2B7H,KAAK4D,IAAInB,EAAiBsF,IAAqB,GAAKO,EA4BjDE,CAAeF,EAAMC,EAAKR,iBC1DvDU,EAAA,CAAwCC,EAASC,KACnD,MAAMC,EAAUC,SAASC,gBAHP,6BAGsCJ,GACxD,IAAK,MAAOK,EAAW5D,KAAU6D,OAAOC,QAAQN,GAAaC,EAAQM,aAAaH,EAAW5D,GAC7F,OAAOyD,GAGLO,EAAcC,GAA2B,oCAAoCC,mBAAmBD,KA4BzFE,EAAA,CAAgBC,EAA2BC,KACpD,MAAMC,EAAwB,UAAfF,EAAMpH,KAAmBgH,EAzBtC,GACAO,YAAYlK,EAA0BmK,eAAe,QAASC,iBAAiB,GACjFJ,KAEA,MAAMK,EAAMpB,EAAW,MAAO,CAAEqB,QAAS,YAAatC,MAAOuC,OAAOP,GAAO7B,OAAQoC,OAAOP,KAY1F,OAXAK,EAAIG,OACAvB,EAAW,OAAQ,CACfwB,EAAG,sBACHC,KAAMR,EACNS,OAAQR,EACR,iBAAkBI,OAAOH,GACzB,eAAgB,IAChB,kBAAmB,QACnB,cAAe,aAGhB,IAAIQ,eAAgBC,kBAAkBR,IASMS,CAAYf,EAAOC,IAASD,EAAMA,MACrF,GAAsB,iBAAXE,EAAqB,OAAOA,EAEvC,MAAMb,EAAU,IAAI2B,MAGpB,OAFA3B,EAAQ4B,YAAc,YACtB5B,EAAQ6B,IAAMhB,EAAOiB,SAAS,QAAUvB,EAAWM,GAAUA,EACtDb,GA8CL+B,EAAA,CAAWC,EAAQzI,EAAcsH,KACnC,MAAMoB,EAASD,EAAGE,aAAa3I,GAC/B,IAAK0I,EAAQ,MAAM,IAAIjJ,MAAM,iDAI7B,OAFAgJ,EAAGG,aAAaF,EAAQpB,GACxBmB,EAAGI,cAAcH,GACVA,GA0DEI,EAAb,MAcyBC,IAbrB9I,GAAc,yBAAyB9C,MACvC6C,KAAgB,SAChBgJ,cAAyB,KAEzBC,OAAsC,CAAC,EACvCC,OACAC,OACAV,GACAW,QACAC,SAAkB,EAClBC,cAEA,WAAAC,CACIR,EACAE,GADiBO,KAAAT,IAAAA,EAGjBS,KAAKF,cAAgBP,EAAIU,sBAAsB,CAAEC,eAAA,IAAsBF,KAAKG,YAC5EH,KAAKI,mBACLJ,KAAKK,MAAMZ,EACf,CAEA,KAAAY,CAAMZ,GACFO,KAAKP,OAASA,EACd,MAAMa,EAAOb,EAAOa,MAxLP,GAIf,IAAeC,EAkDHC,OAAO5C,EAA2BC,KAChD,MAAMZ,EAAUU,EAAaC,EAAOC,SAC9BZ,EAAQwD,SACd,MAAMC,EAASxD,SAASyD,cAAc,UACtCD,EAAO7E,MAAQgC,EACf6C,EAAO1E,OAAS6B,EAChB,MAAM+C,EAAQ/C,EAAOxJ,KAAKqF,IAAIuD,EAAQ4D,aAAc5D,EAAQ6D,cAAe,IACpEjF,EAAOG,GAAU,CAACiB,EAAQ4D,aAAeD,EAAO3D,EAAQ6D,cAAgBF,GAE/E,OADAF,EAAOK,WAAW,OAAOC,UAAU/D,GAAUY,EAAOhC,GAAS,GAAIgC,EAAO7B,GAAU,EAAGH,EAAOG,GACrF0E,GA0HHO,CAAUxB,EAAO7B,OAAS,CAAEpH,KAAM,UArLrB+J,EAqL4CD,EApL7DjM,KAAKoF,IAAI,GAAKpF,KAAK6M,KAAK7M,KAAKgI,KAAKhI,KAAKqF,IAAI6G,EAAeY,iBAAkB,KAAM,OAqLzEC,KAAMzB,IAECK,KAAKP,SAAWA,IAEpBO,KAAKL,OAASA,EACdK,KAAKqB,iBACLrB,KAAKT,IAAI+B,YAAYC,oBAExBC,MAAOC,GAAUC,QAAQC,KAAK,gDAAiDF,IACpFzB,KAAKT,IAAI+B,YAAYC,gBACzB,CAGA,KAAAK,CAAMC,EAAoBC,GACtB9B,KAAKN,OAAS,CAAEmC,WAAUC,WAC1B9B,KAAKT,IAAI+B,YAAYC,gBACzB,CAEA,MAAAQ,GACI/B,KAAKH,SAAU,EACfG,KAAKF,gBACDE,KAAKT,IAAI+B,YAAYU,SAAShC,KAAKvJ,KAAKuJ,KAAKT,IAAI+B,YAAYW,YAAYjC,KAAKvJ,GACtF,CAEA,KAAAyL,CAAMC,EAAelD,GACjBe,KAAKf,GAAKA,EACVe,KAAKJ,QA3GP,CAAiBX,IACnB,MAAMmD,EAAUnD,EAAGoD,gBACbC,EAAUrD,EAAGsD,eACnB,IAAKH,IAAYE,EAAS,MAAM,IAAIrM,MAAM,oDAO1C,OALAgJ,EAAGuD,aAAaJ,EAASpD,EAAQC,EAAIA,EAAGwD,cA3CtB,iSA4ClBxD,EAAGuD,aAAaJ,EAASpD,EAAQC,EAAIA,EAAGyD,gBAjCpB,+IAkCpBzD,EAAG0D,YAAYP,GACfnD,EAAG2D,WAAW3D,EAAG4D,aAAcP,GAC/BrD,EAAG6D,WAAW7D,EAAG4D,aAAc,IAAIE,aAAa,EAAC,GAAI,EAAI,GAAG,GAAI,EAAI,EAAG,EAAG,IAAK9D,EAAG+D,aAC3E,CACHZ,UACAE,UACAW,aAAS,EACTC,SAAU,CACNC,OAAQlE,EAAGmE,mBAAmBhB,EAAS,YACvCtG,MAAOmD,EAAGmE,mBAAmBhB,EAAS,WACtCiB,QAASpE,EAAGmE,mBAAmBhB,EAAS,aACxCxE,MAAOqB,EAAGmE,mBAAmBhB,EAAS,YAE1CkB,OAAQrE,EAAGsE,kBAAkBnB,EAAS,cAuFvBoB,CAAcvE,EACjC,CAEA,QAAAwE,CAAStB,EAAelD,GACpBe,KAAKqB,iBACDrB,KAAKJ,UACLX,EAAGyE,cAAc1D,KAAKJ,QAAQwC,SAC9BnD,EAAG0E,aAAa3D,KAAKJ,QAAQ0C,UAEjCtC,KAAKJ,aAAU,EACfI,KAAKf,QAAK,CACd,CAEA,MAAA2E,CAAO3E,EAAQjJ,GACX,MAAM4J,QAAEA,EAAAF,OAASA,EAAAC,OAAQA,GAAWK,KACpC,IAAKJ,IAAYF,IAAWC,IAAkC,IAAxBK,KAAKP,OAAOoE,QAAmB,OAErEjE,EAAQqD,UApGV,EAAiBhE,EAAQU,KAC3B,MAAMsD,EAAUhE,EAAG6E,sBAAmB,EAUtC,OATA7E,EAAG8E,YAAY9E,EAAG+E,WAAYf,GAAW,MACzChE,EAAGgF,YAAYhF,EAAGiF,gCAAgC,GAClDjF,EAAGkF,WAAWlF,EAAG+E,WAAY,EAAG/E,EAAGmF,KAAMnF,EAAGmF,KAAMnF,EAAGoF,cAAe1E,GACpEV,EAAGgF,YAAYhF,EAAGiF,gCAAgC,GAClDjF,EAAGqF,eAAerF,EAAG+E,YACrB/E,EAAGsF,cAActF,EAAG+E,WAAY/E,EAAGuF,mBAAoBvF,EAAGwF,sBAC1DxF,EAAGsF,cAActF,EAAG+E,WAAY/E,EAAGyF,mBAAoBzF,EAAG0F,QAC1D1F,EAAGsF,cAActF,EAAG+E,WAAY/E,EAAG2F,eAAgB3F,EAAG4F,eACtD5F,EAAGsF,cAActF,EAAG+E,WAAY/E,EAAG6F,eAAgB7F,EAAG4F,eAC/C5B,GAyFiB8B,CAAc9F,EAAIU,GACtC,MAAM2B,EAActB,KAAKT,IAAI+B,aACtB0D,EAAWC,GAAYvF,EAAOmC,SAG/BqD,EAAS5D,EAAY6D,sBAAsB,CAACH,EAAWC,KAAc,EACrEG,EAAStR,EAAmBuR,WAAW,CAACL,EAAWC,GAAWC,GAC9DI,GAAStF,KAAKP,OAAOa,MA5Od,IA4OsC,GDxMI,GAVhC,IAU8D,GCwMPgB,EAAYiE,YAE1F,MAAMzD,EAAUhL,EAAiB4I,EAAOoC,UACjC0D,EAAMC,GAAU,CAACpR,KAAK2D,IAAI8J,GAAWwD,EAAMjR,KAAK4D,IAAI6J,GAAWwD,GAEtErG,EAAGyG,WAAW9F,EAAQwC,SACtBnD,EAAG0G,iBAAiB/F,EAAQsD,SAASC,QAAQ,EAhG/C,EAAYyC,GAAiC1K,IAAGC,IAAG0K,QACrD,MAAM1C,EAASJ,aAAa7J,KAAK0M,GACjC,IAAK,IAAIE,EAAM,EAAGA,EAAM,EAAGA,IACvB3C,EAAO,GAAK2C,GACRF,EAAWE,GAAO5K,EAAI0K,EAAW,EAAIE,GAAO3K,EAAIyK,EAAW,EAAIE,GAAOD,EAAID,EAAW,GAAKE,GAElG,OAAO3C,GA0FiD4C,CAAS/P,EAAQgQ,sBAAsBC,WAAYb,IACvGnG,EAAGiH,UAAUtG,EAAQsD,SAASpH,MAAO2J,EAAQD,GAC7CvG,EAAGiH,UAAUtG,EAAQsD,SAASG,QAASmC,GAAOC,GAC9CxG,EAAGkH,cAAclH,EAAGmH,UACpBnH,EAAG8E,YAAY9E,EAAG+E,WAAYpE,EAAQqD,SAAW,MACjDhE,EAAGoH,UAAUzG,EAAQsD,SAAStF,MAAO,GACrCqB,EAAG2D,WAAW3D,EAAG4D,aAAcjD,EAAQ0C,SACvCrD,EAAGqH,wBAAwB1G,EAAQ0D,QACnCrE,EAAGsH,oBAAoB3G,EAAQ0D,OAAQ,EAAGrE,EAAGuH,OAAO,EAAO,EAAG,GAE9DvH,EAAGwH,QAAQxH,EAAGyH,YACdzH,EAAG0H,OAAO1H,EAAG2H,OACb3H,EAAG4H,UAAU5H,EAAG6H,IAAK7H,EAAG8H,qBACxB9H,EAAG+H,WAAW/H,EAAGgI,eAAgB,EAAG,EACxC,CAEA,cAAA5F,GACQrB,KAAKJ,SAASqD,SAASjD,KAAKf,IAAIiI,cAAclH,KAAKJ,QAAQqD,SAC3DjD,KAAKJ,UAASI,KAAKJ,QAAQqD,aAAU,EAC7C,CAEA,gBAAA7C,GACQJ,KAAKT,IAAI4H,SACTnH,KAAKG,UAELH,KAAKT,IAAI+B,YAAY8F,KAAK,YAAA,IAAmBpH,KAAKI,mBAE1D,CAEA,OAAAD,GACSH,KAAKH,SAAYG,KAAKT,IAAI+B,YAAYU,SAAShC,KAAKvJ,KAAKuJ,KAAKT,IAAI+B,YAAY+F,SAASrH,KAChG,GCxNEsH,EAAgC,CAClCC,SAAU,IACVC,MAAO,GACPC,IAAK,CAAEC,KAAM,cAAeC,oBAAqB,KACjDhL,KAAM,CAAE+K,KAAM,eAAgBE,kBAAmB,IAO/CC,EACK,EADLA,EAEO,EAFPA,EAGI,EAHJA,EAIK,IAJLA,EAKM,IALNA,EAMS,GANTA,EAOa,GA+CbC,EAAsB,GAgBtBC,EAAapL,GAAyBpD,EAAMoD,EAZjC,GACA,IAgDJqL,EAAb,MA0ByBzI,IAzBrB0I,MACAC,OACAC,WACAC,OAEAC,eACAC,cAAwB,EACxBC,yBAAmC,EACnChB,SAAmB5N,EAAS,GAC5B6O,OAEAxO,OAEAyO,WACAC,mBAA6B,EAG7BpN,SAAmB,CAAEO,MAAO,EAAGG,OAAQ,GAEvC2M,QAAiB,EAEjBC,2BAAoC,EACpCC,YAEA,WAAA9I,CACIR,EACAvJ,GADiBgK,KAAAT,IAAAA,EAGjBS,KAAKiI,MAAQjS,EAAQiS,MACrBjI,KAAKkI,OAAS,IAAKZ,KAAmBtR,EAAQkS,QAC9ClI,KAAKmI,WAAanS,EAAQmS,YAAA,MAAsB,GAChD,MAAM7G,EAAc/B,EAAI+B,YAClBjG,EAAS2E,KAAK8I,eACpB9I,KAAKwI,OAAS,CACVrR,QAASwC,EAAS2H,EAAYyH,cAC9BpM,KAAMhD,EAAS2H,EAAYiE,WAC3BiC,MAAO7N,EAAS2H,EAAY0H,YAC5BC,QAAStP,EAAS0B,EAAOH,GACzBgO,QAASvP,EAAS0B,EAAOF,GACzBgO,UAAWxP,EAAS2H,EAAY8H,sBAChCC,cAAe1P,EAAS,IAE5BqG,KAAKhG,OAAS,CACV7C,QAAS6I,KAAKwI,OAAOrR,QAAQqC,MAC7BmD,KAAMqD,KAAKwI,OAAO7L,KAAKnD,MACvB2P,UAAWnJ,KAAKwI,OAAOW,UAAU3P,MACjC6P,cAAe,GAEnBrJ,KAAKyI,WAAazI,KAAKhG,OAAO2C,KAC9BqD,KAAKsJ,eACLhI,EAAYiI,GAAG,SAAUvJ,KAAKsJ,cAC9BtJ,KAAKoI,OAAS,IAAI9I,EAAeC,EAAKvJ,EAAQoS,QAAU,CAAC,GACzDpI,KAAKwJ,qBACT,CAGA,WAAIC,GACA,YAA+B,IAAxBzJ,KAAKqI,cAChB,CAGA,KAAAjR,GACI,GAAI4I,KAAKyJ,QAAS,OAElB,MAAMnI,EAActB,KAAKT,IAAI+B,YAC7BtB,KAAK4I,0BAA4BtH,EAAYoI,2BAC7CpI,EAAYqI,0BAAyB,GACrC3J,KAAKuH,SAAW5N,EAAS,GACzBqG,KAAKsI,cAAgBsB,YAAYC,MACjC7J,KAAKqI,eAAiByB,sBAAsB9J,KAAK+J,KACrD,CAMA,IAAAC,QACgC,IAAxBhK,KAAKqI,iBAET4B,qBAAqBjK,KAAKqI,gBAC1BrI,KAAKqI,oBAAiB,EACtBrI,KAAKT,IAAI+B,YAAYqI,yBAAyB3J,KAAK4I,2BACvD,CAGA,QAAAsB,CAASjC,GACLjI,KAAKiI,MAAQA,EACbjI,KAAKuI,yBAA2B,EAChCvI,KAAK2I,QAAS,EACV3I,KAAKyJ,QACLzJ,KAAKmK,cAELnK,KAAKwJ,qBAEb,CAGA,SAAAY,CAAUlC,GACNlI,KAAKkI,OAAS,IAAKlI,KAAKkI,UAAWA,GAG/BlI,KAAK2I,SAAW3I,KAAKyJ,SAASzJ,KAAKmK,aAC3C,CAGA,SAAAE,GACI,MAAO,IAAKrK,KAAKkI,OACrB,CAGA,SAAAoC,CAAUlC,GACNpI,KAAKoI,OAAO/H,MAAM+H,EACtB,CAGA,IAAAmC,CAAK7O,GACDsE,KAAKuI,yBAA2BvI,KAAKwK,sBAAwBjR,EAAMmC,EAAU,EAAG,GAChFsE,KAAKmK,aACT,CAMA,MAAApI,GACI/B,KAAKgK,OACLhK,KAAKoI,OAAOrG,SACZ,MAAMT,EAActB,KAAKT,IAAI+B,YAC7BA,EAAYmJ,IAAI,SAAUzK,KAAKsJ,cAC3BtJ,KAAK6I,aAAavH,EAAYoJ,WAAW1K,KAAK6I,YACtD,CAEAS,aAAA,KACI,MAAM5I,EAASV,KAAKT,IAAI+B,YAAYqJ,YACpC3K,KAAK1E,SAAW,CAAEO,MAAOxH,KAAKqF,IAAIgH,EAAOkK,YAAa,GAAI5O,OAAQ3H,KAAKqF,IAAIgH,EAAOmK,aAAc,KAGpG,mBAAAL,GACI,OAAOxK,KAAKiI,MAAMlS,WAAW+U,QAAQC,cACzC,CAEAhB,KAAyBiB,IAErB,MAAM/Q,EAAiBV,GAAOyR,EAAYhL,KAAKsI,eAAiB,IAAM,EAlNpD,KAmNlBtI,KAAKsI,cAAgB0C,EAErB,MAAMC,EAAmBjL,KAAKuH,SAAS/N,MACvCwG,KAAKuH,SAAWzN,EAAckG,KAAKuH,SAAUvH,KAAKkI,OAAOX,SAAUtN,EAAgB4N,GACnF,MAAMqD,GAAmBD,EAAmBjL,KAAKuH,SAAS/N,OAAS,EAAI,IAGvEwG,KAAKuI,yBAA2BvI,KAAKmL,QAAQnL,KAAKuI,yBAA2B2C,EAAkBjR,GAE/F+F,KAAKmK,YAAYlQ,GACjB+F,KAAKqI,eAAiByB,sBAAsB9J,KAAK+J,OAGrD,YAAAqB,CAAqB7C,GACjB,OAAO3U,EAA6BoM,KAAKiI,MAAO,CAAEM,4BACtD,CAIA,UAAA8C,CAAmBC,GACf,OAAOtL,KAAKoL,aAAapL,KAAKmL,QAAQnL,KAAKuI,yBAA2B+C,KAAkBzJ,QAC5F,CAEA,OAAAsJ,CAAgBI,GACZ,MAAMC,EAAcxL,KAAKwK,sBACzB,OAAOgB,EAAc,GAAMD,EAAmBC,EAAeA,GAAeA,EAAc,CAC9F,CAIA,WAAAC,GACI,MAAOC,EAAQC,GAAS,EAAC,GAAsB7D,GAAqBvI,IAC/D+L,GAAmBtL,KAAKoL,aAAapL,KAAKuI,yBAA2B+C,IAAiBzJ,UAE3F,OAAO6J,GAAUC,EAAQ1S,EAAeyS,EAAQC,QAAS,CAC7D,CAEA,YAAAC,GACI,OAAO5L,KAAKiI,MAAMlS,WAAW8V,UAAUD,cAAgB,EAC3D,CAMA,gBAAAE,CAAyBC,EAAO,GAC5B,MAAMH,EAAe5L,KAAK4L,eAC1B,IAAKA,EAAaxV,OAAQ,OAE1B,MAAM0E,EAAO8Q,EAAaI,UACrBC,GAAgBA,EAAYC,oBAAsBlM,KAAKuI,0BAG5D,OAAOqD,EAAavX,KAAKoF,MADH,IAATqB,EAAc,EAAIA,GACKiR,EAAMH,EAAaxV,OAAS,GACpE,CAIA,cAAA+V,GACI,IAAIC,EAAM,EACNC,EAAOrM,KAAKiI,MAAMtR,SAASb,YAAYM,OAAS,EACpD,KAAOgW,EAAMC,GAAM,CACf,MAAMC,EAASjY,KAAKkY,OAAOH,EAAMC,GAAQ,GACnCG,EAAW9Y,EAA8BsM,KAAKiI,MAAOqE,QAC1C,IAAbE,GAA0BA,EAASjB,iBAAmBvL,KAAKuI,yBAC3D6D,EAAME,EAAS,EAEfD,EAAOC,CAEf,CACA,OAAOF,CACX,CAGA,WAAAK,GACI,MAAMhF,EAAMzH,KAAKkI,OAAOT,IACxB,OAAQA,EAAIC,MACR,IAAK,WACD,OAAO1H,KAAKqL,WAAW5D,EAAIiF,aAC/B,IAAK,SAAU,CACX,MAAM5W,EAAckK,KAAKiI,MAAMtR,SAASb,YAExC,OAAOA,GAAakK,KAAKmM,iBAAmB1E,EAAIkF,aAAe7W,EAAYM,OAC/E,CACA,IAAK,cAAe,CAIhB,MAAM6V,EAAcjM,KAAK8L,mBACnBH,EACFM,GAAejM,KAAKmL,QAAQc,EAAYC,oBAAsBlM,KAAKuI,0BACvE,YAAiB,IAAVoD,EAAsB3L,KAAKqL,WAAW5D,EAAIE,qBAAuB3H,KAAKqL,WAAWM,EAC5F,EAER,CAGA,QAAAiB,CAAiBxQ,GACb,MAAMO,EAAOqD,KAAKkI,OAAOvL,KACzB,GAAkB,UAAdA,EAAK+K,KAAkB,OAAO/K,EAAKA,KAEvC,GAAkB,UAAdA,EAAK+K,KAAkB,CACvB,MAAMmF,EAAYxY,KAAKgI,KAAKhI,KAAKqF,IAAIsG,KAAKkI,OAAOX,SAAU,GA/R3C,IAgShB,OAAOQ,EAAU,GA/RG,EA+RY8E,EACpC,CAKA,MAAM7S,EAASgG,KAAK8L,iBAAiBzX,KAAKqF,IAAIiD,EAAKiL,kBAAmB,GAAK,GAC3E,IAAK5N,EAAQ,OAAO+N,EAAU,IAG9B,MAAM+E,EADQ9M,KAAKmL,QAAQnR,EAAOkS,oBAAsBlM,KAAKuI,0BAC/BlU,KAAK4D,IAAInB,EAAiBzC,KAAKoF,IAAIuG,KAAKkI,OAAOV,MA3TpD,MA4TzB,OAAOO,EFlYF,EAAewD,EAA0BwB,EAAgB3Q,IAClED,EAAsB9H,KAAKqF,IAAI6R,EAAkB,GAAKlX,KAAKqF,IAAIqT,EAAQ,GAAI3Q,GEiYtD4Q,CAAYF,EA/SJ,GA+SmB9M,KAAK1E,SAASU,OAAmCI,GACjG,CAWA,aAAA6Q,CAAsBC,EAAgBC,EAAcvQ,GAChD,MAAM4K,EAAQ1Q,EAAiBzC,KAAKoF,IAAIuG,KAAKkI,OAAOV,MAzU3B,KA0UnB4F,EAAwB1Q,EAAuBsD,KAAKwI,OAAO7L,KAAKnD,MAAOoD,GAAQvI,KAAK2D,IAAIwP,GAAU,IAClGkE,EAAS1L,KAAKwI,OAAOrR,QAAQqC,MAAQ,IAC3C,IAAIwC,EAjVa,IAkVjB,IAAK,IAAIqR,EAAS,EAAGA,EAhVH,EAgV+BA,IAAU,CACvD,MAAMC,EAAQD,EAjVA,EAkVRxX,EAAQuC,EAAY8U,EAAME,GAAwB,EAAIE,GAAQ5B,GAAQ/U,SAASb,YAC/EoP,EAASlF,KAAKuN,SAAS1X,QACd,IAAXqP,IAAsBlJ,EAAS3H,KAAKqF,IAAIsC,EAtV/B,KAsV2DkJ,EAASiI,IAAS,EAAIG,IAClG,CACA,MFhYK,EAAyB/B,EAA0B3O,IAC5DT,EAAsB9H,KAAKqF,IAAI6R,EAAkB,GAAKjP,EAAuBM,GAAOA,EAAKR,iBE+X9EoR,CAAsBxR,EAAS3H,KAAK4D,IAAIuP,GAAQ5K,EAC3D,CAIA,QAAA2Q,EAAkBvI,EAAWC,IACzB,OAAOjF,KAAKT,IAAI+B,YAAY6D,sBAAsB,CAACH,EAAWC,UAAc,CAChF,CASA,MAAAwI,CAAeP,GACX,MAAMtQ,EAAmB,CACrBR,gBAAiB8Q,EAAK,GACtB3Q,eAAgByD,KAAK1E,SAASU,OAC9BQ,mBAAoBwD,KAAKT,IAAI+B,YAAYoM,0BAGvC/B,EAAQ3L,KAAKyM,cACfd,IACA3L,KAAKhG,OAAO7C,QHhfX,EAAiBwW,EAAkBR,EAAcS,IAC1DvZ,KAAKoG,IAAInB,EAAaqU,EAAUR,IAASS,EAAkBT,EAAOQ,EG+epCE,CAClB7N,KAAKhG,OAAO7C,QACZ8B,EAAeiU,EAAMvB,GAjZJ,MAqZzB3L,KAAKhG,OAAOqP,cAAgBrJ,KAAKyL,eAAiBzL,KAAKhG,OAAOqP,cAK9D,MAAMyE,EAAS9N,KAAK4M,SAAShQ,EAAKR,iBAC5B2R,EAAqC,iBAA1B/N,KAAKkI,OAAOvL,KAAK+K,KArZpB,GAqZ8D,EACxErT,KAAKoG,IAAIqT,EAAS9N,KAAKyI,YAAcsF,IAAU/N,KAAKyI,WAAaqF,GAErE,MAAMX,EAAOnN,KAAKuN,SAASL,QACd,IAATC,GAEKnN,KAAKT,IAAI+B,YAAY0M,eAAchO,KAAKhG,OAAOmP,UAAY,GAEhEnJ,KAAKhG,OAAO2C,KAAOqD,KAAKyI,aAExBzI,KAAKhG,OAAOmP,UAAYgE,EACxBnN,KAAKhG,OAAO2C,KAAOtI,KAAKoF,IAAIuG,KAAKyI,WAAYzI,KAAKiN,cAAcC,EAAMC,EAAMvQ,IAEpF,CAEA,YAAAkM,GACI,OAAO9I,KAAKkI,OAAO7M,QAAUL,EAAegF,KAAKkI,OAAOV,MAC5D,CAIA,IAAAyG,CAAahU,GACT,MAAMuO,OAAEA,EAAAxO,OAAQA,GAAWgG,KACrB3E,EAAS2E,KAAK8I,eACdoF,EAAA,CAAWnU,EAAgBP,EAAeU,IAC5CJ,EAAcC,EAAQP,EAAOS,EAAgBC,GACjD8F,KAAKwI,OAAS,CACVrR,QAAS0D,EACL2N,EAAOrR,QACP6C,EAAO7C,QACP8C,EACA4N,EAzboB,IA4bxBwB,cAAexO,EACX2N,EAAOa,cACPrP,EAAOqP,cACPpP,EACA4N,GAEJlL,KAAMuR,EAAQ1F,EAAO7L,KAAM3C,EAAO2C,KAAMkL,GACxCsB,UAAW+E,EAAQ1F,EAAOW,UAAWnP,EAAOmP,UAAWtB,GACvDL,MAAO0G,EAAQ1F,EAAOhB,MAAOxH,KAAKkI,OAAOV,MAAOK,GAChDoB,QAASiF,EAAQ1F,EAAOS,QAAS5N,EAAOH,EAAG2M,GAC3CqB,QAASgF,EAAQ1F,EAAOU,QAAS7N,EAAOF,EAAG0M,GAEnD,CAGA,MAAAsG,GACI,MAAM9S,EAAS2E,KAAK8I,eACpB9I,KAAKwI,OAAS,CACVrR,QAASwC,EAASqG,KAAKhG,OAAO7C,SAC9BkS,cAAe1P,EAASqG,KAAKhG,OAAOqP,eACpC1M,KAAMhD,EAASqG,KAAKhG,OAAO2C,MAC3BwM,UAAWxP,EAASqG,KAAKhG,OAAOmP,WAChC3B,MAAO7N,EAASqG,KAAKkI,OAAOV,OAC5ByB,QAAStP,EAAS0B,EAAOH,GACzBgO,QAASvP,EAAS0B,EAAOF,GAEjC,CAGA,mBAAAqO,GACI,MAAM0D,EAAOlN,KAAKqL,WAAW,GAC7BrL,KAAKhG,OAAOqP,cAAgBrJ,KAAKyL,eAAiBzL,KAAKhG,OAAOqP,cAC9DrJ,KAAKwI,OAAOa,cAAgB1P,EAASqG,KAAKhG,OAAOqP,eAC7C6D,GAAMlN,KAAKoI,OAAOxG,MAAMsL,EAAMlN,KAAKhG,OAAOqP,cAClD,CAOA,WAAAc,CAAoBlQ,GAChB,MAAMiT,EAAOlN,KAAKoL,aAAapL,KAAKuI,0BACpC,IAAK2E,EAAM,OAEX,MAAMkB,OAA4B,IAAnBnU,GAAgC+F,KAAK2I,OACpD3I,KAAK0I,oBAAsBzO,GAAkB,EAC7C,MAAMoU,GAAaD,GAAUpO,KAAK0I,oBA5dV,GA6dpB2F,IACArO,KAAK0I,mBAAqB,EAC1B1I,KAAKyN,OAAOP,EAAKrL,WAEjBuM,EACApO,KAAKiO,KAAKhU,GAEV+F,KAAKmO,SAETnO,KAAK2I,QAAS,EAEd,MAAO3D,EAAWC,GAAYiI,EAAKrL,UAC7B1K,QAAEA,EAAAwF,KAASA,EAAA6K,MAAMA,EAAA2B,UAAOA,EAAAF,QAAWA,EAAAC,QAASA,EAAAG,cAASA,GAAkBrJ,KAAKwI,OAC5ElH,EAActB,KAAKT,IAAI+B,YAC7BtB,KAAK6I,cAAgBvH,EAAYgN,aAGjChN,EAAYiN,OAAO,CACfC,OAAQ,CAACxJ,EAAWC,GACpBkE,UAAWA,EAAU3P,MACrBrC,QAASA,EAAQqC,MACjBgO,MAAOA,EAAMhO,MACbmD,KAAMA,EAAKnD,MACXiV,QAASrT,EAAc,CAAEF,EAAG+N,EAAQzP,MAAO2B,EAAG+N,EAAQ1P,OAASwG,KAAK1E,YAExE0E,KAAKoI,OAAOxG,MAAMsL,EAAKrL,SAAUwH,EAAc7P,OAE3C6U,GACArO,KAAKmI,WAAW,CACZI,yBAA0B2E,EAAK3B,iBAC/BmD,sBAAuBxB,EAAKyB,qBAGxC"}
|
|
1
|
+
{"version":3,"file":"index.es.js","names":["calculateProgressAtRoutePoint","generateId","getCoordinateAtRouteProgress","MercatorCoordinate","earthRadius","factors","centimeters","centimetres","cm","degrees","Math","PI","deg","feet","ft","inches","in","kilometers","kilometres","km","meters","metres","m","miles","mi","millimeters","millimetres","mm","nauticalmiles","nmi","radians","rad","yards","yd","point","coordinates","properties","options","Error","Array","isArray","length","isNumber","geom","feat","type","id","bbox","geometry","feature","radiansToDegrees","degreesToRadians","num","isNaN","getCoord","coord","bearing","start","end","final","bear","calculateFinalBearing","coordinates1","coordinates2","lon1","lon2","lat1","lat2","a","sin","cos","b","atan2","destination","origin","distance","longitude1","latitude1","bearingRad","units","factor","lengthToRadians","latitude2","asin","lng","lat","headingBetween","from","to","angle","bearingToAzimuth","shortestTurn","clamp","value","min","max","springAt","velocity","AT_REST","springTowards","spring","target","elapsedSeconds","settleSeconds","maxVelocity","Number","POSITIVE_INFINITY","rate","reach","offset","abs","drift","decay","exp","springTowardsBearing","next","clampFraction","anchorForPitch","pitchDegrees","x","y","anchorPadding","anchor","viewport","fromCentreX","fromCentreY","pad","fraction","extent","left","width","right","top","height","bottom","EQUATOR_METERS_PER_PIXEL","zoomForMetersPerPixel","latitudeDegrees","log2","cameraDistanceInPixels","viewportHeight","fieldOfViewDegrees","tan","cameraDistanceInMeters","zoom","lens","metersPerPixel","svgElement","name","attributes","element","document","createElementNS","attribute","Object","entries","setAttribute","svgDataUri","markup","encodeURIComponent","imageElement","image","side","source","fillColor","outlineColor","outlineOpacity","svg","viewBox","String","append","d","fill","stroke","XMLSerializer","serializeToString","arrowMarkup","Image","crossOrigin","src","includes","compile","gl","shader","createShader","shaderSource","compileShader","PositionMarker","map","renderingMode","config","placed","raster","drawing","removed","stopRestoring","constructor","this","addStyleChangeHandler","onStyleChanged","install","installWhenReady","apply","size","sizeInPixels","async","decode","canvas","createElement","scale","naturalWidth","naturalHeight","getContext","drawImage","rasterise","ceil","devicePixelRatio","then","releaseTexture","mapLibreMap","triggerRepaint","catch","error","console","warn","place","position","heading","remove","getLayer","removeLayer","onAdd","_map","program","createProgram","corners","createBuffer","attachShader","VERTEX_SHADER","FRAGMENT_SHADER","linkProgram","bindBuffer","ARRAY_BUFFER","bufferData","Float32Array","STATIC_DRAW","texture","uniforms","matrix","getUniformLocation","forward","corner","getAttribLocation","createDrawing","onRemove","deleteProgram","deleteBuffer","render","visible","createTexture","bindTexture","TEXTURE_2D","pixelStorei","UNPACK_PREMULTIPLY_ALPHA_WEBGL","texImage2D","RGBA","UNSIGNED_BYTE","generateMipmap","texParameteri","TEXTURE_MIN_FILTER","LINEAR_MIPMAP_LINEAR","TEXTURE_MAG_FILTER","LINEAR","TEXTURE_WRAP_S","CLAMP_TO_EDGE","TEXTURE_WRAP_T","uploadTexture","longitude","latitude","ground","queryTerrainElevation","centre","fromLngLat","half","getZoom","sine","cosine","useProgram","uniformMatrix4fv","projection","z","row","matrixAt","defaultProjectionData","mainMatrix","uniform2f","activeTexture","TEXTURE0","uniform1i","enableVertexAttribArray","vertexAttribPointer","FLOAT","disable","DEPTH_TEST","enable","BLEND","blendFunc","ONE","ONE_MINUS_SRC_ALPHA","drawArrays","TRIANGLE_STRIP","deleteTexture","mapReady","once","addLayer","DEFAULT_CAMERA","speedKMH","pitch","aim","mode","fallbackMetersAhead","instructionsAhead","SETTLE_SECONDS","HEADING_SPAN_METERS","clampZoom","RouteFlyover","route","camera","onProgress","marker","animationFrame","lastFrameTime","traveledDistanceInMeters","motion","framedZoom","secondsSinceSurvey","flying","pageCenterClampedToGround","pagePadding","targetAnchor","getBearing","getPitch","anchorX","anchorY","elevation","getCenterElevation","markerHeading","readViewport","on","placeMarkerOutright","running","getCenterClampedToGround","setCenterClampedToGround","performance","now","requestAnimationFrame","step","stop","cancelAnimationFrame","setRoute","placeCamera","setCamera","getCamera","setMarker","seek","routeLengthInMeters","off","setPadding","getCanvas","clientWidth","clientHeight","summary","lengthInMeters","frameTime","previousSpeedKMH","metersPerSecond","wrapped","coordinateAt","positionAt","offsetInMeters","distanceInMeters","routeLength","roadHeading","behind","ahead","instructions","guidance","instructionAhead","skip","findIndex","instruction","routeOffsetInMeters","pointIndexHere","low","high","middle","floor","progress","aimPosition","metersAhead","pointsAhead","modeZoom","doublings","foreshortened","pixels","zoomFitting","clearanceZoom","here","road","trailingInKilometers","sample","share","groundAt","zoomForCameraDistance","survey","getVerticalFieldOfView","steering","deadbandDegrees","steadyBearing","framed","deadband","getTerrain","ease","towards","settle","smooth","surveying","getPadding","jumpTo","center","padding","traveledTimeInSeconds","travelTimeInSeconds"],"sources":["../../../node_modules/.pnpm/@turf+helpers@7.4.0/node_modules/@turf/helpers/dist/esm/index.js","../../../node_modules/.pnpm/@turf+invariant@7.4.0/node_modules/@turf/invariant/dist/esm/index.js","../../../node_modules/.pnpm/@turf+bearing@7.4.0/node_modules/@turf/bearing/dist/esm/index.js","../../../node_modules/.pnpm/@turf+destination@7.4.0/node_modules/@turf/destination/dist/esm/index.js","../src/geodesy.ts","../src/framing.ts","../src/marker.ts","../src/RouteFlyover.ts"],"sourcesContent":["// index.ts\nvar earthRadius = 63710088e-1;\nvar factors = {\n centimeters: earthRadius * 100,\n centimetres: earthRadius * 100,\n cm: earthRadius * 100,\n degrees: 360 / (2 * Math.PI),\n deg: 360 / (2 * Math.PI),\n feet: earthRadius * 3.28084,\n ft: earthRadius * 3.28084,\n inches: earthRadius * 39.37,\n in: earthRadius * 39.37,\n kilometers: earthRadius / 1e3,\n kilometres: earthRadius / 1e3,\n km: earthRadius / 1e3,\n meters: earthRadius,\n metres: earthRadius,\n m: earthRadius,\n miles: earthRadius / 1609.344,\n mi: earthRadius / 1609.344,\n millimeters: earthRadius * 1e3,\n millimetres: earthRadius * 1e3,\n mm: earthRadius * 1e3,\n nauticalmiles: earthRadius / 1852,\n nmi: earthRadius / 1852,\n radians: 1,\n rad: 1,\n yards: earthRadius * 1.0936,\n yd: earthRadius * 1.0936\n};\nvar areaFactors = {\n acres: 247105e-9,\n ac: 247105e-9,\n centimeters: 1e4,\n centimetres: 1e4,\n cm: 1e4,\n feet: 10.763910417,\n ft: 10.763910417,\n hectares: 1e-4,\n ha: 1e-4,\n inches: 1550.003100006,\n in: 1550.003100006,\n kilometers: 1e-6,\n kilometres: 1e-6,\n km: 1e-6,\n meters: 1,\n metres: 1,\n m: 1,\n miles: 386e-9,\n mi: 386e-9,\n nauticalmiles: 29155334959812285e-23,\n nmi: 29155334959812285e-23,\n millimeters: 1e6,\n millimetres: 1e6,\n mm: 1e6,\n yards: 1.195990046,\n yd: 1.195990046\n};\nfunction feature(geom, properties, options = {}) {\n const feat = { type: \"Feature\" };\n if (options.id === 0 || options.id) {\n feat.id = options.id;\n }\n if (options.bbox) {\n feat.bbox = options.bbox;\n }\n feat.properties = properties || {};\n feat.geometry = geom;\n return feat;\n}\nfunction geometry(type, coordinates, _options = {}) {\n switch (type) {\n case \"Point\":\n return point(coordinates).geometry;\n case \"LineString\":\n return lineString(coordinates).geometry;\n case \"Polygon\":\n return polygon(coordinates).geometry;\n case \"MultiPoint\":\n return multiPoint(coordinates).geometry;\n case \"MultiLineString\":\n return multiLineString(coordinates).geometry;\n case \"MultiPolygon\":\n return multiPolygon(coordinates).geometry;\n default:\n throw new Error(type + \" is invalid\");\n }\n}\nfunction point(coordinates, properties, options = {}) {\n if (!coordinates) {\n throw new Error(\"coordinates is required\");\n }\n if (!Array.isArray(coordinates)) {\n throw new Error(\"coordinates must be an Array\");\n }\n if (coordinates.length < 2) {\n throw new Error(\"coordinates must be at least 2 numbers long\");\n }\n if (!isNumber(coordinates[0]) || !isNumber(coordinates[1])) {\n throw new Error(\"coordinates must contain numbers\");\n }\n const geom = {\n type: \"Point\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction points(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return point(coords, properties);\n }),\n options\n );\n}\nfunction polygon(coordinates, properties, options = {}) {\n for (const ring of coordinates) {\n if (ring.length < 4) {\n throw new Error(\n \"Each LinearRing of a Polygon must have 4 or more Positions.\"\n );\n }\n if (ring[ring.length - 1].length !== ring[0].length) {\n throw new Error(\"First and last Position are not equivalent.\");\n }\n for (let j = 0; j < ring[ring.length - 1].length; j++) {\n if (ring[ring.length - 1][j] !== ring[0][j]) {\n throw new Error(\"First and last Position are not equivalent.\");\n }\n }\n }\n const geom = {\n type: \"Polygon\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction polygons(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return polygon(coords, properties);\n }),\n options\n );\n}\nfunction lineString(coordinates, properties, options = {}) {\n if (coordinates.length < 2) {\n throw new Error(\"coordinates must be an array of two or more positions\");\n }\n const geom = {\n type: \"LineString\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction lineStrings(coordinates, properties, options = {}) {\n return featureCollection(\n coordinates.map((coords) => {\n return lineString(coords, properties);\n }),\n options\n );\n}\nfunction featureCollection(features, options = {}) {\n const fc = { type: \"FeatureCollection\" };\n if (options.id) {\n fc.id = options.id;\n }\n if (options.bbox) {\n fc.bbox = options.bbox;\n }\n fc.features = features;\n return fc;\n}\nfunction multiLineString(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiLineString\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction multiPoint(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiPoint\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction multiPolygon(coordinates, properties, options = {}) {\n const geom = {\n type: \"MultiPolygon\",\n coordinates\n };\n return feature(geom, properties, options);\n}\nfunction geometryCollection(geometries, properties, options = {}) {\n const geom = {\n type: \"GeometryCollection\",\n geometries\n };\n return feature(geom, properties, options);\n}\nfunction round(num, precision = 0) {\n if (precision && !(precision >= 0)) {\n throw new Error(\"precision must be a positive number\");\n }\n const multiplier = Math.pow(10, precision || 0);\n return Math.round(num * multiplier) / multiplier;\n}\nfunction radiansToLength(radians, units = \"kilometers\") {\n const factor = factors[units];\n if (!factor) {\n throw new Error(units + \" units is invalid\");\n }\n return radians * factor;\n}\nfunction lengthToRadians(distance, units = \"kilometers\") {\n const factor = factors[units];\n if (!factor) {\n throw new Error(units + \" units is invalid\");\n }\n return distance / factor;\n}\nfunction lengthToDegrees(distance, units) {\n return radiansToDegrees(lengthToRadians(distance, units));\n}\nfunction bearingToAzimuth(bearing) {\n let angle = bearing % 360;\n if (angle < 0) {\n angle += 360;\n }\n return angle;\n}\nfunction azimuthToBearing(angle) {\n angle = angle % 360;\n if (angle > 180) {\n return angle - 360;\n } else if (angle < -180) {\n return angle + 360;\n }\n return angle;\n}\nfunction radiansToDegrees(radians) {\n const normalisedRadians = radians % (2 * Math.PI);\n return normalisedRadians * 180 / Math.PI;\n}\nfunction degreesToRadians(degrees) {\n const normalisedDegrees = degrees % 360;\n return normalisedDegrees * Math.PI / 180;\n}\nfunction convertLength(length, originalUnit = \"kilometers\", finalUnit = \"kilometers\") {\n if (!(length >= 0)) {\n throw new Error(\"length must be a positive number\");\n }\n return radiansToLength(lengthToRadians(length, originalUnit), finalUnit);\n}\nfunction convertArea(area, originalUnit = \"meters\", finalUnit = \"kilometers\") {\n if (!(area >= 0)) {\n throw new Error(\"area must be a positive number\");\n }\n const startFactor = areaFactors[originalUnit];\n if (!startFactor) {\n throw new Error(\"invalid original units\");\n }\n const finalFactor = areaFactors[finalUnit];\n if (!finalFactor) {\n throw new Error(\"invalid final units\");\n }\n return area / startFactor * finalFactor;\n}\nfunction isNumber(num) {\n return !isNaN(num) && num !== null && !Array.isArray(num);\n}\nfunction isObject(input) {\n return input !== null && typeof input === \"object\" && !Array.isArray(input);\n}\nfunction removeBbox(geojson) {\n delete geojson.bbox;\n if (geojson.type === \"Feature\") {\n if (geojson.geometry) removeBbox(geojson.geometry);\n } else if (geojson.type === \"FeatureCollection\") {\n geojson.features.forEach(removeBbox);\n } else if (geojson.type === \"GeometryCollection\") {\n geojson.geometries.forEach(removeBbox);\n }\n}\nfunction validateBBox(bbox) {\n if (!bbox) {\n throw new Error(\"bbox is required\");\n }\n if (!Array.isArray(bbox)) {\n throw new Error(\"bbox must be an Array\");\n }\n if (bbox.length !== 4 && bbox.length !== 6) {\n throw new Error(\"bbox must be an Array of 4 or 6 numbers\");\n }\n bbox.forEach((num) => {\n if (!isNumber(num)) {\n throw new Error(\"bbox must only contain numbers\");\n }\n });\n}\nfunction validateId(id) {\n if (!id) {\n throw new Error(\"id is required\");\n }\n if ([\"string\", \"number\"].indexOf(typeof id) === -1) {\n throw new Error(\"id must be a number or a string\");\n }\n}\nexport {\n areaFactors,\n azimuthToBearing,\n bearingToAzimuth,\n convertArea,\n convertLength,\n degreesToRadians,\n earthRadius,\n factors,\n feature,\n featureCollection,\n geometry,\n geometryCollection,\n isNumber,\n isObject,\n lengthToDegrees,\n lengthToRadians,\n lineString,\n lineStrings,\n multiLineString,\n multiPoint,\n multiPolygon,\n point,\n points,\n polygon,\n polygons,\n radiansToDegrees,\n radiansToLength,\n removeBbox,\n round,\n validateBBox,\n validateId\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport { isNumber } from \"@turf/helpers\";\nfunction getCoord(coord) {\n if (!coord) {\n throw new Error(\"coord is required\");\n }\n if (!Array.isArray(coord)) {\n if (coord.type === \"Feature\" && coord.geometry !== null && coord.geometry.type === \"Point\") {\n return [...coord.geometry.coordinates];\n }\n if (coord.type === \"Point\") {\n return [...coord.coordinates];\n }\n }\n if (Array.isArray(coord) && coord.length >= 2 && !Array.isArray(coord[0]) && !Array.isArray(coord[1])) {\n return [...coord];\n }\n throw new Error(\"coord must be GeoJSON Point or an Array of numbers\");\n}\nfunction getCoords(coords) {\n if (Array.isArray(coords)) {\n return coords;\n }\n if (coords.type === \"Feature\") {\n if (coords.geometry !== null) {\n return coords.geometry.coordinates;\n }\n } else {\n if (coords.coordinates) {\n return coords.coordinates;\n }\n }\n throw new Error(\n \"coords must be GeoJSON Feature, Geometry Object or an Array\"\n );\n}\nfunction containsNumber(coordinates) {\n if (coordinates.length > 1 && isNumber(coordinates[0]) && isNumber(coordinates[1])) {\n return true;\n }\n if (Array.isArray(coordinates[0]) && coordinates[0].length) {\n return containsNumber(coordinates[0]);\n }\n throw new Error(\"coordinates must only contain numbers\");\n}\nfunction geojsonType(value, type, name) {\n if (!type || !name) {\n throw new Error(\"type and name required\");\n }\n if (!value || value.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + value.type\n );\n }\n}\nfunction featureOf(feature, type, name) {\n if (!feature) {\n throw new Error(\"No feature passed\");\n }\n if (!name) {\n throw new Error(\".featureOf() requires a name\");\n }\n if (!feature || feature.type !== \"Feature\" || !feature.geometry) {\n throw new Error(\n \"Invalid input to \" + name + \", Feature with geometry required\"\n );\n }\n if (!feature.geometry || feature.geometry.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + feature.geometry.type\n );\n }\n}\nfunction collectionOf(featureCollection, type, name) {\n if (!featureCollection) {\n throw new Error(\"No featureCollection passed\");\n }\n if (!name) {\n throw new Error(\".collectionOf() requires a name\");\n }\n if (!featureCollection || featureCollection.type !== \"FeatureCollection\") {\n throw new Error(\n \"Invalid input to \" + name + \", FeatureCollection required\"\n );\n }\n for (const feature of featureCollection.features) {\n if (!feature || feature.type !== \"Feature\" || !feature.geometry) {\n throw new Error(\n \"Invalid input to \" + name + \", Feature with geometry required\"\n );\n }\n if (!feature.geometry || feature.geometry.type !== type) {\n throw new Error(\n \"Invalid input to \" + name + \": must be a \" + type + \", given \" + feature.geometry.type\n );\n }\n }\n}\nfunction getGeom(geojson) {\n if (geojson.type === \"Feature\") {\n return geojson.geometry;\n }\n return geojson;\n}\nfunction getType(geojson, _name) {\n if (geojson.type === \"FeatureCollection\") {\n return \"FeatureCollection\";\n }\n if (geojson.type === \"GeometryCollection\") {\n return \"GeometryCollection\";\n }\n if (geojson.type === \"Feature\" && geojson.geometry !== null) {\n return geojson.geometry.type;\n }\n return geojson.type;\n}\nexport {\n collectionOf,\n containsNumber,\n featureOf,\n geojsonType,\n getCoord,\n getCoords,\n getGeom,\n getType\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport { degreesToRadians, radiansToDegrees } from \"@turf/helpers\";\nimport { getCoord } from \"@turf/invariant\";\nfunction bearing(start, end, options = {}) {\n if (options.final === true) {\n return calculateFinalBearing(start, end);\n }\n const coordinates1 = getCoord(start);\n const coordinates2 = getCoord(end);\n const lon1 = degreesToRadians(coordinates1[0]);\n const lon2 = degreesToRadians(coordinates2[0]);\n const lat1 = degreesToRadians(coordinates1[1]);\n const lat2 = degreesToRadians(coordinates2[1]);\n const a = Math.sin(lon2 - lon1) * Math.cos(lat2);\n const b = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1);\n return radiansToDegrees(Math.atan2(a, b));\n}\nfunction calculateFinalBearing(start, end) {\n let bear = bearing(end, start);\n bear = (bear + 180) % 360;\n return bear > 180 ? bear - 360 : bear;\n}\nvar index_default = bearing;\nexport {\n bearing,\n index_default as default\n};\n//# sourceMappingURL=index.js.map","// index.ts\nimport {\n degreesToRadians,\n lengthToRadians,\n point,\n radiansToDegrees\n} from \"@turf/helpers\";\nimport { getCoord } from \"@turf/invariant\";\nfunction destination(origin, distance, bearing, options = {}) {\n const coordinates1 = getCoord(origin);\n const longitude1 = degreesToRadians(coordinates1[0]);\n const latitude1 = degreesToRadians(coordinates1[1]);\n const bearingRad = degreesToRadians(bearing);\n const radians = lengthToRadians(distance, options.units);\n const latitude2 = Math.asin(\n Math.sin(latitude1) * Math.cos(radians) + Math.cos(latitude1) * Math.sin(radians) * Math.cos(bearingRad)\n );\n const longitude2 = longitude1 + Math.atan2(\n Math.sin(bearingRad) * Math.sin(radians) * Math.cos(latitude1),\n Math.cos(radians) - Math.sin(latitude1) * Math.sin(latitude2)\n );\n const lng = radiansToDegrees(longitude2);\n const lat = radiansToDegrees(latitude2);\n if (coordinates1[2] !== void 0) {\n return point([lng, lat, coordinates1[2]], options.properties);\n }\n return point([lng, lat], options.properties);\n}\nvar index_default = destination;\nexport {\n index_default as default,\n destination\n};\n//# sourceMappingURL=index.js.map","/**\n * The arithmetic the camera runs on: headings, and the spring that carries every value it eases.\n *\n * @module\n * @ignore\n */\nimport { bearing, bearingToAzimuth } from '@turf/turf';\nimport type { Position } from 'geojson';\n\n/**\n * Initial bearing from one coordinate to the other, in degrees clockwise from north within 0..360 —\n * the direction of travel, which is where the camera and the marker face.\n * @ignore\n */\nexport const headingBetween = (from: Position, to: Position): number => bearingToAzimuth(bearing(from, to));\n\n/**\n * The shorter way round between two bearings, so a flight crossing north turns by a few degrees\n * rather than the 350 the raw difference reports.\n * @ignore\n */\nexport const shortestTurn = (from: number, to: number): number => ((to - from + 540) % 360) - 180;\n\n/**\n * The heading to steer for: the road's, once it has moved off the one already being steered for by\n * more than `deadbandDegrees` — and otherwise the one already being steered for, unchanged.\n *\n * A route's vertices are metres apart, so the direction to a point a few hundred metres ahead\n * wanders by a degree or two as the flight crosses them. That is the line's sampling rather than\n * the road's shape, and a camera that answers it is a camera that never quite settles.\n * @ignore\n */\nexport const steadyBearing = (steering: number, road: number, deadbandDegrees: number): number =>\n Math.abs(shortestTurn(steering, road)) > deadbandDegrees ? road : steering;\n\n/** `value` held within `min`..`max`. @ignore */\nexport const clamp = (value: number, min: number, max: number): number => Math.min(Math.max(value, min), max);\n\n/**\n * A value in motion: where it is, and how fast it is getting there, in its own unit per second.\n * @ignore\n */\nexport type Spring = { value: number; velocity: number };\n\n/** @ignore */\nexport const springAt = (value: number): Spring => ({ value, velocity: 0 });\n\n// The spring's rate against the time it is given to settle: at this one, `settleSeconds` closes\n// 91% of a step, and twice that 99.7%.\nconst SETTLE_RATE = 4;\n\n// How close to its target, and how slow, a spring has to be to count as there. It only ever\n// approaches its target, and a value that changes by amounts nothing can show still costs the map\n// a new camera every frame.\nconst AT_REST = 1e-6;\n\n/**\n * `spring` carried `elapsedSeconds` towards `target`, critically damped: no overshoot, and a new\n * target bends the motion instead of kicking its velocity. Solved exactly, so for a target that\n * holds still, two half steps land where one whole one does, at any frame rate.\n *\n * `maxVelocity` caps the chase: the target is held no further off than the spring closes at that speed.\n * @ignore\n */\nexport const springTowards = (\n spring: Spring,\n target: number,\n elapsedSeconds: number,\n settleSeconds: number,\n maxVelocity = Number.POSITIVE_INFINITY,\n): Spring => {\n const rate = SETTLE_RATE / settleSeconds;\n const reach = (2 * maxVelocity) / rate;\n const offset = clamp(spring.value - target, -reach, reach);\n if (Math.abs(offset) < AT_REST && Math.abs(spring.velocity) < AT_REST) return springAt(target);\n\n const drift = (spring.velocity + rate * offset) * elapsedSeconds;\n const decay = Math.exp(-rate * elapsedSeconds);\n return {\n value: spring.value - offset + (offset + drift) * decay,\n velocity: (spring.velocity - rate * drift) * decay,\n };\n};\n\n/**\n * {@link springTowards} for a bearing: it takes the short way round and stays within 0..360.\n * @ignore\n */\nexport const springTowardsBearing = (\n spring: Spring,\n target: number,\n elapsedSeconds: number,\n settleSeconds: number,\n maxVelocity?: number,\n): Spring => {\n const next = springTowards(\n spring,\n spring.value + shortestTurn(spring.value, target),\n elapsedSeconds,\n settleSeconds,\n maxVelocity,\n );\n return { value: (next.value + 360) % 360, velocity: next.velocity };\n};\n","/**\n * Where the followed position sits on screen, and the zooms that fit a stretch of road into the\n * frame or lift the camera to a given height.\n *\n * @module\n */\nimport { degreesToRadians } from '@turf/turf';\nimport type { PaddingOptions } from 'maplibre-gl';\nimport { clamp } from './geodesy';\n\n/**\n * Where the position being followed sits in the view, as fractions of the viewport: `{ x: 0.5, y:\n * 0.5 }` is the middle of the screen, `y: 0.67` two thirds of the way down.\n *\n * @group Flyover\n */\nexport type FlyoverAnchor = { x: number; y: number };\n\n// How far down the frame a fully tilted camera puts the position it follows. Two thirds: the road\n// under the camera belongs in the lower third of a picture that is otherwise all of what lies\n// ahead, which is where a driver's own view of it sits.\nconst TILTED_ANCHOR_Y = 2 / 3;\n\n// The pitch at which the anchor has slid all the way to `TILTED_ANCHOR_Y`; beyond it, no further.\nconst FULLY_TILTED_DEGREES = 60;\n\n// How near the edge of the frame an anchor may put the position. A padding as deep as the viewport\n// leaves the camera nothing to centre in, and the projection stops meaning anything before that.\nconst ANCHOR_LIMIT = 0.05;\n\nconst clampFraction = (value: number): number => clamp(value, ANCHOR_LIMIT, 1 - ANCHOR_LIMIT);\n\n/**\n * Where a camera at this pitch puts the position it follows, absent an anchor of your own.\n *\n * A level map is a plan, so the position belongs in the middle. Tilt the camera and the picture\n * becomes mostly what lies *ahead*, so the position slides down with the pitch, reaching the lower\n * third at 60° — and a top-down pass stays centred without switching modes.\n *\n * @group Flyover\n */\nexport const anchorForPitch = (pitchDegrees: number): FlyoverAnchor => {\n const tilt = clamp(pitchDegrees, 0, FULLY_TILTED_DEGREES) / FULLY_TILTED_DEGREES;\n return { x: 0.5, y: 0.5 + (TILTED_ANCHOR_Y - 0.5) * tilt };\n};\n\n/**\n * The viewport padding that lands the followed position on `anchor`.\n *\n * MapLibre centres in the rectangle the padding leaves, so an anchor is padding on the side it sits\n * away from: two thirds of the way down a 600px view is 200px off the top. One side only, since\n * padding both would shrink the rectangle and take the zoom with it.\n * @ignore\n */\nexport const anchorPadding = (\n anchor: FlyoverAnchor,\n viewport: { width: number; height: number },\n): Required<PaddingOptions> => {\n const fromCentreX = clampFraction(anchor.x) - 0.5;\n const fromCentreY = clampFraction(anchor.y) - 0.5;\n // Not rounded to whole pixels: an anchor eased from one place to another would move the whole\n // view in one-pixel steps.\n const pad = (fraction: number, extent: number) => Math.max(2 * fraction * extent, 0);\n return {\n left: pad(fromCentreX, viewport.width),\n right: pad(-fromCentreX, viewport.width),\n top: pad(fromCentreY, viewport.height),\n bottom: pad(-fromCentreY, viewport.height),\n };\n};\n\n// MapLibre's world at zoom 0, in pixels: one 512-pixel tile.\nconst WORLD_PIXELS_AT_ZOOM_0 = 512;\n\n// Metres per pixel at zoom 0 on the equator: the Earth's circumference over that world, which is\n// what turns a ground distance into a zoom and back.\nconst EQUATOR_METERS_PER_PIXEL = 40_075_016.686 / WORLD_PIXELS_AT_ZOOM_0;\n\n/**\n * How much of MapLibre's mercator square — 1 unit across the world — a CSS pixel spans at `zoom`.\n * @ignore\n */\nexport const mercatorUnitsPerPixel = (zoom: number): number => 1 / (WORLD_PIXELS_AT_ZOOM_0 * 2 ** zoom);\n\nconst metersPerPixel = (zoom: number, latitudeDegrees: number): number =>\n (EQUATOR_METERS_PER_PIXEL * Math.cos(degreesToRadians(latitudeDegrees))) / 2 ** zoom;\n\nconst zoomForMetersPerPixel = (meters: number, latitudeDegrees: number): number =>\n Math.log2((EQUATOR_METERS_PER_PIXEL * Math.cos(degreesToRadians(latitudeDegrees))) / meters);\n\n/**\n * The zoom at which `distanceInMeters` of ground spans `pixels` of screen at `latitudeDegrees`.\n * @ignore\n */\nexport const zoomFitting = (distanceInMeters: number, pixels: number, latitudeDegrees: number): number =>\n zoomForMetersPerPixel(Math.max(distanceInMeters, 1) / Math.max(pixels, 1), latitudeDegrees);\n\n/**\n * What decides how far the camera is from the point it looks at, besides the zoom: MapLibre sets\n * that distance from the viewport's height and vertical field of view.\n * @ignore\n */\nexport type CameraLens = { latitudeDegrees: number; viewportHeight: number; fieldOfViewDegrees: number };\n\n// The camera's distance from the centre, in pixels: MapLibre's `cameraToCenterDistance`.\nconst cameraDistanceInPixels = ({ viewportHeight, fieldOfViewDegrees }: CameraLens): number =>\n (0.5 * viewportHeight) / Math.tan(degreesToRadians(fieldOfViewDegrees) / 2);\n\n/**\n * How far the camera is from the point it looks at, in metres, at `zoom`.\n * @ignore\n */\nexport const cameraDistanceInMeters = (zoom: number, lens: CameraLens): number =>\n cameraDistanceInPixels(lens) * metersPerPixel(zoom, lens.latitudeDegrees);\n\n/**\n * The zoom that puts the camera `distanceInMeters` from the point it looks at.\n * @ignore\n */\nexport const zoomForCameraDistance = (distanceInMeters: number, lens: CameraLens): number =>\n zoomForMetersPerPixel(Math.max(distanceInMeters, 1) / cameraDistanceInPixels(lens), lens.latitudeDegrees);\n","/**\n * What rides the route at the followed position: a MapLibre custom layer drawing one textured quad.\n *\n * - **In the map's own frame**, so map effects — depth of field included — and captures take it along.\n * - **Placed as each frame is drawn**, on the terrain under its route point. A symbol layer's source\n * goes through the worker and lands a frame or more behind the camera, which reads as wobble.\n *\n * @module\n */\nimport { generateId } from '@tomtom-org/maps-sdk/core';\nimport type { CustomImage, SVGIconStyleOptions, TomTomMap } from '@tomtom-org/maps-sdk/map';\nimport { degreesToRadians } from '@turf/turf';\nimport type { Position } from 'geojson';\nimport { type CustomLayerInterface, type CustomRenderMethodInput, MercatorCoordinate } from 'maplibre-gl';\nimport { mercatorUnitsPerPixel } from './framing';\n\n/**\n * The image that rides the route, drawn pointing up: the flight turns it to face the road.\n *\n * - `arrow` — the plugin's own arrow, recoloured with the SDK's SVG icon options. Its fill defaults\n * to `#105287`, the SDK's default route outline colour, not a theme's accent, and its outline to white.\n * - `custom` — yours: a URL, raw SVG markup, a data URI or a loaded `<img>`, as for any SDK custom\n * image. A URL on another origin needs CORS, since the image becomes a WebGL texture.\n *\n * @group Flyover\n */\nexport type FlyoverMarkerImage =\n | ({ type: 'arrow' } & SVGIconStyleOptions)\n | { type: 'custom'; image: NonNullable<CustomImage['image']> };\n\n/**\n * The marker that rides the route at the followed position, lying on the map and facing the road.\n *\n * @group Flyover\n */\nexport type FlyoverMarkerConfig = {\n /** @default true */\n visible?: boolean;\n /** @default `{ type: 'arrow' }` */\n image?: FlyoverMarkerImage;\n /** Width and height on screen, in pixels, before the pitch foreshortens it. @default 44 */\n size?: number;\n};\n\n// Lying on the map, the marker is foreshortened by the pitch: this is half as tall at 60°.\nconst DEFAULT_SIZE = 44;\n\n// The texture's side: a power of two, so it can be mipmapped — a quad seen at a steep pitch is\n// squeezed several times over, and without mipmaps it shimmers — and no more than a display needs.\nconst textureSize = (sizeInPixels: number): number =>\n Math.min(2 ** Math.ceil(Math.log2(Math.max(sizeInPixels * devicePixelRatio, 1))), 512);\n\nconst DEFAULT_ARROW_COLOR = '#105287';\n\nconst SVG_NAMESPACE = 'http://www.w3.org/2000/svg';\n\nconst svgElement = <K extends 'svg' | 'path'>(name: K, attributes: Record<string, string>) => {\n const element = document.createElementNS(SVG_NAMESPACE, name);\n for (const [attribute, value] of Object.entries(attributes)) element.setAttribute(attribute, value);\n return element;\n};\n\nconst svgDataUri = (markup: string): string => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(markup)}`;\n\n// One path, pointing up in a 24-unit box: its stroke is the outline, painted under the fill, so\n// only the outer half of it shows.\nconst arrowMarkup = (\n { fillColor = DEFAULT_ARROW_COLOR, outlineColor = 'white', outlineOpacity = 1 }: SVGIconStyleOptions,\n side: number,\n): string => {\n const svg = svgElement('svg', { viewBox: '0 0 24 24', width: String(side), height: String(side) });\n svg.append(\n svgElement('path', {\n d: 'M12 3l8 18-8-4-8 4z',\n fill: fillColor,\n stroke: outlineColor,\n 'stroke-opacity': String(outlineOpacity),\n 'stroke-width': '3',\n 'stroke-linejoin': 'round',\n 'paint-order': 'stroke',\n }),\n );\n return new XMLSerializer().serializeToString(svg);\n};\n\n/**\n * The image to rasterise: the arrow drawn at `side` pixels, or yours. Raw SVG markup is told apart\n * from a URL the way the SDK's own custom images are.\n * @ignore\n */\nexport const imageElement = (image: FlyoverMarkerImage, side: number): HTMLImageElement => {\n const source = image.type === 'arrow' ? svgDataUri(arrowMarkup(image, side)) : image.image;\n if (typeof source !== 'string') return source;\n\n const element = new Image();\n element.crossOrigin = 'anonymous';\n element.src = source.includes('<svg') ? svgDataUri(source) : source;\n return element;\n};\n\n// The image drawn into a square of `side` pixels, centred and scaled to fit: the texture's source.\nconst rasterise = async (image: FlyoverMarkerImage, side: number): Promise<HTMLCanvasElement> => {\n const element = imageElement(image, side);\n await element.decode();\n const canvas = document.createElement('canvas');\n canvas.width = side;\n canvas.height = side;\n const scale = side / Math.max(element.naturalWidth, element.naturalHeight, 1);\n const [width, height] = [element.naturalWidth * scale, element.naturalHeight * scale];\n canvas.getContext('2d')?.drawImage(element, (side - width) / 2, (side - height) / 2, width, height);\n return canvas;\n};\n\n// A quad of corners at ±1, spanned by the two axes the flight turns to face the road.\nconst VERTEX_SHADER = `\nuniform mat4 u_matrix;\nuniform vec2 u_right;\nuniform vec2 u_forward;\nattribute vec2 a_corner;\nvarying vec2 v_uv;\nvoid main() {\n v_uv = vec2(a_corner.x, -a_corner.y) * 0.5 + 0.5;\n gl_Position = u_matrix * vec4(u_right * a_corner.x + u_forward * a_corner.y, 0.0, 1.0);\n}`;\n\nconst FRAGMENT_SHADER = `\nprecision mediump float;\nuniform sampler2D u_image;\nvarying vec2 v_uv;\nvoid main() {\n gl_FragColor = texture2D(u_image, v_uv);\n}`;\n\ntype GL = WebGLRenderingContext | WebGL2RenderingContext;\n\ntype Drawing = {\n program: WebGLProgram;\n corners: WebGLBuffer;\n texture: WebGLTexture | undefined;\n uniforms: Record<'matrix' | 'right' | 'forward' | 'image', WebGLUniformLocation | null>;\n corner: number;\n};\n\nconst compile = (gl: GL, type: number, source: string): WebGLShader => {\n const shader = gl.createShader(type);\n if (!shader) throw new Error('The fly-over marker could not create a shader');\n\n gl.shaderSource(shader, source);\n gl.compileShader(shader);\n return shader;\n};\n\nconst createDrawing = (gl: GL): Drawing => {\n const program = gl.createProgram();\n const corners = gl.createBuffer();\n if (!program || !corners) throw new Error('The fly-over marker could not create its program');\n\n gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERTEX_SHADER));\n gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER));\n gl.linkProgram(program);\n gl.bindBuffer(gl.ARRAY_BUFFER, corners);\n gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1, -1, 1, -1, -1, 1, 1, 1]), gl.STATIC_DRAW);\n return {\n program,\n corners,\n texture: undefined,\n uniforms: {\n matrix: gl.getUniformLocation(program, 'u_matrix'),\n right: gl.getUniformLocation(program, 'u_right'),\n forward: gl.getUniformLocation(program, 'u_forward'),\n image: gl.getUniformLocation(program, 'u_image'),\n },\n corner: gl.getAttribLocation(program, 'a_corner'),\n };\n};\n\nconst uploadTexture = (gl: GL, raster: HTMLCanvasElement): WebGLTexture | undefined => {\n const texture = gl.createTexture() ?? undefined;\n gl.bindTexture(gl.TEXTURE_2D, texture ?? null);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true);\n gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, raster);\n gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false);\n gl.generateMipmap(gl.TEXTURE_2D);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);\n gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);\n return texture;\n};\n\n// The map's matrix with the marker's centre folded in, in double precision: the quad's own corners\n// are then a few millionths of the world across, which single precision holds exactly where a\n// centre near 0.5 would not.\nconst matrixAt = (projection: ArrayLike<number>, { x, y, z }: MercatorCoordinate): Float32Array => {\n const matrix = Float32Array.from(projection);\n for (let row = 0; row < 4; row++) {\n matrix[12 + row] =\n projection[row] * x + projection[4 + row] * y + projection[8 + row] * z + projection[12 + row];\n }\n return matrix;\n};\n\n/**\n * The marker's layer, which re-adds itself after a style change and remembers where it was last\n * put so a new image lands in its place.\n * @ignore\n */\nexport class PositionMarker implements CustomLayerInterface {\n readonly id = `tomtom-flyover-marker-${generateId()}`;\n readonly type = 'custom';\n readonly renderingMode = '2d';\n\n private config: FlyoverMarkerConfig = {};\n private placed: { position: Position; heading: number } | undefined;\n private raster: HTMLCanvasElement | undefined;\n private gl: GL | undefined;\n private drawing: Drawing | undefined;\n private removed = false;\n private readonly stopRestoring: () => void;\n\n constructor(\n private readonly map: TomTomMap,\n config: FlyoverMarkerConfig,\n ) {\n this.stopRestoring = map.addStyleChangeHandler({ onStyleChanged: () => this.install() });\n this.installWhenReady();\n this.apply(config);\n }\n\n apply(config: FlyoverMarkerConfig): void {\n this.config = config;\n const size = config.size ?? DEFAULT_SIZE;\n rasterise(config.image ?? { type: 'arrow' }, textureSize(size))\n .then((raster) => {\n // A later image may have been asked for while this one loaded.\n if (this.config !== config) return;\n\n this.raster = raster;\n this.releaseTexture();\n this.map.mapLibreMap.triggerRepaint();\n })\n .catch((error) => console.warn('The fly-over marker image could not be loaded', error));\n this.map.mapLibreMap.triggerRepaint();\n }\n\n /** Puts the marker at a position on the route, facing `heading`. */\n place(position: Position, heading: number): void {\n this.placed = { position, heading };\n this.map.mapLibreMap.triggerRepaint();\n }\n\n remove(): void {\n this.removed = true;\n this.stopRestoring();\n if (this.map.mapLibreMap.getLayer(this.id)) this.map.mapLibreMap.removeLayer(this.id);\n }\n\n onAdd(_map: unknown, gl: GL): void {\n this.gl = gl;\n this.drawing = createDrawing(gl);\n }\n\n onRemove(_map: unknown, gl: GL): void {\n this.releaseTexture();\n if (this.drawing) {\n gl.deleteProgram(this.drawing.program);\n gl.deleteBuffer(this.drawing.corners);\n }\n this.drawing = undefined;\n this.gl = undefined;\n }\n\n render(gl: GL, options: CustomRenderMethodInput): void {\n const { drawing, placed, raster } = this;\n if (!drawing || !placed || !raster || this.config.visible === false) return;\n\n drawing.texture ??= uploadTexture(gl, raster);\n const mapLibreMap = this.map.mapLibreMap;\n const [longitude, latitude] = placed.position;\n // The ground under the route point, read as the frame is drawn: whatever the camera is\n // doing, the marker is on the road.\n const ground = mapLibreMap.queryTerrainElevation([longitude, latitude]) ?? 0;\n const centre = MercatorCoordinate.fromLngLat([longitude, latitude], ground);\n const half = ((this.config.size ?? DEFAULT_SIZE) / 2) * mercatorUnitsPerPixel(mapLibreMap.getZoom());\n // Mercator y runs south, so the road's heading points up the square at -cos.\n const heading = degreesToRadians(placed.heading);\n const [sine, cosine] = [Math.sin(heading) * half, Math.cos(heading) * half];\n\n gl.useProgram(drawing.program);\n gl.uniformMatrix4fv(drawing.uniforms.matrix, false, matrixAt(options.defaultProjectionData.mainMatrix, centre));\n gl.uniform2f(drawing.uniforms.right, cosine, sine);\n gl.uniform2f(drawing.uniforms.forward, sine, -cosine);\n gl.activeTexture(gl.TEXTURE0);\n gl.bindTexture(gl.TEXTURE_2D, drawing.texture ?? null);\n gl.uniform1i(drawing.uniforms.image, 0);\n gl.bindBuffer(gl.ARRAY_BUFFER, drawing.corners);\n gl.enableVertexAttribArray(drawing.corner);\n gl.vertexAttribPointer(drawing.corner, 2, gl.FLOAT, false, 0, 0);\n // Always on top of the ground it lies on, and blended as the premultiplied texture it is.\n gl.disable(gl.DEPTH_TEST);\n gl.enable(gl.BLEND);\n gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA);\n gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);\n }\n\n private releaseTexture(): void {\n if (this.drawing?.texture) this.gl?.deleteTexture(this.drawing.texture);\n if (this.drawing) this.drawing.texture = undefined;\n }\n\n private installWhenReady(): void {\n if (this.map.mapReady) {\n this.install();\n } else {\n this.map.mapLibreMap.once('styledata', () => this.installWhenReady());\n }\n }\n\n private install(): void {\n if (!this.removed && !this.map.mapLibreMap.getLayer(this.id)) this.map.mapLibreMap.addLayer(this);\n }\n}\n","import type { Route } from '@tomtom-org/maps-sdk/core';\nimport { calculateProgressAtRoutePoint, getCoordinateAtRouteProgress } from '@tomtom-org/maps-sdk/core';\nimport type { TomTomMap } from '@tomtom-org/maps-sdk/map';\nimport { degreesToRadians, destination } from '@turf/turf';\nimport type { Position } from 'geojson';\nimport type { PaddingOptions } from 'maplibre-gl';\nimport {\n anchorForPitch,\n anchorPadding,\n type CameraLens,\n cameraDistanceInMeters,\n type FlyoverAnchor,\n zoomFitting,\n zoomForCameraDistance,\n} from './framing';\nimport {\n clamp,\n headingBetween,\n type Spring,\n springAt,\n springTowards,\n springTowardsBearing,\n steadyBearing,\n} from './geodesy';\nimport { type FlyoverMarkerConfig, PositionMarker } from './marker';\n\n/**\n * What the camera points at, which is what decides how early it turns into a bend.\n *\n * - `distance` — a fixed distance down the road. Predictable, and blind to what the road does.\n * - `points` — a number of the route's own vertices. The line is denser through bends, so the aim\n * shortens exactly where the road is turning.\n * - `instruction` — the next guidance instruction, so the camera looks at the junction it is\n * heading for. Needs a route asked for with `guidance`; without instructions it aims by distance.\n *\n * @group Flyover\n */\nexport type FlyoverAim =\n | { mode: 'distance'; metersAhead: number }\n | { mode: 'points'; pointsAhead: number }\n | { mode: 'instruction'; fallbackMetersAhead: number };\n\n/**\n * How high the camera rides. Whatever the mode, it pulls back further where terrain would otherwise\n * come between the camera and the road.\n *\n * - `fixed` — one MapLibre zoom, whatever the road does.\n * - `instructions` — framed on the junctions ahead: the zoom that fits the next N instructions (one\n * at least) in the view, so a run of hairpins pulls back and a straight closes in.\n * - `speed` — tied to the ground speed, the way a driver's eyes lift with it.\n *\n * @group Flyover\n */\nexport type FlyoverZoom =\n | { mode: 'fixed'; zoom: number }\n | { mode: 'instructions'; instructionsAhead: number }\n | { mode: 'speed' };\n\n/**\n * How the camera flies.\n *\n * @group Flyover\n */\nexport type FlyoverCamera = {\n /** Ground speed of the camera, in kilometres per hour. @default 200 */\n speedKMH: number;\n /** Camera tilt, in degrees; the map's `maxPitch` is the ceiling. @default 70 */\n pitch: number;\n /** What the camera aims at down the route. @default `{ mode: 'instruction', fallbackMetersAhead: 250 }` */\n aim: FlyoverAim;\n /** What sets the camera's height. @default `{ mode: 'instructions', instructionsAhead: 2 }` */\n zoom: FlyoverZoom;\n /**\n * Where the followed position sits on screen. Omitted, it follows the pitch — see\n * {@link anchorForPitch}.\n */\n anchor?: FlyoverAnchor;\n};\n\n/**\n * How far along the route the flight has got, reported as it advances.\n *\n * @group Flyover\n */\nexport type FlyoverProgress = {\n traveledDistanceInMeters: number;\n traveledTimeInSeconds: number;\n};\n\n/**\n * @group Flyover\n */\nexport type RouteFlyoverOptions = {\n /** The route to fly. Replace it later with {@link RouteFlyover.setRoute}. */\n route: Route;\n /** Overrides for the camera; anything left out takes the default on {@link FlyoverCamera}. */\n camera?: Partial<FlyoverCamera>;\n /** The marker riding the route at the followed position; an arrow by default. */\n marker?: FlyoverMarkerConfig;\n /** Called with the flight's progress, once per survey rather than once per frame. */\n onProgress?: (progress: FlyoverProgress) => void;\n};\n\nconst DEFAULT_CAMERA: FlyoverCamera = {\n speedKMH: 200,\n pitch: 70,\n aim: { mode: 'instruction', fallbackMetersAhead: 250 },\n zoom: { mode: 'instructions', instructionsAhead: 2 },\n};\n\n/**\n * How long each eased value takes to settle onto a new target, in seconds — see\n * {@link springTowards}. The altitude is the quickest, so what the camera looks at stays on the road.\n */\nconst SETTLE_SECONDS = {\n speed: 2,\n bearing: 2,\n zoom: 3,\n pitch: 1.5,\n anchor: 1.5,\n elevation: 0.6,\n markerHeading: 0.5,\n} as const;\n\n/**\n * How far the road's direction has to move before the camera adopts a new heading, in degrees (see\n * {@link steadyBearing}), and the fastest it may then turn, in degrees per second. The rate cap\n * covers a genuine change of target, such as the next junction becoming the current one.\n */\nconst BEARING_DEADBAND_DEGREES = 2.5;\nconst MAX_TURN_DEGREES_PER_SECOND = 22;\n\n// How much the framing has to want before the zoom follows it. A junction the camera is closing on\n// asks for a little more zoom every survey, and a map that re-tiles for every tenth of a level\n// spends the flight loading tiles it is about to leave.\nconst ZOOM_DEADBAND = 0.2;\n\n/**\n * How often the camera works out *where it is going*, in seconds — as opposed to where it is,\n * which it reads every frame.\n *\n * The route lookups behind the aim, the framing, the viewport and the terrain clearance all live on\n * this tick: their answers feed targets the camera eases toward anyway, and a target recomputed\n * five times a second cannot jitter at sixty.\n */\nconst SURVEY_INTERVAL_SECONDS = 0.2;\n\n/**\n * The longest frame the flight will act on: a backgrounded tab hands back one frame worth many\n * seconds, and without this the camera teleports down the route on return.\n *\n * Loose enough not to punish a slow renderer, which is the other thing a long frame means. Any\n * frame over the cap advances the flight by less than its own time, so a clamp at a tenth of a\n * second would fly a machine rendering at 5fps down the route at half speed.\n */\nconst MAX_FRAME_SECONDS = 0.25;\n\n// How high the camera keeps above the ground under it. Along its line of sight the margin narrows\n// to nothing at the road, which is where the camera is looking.\nconst CLEARANCE_METERS = 200;\n// Samples of the ground from under the camera towards the road, the road itself excluded.\nconst CLEARANCE_SAMPLES = 4;\n// The steepest pitch the camera's geometry is worked out for: towards 90° the camera needs an\n// unbounded distance to gain any height, and sees the road ahead foreshortened to nothing.\nconst MAX_WORKED_PITCH_DEGREES = 85;\n\n// Either side of the followed position, the stretch of road the marker takes its heading from:\n// long enough to span a vertex or two, short enough to still be the road under it.\nconst HEADING_SPAN_METERS = 15;\n\n// The zooms the automatic modes may choose between: closer than this the road fills the screen,\n// further out it stops reading as a flight.\nconst MIN_ZOOM = 11;\nconst MAX_ZOOM = 15;\n\n// What the framing modes fit their target into: a share of the viewport, since a junction pinned\n// to the very edge of the screen is one the camera is already passing.\nconst FRAMED_SHARE_OF_VIEWPORT = 0.6;\n\n// The speed the `speed` mode holds `MAX_ZOOM - 1` at, and how far it pulls back per doubling of it:\n// twice the speed needs about twice the road in shot, which is one zoom level.\nconst SPEED_REFERENCE_KMH = 90;\nconst ZOOM_PER_SPEED_DOUBLING = 1;\n\nconst clampZoom = (zoom: number): number => clamp(zoom, MIN_ZOOM, MAX_ZOOM);\n\n/** The values the camera eases, each towards the target the last survey or setting gave it. */\ntype Motion = Record<'bearing' | 'zoom' | 'pitch' | 'anchorX' | 'anchorY' | 'elevation' | 'markerHeading', Spring>;\n\n/**\n * A camera that flies a route on a loop: it advances a distance along the line every frame, reads\n * the coordinate the route has at that distance, and points itself at where it will be next.\n *\n * @remarks\n * The route is the only geometry it needs — no path of camera keyframes to author, and no\n * animation to re-time when the route changes. What it adds on top of following the line:\n *\n * - **it aims ahead** rather than at its own position, by distance, by route vertices or at the\n * next guidance instruction, so the view turns into a bend before the camera reaches it;\n * - **it frames** what is coming: the zoom that fits the next junctions, or one tied to the speed;\n * - **it clears the ground**, pulling back where terrain would come between the camera and the road;\n * - **it parks the position where you want it on screen** — see {@link FlyoverAnchor} — and marks\n * it with an arrow, or an image of your own, facing the road;\n * - **it moves like a camera**: every value it changes eases on a critically damped spring, so a new\n * heading, zoom or setting bends the motion rather than kicking it, and a start ramps the speed.\n *\n * While it runs, the flight owns the altitude of the point the camera looks at — the ground under\n * the followed position — so it takes MapLibre off clamping the centre to the ground, and gives\n * that back when it stops.\n *\n * @example\n * ```typescript\n * import { RouteFlyover } from '@tomtom-org/maps-sdk-plugin-flyover';\n *\n * const [route] = (await calculateRoute({ locations, guidance: { type: 'coded' } })).features;\n * const flyover = new RouteFlyover(map, { route, camera: { speedKMH: 300, pitch: 76 } });\n * flyover.start();\n * ```\n *\n * @group Flyover\n */\nexport class RouteFlyover {\n private route: Route;\n private camera: FlyoverCamera;\n private readonly onProgress: (progress: FlyoverProgress) => void;\n private readonly marker: PositionMarker;\n\n private animationFrame: number | undefined;\n private lastFrameTime = 0;\n private traveledDistanceInMeters = 0;\n private speedKMH = springAt(0);\n private motion: Motion;\n // What the last survey decided the camera should become; the flight is the motion closing on it.\n private readonly target: Record<'bearing' | 'zoom' | 'elevation' | 'markerHeading', number>;\n // The zoom the framing mode last asked for, before the terrain has its say.\n private framedZoom: number;\n private secondsSinceSurvey = 0;\n // Read when the map resizes rather than in flight: asking an element for its size mid-flight is\n // a layout the browser has to do before it can answer.\n private viewport = { width: 1, height: 1 };\n // Whether the camera is on the route at all: until it is, the page owns the view.\n private flying = false;\n // The page's own camera settings, handed back when the flight lets go of them.\n private pageCenterClampedToGround = true;\n private pagePadding: PaddingOptions | undefined;\n\n constructor(\n private readonly map: TomTomMap,\n options: RouteFlyoverOptions,\n ) {\n this.route = options.route;\n this.camera = { ...DEFAULT_CAMERA, ...options.camera };\n this.onProgress = options.onProgress ?? (() => {});\n const mapLibreMap = map.mapLibreMap;\n const anchor = this.targetAnchor();\n this.motion = {\n bearing: springAt(mapLibreMap.getBearing()),\n zoom: springAt(mapLibreMap.getZoom()),\n pitch: springAt(mapLibreMap.getPitch()),\n anchorX: springAt(anchor.x),\n anchorY: springAt(anchor.y),\n elevation: springAt(mapLibreMap.getCenterElevation()),\n markerHeading: springAt(0),\n };\n this.target = {\n bearing: this.motion.bearing.value,\n zoom: this.motion.zoom.value,\n elevation: this.motion.elevation.value,\n markerHeading: 0,\n };\n this.framedZoom = this.target.zoom;\n this.readViewport();\n mapLibreMap.on('resize', this.readViewport);\n this.marker = new PositionMarker(map, options.marker ?? {});\n this.placeMarkerOutright();\n }\n\n /** Whether the flight is advancing. */\n get running(): boolean {\n return this.animationFrame !== undefined;\n }\n\n /** Starts, or resumes, the flight, ramping up to its speed. */\n start(): void {\n if (this.running) return;\n\n const mapLibreMap = this.map.mapLibreMap;\n this.pageCenterClampedToGround = mapLibreMap.getCenterClampedToGround();\n mapLibreMap.setCenterClampedToGround(false);\n this.speedKMH = springAt(0);\n this.lastFrameTime = performance.now();\n this.animationFrame = requestAnimationFrame(this.step);\n }\n\n /**\n * Stops the flight where it is; {@link start} resumes from there. The centre clamp goes back to\n * what the map had before {@link start}.\n */\n stop(): void {\n if (this.animationFrame === undefined) return;\n\n cancelAnimationFrame(this.animationFrame);\n this.animationFrame = undefined;\n this.map.mapLibreMap.setCenterClampedToGround(this.pageCenterClampedToGround);\n }\n\n /** Puts a new route under the flight, from its departure point. */\n setRoute(route: Route): void {\n this.route = route;\n this.traveledDistanceInMeters = 0;\n this.flying = false;\n if (this.running) {\n this.placeCamera();\n } else {\n this.placeMarkerOutright();\n }\n }\n\n /** Applies camera settings — speed, pitch, aim, framing, anchor — paused or under way. */\n setCamera(camera: Partial<FlyoverCamera>): void {\n this.camera = { ...this.camera, ...camera };\n // Before the first frame the page still owns the view — that is how a whole route can be\n // shown before anyone flies it — so this only reframes a flight already on the line.\n if (this.flying && !this.running) this.placeCamera();\n }\n\n /** The camera the flight is using, including the defaults it was not given. */\n getCamera(): FlyoverCamera {\n return { ...this.camera };\n }\n\n /** Replaces the marker riding the route, or hides it with `visible: false`. */\n setMarker(marker: FlyoverMarkerConfig): void {\n this.marker.apply(marker);\n }\n\n /** Moves the flight to a point along the route, as a share of its length within 0..1. */\n seek(fraction: number): void {\n this.traveledDistanceInMeters = this.routeLengthInMeters() * clamp(fraction, 0, 1);\n this.placeCamera();\n }\n\n /**\n * Stops the flight and gives the map back: the marker leaves, and the padding returns to what\n * the map had before the flight first placed the camera.\n */\n remove(): void {\n this.stop();\n this.marker.remove();\n const mapLibreMap = this.map.mapLibreMap;\n mapLibreMap.off('resize', this.readViewport);\n if (this.pagePadding) mapLibreMap.setPadding(this.pagePadding);\n }\n\n private readonly readViewport = (): void => {\n const canvas = this.map.mapLibreMap.getCanvas();\n this.viewport = { width: Math.max(canvas.clientWidth, 1), height: Math.max(canvas.clientHeight, 1) };\n };\n\n private routeLengthInMeters(): number {\n return this.route.properties.summary.lengthInMeters;\n }\n\n private readonly step = (frameTime: number): void => {\n // A frame's time is its vsync, which can precede the `performance.now()` that `start` read.\n const elapsedSeconds = clamp((frameTime - this.lastFrameTime) / 1000, 0, MAX_FRAME_SECONDS);\n this.lastFrameTime = frameTime;\n\n const previousSpeedKMH = this.speedKMH.value;\n this.speedKMH = springTowards(this.speedKMH, this.camera.speedKMH, elapsedSeconds, SETTLE_SECONDS.speed);\n const metersPerSecond = (previousSpeedKMH + this.speedKMH.value) / 2 / 3.6;\n // Wrapping rather than stopping is what makes the flight endless: the arrival is also the\n // departure, and a loop over a round trip never lands.\n this.traveledDistanceInMeters = this.wrapped(this.traveledDistanceInMeters + metersPerSecond * elapsedSeconds);\n\n this.placeCamera(elapsedSeconds);\n this.animationFrame = requestAnimationFrame(this.step);\n };\n\n private coordinateAt(traveledDistanceInMeters: number) {\n return getCoordinateAtRouteProgress(this.route, { traveledDistanceInMeters });\n }\n\n // The position on the route a given distance from where the flight currently is, wrapped into\n // the route the way the flight itself wraps.\n private positionAt(offsetInMeters: number): Position | undefined {\n return this.coordinateAt(this.wrapped(this.traveledDistanceInMeters + offsetInMeters))?.position;\n }\n\n private wrapped(distanceInMeters: number): number {\n const routeLength = this.routeLengthInMeters();\n return routeLength > 0 ? ((distanceInMeters % routeLength) + routeLength) % routeLength : 0;\n }\n\n // The direction of the road under the followed position, which the marker faces. Not wrapped:\n // at the departure, the road behind is the departure itself rather than the far end of the loop.\n private roadHeading(): number | undefined {\n const [behind, ahead] = [-HEADING_SPAN_METERS, HEADING_SPAN_METERS].map(\n (offsetInMeters) => this.coordinateAt(this.traveledDistanceInMeters + offsetInMeters)?.position,\n );\n return behind && ahead ? headingBetween(behind, ahead) : undefined;\n }\n\n private instructions() {\n return this.route.properties.guidance?.instructions ?? [];\n }\n\n /**\n * The instruction the flight is heading for — the first one still ahead of it, and the first of\n * all once the loop has passed the last. `undefined` when the route carries no guidance.\n */\n private instructionAhead(skip = 0) {\n const instructions = this.instructions();\n if (!instructions.length) return undefined;\n\n const next = instructions.findIndex(\n (instruction) => instruction.routeOffsetInMeters > this.traveledDistanceInMeters,\n );\n const from = next === -1 ? 0 : next;\n return instructions[Math.min(from + skip, instructions.length - 1)];\n }\n\n // The route vertex the flight is standing on, found by bisecting the progress the route\n // carries per point rather than by measuring the line again.\n private pointIndexHere(): number {\n let low = 0;\n let high = this.route.geometry.coordinates.length - 1;\n while (low < high) {\n const middle = Math.floor((low + high) / 2);\n const progress = calculateProgressAtRoutePoint(this.route, middle);\n if (progress !== undefined && progress.distanceInMeters < this.traveledDistanceInMeters) {\n low = middle + 1;\n } else {\n high = middle;\n }\n }\n return low;\n }\n\n /** Where the camera looks: the point the current {@link FlyoverAim} picks out. */\n private aimPosition(): Position | undefined {\n const aim = this.camera.aim;\n switch (aim.mode) {\n case 'distance':\n return this.positionAt(aim.metersAhead);\n case 'points': {\n const coordinates = this.route.geometry.coordinates;\n // Wrapped, so the aim crosses the loop's seam rather than pinning to the last vertex.\n return coordinates[(this.pointIndexHere() + aim.pointsAhead) % coordinates.length];\n }\n case 'instruction': {\n // Through the instruction's distance along the route rather than its own\n // `maneuverPoint`: the flight already speaks in distances, and the junction the\n // router names sits a few metres off the sampled line.\n const instruction = this.instructionAhead();\n const ahead =\n instruction && this.wrapped(instruction.routeOffsetInMeters - this.traveledDistanceInMeters);\n return ahead === undefined ? this.positionAt(aim.fallbackMetersAhead) : this.positionAt(ahead);\n }\n }\n }\n\n /** The zoom the current {@link FlyoverZoom} asks for at this point of the route. */\n private modeZoom(latitudeDegrees: number): number {\n const zoom = this.camera.zoom;\n if (zoom.mode === 'fixed') return zoom.zoom;\n\n if (zoom.mode === 'speed') {\n const doublings = Math.log2(Math.max(this.camera.speedKMH, 1) / SPEED_REFERENCE_KMH);\n return clampZoom(MAX_ZOOM - 1 - doublings * ZOOM_PER_SPEED_DOUBLING);\n }\n\n // Framed on the junctions ahead: the road still to come before the Nth of them, fitted into\n // the share of the viewport a flight wants it to occupy. A tilted camera sees that road\n // foreshortened, by the cosine of the pitch.\n const target = this.instructionAhead(Math.max(zoom.instructionsAhead, 1) - 1);\n if (!target) return clampZoom(MAX_ZOOM - 1);\n\n const ahead = this.wrapped(target.routeOffsetInMeters - this.traveledDistanceInMeters);\n const foreshortened = ahead * Math.cos(degreesToRadians(Math.min(this.camera.pitch, MAX_WORKED_PITCH_DEGREES)));\n return clampZoom(zoomFitting(foreshortened, this.viewport.height * FRAMED_SHARE_OF_VIEWPORT, latitudeDegrees));\n }\n\n /**\n * The closest zoom at which nothing between the camera and the road stands higher than the line\n * of sight.\n *\n * The camera trails the road along the bearing, so its ground track is sampled from under the\n * camera towards the followed position. A sample a share `t` of the way along needs the camera\n * `(ground − road) / (1 − t)` higher than the road, on top of a clearance that narrows the same\n * way — which puts the camera itself `CLEARANCE_METERS` over the ground beneath it.\n */\n private clearanceZoom(here: Position, road: number, lens: CameraLens): number {\n const pitch = degreesToRadians(Math.min(this.camera.pitch, MAX_WORKED_PITCH_DEGREES));\n const trailingInKilometers = (cameraDistanceInMeters(this.motion.zoom.value, lens) * Math.sin(pitch)) / 1000;\n const behind = this.motion.bearing.value + 180;\n let height = CLEARANCE_METERS;\n for (let sample = 0; sample < CLEARANCE_SAMPLES; sample++) {\n const share = sample / CLEARANCE_SAMPLES;\n const point = destination(here, trailingInKilometers * (1 - share), behind).geometry.coordinates;\n const ground = this.groundAt(point);\n if (ground !== undefined) height = Math.max(height, CLEARANCE_METERS + (ground - road) / (1 - share));\n }\n return zoomForCameraDistance(height / Math.cos(pitch), lens);\n }\n\n // Terrain elevations come back exaggerated, as the rendered mesh is, and `null` wherever the map\n // has no terrain or a tile has yet to load.\n private groundAt([longitude, latitude]: Position): number | undefined {\n return this.map.mapLibreMap.queryTerrainElevation([longitude, latitude]) ?? undefined;\n }\n\n /**\n * Where the camera is going: the heading the road asks for, the zoom the framing and the\n * terrain allow, and the ground the followed position is on.\n *\n * Everything in here costs a walk of the route or a question to the terrain, and none of it is\n * worth asking every frame — see {@link SURVEY_INTERVAL_SECONDS}.\n */\n private survey(here: Position): void {\n const lens: CameraLens = {\n latitudeDegrees: here[1],\n viewportHeight: this.viewport.height,\n fieldOfViewDegrees: this.map.mapLibreMap.getVerticalFieldOfView(),\n };\n\n const ahead = this.aimPosition();\n if (ahead) {\n this.target.bearing = steadyBearing(\n this.target.bearing,\n headingBetween(here, ahead),\n BEARING_DEADBAND_DEGREES,\n );\n }\n this.target.markerHeading = this.roadHeading() ?? this.target.markerHeading;\n\n // The deadband belongs to the framing mode alone, whose target creeps up every survey as\n // the junction it frames comes closer. The other two follow a caller's own figure, and a\n // value someone chose has to land whatever its size.\n const framed = this.modeZoom(lens.latitudeDegrees);\n const deadband = this.camera.zoom.mode === 'instructions' ? ZOOM_DEADBAND : 0;\n if (Math.abs(framed - this.framedZoom) > deadband) this.framedZoom = framed;\n\n const road = this.groundAt(here);\n if (road === undefined) {\n // A map without terrain is flat at sea level; a tile yet to load keeps the last height.\n if (!this.map.mapLibreMap.getTerrain()) this.target.elevation = 0;\n\n this.target.zoom = this.framedZoom;\n } else {\n this.target.elevation = road;\n this.target.zoom = Math.min(this.framedZoom, this.clearanceZoom(here, road, lens));\n }\n }\n\n private targetAnchor(): FlyoverAnchor {\n return this.camera.anchor ?? anchorForPitch(this.camera.pitch);\n }\n\n // A frame's step of every value towards its target, with the turn additionally capped so no\n // second can swing the view further than a passenger's head would.\n private ease(elapsedSeconds: number): void {\n const { motion, target } = this;\n const anchor = this.targetAnchor();\n const towards = (spring: Spring, value: number, settleSeconds: number) =>\n springTowards(spring, value, elapsedSeconds, settleSeconds);\n this.motion = {\n bearing: springTowardsBearing(\n motion.bearing,\n target.bearing,\n elapsedSeconds,\n SETTLE_SECONDS.bearing,\n MAX_TURN_DEGREES_PER_SECOND,\n ),\n markerHeading: springTowardsBearing(\n motion.markerHeading,\n target.markerHeading,\n elapsedSeconds,\n SETTLE_SECONDS.markerHeading,\n ),\n zoom: towards(motion.zoom, target.zoom, SETTLE_SECONDS.zoom),\n elevation: towards(motion.elevation, target.elevation, SETTLE_SECONDS.elevation),\n pitch: towards(motion.pitch, this.camera.pitch, SETTLE_SECONDS.pitch),\n anchorX: towards(motion.anchorX, anchor.x, SETTLE_SECONDS.anchor),\n anchorY: towards(motion.anchorY, anchor.y, SETTLE_SECONDS.anchor),\n };\n }\n\n // Every value put at its target outright, at rest: there is no motion to be smooth about.\n private settle(): void {\n const anchor = this.targetAnchor();\n this.motion = {\n bearing: springAt(this.target.bearing),\n markerHeading: springAt(this.target.markerHeading),\n zoom: springAt(this.target.zoom),\n elevation: springAt(this.target.elevation),\n pitch: springAt(this.camera.pitch),\n anchorX: springAt(anchor.x),\n anchorY: springAt(anchor.y),\n };\n }\n\n // Where the page can see the flight will start, before the camera is on the route.\n private placeMarkerOutright(): void {\n const here = this.positionAt(0);\n this.target.markerHeading = this.roadHeading() ?? this.target.markerHeading;\n this.motion.markerHeading = springAt(this.target.markerHeading);\n if (here) this.marker.place(here, this.target.markerHeading);\n }\n\n /**\n * One frame: where the flight has got to, and a step of the camera towards what the last survey\n * decided. Without `elapsedSeconds` — a seek, a new route, a changed setting — the camera is\n * surveyed and placed at its targets outright.\n */\n private placeCamera(elapsedSeconds?: number): void {\n const here = this.coordinateAt(this.traveledDistanceInMeters);\n if (!here) return;\n\n const smooth = elapsedSeconds !== undefined && this.flying;\n this.secondsSinceSurvey += elapsedSeconds ?? 0;\n const surveying = !smooth || this.secondsSinceSurvey >= SURVEY_INTERVAL_SECONDS;\n if (surveying) {\n this.secondsSinceSurvey = 0;\n this.survey(here.position);\n }\n if (smooth) {\n this.ease(elapsedSeconds);\n } else {\n this.settle();\n }\n this.flying = true;\n\n const [longitude, latitude] = here.position;\n const { bearing, zoom, pitch, elevation, anchorX, anchorY, markerHeading } = this.motion;\n const mapLibreMap = this.map.mapLibreMap;\n this.pagePadding ??= mapLibreMap.getPadding();\n // The centre is the followed position on the ground, so the anchor puts the road itself on\n // screen where it asked — not a point above it.\n mapLibreMap.jumpTo({\n center: [longitude, latitude],\n elevation: elevation.value,\n bearing: bearing.value,\n pitch: pitch.value,\n zoom: zoom.value,\n padding: anchorPadding({ x: anchorX.value, y: anchorY.value }, this.viewport),\n });\n this.marker.place(here.position, markerHeading.value);\n\n if (surveying) {\n this.onProgress({\n traveledDistanceInMeters: here.distanceInMeters,\n traveledTimeInSeconds: here.travelTimeInSeconds,\n });\n }\n }\n}\n"],"ignoreList":[0,1,2,3],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@tomtom-org/maps-sdk-plugin-flyover",
|
|
3
3
|
"author": "TomTom International B.V.",
|
|
4
4
|
"license": "See in LICENSE.txt",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.3",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "A camera that flies a route on a TomTomMap: it follows the line, aims at the junctions ahead, frames them, clears the terrain it crosses, and parks the followed position where you want it on screen",
|
|
8
8
|
"types": "dist/index.d.ts",
|
|
@@ -10,21 +10,20 @@
|
|
|
10
10
|
"@turf/turf": "^7.4.0"
|
|
11
11
|
},
|
|
12
12
|
"peerDependencies": {
|
|
13
|
-
"@tomtom-org/maps-sdk": ">=0.
|
|
13
|
+
"@tomtom-org/maps-sdk": ">=0.64.0 <1.0.0",
|
|
14
14
|
"maplibre-gl": "^6.11.2"
|
|
15
15
|
},
|
|
16
16
|
"devDependencies": {
|
|
17
17
|
"@tsconfig/recommended": "^1.0.13",
|
|
18
18
|
"@types/geojson": "^7946.0.16",
|
|
19
|
-
"@types/node": "^26.6.
|
|
19
|
+
"@types/node": "^26.6.4",
|
|
20
20
|
"@vitest/coverage-v8": "^4.1.11",
|
|
21
21
|
"jsdom": "^29.1.1",
|
|
22
|
-
"rolldown": "^1.2.
|
|
23
|
-
"ts-node": "^10.9.2",
|
|
22
|
+
"rolldown": "^1.2.12",
|
|
24
23
|
"typescript": "^6.0.3",
|
|
25
|
-
"vite": "^8.3.
|
|
24
|
+
"vite": "^8.3.2",
|
|
26
25
|
"vitest": "^4.1.11",
|
|
27
|
-
"@tomtom-org/maps-sdk": "0.
|
|
26
|
+
"@tomtom-org/maps-sdk": "0.64.0"
|
|
28
27
|
},
|
|
29
28
|
"files": [
|
|
30
29
|
"dist",
|