Compare commits
381 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
af9f26b9d3 | ||
|
|
6704cf3ace | ||
|
|
afa96df55d | ||
|
|
a21470f6b4 | ||
|
|
b71e8ebb73 | ||
|
|
bdf969cc95 | ||
|
|
5357b4fced | ||
|
|
e683c0a0c5 | ||
|
|
ad77143417 | ||
|
|
cf1191505e | ||
|
|
3909938a70 | ||
|
|
e9e75cd8af | ||
|
|
2d7e55e26a | ||
|
|
dc957ec104 | ||
|
|
a30a92a963 | ||
|
|
10c7f08fa4 | ||
|
|
c196f2f7b0 | ||
|
|
0034d6f206 | ||
|
|
b4e51ed841 | ||
|
|
72907566bb | ||
|
|
612910bcd2 | ||
|
|
8e6b5ce0bf | ||
|
|
a073c5ab1e | ||
|
|
5e8102bbd1 | ||
|
|
23c2e1062c | ||
|
|
7dd42ef19f | ||
|
|
061ccb987b | ||
|
|
001128c98e | ||
|
|
27169c2942 | ||
|
|
2b863793d0 | ||
|
|
82dd764d13 | ||
|
|
7b55fe381e | ||
|
|
3c0ff15414 | ||
|
|
2401e0af00 | ||
|
|
f66b44068c | ||
|
|
25fcc6b604 | ||
|
|
820984d0eb | ||
|
|
ea12f7945f | ||
|
|
7334951056 | ||
|
|
78202c78b9 | ||
|
|
bfca3cf713 | ||
|
|
929b9f4068 | ||
|
|
149ca7efad | ||
|
|
9af1e223af | ||
|
|
cdafc4fa05 | ||
|
|
2c7f58dbed | ||
|
|
6013763480 | ||
|
|
99a1641afe | ||
|
|
e3ad05f805 | ||
|
|
660845f5b8 | ||
|
|
098885a006 | ||
|
|
48b79cf7d1 | ||
|
|
a35794ae97 | ||
|
|
3d8495742b | ||
|
|
190cd202a1 | ||
|
|
0c9324f398 | ||
|
|
67c37c2163 | ||
|
|
3b760dc308 | ||
|
|
b560dab513 | ||
|
|
f6bbf414a8 | ||
|
|
f6ede1a9c0 | ||
|
|
516a75ae22 | ||
|
|
bad0ff38ca | ||
|
|
772741cd0e | ||
|
|
c92f72ac3f | ||
|
|
e583e0775d | ||
|
|
6ca6c70aac | ||
|
|
8222118fb1 | ||
|
|
2537da690a | ||
|
|
b1b01cf943 | ||
|
|
0a7f7db817 | ||
|
|
b39e12406a | ||
|
|
dd44ecf185 | ||
|
|
a6a5b72c57 | ||
|
|
1020c384bb | ||
|
|
f89b32de66 | ||
|
|
f24ecf7b2c | ||
|
|
0366bca7c1 | ||
|
|
597e3a6bca | ||
|
|
03ea8911f6 | ||
|
|
83a5cd63c6 | ||
|
|
2d8782b1da | ||
|
|
f50ae44150 | ||
|
|
6aa398cbec | ||
|
|
fbe7b0bd78 | ||
|
|
98e8bec370 | ||
|
|
8f46bb563b | ||
|
|
c3d9ac6265 | ||
|
|
60dd85fa08 | ||
|
|
d70b3aa3d5 | ||
|
|
576f50331b | ||
|
|
5113d70701 | ||
|
|
0e1af6836f | ||
|
|
828becf68e | ||
|
|
941df3b270 | ||
|
|
28f390828d | ||
|
|
ade9ac8857 | ||
|
|
bc1be50cbc | ||
|
|
30ac91c4ae | ||
|
|
b9bfe45d86 | ||
|
|
3f7f999db0 | ||
|
|
6dcc54bfb8 | ||
|
|
f80c175263 | ||
|
|
3217bf1316 | ||
|
|
10c3330580 | ||
|
|
06f6ba13c8 | ||
|
|
bb1ca76bcc | ||
|
|
a93edb338b | ||
|
|
56edbb2d73 | ||
|
|
3f5022630b | ||
|
|
8b76f52652 | ||
|
|
717b8ad0cf | ||
|
|
f896d9fb03 | ||
|
|
3944a5a038 | ||
|
|
c7f2399e7f | ||
|
|
306a773e61 | ||
|
|
6e4351f119 | ||
|
|
7c23c501c3 | ||
|
|
98ddf7a147 | ||
|
|
6969ee6275 | ||
|
|
1331131e04 | ||
|
|
b70c9a70f8 | ||
|
|
0fdfed439c | ||
|
|
3512638900 | ||
|
|
124d00daf2 | ||
|
|
2b6e767840 | ||
|
|
d61da37191 | ||
|
|
7dd6bb7c1b | ||
|
|
274135db2e | ||
|
|
5ab7f5dacf | ||
|
|
d8b2e452d5 | ||
|
|
bbc8870832 | ||
|
|
52d840b35d | ||
|
|
24f9e1c6ac | ||
|
|
fbb0364ea5 | ||
|
|
f942c482d8 | ||
|
|
7bf44078e1 | ||
|
|
d5b3d27e62 | ||
|
|
3674ec5481 | ||
|
|
0af85f7396 | ||
|
|
9fe84e4dba | ||
|
|
dca694ccc9 | ||
|
|
ca6288a800 | ||
|
|
d85b5065f5 | ||
|
|
bb0c6635a2 | ||
|
|
21b2d8aa48 | ||
|
|
7bc7affccc | ||
|
|
47ce1490f8 | ||
|
|
e5131f811f | ||
|
|
3c1ad1fc1d | ||
|
|
406c38403b | ||
|
|
859bf338a2 | ||
|
|
873cccc4f3 | ||
|
|
f2477622c3 | ||
|
|
058ca59233 | ||
|
|
022caae848 | ||
|
|
092a199757 | ||
|
|
0b7b979c89 | ||
|
|
4ca966bd92 | ||
|
|
f448d7179a | ||
|
|
7299c0dd04 | ||
|
|
2f35a3be75 | ||
|
|
5f283496ed | ||
|
|
e59aa02a15 | ||
|
|
4fa454f2b3 | ||
|
|
ed10ac168b | ||
|
|
5e51e397bb | ||
|
|
e099257461 | ||
|
|
dd480feda1 | ||
|
|
9f76ec197a | ||
|
|
a1386e3a1f | ||
|
|
7b4e522e8f | ||
|
|
e6a2d4b235 | ||
|
|
85d84a0c40 | ||
|
|
7c22b5a238 | ||
|
|
230c6b011e | ||
|
|
c5fd013e31 | ||
|
|
6019a61cca | ||
|
|
831c5f8c69 | ||
|
|
ed748bf173 | ||
|
|
51b4ee3137 | ||
|
|
686b4dbdc8 | ||
|
|
c1f40b4d98 | ||
|
|
5424e3eae2 | ||
|
|
ad476f6086 | ||
|
|
9ef220d5de | ||
|
|
d4b76299e7 | ||
|
|
2123d19926 | ||
|
|
0613444f12 | ||
|
|
80c36b298d | ||
|
|
365e14148a | ||
|
|
a8dcadbd99 | ||
|
|
601023acf4 | ||
|
|
1d4744b730 | ||
|
|
2d57e795e7 | ||
|
|
8c3da1167c | ||
|
|
9d0b139432 | ||
|
|
408eac2524 | ||
|
|
737c30fedc | ||
|
|
c4d864ed45 | ||
|
|
315db526c9 | ||
|
|
a34b22f1d9 | ||
|
|
6d16fe8405 | ||
|
|
21fcb4621b | ||
|
|
29387a2cbb | ||
|
|
40d44db85e | ||
|
|
7a37231a87 | ||
|
|
d2b26e198b | ||
|
|
24b252bb1d | ||
|
|
6384b83cc1 | ||
|
|
88f958286b | ||
|
|
f59f4e2d56 | ||
|
|
9f6b021000 | ||
|
|
3014df1677 | ||
|
|
1d7d7a7eef | ||
|
|
457e7216b4 | ||
|
|
ce6d7bd55c | ||
|
|
730a0dbe09 | ||
|
|
5ba978311e | ||
|
|
d28be3c160 | ||
|
|
6370175b28 | ||
|
|
e484eca1a1 | ||
|
|
bc8499472c | ||
|
|
47ed2963c4 | ||
|
|
203f557883 | ||
|
|
b8e34ef5bf | ||
|
|
0fd336daa5 | ||
|
|
e235ad0708 | ||
|
|
a072e3acea | ||
|
|
ac2b58c554 | ||
|
|
633396e7df | ||
|
|
ba84cfad61 | ||
|
|
f18b78d2da | ||
|
|
df1d0ac4a0 | ||
|
|
a651667f24 | ||
|
|
b695320dcf | ||
|
|
10a624db3c | ||
|
|
591e5ce01b | ||
|
|
e9227a9bc1 | ||
|
|
59f14eaa2e | ||
|
|
a03cfa35ed | ||
|
|
adcf57ef20 | ||
|
|
b25fc6741e | ||
|
|
01b3f9a97b | ||
|
|
9039e2629b | ||
|
|
737f3a5066 | ||
|
|
e02ede8f76 | ||
|
|
58ef1ab166 | ||
|
|
89b5adc601 | ||
|
|
c987b1f1b0 | ||
|
|
6ae6d0b835 | ||
|
|
c2505f938e | ||
|
|
6b9a35b7b9 | ||
|
|
df8c0f3e6f | ||
|
|
bf2a7bd21a | ||
|
|
ee8ec6f03a | ||
|
|
b46b250d75 | ||
|
|
2dec296aae | ||
|
|
646ecfd405 | ||
|
|
99d9a9ff86 | ||
|
|
15beb1f2c6 | ||
|
|
b613e8e77f | ||
|
|
ccafb4b91d | ||
|
|
1b4ed08bc5 | ||
|
|
cc561dab8e | ||
|
|
8b857eed26 | ||
|
|
7640bcd163 | ||
|
|
d3b9b25429 | ||
|
|
f7871d6103 | ||
|
|
b9455bfad9 | ||
|
|
2b8582fcad | ||
|
|
d56513b722 | ||
|
|
99ecce2a87 | ||
|
|
b919074aa3 | ||
|
|
6187118166 | ||
|
|
f5a996a64e | ||
|
|
05d3386995 | ||
|
|
e731574e06 | ||
|
|
c20e5bf58a | ||
|
|
59142f800c | ||
|
|
bc2c49165e | ||
|
|
00275f1a67 | ||
|
|
3558dbe235 | ||
|
|
a74d33dafb | ||
|
|
c28793ae04 | ||
|
|
4a11bcccbb | ||
|
|
608d515f1f | ||
|
|
ef63644211 | ||
|
|
6b9791eeed | ||
|
|
d6eb66f083 | ||
|
|
13713bab51 | ||
|
|
74d26f04fa | ||
|
|
f154a854c3 | ||
|
|
c73034fe7d | ||
|
|
da7f51c085 | ||
|
|
3a92a8091a | ||
|
|
d7aa47eedc | ||
|
|
cac903c837 | ||
|
|
bdeab6ab49 | ||
|
|
2c213f805c | ||
|
|
502bb8a169 | ||
|
|
f98f66c529 | ||
|
|
575ef48b3a | ||
|
|
7fa4b2cb24 | ||
|
|
341e482f30 | ||
|
|
7fee85734a | ||
|
|
b6abe036ce | ||
|
|
6f18350eda | ||
|
|
ab9d7cdc70 | ||
|
|
5ea0b52ba8 | ||
|
|
1481ec7d85 | ||
|
|
7c394c6a1b | ||
|
|
07b02fe947 | ||
|
|
8f9552e78c | ||
|
|
1416e30127 | ||
|
|
d5f249f0ef | ||
|
|
eebb2cb9ae | ||
|
|
fa4fcf3886 | ||
|
|
4cf093ab75 | ||
|
|
c5885cc649 | ||
|
|
d0e439ee80 | ||
|
|
edb94af314 | ||
|
|
501546bc44 | ||
|
|
d102f561f9 | ||
|
|
265eb5421d | ||
|
|
fbaa4da153 | ||
|
|
3dc4e546bf | ||
|
|
3df7c4a1da | ||
|
|
09689e547c | ||
|
|
bf38796ec7 | ||
|
|
e81721cada | ||
|
|
c4231a25e3 | ||
|
|
058d1c2e51 | ||
|
|
4b508185b3 | ||
|
|
14e22e6bc9 | ||
|
|
9a03ca8417 | ||
|
|
5b7d2d15e9 | ||
|
|
88fbdff322 | ||
|
|
533b38a3c1 | ||
|
|
2e1e0dba54 | ||
|
|
d93073f8d0 | ||
|
|
5150378983 | ||
|
|
a6c195e528 | ||
|
|
bed2b6e222 | ||
|
|
f3ce8e23b4 | ||
|
|
804c307fa9 | ||
|
|
f48a1a5f26 | ||
|
|
2c7c639f4b | ||
|
|
0471b6e650 | ||
|
|
003e415382 | ||
|
|
16e46ea2c0 | ||
|
|
8fe71bbbff | ||
|
|
ea5c91e19e | ||
|
|
cc86719388 | ||
|
|
0e9b43ed93 | ||
|
|
9c21bda88e | ||
|
|
c914ac2a64 | ||
|
|
643552f13f | ||
|
|
0b893f11d3 | ||
|
|
f302b5883e | ||
|
|
33a8466913 | ||
|
|
8750cb0b1a | ||
|
|
32238806a6 | ||
|
|
4e1ca0a986 | ||
|
|
8b6d5eb5c8 | ||
|
|
1d5f9ae369 | ||
|
|
7a77793d69 | ||
|
|
262373a4b5 | ||
|
|
8742d8fbc7 | ||
|
|
af18045fc8 | ||
|
|
1d0f0f5205 | ||
|
|
300cc282e4 | ||
|
|
5d922fc53b | ||
|
|
0057144b52 | ||
|
|
c0f058f5cf | ||
|
|
2e4f989d7b | ||
|
|
0c04293d5f | ||
|
|
ccf3532eb2 | ||
|
|
d2b0599177 | ||
|
|
5b1df4438d | ||
|
|
9d8609dd08 |
@@ -2,7 +2,7 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
docker:
|
docker:
|
||||||
- image: circleci/node:latest-browsers
|
- image: circleci/node:current-browsers
|
||||||
|
|
||||||
working_directory: ~/repo
|
working_directory: ~/repo
|
||||||
|
|
||||||
|
|||||||
21
README.md
21
README.md
@@ -41,14 +41,26 @@ See the following examples for more detail on bundling OpenLayers with your appl
|
|||||||
* Using [Parcel](https://github.com/openlayers/ol-parcel)
|
* Using [Parcel](https://github.com/openlayers/ol-parcel)
|
||||||
* Using [Browserify](https://github.com/openlayers/ol-browserify)
|
* Using [Browserify](https://github.com/openlayers/ol-browserify)
|
||||||
|
|
||||||
|
## Sponsors
|
||||||
|
|
||||||
|
OpenLayers appreciates contributions of all kinds. We especially want to thank our fiscal sponsors who contribute to ongoing project maintenance.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
> Pozi helps connect communities through spatial thinking.
|
||||||
|
> We love Openlayers and it forms a core part of our platform.
|
||||||
|
> https://pozi.com/ https://app.pozi.com/
|
||||||
|
|
||||||
|
See our [Open Collective](https://opencollective.com/openlayers/contribute/sponsors-214/checkout) page if you too are interested in becoming a regular sponsor.
|
||||||
|
|
||||||
## IntelliSense support and type checking for VS Code
|
## IntelliSense support and type checking for VS Code
|
||||||
|
|
||||||
The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScript can get type definitions from these sources with a `jsconfig.json` config file in the project root:
|
The ol package contains a src/ folder with JSDoc annotated sources. TypeScript can get type definitions from these sources with a `jsconfig.json` config file in the project root:
|
||||||
```js
|
|
||||||
|
```json
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"checkJs": true,
|
"checkJs": true,
|
||||||
// Point to the JSDoc typed sources when using modules from the ol package
|
|
||||||
"baseUrl": "./",
|
"baseUrl": "./",
|
||||||
"paths": {
|
"paths": {
|
||||||
"ol": ["node_modules/ol/src"],
|
"ol": ["node_modules/ol/src"],
|
||||||
@@ -61,9 +73,10 @@ The `ol` package contains a `src/` folder with JSDoc annotated sources. TypeScri
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Project template with this configuration: https://gist.github.com/9a7253cb4712e8bf38d75d8ac898e36c.
|
Project template with this configuration: https://gist.github.com/9a7253cb4712e8bf38d75d8ac898e36c.
|
||||||
|
|
||||||
Note that the above only works when authoring in plain JavaScript. For similar configurations with a `tsconfig.json` in TypeScript projects, your mileage may vary.
|
Note that the above only works when authoring in plain JavaScript. For similar configurations with a `tsconfig.json` in TypeScript projects, your mileage may vary. You may want to use a [third-party types package](https://github.com/hanreev/types-ol) in this case.
|
||||||
|
|
||||||
## Supported Browsers
|
## Supported Browsers
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
## Upgrade notes
|
## Upgrade notes
|
||||||
|
|
||||||
|
### v6.3.0
|
||||||
|
|
||||||
|
#### Vector source loading when extent crosses +/-180
|
||||||
|
|
||||||
|
Previously, when an extent crossed the date line, vector source loaders were called with an extent with 540 degrees of longitude. Now, two loader calls with the visible extent on both sides of the projection extent are issued. This should not require any application code changes, but may affect custom loaders.
|
||||||
|
|
||||||
### v6.0.0
|
### v6.0.0
|
||||||
|
|
||||||
#### Backwards incompatible changes
|
#### Backwards incompatible changes
|
||||||
|
|||||||
13
changelog/v6.2.1.md
Normal file
13
changelog/v6.2.1.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 6.2.1
|
||||||
|
|
||||||
|
This is a bugfix release which resolves bundler issues due to a circular dependency, and brings a few documentation and example fixes.
|
||||||
|
|
||||||
|
## List of all changes
|
||||||
|
|
||||||
|
* [#10656](https://github.com/openlayers/openlayers/pull/10656) - Fix for export PDF example compatibility issues, and layer opacity handling. ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10653](https://github.com/openlayers/openlayers/pull/10653) - More reliable check for module content beyond classes ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10617](https://github.com/openlayers/openlayers/pull/10617) - Improve apidoc generation performance ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10625](https://github.com/openlayers/openlayers/pull/10625) - Apidoc cleanup navigation html ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10649](https://github.com/openlayers/openlayers/pull/10649) - Remove circular dependency ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10637](https://github.com/openlayers/openlayers/pull/10637) - Develop on 6.2.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
|
||||||
123
changelog/v6.3.0.md
Normal file
123
changelog/v6.3.0.md
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
# 6.3.0
|
||||||
|
|
||||||
|
With more than 70 pull requests, this release not only brings significant improvements to the API documentation. It also fixes some old bugs and brings frequently requested improvments. And good news for TypeScript users: OpenLayers now ships with type definitions in `.d.ts` files.
|
||||||
|
|
||||||
|
## New features and improvements
|
||||||
|
|
||||||
|
* Several improvements to the Graticule layer, like consistent labeling, no more missing graticule lines, and it now works for views that cross the date line.
|
||||||
|
* Better support for KML icon colors, as well as fills and outlines in PolyStyle
|
||||||
|
* Better `ol/Overlay` performance and support for panning off-screen overlays into view
|
||||||
|
* Most of the rendering code can now be run in web workers, e.g. to render to an OffscreenCanvas
|
||||||
|
* OpenLayers now works fine in web components with shadow root
|
||||||
|
* WebGL point layers now support rotation based on feature attributes
|
||||||
|
|
||||||
|
## List of all changes
|
||||||
|
|
||||||
|
* [#10490](https://github.com/openlayers/openlayers/pull/10490) - Select style multiple select interactions removed ([@bepremeg](https://github.com/bepremeg))
|
||||||
|
* [#10531](https://github.com/openlayers/openlayers/pull/10531) - Dynamically chose the number of subdivisions based on the size of the Image to reproject ([@pjsg](https://github.com/pjsg))
|
||||||
|
* [#10618](https://github.com/openlayers/openlayers/pull/10618) - Add apidoc-debug task to debug the apidoc generation process ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10343](https://github.com/openlayers/openlayers/pull/10343) - Correct interactions with circle geometries when using user coordinates ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10864](https://github.com/openlayers/openlayers/pull/10864) - Update dependencies ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10859](https://github.com/openlayers/openlayers/pull/10859) - Add an example of clipping layer based on a vector source ([@SDaron](https://github.com/SDaron))
|
||||||
|
* [#10850](https://github.com/openlayers/openlayers/pull/10850) - API docs for enums ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10857](https://github.com/openlayers/openlayers/pull/10857) - Make OSM XML example work at dateline and replace Bing with MapTiler ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10858](https://github.com/openlayers/openlayers/pull/10858) - Perform auto-pan when adding an Overlay to a Map ([@ejn](https://github.com/ejn))
|
||||||
|
* [#10646](https://github.com/openlayers/openlayers/pull/10646) - Write fill and outline in KML PolyStyle ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10800](https://github.com/openlayers/openlayers/pull/10800) - Make Overlay.panIntoView an API method ([@ejn](https://github.com/ejn))
|
||||||
|
* [#10807](https://github.com/openlayers/openlayers/pull/10807) - Handle Graticule wrapX without calculating excess meridians ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10795](https://github.com/openlayers/openlayers/pull/10795) - Show graticule labels in wrapped worlds ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10824](https://github.com/openlayers/openlayers/pull/10824) - Fix drawing svg icon with color option in ie11 ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10802](https://github.com/openlayers/openlayers/pull/10802) - Apidoc add default-exported enums ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10805](https://github.com/openlayers/openlayers/pull/10805) - make ImageSourceEventType available for consumers ([@regileeso](https://github.com/regileeso))
|
||||||
|
* [#10822](https://github.com/openlayers/openlayers/pull/10822) - parsing color from IconStyle in KML files ([@lysek](https://github.com/lysek))
|
||||||
|
* [#10848](https://github.com/openlayers/openlayers/pull/10848) - Speed up Overlay element positioning using CSS translate() ([@horsenit](https://github.com/horsenit))
|
||||||
|
* [#9590](https://github.com/openlayers/openlayers/pull/9590) - Calculate tile grid extent from extent of bottom-level tile matrix ([@mloskot](https://github.com/mloskot))
|
||||||
|
* [#10845](https://github.com/openlayers/openlayers/pull/10845) - Fix createHitDetectionImageData error for features with no size ([@gedaiu](https://github.com/gedaiu))
|
||||||
|
* [#10842](https://github.com/openlayers/openlayers/pull/10842) - Fix custom symbol example short description ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10828](https://github.com/openlayers/openlayers/pull/10828) - Offscreen canvas example ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10816](https://github.com/openlayers/openlayers/pull/10816) - Add 'funding' field to `package.json` ([@marcjansen](https://github.com/marcjansen))
|
||||||
|
* [#10813](https://github.com/openlayers/openlayers/pull/10813) - Add sponsors section to the readme ([@tschaub](https://github.com/tschaub))
|
||||||
|
* [#10474](https://github.com/openlayers/openlayers/pull/10474) - Fix for undefined source in Image layer ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10785](https://github.com/openlayers/openlayers/pull/10785) - Detect Zoomify server-side retina tiles ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10787](https://github.com/openlayers/openlayers/pull/10787) - Improved projection extent in the "Reprojection with EPSG.io Search" example ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10792](https://github.com/openlayers/openlayers/pull/10792) - Add support for EventListener Object ([@flexjoly](https://github.com/flexjoly))
|
||||||
|
* [#10777](https://github.com/openlayers/openlayers/pull/10777) - Keep the render loop running during simulation ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10791](https://github.com/openlayers/openlayers/pull/10791) - iOS 12 touchmove: Prevent touchmove event default when no preceding pointer event ([@sosmo](https://github.com/sosmo))
|
||||||
|
* [#10786](https://github.com/openlayers/openlayers/pull/10786) - Resolve constraints when updating size ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10788](https://github.com/openlayers/openlayers/pull/10788) - Add safeguard to handleTouchMove ([@sosmo](https://github.com/sosmo))
|
||||||
|
* [#10722](https://github.com/openlayers/openlayers/pull/10722) - fix: handle layer clear event in case clear(true) called ([@jellyedwards](https://github.com/jellyedwards))
|
||||||
|
* [#10723](https://github.com/openlayers/openlayers/pull/10723) - Improve the extent transforms used by Graticule and handle extents crossing the dateline ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10744](https://github.com/openlayers/openlayers/pull/10744) - Ensure the Modify Features Test example opens at correct zoom ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10767](https://github.com/openlayers/openlayers/pull/10767) - Replace Bing layer with MapTiler in examples ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10751](https://github.com/openlayers/openlayers/pull/10751) - Sort events / observables in all cases ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10763](https://github.com/openlayers/openlayers/pull/10763) - TypeScript: Fix inconsistent optionality in various APIs ([@jumpinjackie](https://github.com/jumpinjackie))
|
||||||
|
* [#10758](https://github.com/openlayers/openlayers/pull/10758) - Allow using feature attributes for symbol rotation in WebGL layers ([@jahow](https://github.com/jahow))
|
||||||
|
* [#10748](https://github.com/openlayers/openlayers/pull/10748) - Fix "Cannot read property 'anchor' of undefined" in ol/View ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10746](https://github.com/openlayers/openlayers/pull/10746) - Fix building apidoc on windows ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10720](https://github.com/openlayers/openlayers/pull/10720) - Apidoc better search ([@MoonE](https://github.com/MoonE))
|
||||||
|
* [#10743](https://github.com/openlayers/openlayers/pull/10743) - Ignore user provided tile cache size when too small ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10736](https://github.com/openlayers/openlayers/pull/10736) - Allow cluster source to unlisten from its source ([@M393](https://github.com/M393))
|
||||||
|
* [#10739](https://github.com/openlayers/openlayers/pull/10739) - Fix typo in trackpad timeout ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10740](https://github.com/openlayers/openlayers/pull/10740) - Document tabindex behavior for MouseWheelZoom and DragPan ([@matthias-ccri](https://github.com/matthias-ccri))
|
||||||
|
* [#10738](https://github.com/openlayers/openlayers/pull/10738) - Fix text background decluttering ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10715](https://github.com/openlayers/openlayers/pull/10715) - Fix disappearing graticule labels when rotation returns to 0 ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10713](https://github.com/openlayers/openlayers/pull/10713) - Draw graticule labels in a postrender function ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10711](https://github.com/openlayers/openlayers/pull/10711) - Make sure that optional args are typed accordingly ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10710](https://github.com/openlayers/openlayers/pull/10710) - Fix stylefunction return type ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10709](https://github.com/openlayers/openlayers/pull/10709) - Fix type and documentation of style function ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10708](https://github.com/openlayers/openlayers/pull/10708) - Handle Select interactions with falsey select style ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10707](https://github.com/openlayers/openlayers/pull/10707) - Get default projection for overview map from main map. ([@AugustusKling](https://github.com/AugustusKling))
|
||||||
|
* [#10699](https://github.com/openlayers/openlayers/pull/10699) - Make Select interaction work when there are multiple instances ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10694](https://github.com/openlayers/openlayers/pull/10694) - Draw image with configured opacity ([@M393](https://github.com/M393))
|
||||||
|
* [#10703](https://github.com/openlayers/openlayers/pull/10703) - CI and test fixes ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10698](https://github.com/openlayers/openlayers/pull/10698) - Shadow root ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10688](https://github.com/openlayers/openlayers/pull/10688) - Publish type definition files ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10691](https://github.com/openlayers/openlayers/pull/10691) - Do not exceed color range ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10683](https://github.com/openlayers/openlayers/pull/10683) - Dispatch enterfullscreen and leavefullscreen from the FullScreen control ([@fredj](https://github.com/fredj))
|
||||||
|
* [#10676](https://github.com/openlayers/openlayers/pull/10676) - Document that overviewmap view must use same projection as main map ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10678](https://github.com/openlayers/openlayers/pull/10678) - Add maxResolution option to ol/tilegrid.createXYZ() and ol/source/XYZ ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10690](https://github.com/openlayers/openlayers/pull/10690) - Document minZoom and maxZoom options for all layers ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10672](https://github.com/openlayers/openlayers/pull/10672) - Nicer mousewheel and trackpad zooming ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10687](https://github.com/openlayers/openlayers/pull/10687) - Increase timeout in listenImage test ([@fredj](https://github.com/fredj))
|
||||||
|
* [#10684](https://github.com/openlayers/openlayers/pull/10684) - perf: only do expensive reload when texture changes ([@jellyedwards](https://github.com/jellyedwards))
|
||||||
|
* [#10675](https://github.com/openlayers/openlayers/pull/10675) - typo ([@jipexu](https://github.com/jipexu))
|
||||||
|
* [#10669](https://github.com/openlayers/openlayers/pull/10669) - More browser compatible Export Map example ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#10667](https://github.com/openlayers/openlayers/pull/10667) - Do not render label with the current linedash ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10666](https://github.com/openlayers/openlayers/pull/10666) - Load polyfill before example specific scripts in examples template ([@mike-000](https://github.com/mike-000))
|
||||||
|
* [#6526](https://github.com/openlayers/openlayers/pull/6526) - Draw interaction: add abortDrawing method and drawabort event ([@tchandelle](https://github.com/tchandelle))
|
||||||
|
* [#10657](https://github.com/openlayers/openlayers/pull/10657) - Changelog for v6.2.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Dependency Updates</summary>
|
||||||
|
|
||||||
|
* [#10855](https://github.com/openlayers/openlayers/pull/10855) - Bump rollup from 2.1.0 to 2.3.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10854](https://github.com/openlayers/openlayers/pull/10854) - Bump ol-mapbox-style from 6.1.0 to 6.1.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10853](https://github.com/openlayers/openlayers/pull/10853) - Bump buble from 0.19.8 to 0.20.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10852](https://github.com/openlayers/openlayers/pull/10852) - Bump webpack from 4.42.0 to 4.42.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10837](https://github.com/openlayers/openlayers/pull/10837) - Bump ol-mapbox-style from 6.0.1 to 6.1.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10836](https://github.com/openlayers/openlayers/pull/10836) - Bump coveralls from 3.0.9 to 3.0.11 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10835](https://github.com/openlayers/openlayers/pull/10835) - Bump @babel/preset-env from 7.8.7 to 7.9.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10834](https://github.com/openlayers/openlayers/pull/10834) - Bump rollup from 1.32.1 to 2.1.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10833](https://github.com/openlayers/openlayers/pull/10833) - Bump fs-extra from 8.1.0 to 9.0.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10832](https://github.com/openlayers/openlayers/pull/10832) - Bump @babel/core from 7.8.7 to 7.9.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10831](https://github.com/openlayers/openlayers/pull/10831) - Bump babel-loader from 8.0.6 to 8.1.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10830](https://github.com/openlayers/openlayers/pull/10830) - Bump mocha from 7.1.0 to 7.1.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10829](https://github.com/openlayers/openlayers/pull/10829) - Bump marked from 0.8.0 to 0.8.2 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10811](https://github.com/openlayers/openlayers/pull/10811) - Bump sinon from 9.0.0 to 9.0.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10810](https://github.com/openlayers/openlayers/pull/10810) - Bump rollup-plugin-terser from 5.2.0 to 5.3.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10809](https://github.com/openlayers/openlayers/pull/10809) - Bump yargs from 15.3.0 to 15.3.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10806](https://github.com/openlayers/openlayers/pull/10806) - [Security] Bump acorn from 6.1.1 to 6.4.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10755](https://github.com/openlayers/openlayers/pull/10755) - Bump rollup from 1.31.1 to 1.32.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10754](https://github.com/openlayers/openlayers/pull/10754) - Bump @babel/preset-env from 7.8.4 to 7.8.6 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10753](https://github.com/openlayers/openlayers/pull/10753) - Bump mocha from 7.0.1 to 7.1.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10752](https://github.com/openlayers/openlayers/pull/10752) - Bump @babel/core from 7.8.4 to 7.8.6 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10725](https://github.com/openlayers/openlayers/pull/10725) - Bump elm-pep from 1.0.4 to 1.0.6 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10726](https://github.com/openlayers/openlayers/pull/10726) - Bump sinon from 8.1.1 to 9.0.0 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10680](https://github.com/openlayers/openlayers/pull/10680) - Bump terser-webpack-plugin from 2.3.4 to 2.3.5 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10682](https://github.com/openlayers/openlayers/pull/10682) - Bump webpack from 4.41.5 to 4.41.6 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10681](https://github.com/openlayers/openlayers/pull/10681) - Bump webpack-cli from 3.3.10 to 3.3.11 ([@openlayers](https://github.com/openlayers))
|
||||||
|
* [#10679](https://github.com/openlayers/openlayers/pull/10679) - Bump rollup from 1.31.0 to 1.31.1 ([@openlayers](https://github.com/openlayers))
|
||||||
|
|
||||||
|
|
||||||
|
</details>
|
||||||
9
changelog/v6.3.1.md
Normal file
9
changelog/v6.3.1.md
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# 6.3.1
|
||||||
|
|
||||||
|
This is a bugfix release which removes the auto-generated `.d.ts` TypeScript type files from the published package.
|
||||||
|
|
||||||
|
## List of all changes
|
||||||
|
|
||||||
|
* [#10877](https://github.com/openlayers/openlayers/pull/10877) - Remove .d.ts files from the package ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
* [#10872](https://github.com/openlayers/openlayers/pull/10872) - Use TypeScript 3.9 for type generation for better enums ([@ahocevar](https://github.com/ahocevar))
|
||||||
|
|
||||||
@@ -21,15 +21,11 @@ exports.defineTags = function(dictionary) {
|
|||||||
* from the documentation.
|
* from the documentation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const api = [];
|
const api = {};
|
||||||
const classes = {};
|
const classes = {};
|
||||||
const types = {};
|
const types = {};
|
||||||
const modules = {};
|
const modules = {};
|
||||||
|
|
||||||
function hasApiMembers(doclet) {
|
|
||||||
return doclet.longname.split('#')[0] == this.longname;
|
|
||||||
}
|
|
||||||
|
|
||||||
function includeAugments(doclet) {
|
function includeAugments(doclet) {
|
||||||
// Make sure that `observables` and `fires` are taken from an already processed `class` doclet.
|
// Make sure that `observables` and `fires` are taken from an already processed `class` doclet.
|
||||||
// This is necessary because JSDoc generates multiple doclets with the same longname.
|
// This is necessary because JSDoc generates multiple doclets with the same longname.
|
||||||
@@ -77,9 +73,6 @@ function includeAugments(doclet) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
cls._hideConstructor = true;
|
cls._hideConstructor = true;
|
||||||
if (!cls.undocumented) {
|
|
||||||
cls._documented = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,13 +103,41 @@ function includeTypes(doclet) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const defaultExports = {};
|
||||||
|
const path = require('path');
|
||||||
|
const moduleRoot = path.join(process.cwd(), 'src');
|
||||||
|
|
||||||
|
// Tag default exported Identifiers because their name should be the same as the module name.
|
||||||
|
exports.astNodeVisitor = {
|
||||||
|
visitNode: function(node, e, parser, currentSourceName) {
|
||||||
|
if (node.parent && node.parent.type === 'ExportDefaultDeclaration') {
|
||||||
|
const modulePath = path.relative(moduleRoot, currentSourceName).replace(/\.js$/, '');
|
||||||
|
const exportName = 'module:' + modulePath.replace(/\\/g, '/') + (node.name ? '~' + node.name : '');
|
||||||
|
defaultExports[exportName] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function sortOtherMembers(doclet) {
|
||||||
|
if (doclet.fires) {
|
||||||
|
doclet.fires.sort(function(a, b) {
|
||||||
|
return a.split(/#?event:/)[1] < b.split(/#?event:/)[1] ? -1 : 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (doclet.observables) {
|
||||||
|
doclet.observables.sort(function(a, b) {
|
||||||
|
return a.name < b.name ? -1 : 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.handlers = {
|
exports.handlers = {
|
||||||
|
|
||||||
newDoclet: function(e) {
|
newDoclet: function(e) {
|
||||||
const doclet = e.doclet;
|
const doclet = e.doclet;
|
||||||
if (doclet.stability) {
|
if (doclet.stability) {
|
||||||
modules[doclet.longname.split(/[~\.]/).shift()] = true;
|
modules[doclet.longname.split(/[~\.]/).shift()] = true;
|
||||||
api.push(doclet);
|
api[doclet.longname.split('#')[0]] = true;
|
||||||
}
|
}
|
||||||
if (doclet.kind == 'class') {
|
if (doclet.kind == 'class') {
|
||||||
if (!(doclet.longname in classes)) {
|
if (!(doclet.longname in classes)) {
|
||||||
@@ -133,22 +154,14 @@ exports.handlers = {
|
|||||||
|
|
||||||
parseComplete: function(e) {
|
parseComplete: function(e) {
|
||||||
const doclets = e.doclets;
|
const doclets = e.doclets;
|
||||||
|
const byLongname = doclets.index.longname;
|
||||||
for (let i = doclets.length - 1; i >= 0; --i) {
|
for (let i = doclets.length - 1; i >= 0; --i) {
|
||||||
const doclet = doclets[i];
|
const doclet = doclets[i];
|
||||||
if (doclet.stability) {
|
if (doclet.stability) {
|
||||||
if (doclet.kind == 'class') {
|
if (doclet.kind == 'class') {
|
||||||
includeAugments(doclet);
|
includeAugments(doclet);
|
||||||
}
|
}
|
||||||
if (doclet.fires) {
|
sortOtherMembers(doclet);
|
||||||
doclet.fires.sort(function(a, b) {
|
|
||||||
return a.split(/#?event:/)[1] < b.split(/#?event:/)[1] ? -1 : 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (doclet.observables) {
|
|
||||||
doclet.observables.sort(function(a, b) {
|
|
||||||
return a.name < b.name ? -1 : 1;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Always document namespaces and items with stability annotation
|
// Always document namespaces and items with stability annotation
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -159,20 +172,32 @@ exports.handlers = {
|
|||||||
if (doclet.isEnum || doclet.kind == 'typedef') {
|
if (doclet.isEnum || doclet.kind == 'typedef') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (doclet.kind == 'class' && api.some(hasApiMembers, doclet)) {
|
if (doclet.kind == 'class' && doclet.longname in api) {
|
||||||
// Mark undocumented classes with documented members as unexported.
|
// Mark undocumented classes with documented members as unexported.
|
||||||
// This is used in ../template/tmpl/container.tmpl to hide the
|
// This is used in ../template/tmpl/container.tmpl to hide the
|
||||||
// constructor from the docs.
|
// constructor from the docs.
|
||||||
doclet._hideConstructor = true;
|
doclet._hideConstructor = true;
|
||||||
includeAugments(doclet);
|
includeAugments(doclet);
|
||||||
} else if (!doclet._hideConstructor && !(doclet.kind == 'typedef' && doclet.longname in types)) {
|
sortOtherMembers(doclet);
|
||||||
|
} else if (!doclet._hideConstructor) {
|
||||||
// Remove all other undocumented symbols
|
// Remove all other undocumented symbols
|
||||||
doclet.undocumented = true;
|
doclet.undocumented = true;
|
||||||
}
|
}
|
||||||
if (doclet._documented) {
|
if (doclet.memberof && byLongname[doclet.memberof] &&
|
||||||
delete doclet.undocumented;
|
byLongname[doclet.memberof][0].isEnum &&
|
||||||
|
!byLongname[doclet.memberof][0].properties.some(p => p.stability)) {
|
||||||
|
byLongname[doclet.memberof][0].undocumented = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
processingComplete(e) {
|
||||||
|
const byLongname = e.doclets.index.longname;
|
||||||
|
for (const name in defaultExports) {
|
||||||
|
byLongname[name].forEach(function(doclet) {
|
||||||
|
doclet.isDefaultExport = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -42,15 +42,15 @@ exports.handlers = {
|
|||||||
|
|
||||||
parseComplete: function(e) {
|
parseComplete: function(e) {
|
||||||
let ancestors, candidate, candidates, doclet, i, j, k, l, key;
|
let ancestors, candidate, candidates, doclet, i, j, k, l, key;
|
||||||
let incompleteDoclet, stability, incomplete, incompletes;
|
let stability, incomplete, incompletes;
|
||||||
const doclets = e.doclets;
|
const doclets = e.doclets;
|
||||||
for (i = doclets.length - 1; i >= 0; --i) {
|
for (i = doclets.length - 1; i >= 0; --i) {
|
||||||
doclet = doclets[i];
|
doclet = doclets[i];
|
||||||
if (doclet.augments) {
|
|
||||||
ancestors = [].concat(doclet.augments);
|
|
||||||
}
|
|
||||||
incompletes = incompleteByClass[doclet.longname];
|
incompletes = incompleteByClass[doclet.longname];
|
||||||
if (ancestors && incompletes) {
|
if (!doclet.augments || !incompletes) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ancestors = doclet.augments.slice();
|
||||||
// collect ancestors from the whole hierarchy
|
// collect ancestors from the whole hierarchy
|
||||||
for (j = 0; j < ancestors.length; ++j) {
|
for (j = 0; j < ancestors.length; ++j) {
|
||||||
candidates = lookup[ancestors[j]];
|
candidates = lookup[ancestors[j]];
|
||||||
@@ -58,12 +58,13 @@ exports.handlers = {
|
|||||||
for (k = candidates.length - 1; k >= 0; --k) {
|
for (k = candidates.length - 1; k >= 0; --k) {
|
||||||
candidate = candidates[k];
|
candidate = candidates[k];
|
||||||
if (candidate.augments) {
|
if (candidate.augments) {
|
||||||
ancestors = ancestors.concat(candidate.augments);
|
Array.prototype.push.apply(ancestors, candidate.augments);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// walk through all inheritDoc members
|
// walk through all inheritDoc members
|
||||||
|
let incompleteDoclet;
|
||||||
for (j = incompletes.length - 1; j >= 0; --j) {
|
for (j = incompletes.length - 1; j >= 0; --j) {
|
||||||
incomplete = incompletes[j];
|
incomplete = incompletes[j];
|
||||||
candidates = lookup[doclet.longname + '#' + incomplete];
|
candidates = lookup[doclet.longname + '#' + incomplete];
|
||||||
@@ -105,6 +106,5 @@ exports.handlers = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const path = require('jsdoc/lib/jsdoc/path');
|
|||||||
const taffy = require('taffydb').taffy;
|
const taffy = require('taffydb').taffy;
|
||||||
const handle = require('jsdoc/lib/jsdoc/util/error').handle;
|
const handle = require('jsdoc/lib/jsdoc/util/error').handle;
|
||||||
const helper = require('jsdoc/lib/jsdoc/util/templateHelper');
|
const helper = require('jsdoc/lib/jsdoc/util/templateHelper');
|
||||||
const _ = require('underscore');
|
|
||||||
const htmlsafe = helper.htmlsafe;
|
const htmlsafe = helper.htmlsafe;
|
||||||
const linkto = helper.linkto;
|
const linkto = helper.linkto;
|
||||||
const resolveAuthorLinks = helper.resolveAuthorLinks;
|
const resolveAuthorLinks = helper.resolveAuthorLinks;
|
||||||
@@ -188,10 +187,12 @@ function attachModuleSymbols(doclets, modules) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrettyName(longname) {
|
function getPrettyName(doclet) {
|
||||||
return longname
|
const fullname = doclet.longname.replace('module:', '');
|
||||||
.split('~')[0]
|
if (doclet.isDefaultExport) {
|
||||||
.replace('module:', '');
|
return fullname.split('~')[0];
|
||||||
|
}
|
||||||
|
return fullname;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -209,27 +210,13 @@ function getPrettyName(longname) {
|
|||||||
*/
|
*/
|
||||||
function buildNav(members) {
|
function buildNav(members) {
|
||||||
const nav = [];
|
const nav = [];
|
||||||
// merge namespaces and classes, then sort
|
members.classes.forEach(function(v) {
|
||||||
const merged = members.modules.concat(members.classes);
|
|
||||||
merged.sort(function(a, b) {
|
|
||||||
const prettyNameA = getPrettyName(a.longname).toLowerCase();
|
|
||||||
const prettyNameB = getPrettyName(b.longname).toLowerCase();
|
|
||||||
if (prettyNameA > prettyNameB) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
if (prettyNameA < prettyNameB) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
_.each(merged, function(v) {
|
|
||||||
// exclude interfaces from sidebar
|
// exclude interfaces from sidebar
|
||||||
if (v.interface !== true && v.kind === 'class') {
|
if (v.interface !== true) {
|
||||||
nav.push({
|
nav.push({
|
||||||
type: 'class',
|
type: 'class',
|
||||||
longname: v.longname,
|
longname: v.longname,
|
||||||
prettyname: getPrettyName(v.longname),
|
prettyname: getPrettyName(v),
|
||||||
name: v.name,
|
name: v.name,
|
||||||
module: find({
|
module: find({
|
||||||
kind: 'module',
|
kind: 'module',
|
||||||
@@ -253,7 +240,9 @@ function buildNav(members) {
|
|||||||
memberof: v.longname
|
memberof: v.longname
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
} else if (v.kind == 'module') {
|
}
|
||||||
|
});
|
||||||
|
members.modules.forEach(function(v) {
|
||||||
const classes = find({
|
const classes = find({
|
||||||
kind: 'class',
|
kind: 'class',
|
||||||
memberof: v.longname
|
memberof: v.longname
|
||||||
@@ -274,13 +263,13 @@ function buildNav(members) {
|
|||||||
kind: 'event',
|
kind: 'event',
|
||||||
memberof: v.longname
|
memberof: v.longname
|
||||||
});
|
});
|
||||||
// only add modules that have more to show than just classes
|
// Only add modules that contain more than just classes with their
|
||||||
const numItems = classes.length - 1 + members.length + methods.length + typedefs.length + events.length;
|
// associated Options typedef
|
||||||
if (!classes.length || (numItems > 0 && numItems !== classes.length)) {
|
if (typedefs.length > classes.length || members.length + methods.length > 0) {
|
||||||
nav.push({
|
nav.push({
|
||||||
type: 'module',
|
type: 'module',
|
||||||
longname: v.longname,
|
longname: v.longname,
|
||||||
prettyname: getPrettyName(v.longname),
|
prettyname: getPrettyName(v),
|
||||||
name: v.name,
|
name: v.name,
|
||||||
members: members,
|
members: members,
|
||||||
methods: methods,
|
methods: methods,
|
||||||
@@ -289,7 +278,18 @@ function buildNav(members) {
|
|||||||
events: events
|
events: events
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nav.sort(function(a, b) {
|
||||||
|
const prettyNameA = a.prettyname.toLowerCase();
|
||||||
|
const prettyNameB = b.prettyname.toLowerCase();
|
||||||
|
if (prettyNameA > prettyNameB) {
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (prettyNameA < prettyNameB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
});
|
});
|
||||||
return nav;
|
return nav;
|
||||||
}
|
}
|
||||||
@@ -477,6 +477,7 @@ exports.publish = function(taffyData, opts, tutorials) {
|
|||||||
// index page displays information from package.json and lists files
|
// index page displays information from package.json and lists files
|
||||||
const files = find({kind: 'file'});
|
const files = find({kind: 'file'});
|
||||||
|
|
||||||
|
view.navigationHtml = helper.resolveLinks(view.partial('navigation.tmpl'));
|
||||||
generate('Index',
|
generate('Index',
|
||||||
[{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}].concat(files),
|
[{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}].concat(files),
|
||||||
indexUrl);
|
indexUrl);
|
||||||
|
|||||||
@@ -1,99 +1,258 @@
|
|||||||
$(function () {
|
$(function () {
|
||||||
// Search Items
|
'use strict';
|
||||||
$('#include_modules').change(function (e) {
|
|
||||||
console.log('change');
|
|
||||||
if ($(this).is(':checked')) {
|
|
||||||
|
|
||||||
} else {
|
// Allow user configuration?
|
||||||
|
const allowRegex = true;
|
||||||
|
const minInputForSearch = 1;
|
||||||
|
const minInputForFullText = 2;
|
||||||
|
const expandAllOnInputWithoutSearch = true;
|
||||||
|
|
||||||
|
function constructRegex(searchTerm, makeRe, allowRegex) {
|
||||||
|
try {
|
||||||
|
if (allowRegex) {
|
||||||
|
return makeRe(searchTerm);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
}
|
||||||
|
// In case of invalid regexp fall back to non-regexp, but still allow . to match /
|
||||||
|
return makeRe(searchTerm.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\./g, '[./]'));
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
var getSearchWeight = function (searchTerm, $matchedItem) {
|
function getWeightFunction(searchTerm, allowRegex) {
|
||||||
let weight = 0;
|
function makeRe(searchTerm) {
|
||||||
|
return {
|
||||||
|
begin: new RegExp('\\b' + searchTerm), // Begin matches word boundary
|
||||||
|
baseName: new RegExp('\\b' + searchTerm + '[^/]*$'), // Begin matches word boundary of class / module name
|
||||||
|
fullName: new RegExp('\\b' + searchTerm + '(?:[~.]|$)'), // Complete word(s) of class / module matches
|
||||||
|
completeName: new RegExp('^' + searchTerm + '$') // Match from start to finish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const re = constructRegex(searchTerm, makeRe, allowRegex);
|
||||||
|
return function (matchedItem, beginOnly) {
|
||||||
// We could get smarter on the weight here
|
// We could get smarter on the weight here
|
||||||
if ($matchedItem.data('shortname')
|
const name = matchedItem.dataset.name;
|
||||||
&& $matchedItem.data('shortname').toLowerCase() === searchTerm.toLowerCase()) {
|
if (beginOnly) {
|
||||||
weight++;
|
return re.baseName.test(name) ? 100 : 1;
|
||||||
|
}
|
||||||
|
// If everything else is equal, prefer shorter names, and prefer classes over modules
|
||||||
|
let weight = 10000 + matchedItem.dataset.longname.length - name.length * 100;
|
||||||
|
if (name.match(re.begin)) {
|
||||||
|
weight += 10000;
|
||||||
|
if (re.baseName.test(name)) {
|
||||||
|
weight += 10000;
|
||||||
|
if (re.fullName.test(name)) {
|
||||||
|
weight += 10000;
|
||||||
|
if (re.completeName.test(name)) {
|
||||||
|
weight += 10000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return weight;
|
return weight;
|
||||||
};
|
|
||||||
|
|
||||||
// sort function callback
|
|
||||||
var weightSorter = function (a, b) {
|
|
||||||
var aW = $(a).data('weight') || 0;
|
|
||||||
var bW = $(b).data('weight') || 0;
|
|
||||||
return bW - aW;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Search Items
|
|
||||||
$('#search').on('keyup', function (e) {
|
|
||||||
var value = $(this).val();
|
|
||||||
var $el = $('.navigation');
|
|
||||||
|
|
||||||
if (value && value.length > 1) {
|
|
||||||
var regexp = new RegExp(value, 'i');
|
|
||||||
$el.find('li, .itemMembers').hide();
|
|
||||||
|
|
||||||
$el.find('li').each(function (i, v) {
|
|
||||||
const $item = $(v);
|
|
||||||
const name = $item.data('name');
|
|
||||||
|
|
||||||
if (name && regexp.test(name)) {
|
|
||||||
const $classEntry = $item.closest('.item');
|
|
||||||
const $members = $item.closest('.itemMembers');
|
|
||||||
|
|
||||||
// Do the weight thing
|
|
||||||
$classEntry.removeData('weight');
|
|
||||||
$classEntry.show();
|
|
||||||
const weight = getSearchWeight(value, $classEntry);
|
|
||||||
$classEntry.data('weight', weight);
|
|
||||||
|
|
||||||
$members.show();
|
|
||||||
$classEntry.show();
|
|
||||||
$item.show();
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
$(".navigation ul.list li.item:visible")
|
|
||||||
.sort(weightSorter) // sort elements
|
|
||||||
.appendTo(".navigation ul.list"); // append again to the list
|
|
||||||
|
|
||||||
} else {
|
|
||||||
$el.find('.item, .itemMembers').show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$el.find('.list').scrollTop(0);
|
const search = (function () {
|
||||||
});
|
const $navList = $('.navigation-list');
|
||||||
|
const navListNode = $navList.get(0);
|
||||||
// Toggle when click an item element
|
let $classItems;
|
||||||
$('.navigation').on('click', '.toggle', function (e) {
|
let $members;
|
||||||
$(this).parent().parent().find('.itemMembers').toggle();
|
let stateClass = (function () {
|
||||||
});
|
$navList.removeClass('search-started searching');
|
||||||
|
$navList.addClass('search-empty');
|
||||||
|
return 'search-empty';
|
||||||
|
})();
|
||||||
|
let manualToggles = {};
|
||||||
|
|
||||||
// Show an item related a current documentation automatically
|
// Show an item related a current documentation automatically
|
||||||
var filename = $('.page-title').data('filename')
|
const longname = $('.page-title').data('filename')
|
||||||
.replace(/\.[a-z]+$/, '')
|
.replace(/\.[a-z]+$/, '')
|
||||||
.replace('module-', 'module:')
|
.replace('module-', 'module:')
|
||||||
.replace(/_/g, '/')
|
.replace(/_/g, '/')
|
||||||
.replace(/-/g, '~');
|
.replace(/-/g, '~');
|
||||||
var $currentItem = $('.navigation .item[data-name*="' + filename + '"]:eq(0)');
|
const currentItem = navListNode.querySelector('.item[data-longname="' + longname + '"]');
|
||||||
|
if (currentItem) {
|
||||||
if ($currentItem.length) {
|
$navList.prepend(currentItem);
|
||||||
$currentItem
|
|
||||||
.remove()
|
|
||||||
.prependTo('.navigation .list')
|
|
||||||
.show()
|
|
||||||
.find('.itemMembers')
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
$navList: $navList,
|
||||||
|
$currentItem: currentItem ? $(currentItem) : undefined,
|
||||||
|
lastSearchTerm: undefined,
|
||||||
|
lastState: {},
|
||||||
|
getClassList: function () {
|
||||||
|
return $classItems || ($classItems = $navList.find('li.item'));
|
||||||
|
},
|
||||||
|
getMembers: function () {
|
||||||
|
return $members || ($members = $navList.find('.item li'));
|
||||||
|
},
|
||||||
|
changeStateClass: function (newClass) {
|
||||||
|
if (newClass !== stateClass) {
|
||||||
|
navListNode.classList.remove(stateClass);
|
||||||
|
navListNode.classList.add(newClass);
|
||||||
|
stateClass = newClass;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
manualToggle: function ($node, show) {
|
||||||
|
$node.addClass('toggle-manual');
|
||||||
|
$node.toggleClass('toggle-manual-hide', !show);
|
||||||
|
$node.toggleClass('toggle-manual-show', show);
|
||||||
|
manualToggles[$node.data('longname')] = $node;
|
||||||
|
},
|
||||||
|
clearManualToggles: function() {
|
||||||
|
for (let clsName in manualToggles) {
|
||||||
|
manualToggles[clsName].removeClass('toggle-manual toggle-manual-show toggle-manual-hide');
|
||||||
|
}
|
||||||
|
manualToggles = {};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
const dummy = {subItems: {}};
|
||||||
|
function clearOldMatches(lastState, searchState) {
|
||||||
|
for (let itemName in lastState) {
|
||||||
|
const lastItem = lastState[itemName];
|
||||||
|
const item = searchState[itemName];
|
||||||
|
if (!item) {
|
||||||
|
lastItem.item.classList.remove('match');
|
||||||
|
}
|
||||||
|
if (lastItem.subItems) {
|
||||||
|
clearOldMatches(lastItem.subItems, (item || dummy).subItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSearch(searchTerm) {
|
||||||
|
searchTerm = searchTerm.toLowerCase();
|
||||||
|
const lastSearchTerm = search.lastSearchTerm;
|
||||||
|
if (searchTerm === lastSearchTerm) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Avoid layout reflow by scrolling to top first.
|
||||||
|
search.$navList.scrollTop(0);
|
||||||
|
search.lastSearchTerm = searchTerm;
|
||||||
|
search.clearManualToggles();
|
||||||
|
|
||||||
|
if (searchTerm.length < minInputForSearch) {
|
||||||
|
const state = searchTerm.length && expandAllOnInputWithoutSearch ? 'search-started' : 'search-empty';
|
||||||
|
search.changeStateClass(state);
|
||||||
|
if (lastSearchTerm !== undefined && lastSearchTerm.length >= minInputForSearch) {
|
||||||
|
// Restore the original, sorted order
|
||||||
|
search.$navList.append(search.getClassList());
|
||||||
|
}
|
||||||
|
if (state === 'search-empty' && search.$currentItem) {
|
||||||
|
search.manualToggle(search.$currentItem, true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
search.changeStateClass('searching');
|
||||||
|
searchTerm = searchTerm.toLowerCase();
|
||||||
|
const beginOnly = searchTerm.length < minInputForFullText;
|
||||||
|
const getSearchWeight = getWeightFunction(searchTerm, allowRegex);
|
||||||
|
const re = constructRegex(searchTerm, function (searchTerm) {
|
||||||
|
return new RegExp((beginOnly ? '\\b' : '') + searchTerm);
|
||||||
|
}, allowRegex);
|
||||||
|
const navList = search.$navList.get(0);
|
||||||
|
const classes = [];
|
||||||
|
const searchState = {};
|
||||||
|
search.getClassList().each(function (i, classEntry) {
|
||||||
|
const className = classEntry.dataset.longname;
|
||||||
|
if (!(className in searchState) && re.test(classEntry.dataset.name)) {
|
||||||
|
const cls = searchState[className] = {
|
||||||
|
item: classEntry,
|
||||||
|
// Do the weight thing
|
||||||
|
weight: getSearchWeight(classEntry, beginOnly) * 100000,
|
||||||
|
subItems: {}
|
||||||
|
};
|
||||||
|
classes.push(cls);
|
||||||
|
classEntry.classList.add('match');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
search.getMembers().each(function (i, li) {
|
||||||
|
const name = li.dataset.name;
|
||||||
|
if (re.test(name)) {
|
||||||
|
const itemMember = li.parentElement.parentElement;
|
||||||
|
const classEntry = itemMember.parentElement;
|
||||||
|
const className = classEntry.dataset.longname;
|
||||||
|
let cls = searchState[className];
|
||||||
|
if (!cls) {
|
||||||
|
cls = searchState[className] = {
|
||||||
|
item: classEntry,
|
||||||
|
weight: 0,
|
||||||
|
subItems: {}
|
||||||
|
};
|
||||||
|
classes.push(cls);
|
||||||
|
classEntry.classList.add('match');
|
||||||
|
}
|
||||||
|
cls.weight += getSearchWeight(li, true);
|
||||||
|
const memberType = itemMember.dataset.type;
|
||||||
|
let members = cls.subItems[memberType];
|
||||||
|
if (!members) {
|
||||||
|
members = cls.subItems[memberType] = {
|
||||||
|
item: itemMember,
|
||||||
|
subItems: {}
|
||||||
|
};
|
||||||
|
itemMember.classList.add('match');
|
||||||
|
}
|
||||||
|
members.subItems[name] = { item: li };
|
||||||
|
li.classList.add('match');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
clearOldMatches(search.lastState, searchState);
|
||||||
|
search.lastState = searchState;
|
||||||
|
|
||||||
|
classes.sort(function (a, b) {
|
||||||
|
return a.weight - b.weight;
|
||||||
|
});
|
||||||
|
for (let i = classes.length - 1; i >= 0; --i) {
|
||||||
|
navList.appendChild(classes[i].item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const searchInput = $('#search').get(0);
|
||||||
|
// Skip searches when typing fast.
|
||||||
|
let key;
|
||||||
|
function queueSearch() {
|
||||||
|
if (!key) {
|
||||||
|
key = setTimeout(function () {
|
||||||
|
key = undefined;
|
||||||
|
|
||||||
|
const searchTerm = searchInput.value;
|
||||||
|
doSearch(searchTerm);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search Items
|
||||||
|
searchInput.addEventListener('input', queueSearch);
|
||||||
|
doSearch(searchInput.value);
|
||||||
|
|
||||||
|
// Toggle when click an item element
|
||||||
|
search.$navList.on('click', '.toggle', function (e) {
|
||||||
|
if (event.target.tagName.toLowerCase() === 'a') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const clsItem = $(this).closest('.item');
|
||||||
|
let show;
|
||||||
|
if (clsItem.hasClass('toggle-manual-show')) {
|
||||||
|
show = false;
|
||||||
|
} else if (clsItem.hasClass('toggle-manual-hide')) {
|
||||||
|
show = true;
|
||||||
|
} else {
|
||||||
|
clsItem.find('.member-list li').each(function (i, v) {
|
||||||
|
show = $(v).is(':hidden');
|
||||||
|
return !show;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
search.manualToggle(clsItem, !!show);
|
||||||
|
});
|
||||||
|
|
||||||
// Auto resizing on navigation
|
// Auto resizing on navigation
|
||||||
var _onResize = function () {
|
var _onResize = function () {
|
||||||
var height = $(window).height();
|
var height = $(window).height();
|
||||||
var $el = $('.navigation');
|
var $el = $('.navigation');
|
||||||
|
|
||||||
$el.height(height).find('.list').height(height - 133);
|
$el.height(height).find('.navigation-list').height(height - 133);
|
||||||
};
|
};
|
||||||
|
|
||||||
$(window).on('resize', _onResize);
|
$(window).on('resize', _onResize);
|
||||||
@@ -137,22 +296,4 @@ $(function () {
|
|||||||
'<a href="' + link + textParts[1].replace('line ', '#L') + '">' +
|
'<a href="' + link + textParts[1].replace('line ', '#L') + '">' +
|
||||||
textParts[1] + '</a>';
|
textParts[1] + '</a>';
|
||||||
});
|
});
|
||||||
|
|
||||||
// Highlighting current anchor
|
|
||||||
|
|
||||||
var anchors = $('.anchor');
|
|
||||||
var _onHashChange = function () {
|
|
||||||
var activeHash = window.document.location.hash
|
|
||||||
.replace(/\./g, '\\.') // Escape dot in element id
|
|
||||||
.replace(/\~/g, '\\~'); // Escape tilde in element id
|
|
||||||
|
|
||||||
anchors.removeClass('highlighted');
|
|
||||||
|
|
||||||
if (activeHash.length > 0) {
|
|
||||||
anchors.filter(activeHash).addClass('highlighted');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(window).on('hashchange', _onHashChange);
|
|
||||||
_onHashChange();
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ body {
|
|||||||
width: 0px;
|
width: 0px;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
}
|
}
|
||||||
.nameContainer .anchor.highlighted + h4 {
|
/* Highlighting current anchor */
|
||||||
|
.nameContainer .anchor:target + h4 {
|
||||||
background-color: #faebcc;
|
background-color: #faebcc;
|
||||||
}
|
}
|
||||||
a {
|
a {
|
||||||
@@ -123,7 +124,7 @@ li {
|
|||||||
color: #fff;
|
color: #fff;
|
||||||
border-color: #555;
|
border-color: #555;
|
||||||
}
|
}
|
||||||
.navigation .list {
|
.navigation .navigation-list {
|
||||||
padding: 10px 15px 0 15px;
|
padding: 10px 15px 0 15px;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -135,6 +136,24 @@ li {
|
|||||||
border-bottom: 1px solid #333;
|
border-bottom: 1px solid #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navigation .glyphicon {
|
||||||
|
margin-right: 3px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.navigation .item .glyphicon:before {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
.navigation .item.toggle-manual .glyphicon:before {
|
||||||
|
transition: transform .1s;
|
||||||
|
}
|
||||||
|
.navigation .item-class.toggle-manual-show .glyphicon:before {
|
||||||
|
/* With 90deg the icon slightly slides left at transition end */
|
||||||
|
transform: rotate(89.9deg);
|
||||||
|
}
|
||||||
|
.navigation .item-module.toggle-manual-show .glyphicon:before {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
|
||||||
.navigation li.perfect-match {
|
.navigation li.perfect-match {
|
||||||
border: 5px solid orange;
|
border: 5px solid orange;
|
||||||
}
|
}
|
||||||
@@ -147,8 +166,8 @@ li {
|
|||||||
}
|
}
|
||||||
.navigation li.item .title {
|
.navigation li.item .title {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: relative;
|
display: -ms-flexbox;
|
||||||
display: block;
|
display: flex;
|
||||||
font-size: 0.85em;
|
font-size: 0.85em;
|
||||||
}
|
}
|
||||||
.navigation li.item .title a {
|
.navigation li.item .title a {
|
||||||
@@ -183,10 +202,42 @@ li {
|
|||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
.navigation li.item .itemMembers {
|
.navigation li.item .member-list {
|
||||||
display: none;
|
|
||||||
padding-left: 8px;
|
padding-left: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* search state */
|
||||||
|
/* show all classes when search is empty */
|
||||||
|
.navigation-list.search-empty .item {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* hide all members by default when search is empty */
|
||||||
|
.navigation-list.search-empty .item .member-list {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
/* expand all members when input in search field available but too short to search */
|
||||||
|
.navigation-list.search-started li,
|
||||||
|
.navigation-list.search-started .member-list {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* when searching hide everything that is not a match */
|
||||||
|
.navigation-list.searching li,
|
||||||
|
.navigation-list.searching .member-list {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.navigation-list.searching .match {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
/* allow user to hide / show members */
|
||||||
|
.navigation-list .item.toggle-manual-show li,
|
||||||
|
.navigation-list .item.toggle-manual-show .member-list {
|
||||||
|
display: block!important;
|
||||||
|
}
|
||||||
|
.navigation-list:not(.searching) .item.toggle-manual-hide li,
|
||||||
|
.navigation-list:not(.searching) .item.toggle-manual-hide .member-list {
|
||||||
|
display: none!important;
|
||||||
|
}
|
||||||
|
|
||||||
.main {
|
.main {
|
||||||
padding: 20px 20px;
|
padding: 20px 20px;
|
||||||
margin-left: 250px;
|
margin-left: 250px;
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ var version = obj.packageInfo.version;
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="wrap" class="clearfix">
|
<div id="wrap" class="clearfix">
|
||||||
<?js= this.partial('navigation.tmpl', this) ?>
|
<?js= this.navigationHtml ?>
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<h1 class="page-title" data-filename="<?js= filename ?>"><?js= title ?></h1>
|
<h1 class="page-title" data-filename="<?js= filename ?>"><?js= title ?></h1>
|
||||||
<div id="latest-check" class="alert alert-warning alert-dismissible" role="alert" style="display:none">
|
<div id="latest-check" class="alert alert-warning alert-dismissible" role="alert" style="display:none">
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ var self = this;
|
|||||||
<ul><?js fires.forEach(function(f) {
|
<ul><?js fires.forEach(function(f) {
|
||||||
var parts = f.split(/#?event:/);
|
var parts = f.split(/#?event:/);
|
||||||
var type = parts.pop();
|
var type = parts.pop();
|
||||||
var eventClassName = parts[0];
|
var eventClass = self.find({longname: parts[0]})[0];
|
||||||
parts = type.split(' ');
|
parts = type.split(' ');
|
||||||
type = parts.shift();
|
type = parts.shift();
|
||||||
var description = parts.length ? parts.join(' ') : '';
|
var description = parts.length ? parts.join(' ') : '';
|
||||||
@@ -74,13 +74,10 @@ var self = this;
|
|||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<li class="<?js= (eventDoclet || data).stability !== 'stable' ? 'unstable' : '' ?>">
|
<li class="<?js= (eventDoclet || data).stability !== 'stable' ? 'unstable' : '' ?>">
|
||||||
<code><?js= eventClassName ? self.linkto(f, type) : type ?></code>
|
<code><?js= eventClass ? self.linkto(f, type) : type ?></code>
|
||||||
<?js if (eventClassName) {
|
<?js if (eventClass) { ?>
|
||||||
var eventClass = self.find({longname: eventClassName})[0];
|
|
||||||
if (eventClass) { ?>
|
|
||||||
(<?js= self.linkto(eventClass.longname) ?>)
|
(<?js= self.linkto(eventClass.longname) ?>)
|
||||||
<?js } ?>
|
<?js } ?>
|
||||||
<?js } ?>
|
|
||||||
<?js= self.partial('stability.tmpl', eventDoclet || (data.stability ? data : {})) ?>
|
<?js= self.partial('stability.tmpl', eventDoclet || (data.stability ? data : {})) ?>
|
||||||
<?js if (description) { ?> -
|
<?js if (description) { ?> -
|
||||||
<?js= description ?>
|
<?js= description ?>
|
||||||
|
|||||||
@@ -1,95 +1,59 @@
|
|||||||
<?js
|
<?js
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
function toShortName(name) {
|
function toShortName(name) {
|
||||||
return name.indexOf('module:') === 0 ? name.split('/').pop() : name;
|
return name.indexOf('module:') === 0 ? name.split('/').pop() : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getItemCssClass(type) {
|
||||||
|
if (type === 'module') {
|
||||||
|
return 'glyphicon-plus';
|
||||||
|
} else if (type === 'class') {
|
||||||
|
return 'glyphicon-chevron-right';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const printList = v => { ?>
|
||||||
|
<li data-name="<?js= toShortName(v.name).toLowerCase() ?>"><?js
|
||||||
|
}
|
||||||
|
const printListWithStability = v => {
|
||||||
|
const cls = v.stability && v.stability !== 'stable' ? ' class="unstable"' : ''; ?>
|
||||||
|
<li data-name="<?js= toShortName(v.name).toLowerCase() ?>"<?js= cls ?>><?js
|
||||||
|
}
|
||||||
|
|
||||||
|
function listContent(item, title, listItemPrinter) {
|
||||||
|
const type = title.toLowerCase();
|
||||||
|
if (item[type] && item[type].length) { ?>
|
||||||
|
<div class="member-list" data-type="<?js= type ?>">
|
||||||
|
<span class="subtitle"><?js= title ?></span>
|
||||||
|
<ul><?js
|
||||||
|
item[type].forEach(function (v) {
|
||||||
|
listItemPrinter(v); ?><?js= self.linkto(v.longname, toShortName(v.name)) ?><?js
|
||||||
|
}); ?>
|
||||||
|
</ul>
|
||||||
|
</div><?js
|
||||||
|
}
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
<div class="navigation">
|
<div class="navigation">
|
||||||
<div class="search">
|
<div class="search">
|
||||||
<input id="search" type="text" class="form-control input-sm" placeholder="Search Documentation">
|
<input id="search" type="text" class="form-control input-sm" placeholder="Search Documentation">
|
||||||
</div>
|
</div>
|
||||||
<ul class="list">
|
<ul class="navigation-list search-empty"><?js
|
||||||
<?js
|
this.nav.forEach(function (item) { ?>
|
||||||
this.nav.forEach(function (item) {
|
<li class="item item-<?js= item.type ?>" data-longname="<?js= item.longname ?>" data-name="<?js= item.prettyname.toLowerCase() ?>">
|
||||||
?>
|
<span class="title toggle">
|
||||||
<li class="item" data-name="<?js= item.longname ?>" data-shortname="<?js= item.name.toLowerCase() ?>">
|
<span class="glyphicon <?js= getItemCssClass(item.type) ?>"></span>
|
||||||
<span class="title">
|
<span><?js= self.linkto(item.longname, item.prettyname.replace(/[.~]/g, '\u200b$&')) ?></span>
|
||||||
<?js if (item.type === 'module') { ?>
|
</span><?js
|
||||||
<span class="glyphicon glyphicon-plus toggle"></span>
|
listContent(item, 'Members', printList);
|
||||||
<?js } else if (item.type === 'class') { ?>
|
listContent(item, 'Typedefs', printListWithStability);
|
||||||
<span class="glyphicon glyphicon-chevron-right toggle"></span>
|
listContent(item, 'Methods', printListWithStability);
|
||||||
<?js } ?>
|
if (item.fires) {
|
||||||
<?js= self.linkto(item.longname, item.prettyname) ?>
|
const fires = item.fires.map(v => self.find({longname: v})[0] || {longname: v, name: v.split(/#?event:/)[1]});
|
||||||
<?js if (item.type === 'namespace' &&
|
listContent({fires: fires}, 'Fires', printListWithStability)
|
||||||
(item.members.length + item.typedefs.length + item.methods.length +
|
|
||||||
item.events.length > 0)) { ?>
|
|
||||||
<?js } ?>
|
|
||||||
</span>
|
|
||||||
<ul class="members itemMembers">
|
|
||||||
<?js
|
|
||||||
if (item.members.length) {
|
|
||||||
?>
|
|
||||||
<span class="subtitle">Members</span>
|
|
||||||
<?js
|
|
||||||
item.members.forEach(function (v) {
|
|
||||||
?>
|
|
||||||
<li data-name="<?js= v.longname ?>"><?js= self.linkto(v.longname, toShortName(v.name)) ?></li>
|
|
||||||
<?js
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
?>
|
}); ?>
|
||||||
</ul>
|
|
||||||
<ul class="typedefs itemMembers">
|
|
||||||
<?js
|
|
||||||
if (item.typedefs.length) {
|
|
||||||
?>
|
|
||||||
<span class="subtitle">Typedefs</span>
|
|
||||||
<?js
|
|
||||||
item.typedefs.forEach(function (v) {
|
|
||||||
?>
|
|
||||||
<li data-name="<?js= v.longname ?>" class="<?js= (v.stability && v.stability !== 'stable') ? 'unstable' : ''?>">
|
|
||||||
<?js= self.linkto(v.longname, toShortName(v.name)) ?>
|
|
||||||
</li>
|
|
||||||
<?js
|
|
||||||
});
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</ul>
|
|
||||||
<ul class="methods itemMembers">
|
|
||||||
<?js
|
|
||||||
if (item.methods.length) {
|
|
||||||
?>
|
|
||||||
<span class="subtitle">Methods</span>
|
|
||||||
<?js
|
|
||||||
|
|
||||||
item.methods.forEach(function (v) {
|
|
||||||
?>
|
|
||||||
<li data-name="<?js= v.longname ?>" class="<?js= (v.stability && v.stability !== 'stable') ? 'unstable' : ''?>">
|
|
||||||
<?js= self.linkto(v.longname, toShortName(v.name)) ?>
|
|
||||||
</li>
|
|
||||||
<?js
|
|
||||||
});
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</ul>
|
|
||||||
<ul class="fires itemMembers">
|
|
||||||
<?js
|
|
||||||
if (item.fires && item.fires.length) {
|
|
||||||
?>
|
|
||||||
<span class="subtitle">Fires</span>
|
|
||||||
<?js
|
|
||||||
item.fires.forEach(function (v) {
|
|
||||||
v = self.find({longname: v})[0] || {longname: v, name: v.split(/#?event:/)[1]};
|
|
||||||
?>
|
|
||||||
<li data-name="<?js= v.longname ?>" class="<?js= (v.stability && v.stability != 'stable') ? 'unstable' : '' ?>">
|
|
||||||
<?js= self.linkto(v.longname, toShortName(v.name)) ?>
|
|
||||||
</li>
|
|
||||||
<?js
|
|
||||||
});
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<?js }); ?>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ docs: >
|
|||||||
<p>When the Bing Maps tile service doesn't have tiles for a given resolution and region it returns "placeholder" tiles indicating that. Zoom the map beyond level 19 to see the "placeholder" tiles. If you want OpenLayers to display stretched tiles in place of "placeholder" tiles beyond zoom level 19 then set <code>maxZoom</code> to <code>19</code> in the options passed to <code>ol/source/BingMaps</code>.</p>
|
<p>When the Bing Maps tile service doesn't have tiles for a given resolution and region it returns "placeholder" tiles indicating that. Zoom the map beyond level 19 to see the "placeholder" tiles. If you want OpenLayers to display stretched tiles in place of "placeholder" tiles beyond zoom level 19 then set <code>maxZoom</code> to <code>19</code> in the options passed to <code>ol/source/BingMaps</code>.</p>
|
||||||
tags: "bing, bing-maps"
|
tags: "bing, bing-maps"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ for (i = 0, ii = styles.length; i < ii; ++i) {
|
|||||||
visible: false,
|
visible: false,
|
||||||
preload: Infinity,
|
preload: Infinity,
|
||||||
source: new BingMaps({
|
source: new BingMaps({
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||||
imagerySet: styles[i]
|
imagerySet: styles[i]
|
||||||
// use maxZoom 19 to see stretched tiles instead of the BingMaps
|
// use maxZoom 19 to see stretched tiles instead of the BingMaps
|
||||||
// "no photos at this zoom level" tiles
|
// "no photos at this zoom level" tiles
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||||||
<g>
|
<g>
|
||||||
<rect width="20" height="20" style="fill:#fff" />
|
<rect width="20" height="20" style="fill:#fff; stroke-width:4px; stroke:#000" />
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 225 B |
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
layout: example.html
|
layout: example.html
|
||||||
title: Earthquakes with custom symbols
|
title: Earthquakes with custom symbols
|
||||||
shortdesc: Demonstrates the use of `toCanvas` to create custom icon symbols.
|
shortdesc: Demonstrates the use of `toContext` to create custom icon symbols.
|
||||||
docs: >
|
docs: >
|
||||||
This example parses a KML file and renders the features as a vector layer. The layer is given a <code>style</code> that renders earthquake locations with a custom lightning symbol and a size relative to their magnitude.
|
This example parses a KML file and renders the features as a vector layer. The layer is given a <code>style</code> that renders earthquake locations with a custom lightning symbol and a size relative to their magnitude.
|
||||||
tags: "KML, vector, style, canvas, symbol"
|
tags: "KML, vector, style, canvas, symbol"
|
||||||
|
|||||||
9
examples/es2015-custom-element.html
Normal file
9
examples/es2015-custom-element.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Custom map element
|
||||||
|
shortdesc: Example of a custom element with a map.
|
||||||
|
docs: >
|
||||||
|
The example creates and registers a custom element, `ol-map`, which contains a simple map. **Note:** Only works in browsers that supports `ShadowRoot`.
|
||||||
|
tags: "es2015, web-component, custom-element, shadow-dom"
|
||||||
|
---
|
||||||
|
<ol-map id="map" class="map"></ol-map>
|
||||||
43
examples/es2015-custom-element.js
Normal file
43
examples/es2015-custom-element.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import TileLayer from '../src/ol/layer/Tile.js';
|
||||||
|
import OSM from '../src/ol/source/OSM.js';
|
||||||
|
|
||||||
|
class OLComponent extends HTMLElement {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.shadow = this.attachShadow({mode: 'open'});
|
||||||
|
const link = document.createElement('link');
|
||||||
|
link.setAttribute('rel', 'stylesheet');
|
||||||
|
link.setAttribute('href', 'css/ol.css');
|
||||||
|
this.shadow.appendChild(link);
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.innerText = `
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
this.shadow.appendChild(style);
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.style.width = '100%';
|
||||||
|
div.style.height = '100%';
|
||||||
|
this.shadow.appendChild(div);
|
||||||
|
|
||||||
|
this.map = new Map({
|
||||||
|
target: div,
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new OSM()
|
||||||
|
})
|
||||||
|
],
|
||||||
|
view: new View({
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define('ol-map', OLComponent);
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
.overlay {
|
|
||||||
background-color: yellow;
|
|
||||||
border-radius: 6px;
|
|
||||||
padding: 4px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
@@ -3,13 +3,9 @@ layout: example.html
|
|||||||
title: Map Export
|
title: Map Export
|
||||||
shortdesc: Example of exporting a map as a PNG image.
|
shortdesc: Example of exporting a map as a PNG image.
|
||||||
docs: >
|
docs: >
|
||||||
Example of exporting a map as a PNG image. This example use the <a href="https://www.npmjs.com/package/html-to-image">html-to-image</a>
|
Example of exporting a map as a PNG image.
|
||||||
library.
|
|
||||||
tags: "export, png, openstreetmap"
|
tags: "export, png, openstreetmap"
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
<div style="display: none;">
|
|
||||||
<div class="overlay" id="null">Null Island</div>
|
|
||||||
</div>
|
|
||||||
<a id="export-png" class="btn btn-default"><i class="fa fa-download"></i> Download PNG</a>
|
<a id="export-png" class="btn btn-default"><i class="fa fa-download"></i> Download PNG</a>
|
||||||
<a id="image-download" download="map.png"></a>
|
<a id="image-download" download="map.png"></a>
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
import Map from '../src/ol/Map.js';
|
import Map from '../src/ol/Map.js';
|
||||||
import View from '../src/ol/View.js';
|
import View from '../src/ol/View.js';
|
||||||
import Overlay from '../src/ol/Overlay.js';
|
|
||||||
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
||||||
|
|
||||||
import {toPng} from 'html-to-image';
|
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
layers: [
|
layers: [
|
||||||
new TileLayer({
|
new TileLayer({
|
||||||
@@ -16,7 +13,8 @@ const map = new Map({
|
|||||||
source: new VectorSource({
|
source: new VectorSource({
|
||||||
url: 'data/geojson/countries.geojson',
|
url: 'data/geojson/countries.geojson',
|
||||||
format: new GeoJSON()
|
format: new GeoJSON()
|
||||||
})
|
}),
|
||||||
|
opacity: 0.5
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
target: 'map',
|
target: 'map',
|
||||||
@@ -26,28 +24,33 @@ const map = new Map({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
map.addOverlay(new Overlay({
|
|
||||||
position: [0, 0],
|
|
||||||
element: document.getElementById('null')
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
// export options for html-to-image.
|
|
||||||
// See: https://github.com/bubkoo/html-to-image#options
|
|
||||||
const exportOptions = {
|
|
||||||
filter: function(element) {
|
|
||||||
return element.className ? element.className.indexOf('ol-control') === -1 : true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
document.getElementById('export-png').addEventListener('click', function() {
|
document.getElementById('export-png').addEventListener('click', function() {
|
||||||
map.once('rendercomplete', function() {
|
map.once('rendercomplete', function() {
|
||||||
toPng(map.getTargetElement(), exportOptions)
|
const mapCanvas = document.createElement('canvas');
|
||||||
.then(function(dataURL) {
|
const size = map.getSize();
|
||||||
const link = document.getElementById('image-download');
|
mapCanvas.width = size[0];
|
||||||
link.href = dataURL;
|
mapCanvas.height = size[1];
|
||||||
link.click();
|
const mapContext = mapCanvas.getContext('2d');
|
||||||
|
Array.prototype.forEach.call(document.querySelectorAll('.ol-layer canvas'), function(canvas) {
|
||||||
|
if (canvas.width > 0) {
|
||||||
|
const opacity = canvas.parentNode.style.opacity;
|
||||||
|
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
|
||||||
|
const transform = canvas.style.transform;
|
||||||
|
// Get the transform parameters from the style's transform matrix
|
||||||
|
const matrix = transform.match(/^matrix\(([^\(]*)\)$/)[1].split(',').map(Number);
|
||||||
|
// Apply the transform to the export map context
|
||||||
|
CanvasRenderingContext2D.prototype.setTransform.apply(mapContext, matrix);
|
||||||
|
mapContext.drawImage(canvas, 0, 0);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
if (navigator.msSaveBlob) {
|
||||||
|
// link download attribuute does not work on MS browsers
|
||||||
|
navigator.msSaveBlob(mapCanvas.msToBlob(), 'map.png');
|
||||||
|
} else {
|
||||||
|
const link = document.getElementById('image-download');
|
||||||
|
link.href = mapCanvas.toDataURL();
|
||||||
|
link.click();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
map.renderSync();
|
map.renderSync();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ docs: >
|
|||||||
Example of exporting a map as a PDF using the <a href="https://github.com/MrRio/jsPDF" target="_blank">jsPDF</a> library.
|
Example of exporting a map as a PDF using the <a href="https://github.com/MrRio/jsPDF" target="_blank">jsPDF</a> library.
|
||||||
tags: "export, pdf, openstreetmap"
|
tags: "export, pdf, openstreetmap"
|
||||||
resources:
|
resources:
|
||||||
- https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js
|
- https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js
|
||||||
---
|
---
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ feature.getGeometry().transform('EPSG:4326', 'EPSG:3857');
|
|||||||
const vector = new VectorLayer({
|
const vector = new VectorLayer({
|
||||||
source: new VectorSource({
|
source: new VectorSource({
|
||||||
features: [feature]
|
features: [feature]
|
||||||
})
|
}),
|
||||||
|
opacity: 0.5
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -62,8 +63,10 @@ exportButton.addEventListener('click', function() {
|
|||||||
mapCanvas.width = width;
|
mapCanvas.width = width;
|
||||||
mapCanvas.height = height;
|
mapCanvas.height = height;
|
||||||
const mapContext = mapCanvas.getContext('2d');
|
const mapContext = mapCanvas.getContext('2d');
|
||||||
document.querySelectorAll('.ol-layer canvas').forEach(function(canvas) {
|
Array.prototype.forEach.call(document.querySelectorAll('.ol-layer canvas'), function(canvas) {
|
||||||
if (canvas.width > 0) {
|
if (canvas.width > 0) {
|
||||||
|
const opacity = canvas.parentNode.style.opacity;
|
||||||
|
mapContext.globalAlpha = opacity === '' ? 1 : Number(opacity);
|
||||||
const transform = canvas.style.transform;
|
const transform = canvas.style.transform;
|
||||||
// Get the transform parameters from the style's transform matrix
|
// Get the transform parameters from the style's transform matrix
|
||||||
const matrix = transform.match(/^matrix\(([^\(]*)\)$/)[1].split(',').map(Number);
|
const matrix = transform.match(/^matrix\(([^\(]*)\)$/)[1].split(',').map(Number);
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ function updateView() {
|
|||||||
view.setCenter(getCenterWithHeading(c, -c[2], view.getResolution()));
|
view.setCenter(getCenterWithHeading(c, -c[2], view.getResolution()));
|
||||||
view.setRotation(-c[2]);
|
view.setRotation(-c[2]);
|
||||||
marker.setPosition(c);
|
marker.setPosition(c);
|
||||||
|
map.render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ rome.setStyle(new Style({
|
|||||||
image: new Icon({
|
image: new Icon({
|
||||||
color: '#8959A8',
|
color: '#8959A8',
|
||||||
crossOrigin: 'anonymous',
|
crossOrigin: 'anonymous',
|
||||||
|
imgSize: [20, 20],
|
||||||
src: 'data/square.svg'
|
src: 'data/square.svg'
|
||||||
})
|
})
|
||||||
}));
|
}));
|
||||||
|
|||||||
3
examples/layer-clipping-vector.css
Normal file
3
examples/layer-clipping-vector.css
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#map {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
9
examples/layer-clipping-vector.html
Normal file
9
examples/layer-clipping-vector.html
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Vector Clipping Layer
|
||||||
|
shortdesc: Vector Clipping Layer example
|
||||||
|
docs: >
|
||||||
|
Example of a clipping layer based on a vector source
|
||||||
|
tags: "clipping, openstreetmap, vector"
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
46
examples/layer-clipping-vector.js
Normal file
46
examples/layer-clipping-vector.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
|
import VectorSource from '../src/ol/source/Vector.js';
|
||||||
|
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||||
|
import OSM from '../src/ol/source/OSM.js';
|
||||||
|
import {Fill, Style} from '../src/ol/style.js';
|
||||||
|
import {getVectorContext} from '../src/ol/render.js';
|
||||||
|
import {fromLonLat} from '../src/ol/proj.js';
|
||||||
|
|
||||||
|
const base = new TileLayer({
|
||||||
|
source: new OSM()
|
||||||
|
});
|
||||||
|
|
||||||
|
const clipLayer = new VectorLayer({
|
||||||
|
style: null,
|
||||||
|
source: new VectorSource({
|
||||||
|
url:
|
||||||
|
'./data/geojson/switzerland.geojson',
|
||||||
|
format: new GeoJSON()
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = new Style({
|
||||||
|
fill: new Fill({
|
||||||
|
color: 'black'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
base.on('postrender', function(e) {
|
||||||
|
e.context.globalCompositeOperation = 'destination-in';
|
||||||
|
const vectorContext = getVectorContext(e);
|
||||||
|
clipLayer.getSource().forEachFeature(function(feature) {
|
||||||
|
vectorContext.drawFeature(feature, style);
|
||||||
|
});
|
||||||
|
e.context.globalCompositeOperation = 'source-over';
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [base, clipLayer],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: fromLonLat([8.23, 46.86]),
|
||||||
|
zoom: 7
|
||||||
|
})
|
||||||
|
});
|
||||||
6
examples/mapbox-style.css
Normal file
6
examples/mapbox-style.css
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.ol-rotate {
|
||||||
|
left: .5em;
|
||||||
|
bottom: .5em;
|
||||||
|
top: unset;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
layout: example-verbatim.html
|
layout: example.html
|
||||||
title: Vector tiles created from a Mapbox Style object
|
title: Vector tiles created from a Mapbox Style object
|
||||||
shortdesc: Example of using ol-mapbox-style with tiles from maptiler.com.
|
shortdesc: Example of using ol-mapbox-style with tiles from maptiler.com.
|
||||||
docs: >
|
docs: >
|
||||||
@@ -10,27 +10,4 @@ cloak:
|
|||||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
---
|
---
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="chrome=1">
|
|
||||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
|
||||||
<title>Mapbox Style objects with ol-mapbox-style</title>
|
|
||||||
<link rel="stylesheet" href="../css/ol.css" type="text/css">
|
|
||||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,String.prototype.startsWith,Object.assign"></script>
|
|
||||||
<style type="text/css">
|
|
||||||
html, body, .map {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
<script src="common.js"></script>
|
|
||||||
<script src="mapbox-style.js"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
import apply from 'ol-mapbox-style';
|
import apply from 'ol-mapbox-style';
|
||||||
|
import FullScreen from '../src/ol/control/FullScreen.js';
|
||||||
|
|
||||||
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB');
|
apply('map', 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB').then(function(map) {
|
||||||
|
map.addControl(new FullScreen());
|
||||||
|
});
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ title: Full-Screen Mobile
|
|||||||
shortdesc: Example of a full screen map.
|
shortdesc: Example of a full screen map.
|
||||||
tags: "fullscreen, geolocation, mobile"
|
tags: "fullscreen, geolocation, mobile"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||||
---
|
---
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const map = new Map({
|
|||||||
layers: [
|
layers: [
|
||||||
new TileLayer({
|
new TileLayer({
|
||||||
source: new BingMaps({
|
source: new BingMaps({
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||||
imagerySet: 'RoadOnDemand'
|
imagerySet: 'RoadOnDemand'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -232,6 +232,7 @@ const map = new Map({
|
|||||||
target: 'map',
|
target: 'map',
|
||||||
view: new View({
|
view: new View({
|
||||||
center: [0, 1000000],
|
center: [0, 1000000],
|
||||||
zoom: 2
|
zoom: 2,
|
||||||
|
multiWorld: true
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
9
examples/offscreen-canvas.css
Normal file
9
examples/offscreen-canvas.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.map {
|
||||||
|
background: rgba(232, 230, 223, 1);
|
||||||
|
}
|
||||||
|
.ol-rotate {
|
||||||
|
left: .5em;
|
||||||
|
bottom: .5em;
|
||||||
|
top: unset;
|
||||||
|
right: unset;
|
||||||
|
}
|
||||||
10
examples/offscreen-canvas.html
Normal file
10
examples/offscreen-canvas.html
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
layout: example.html
|
||||||
|
title: Vector tiles rendered in an offscreen canvas
|
||||||
|
shortdesc: Example of a map that delegates rendering to a worker.
|
||||||
|
docs: >
|
||||||
|
The map in this example is rendered in a web worker, using `OffscreenCanvas`. **Note:** This is currently only supported in Chrome and Edge.
|
||||||
|
tags: "worker, offscreencanvas, vector-tiles"
|
||||||
|
experimental: true
|
||||||
|
---
|
||||||
|
<div id="map" class="map"></div>
|
||||||
125
examples/offscreen-canvas.js
Normal file
125
examples/offscreen-canvas.js
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
import Map from '../src/ol/Map.js';
|
||||||
|
import View from '../src/ol/View.js';
|
||||||
|
import Layer from '../src/ol/layer/Layer.js';
|
||||||
|
import Worker from 'worker-loader!./offscreen-canvas.worker.js'; //eslint-disable-line
|
||||||
|
import {compose, create} from '../src/ol/transform.js';
|
||||||
|
import {createTransformString} from '../src/ol/render/canvas.js';
|
||||||
|
import {createXYZ} from '../src/ol/tilegrid.js';
|
||||||
|
import {FullScreen} from '../src/ol/control.js';
|
||||||
|
import stringify from 'json-stringify-safe';
|
||||||
|
import Source from '../src/ol/source/Source.js';
|
||||||
|
|
||||||
|
const worker = new Worker();
|
||||||
|
|
||||||
|
let container, transformContainer, canvas, rendering, workerFrameState, mainThreadFrameState;
|
||||||
|
|
||||||
|
// Transform the container to account for the differnece between the (newer)
|
||||||
|
// main thread frameState and the (older) worker frameState
|
||||||
|
function updateContainerTransform() {
|
||||||
|
if (workerFrameState) {
|
||||||
|
const viewState = mainThreadFrameState.viewState;
|
||||||
|
const renderedViewState = workerFrameState.viewState;
|
||||||
|
const center = viewState.center;
|
||||||
|
const resolution = viewState.resolution;
|
||||||
|
const rotation = viewState.rotation;
|
||||||
|
const renderedCenter = renderedViewState.center;
|
||||||
|
const renderedResolution = renderedViewState.resolution;
|
||||||
|
const renderedRotation = renderedViewState.rotation;
|
||||||
|
const transform = create();
|
||||||
|
// Skip the extra transform for rotated views, because it will not work
|
||||||
|
// correctly in that case
|
||||||
|
if (!rotation) {
|
||||||
|
compose(transform,
|
||||||
|
(renderedCenter[0] - center[0]) / resolution,
|
||||||
|
(center[1] - renderedCenter[1]) / resolution,
|
||||||
|
renderedResolution / resolution, renderedResolution / resolution,
|
||||||
|
rotation - renderedRotation,
|
||||||
|
0, 0);
|
||||||
|
}
|
||||||
|
transformContainer.style.transform = createTransformString(transform);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const map = new Map({
|
||||||
|
layers: [
|
||||||
|
new Layer({
|
||||||
|
render: function(frameState) {
|
||||||
|
if (!container) {
|
||||||
|
container = document.createElement('div');
|
||||||
|
container.style.position = 'absolute';
|
||||||
|
container.style.width = '100%';
|
||||||
|
container.style.height = '100%';
|
||||||
|
transformContainer = document.createElement('div');
|
||||||
|
transformContainer.style.position = 'absolute';
|
||||||
|
transformContainer.style.width = '100%';
|
||||||
|
transformContainer.style.height = '100%';
|
||||||
|
container.appendChild(transformContainer);
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.transformOrigin = 'top left';
|
||||||
|
transformContainer.appendChild(canvas);
|
||||||
|
}
|
||||||
|
mainThreadFrameState = frameState;
|
||||||
|
updateContainerTransform();
|
||||||
|
if (!rendering) {
|
||||||
|
rendering = true;
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'render',
|
||||||
|
frameState: JSON.parse(stringify(frameState))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
frameState.animate = true;
|
||||||
|
}
|
||||||
|
return container;
|
||||||
|
},
|
||||||
|
source: new Source({
|
||||||
|
attributions: [
|
||||||
|
'<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a>',
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>'
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
resolutions: createXYZ({tileSize: 512}).getResolutions89,
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2
|
||||||
|
})
|
||||||
|
});
|
||||||
|
map.addControl(new FullScreen());
|
||||||
|
|
||||||
|
// Worker messaging and actions
|
||||||
|
worker.addEventListener('message', message => {
|
||||||
|
if (message.data.action === 'loadImage') {
|
||||||
|
// Image loader for ol-mapbox-style
|
||||||
|
const image = new Image();
|
||||||
|
image.crossOrigin = 'anonymous';
|
||||||
|
image.addEventListener('load', function() {
|
||||||
|
createImageBitmap(image, 0, 0, image.width, image.height).then(imageBitmap => {
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'imageLoaded',
|
||||||
|
image: imageBitmap,
|
||||||
|
src: message.data.src
|
||||||
|
}, [imageBitmap]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
image.src = event.data.src;
|
||||||
|
} else if (message.data.action === 'requestRender') {
|
||||||
|
// Worker requested a new render frame
|
||||||
|
map.render();
|
||||||
|
} else if (canvas && message.data.action === 'rendered') {
|
||||||
|
// Worker provies a new render frame
|
||||||
|
requestAnimationFrame(function() {
|
||||||
|
const imageData = message.data.imageData;
|
||||||
|
canvas.width = imageData.width;
|
||||||
|
canvas.height = imageData.height;
|
||||||
|
canvas.getContext('2d').drawImage(imageData, 0, 0);
|
||||||
|
canvas.style.transform = message.data.transform;
|
||||||
|
workerFrameState = message.data.frameState;
|
||||||
|
updateContainerTransform();
|
||||||
|
});
|
||||||
|
rendering = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
136
examples/offscreen-canvas.worker.js
Normal file
136
examples/offscreen-canvas.worker.js
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
import VectorTileLayer from '../src/ol/layer/VectorTile.js';
|
||||||
|
import VectorTileSource from '../src/ol/source/VectorTile.js';
|
||||||
|
import MVT from '../src/ol/format/MVT.js';
|
||||||
|
import {Projection} from '../src/ol/proj.js';
|
||||||
|
import TileQueue from '../src/ol/TileQueue.js';
|
||||||
|
import {getTilePriority as tilePriorityFunction} from '../src/ol/TileQueue.js';
|
||||||
|
import {renderDeclutterItems} from '../src/ol/render.js';
|
||||||
|
import styleFunction from 'ol-mapbox-style/dist/stylefunction.js';
|
||||||
|
import {inView} from '../src/ol/layer/Layer.js';
|
||||||
|
import stringify from 'json-stringify-safe';
|
||||||
|
|
||||||
|
/** @type {any} */
|
||||||
|
const worker = self;
|
||||||
|
|
||||||
|
let frameState, pixelRatio, rendererTransform;
|
||||||
|
const canvas = new OffscreenCanvas(1, 1);
|
||||||
|
// OffscreenCanvas does not have a style, so we mock it
|
||||||
|
canvas.style = {};
|
||||||
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
|
const sources = {
|
||||||
|
landcover: new VectorTileSource({
|
||||||
|
maxZoom: 9,
|
||||||
|
format: new MVT(),
|
||||||
|
url: 'https://api.maptiler.com/tiles/landcover/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
}),
|
||||||
|
contours: new VectorTileSource({
|
||||||
|
minZoom: 9,
|
||||||
|
maxZoom: 14,
|
||||||
|
format: new MVT(),
|
||||||
|
url: 'https://api.maptiler.com/tiles/contours/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
}),
|
||||||
|
openmaptiles: new VectorTileSource({
|
||||||
|
format: new MVT(),
|
||||||
|
maxZoom: 14,
|
||||||
|
url: 'https://api.maptiler.com/tiles/v3/{z}/{x}/{y}.pbf?key=get_your_own_D6rA4zTHduk6KOKTXzGB'
|
||||||
|
})
|
||||||
|
};
|
||||||
|
const layers = [];
|
||||||
|
|
||||||
|
// Font replacement so we do not need to load web fonts in the worker
|
||||||
|
function getFont(font) {
|
||||||
|
return font[0]
|
||||||
|
.replace('Noto Sans', 'serif')
|
||||||
|
.replace('Roboto', 'sans-serif');
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadStyles() {
|
||||||
|
const styleUrl = 'https://api.maptiler.com/maps/topo/style.json?key=get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
|
||||||
|
fetch(styleUrl).then(data => data.json()).then(styleJson => {
|
||||||
|
const buckets = [];
|
||||||
|
let currentSource;
|
||||||
|
styleJson.layers.forEach(layer => {
|
||||||
|
if (!layer.source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (currentSource !== layer.source) {
|
||||||
|
currentSource = layer.source;
|
||||||
|
buckets.push({
|
||||||
|
source: layer.source,
|
||||||
|
layers: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
buckets[buckets.length - 1].layers.push(layer.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
const spriteUrl = styleJson.sprite + (pixelRatio > 1 ? '@2x' : '') + '.json';
|
||||||
|
const spriteImageUrl = styleJson.sprite + (pixelRatio > 1 ? '@2x' : '') + '.png';
|
||||||
|
fetch(spriteUrl).then(data => data.json()).then(spriteJson => {
|
||||||
|
buckets.forEach(bucket => {
|
||||||
|
const source = sources[bucket.source];
|
||||||
|
if (!source) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const layer = new VectorTileLayer({
|
||||||
|
declutter: true,
|
||||||
|
source,
|
||||||
|
minZoom: source.getTileGrid().getMinZoom()
|
||||||
|
});
|
||||||
|
layer.getRenderer().useContainer = function(target, transform) {
|
||||||
|
this.containerReused = this.getLayer() !== layers[0];
|
||||||
|
this.canvas = canvas;
|
||||||
|
this.context = context;
|
||||||
|
this.container = {
|
||||||
|
firstElementChild: canvas
|
||||||
|
};
|
||||||
|
rendererTransform = transform;
|
||||||
|
};
|
||||||
|
styleFunction(layer, styleJson, bucket.layers, undefined, spriteJson, spriteImageUrl, getFont);
|
||||||
|
layers.push(layer);
|
||||||
|
});
|
||||||
|
worker.postMessage({action: 'requestRender'});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Minimal map-like functionality for rendering
|
||||||
|
|
||||||
|
const tileQueue = new TileQueue(
|
||||||
|
(tile, tileSourceKey, tileCenter, tileResolution) => tilePriorityFunction(frameState, tile, tileSourceKey, tileCenter, tileResolution),
|
||||||
|
() => worker.postMessage({action: 'requestRender'}));
|
||||||
|
|
||||||
|
const maxTotalLoading = 8;
|
||||||
|
const maxNewLoads = 2;
|
||||||
|
|
||||||
|
worker.addEventListener('message', event => {
|
||||||
|
if (event.data.action !== 'render') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frameState = event.data.frameState;
|
||||||
|
if (!pixelRatio) {
|
||||||
|
pixelRatio = frameState.pixelRatio;
|
||||||
|
loadStyles();
|
||||||
|
}
|
||||||
|
frameState.tileQueue = tileQueue;
|
||||||
|
frameState.viewState.projection.__proto__ = Projection.prototype;
|
||||||
|
layers.forEach(layer => {
|
||||||
|
if (inView(layer.getLayerState(), frameState.viewState)) {
|
||||||
|
const renderer = layer.getRenderer();
|
||||||
|
renderer.renderFrame(frameState, canvas);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
renderDeclutterItems(frameState, null);
|
||||||
|
if (tileQueue.getTilesLoading() < maxTotalLoading) {
|
||||||
|
tileQueue.reprioritize();
|
||||||
|
tileQueue.loadMoreTiles(maxTotalLoading, maxNewLoads);
|
||||||
|
}
|
||||||
|
const imageData = canvas.transferToImageBitmap();
|
||||||
|
worker.postMessage({
|
||||||
|
action: 'rendered',
|
||||||
|
imageData: imageData,
|
||||||
|
transform: rendererTransform,
|
||||||
|
frameState: JSON.parse(stringify(frameState))
|
||||||
|
}, [imageData]);
|
||||||
|
});
|
||||||
@@ -6,7 +6,7 @@ docs: >
|
|||||||
<p>The map on the top preloads low resolution tiles. The map on the bottom does not use any preloading. Try zooming out and panning to see the difference.</p>
|
<p>The map on the top preloads low resolution tiles. The map on the bottom does not use any preloading. Try zooming out and panning to see the difference.</p>
|
||||||
tags: "preload, bing"
|
tags: "preload, bing"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||||
---
|
---
|
||||||
<div id="map1" class="map"></div>
|
<div id="map1" class="map"></div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ const map1 = new Map({
|
|||||||
new TileLayer({
|
new TileLayer({
|
||||||
preload: Infinity,
|
preload: Infinity,
|
||||||
source: new BingMaps({
|
source: new BingMaps({
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||||
imagerySet: 'Aerial'
|
imagerySet: 'Aerial'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -28,7 +28,7 @@ const map2 = new Map({
|
|||||||
new TileLayer({
|
new TileLayer({
|
||||||
preload: 0, // default value
|
preload: 0, // default value
|
||||||
source: new BingMaps({
|
source: new BingMaps({
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||||
imagerySet: 'AerialWithLabelsOnDemand'
|
imagerySet: 'AerialWithLabelsOnDemand'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,12 +6,12 @@ docs: >
|
|||||||
This example shows client-side raster reprojection capabilities from
|
This example shows client-side raster reprojection capabilities from
|
||||||
OSM (EPSG:3857) to arbitrary projection by searching
|
OSM (EPSG:3857) to arbitrary projection by searching
|
||||||
in <a href="https://epsg.io/">EPSG.io</a> database.
|
in <a href="https://epsg.io/">EPSG.io</a> database.
|
||||||
tags: "reprojection, projection, proj4js, epsg.io"
|
tags: "reprojection, projection, proj4js, epsg.io, graticule"
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
<form class="form-inline">
|
<form class="form-inline">
|
||||||
<label for="epsg-query">Search projection:</label>
|
<label for="epsg-query">Search projection:</label>
|
||||||
<input type="text" id="epsg-query" placeholder="4326, 27700, US National Atlas, Swiss, France, ..." class="form-control" size="50" />
|
<input type="text" id="epsg-query" placeholder="4326, 27700, 3031, US National Atlas, Swiss, France, ..." class="form-control" size="50" />
|
||||||
<button id="epsg-search" class="btn">Search</button>
|
<button id="epsg-search" class="btn">Search</button>
|
||||||
<span id="epsg-result"></span>
|
<span id="epsg-result"></span>
|
||||||
<div>
|
<div>
|
||||||
@@ -19,5 +19,9 @@ tags: "reprojection, projection, proj4js, epsg.io"
|
|||||||
Render reprojection edges
|
Render reprojection edges
|
||||||
<input type="checkbox" id="render-edges">
|
<input type="checkbox" id="render-edges">
|
||||||
</label>
|
</label>
|
||||||
|
<label for="show-graticule">
|
||||||
|
Show graticule
|
||||||
|
<input type="checkbox" id="show-graticule" />
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -1,19 +1,34 @@
|
|||||||
import Map from '../src/ol/Map.js';
|
import Map from '../src/ol/Map.js';
|
||||||
import View from '../src/ol/View.js';
|
import View from '../src/ol/View.js';
|
||||||
import {applyTransform} from '../src/ol/extent.js';
|
import {applyTransform} from '../src/ol/extent.js';
|
||||||
|
import Graticule from '../src/ol/layer/Graticule.js';
|
||||||
import TileLayer from '../src/ol/layer/Tile.js';
|
import TileLayer from '../src/ol/layer/Tile.js';
|
||||||
import {get as getProjection, getTransform} from '../src/ol/proj.js';
|
import {get as getProjection, getTransform} from '../src/ol/proj.js';
|
||||||
import {register} from '../src/ol/proj/proj4.js';
|
import {register} from '../src/ol/proj/proj4.js';
|
||||||
import OSM from '../src/ol/source/OSM.js';
|
import OSM from '../src/ol/source/OSM.js';
|
||||||
import TileImage from '../src/ol/source/TileImage.js';
|
import TileImage from '../src/ol/source/TileImage.js';
|
||||||
|
import Stroke from '../src/ol/style/Stroke.js';
|
||||||
import proj4 from 'proj4';
|
import proj4 from 'proj4';
|
||||||
|
|
||||||
|
|
||||||
|
const graticule = new Graticule({
|
||||||
|
// the style to use for the lines, optional.
|
||||||
|
strokeStyle: new Stroke({
|
||||||
|
color: 'rgba(255,120,0,0.9)',
|
||||||
|
width: 2,
|
||||||
|
lineDash: [0.5, 4]
|
||||||
|
}),
|
||||||
|
showLabels: true,
|
||||||
|
visible: false,
|
||||||
|
wrapX: false
|
||||||
|
});
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
layers: [
|
layers: [
|
||||||
new TileLayer({
|
new TileLayer({
|
||||||
source: new OSM()
|
source: new OSM()
|
||||||
})
|
}),
|
||||||
|
graticule
|
||||||
],
|
],
|
||||||
target: 'map',
|
target: 'map',
|
||||||
view: new View({
|
view: new View({
|
||||||
@@ -28,6 +43,7 @@ const queryInput = document.getElementById('epsg-query');
|
|||||||
const searchButton = document.getElementById('epsg-search');
|
const searchButton = document.getElementById('epsg-search');
|
||||||
const resultSpan = document.getElementById('epsg-result');
|
const resultSpan = document.getElementById('epsg-result');
|
||||||
const renderEdgesCheckbox = document.getElementById('render-edges');
|
const renderEdgesCheckbox = document.getElementById('render-edges');
|
||||||
|
const showGraticuleCheckbox = document.getElementById('show-graticule');
|
||||||
|
|
||||||
function setProjection(code, name, proj4def, bbox) {
|
function setProjection(code, name, proj4def, bbox) {
|
||||||
if (code === null || name === null || proj4def === null || bbox === null) {
|
if (code === null || name === null || proj4def === null || bbox === null) {
|
||||||
@@ -48,9 +64,15 @@ function setProjection(code, name, proj4def, bbox) {
|
|||||||
const newProj = getProjection(newProjCode);
|
const newProj = getProjection(newProjCode);
|
||||||
const fromLonLat = getTransform('EPSG:4326', newProj);
|
const fromLonLat = getTransform('EPSG:4326', newProj);
|
||||||
|
|
||||||
// very approximate calculation of projection extent
|
let worldExtent = [bbox[1], bbox[2], bbox[3], bbox[0]];
|
||||||
const extent = applyTransform(
|
newProj.setWorldExtent(worldExtent);
|
||||||
[bbox[1], bbox[2], bbox[3], bbox[0]], fromLonLat);
|
|
||||||
|
// approximate calculation of projection extent,
|
||||||
|
// checking if the world extent crosses the dateline
|
||||||
|
if (bbox[1] > bbox[3]) {
|
||||||
|
worldExtent = [bbox[1], bbox[2], bbox[3] + 360, bbox[0]];
|
||||||
|
}
|
||||||
|
const extent = applyTransform(worldExtent, fromLonLat, undefined, 8);
|
||||||
newProj.setExtent(extent);
|
newProj.setExtent(extent);
|
||||||
const newView = new View({
|
const newView = new View({
|
||||||
projection: newProj
|
projection: newProj
|
||||||
@@ -98,7 +120,7 @@ searchButton.onclick = function(event) {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle change event.
|
* Handle checkbox change event.
|
||||||
*/
|
*/
|
||||||
renderEdgesCheckbox.onchange = function() {
|
renderEdgesCheckbox.onchange = function() {
|
||||||
map.getLayers().forEach(function(layer) {
|
map.getLayers().forEach(function(layer) {
|
||||||
@@ -110,3 +132,10 @@ renderEdgesCheckbox.onchange = function() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle checkbox change event.
|
||||||
|
*/
|
||||||
|
showGraticuleCheckbox.onchange = function() {
|
||||||
|
graticule.setVisible(showGraticuleCheckbox.checked);
|
||||||
|
};
|
||||||
|
|||||||
@@ -57,6 +57,8 @@
|
|||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const html = document.getElementById('example-html-source').innerText;
|
const html = document.getElementById('example-html-source').innerText;
|
||||||
const js = document.getElementById('example-js-source').innerText;
|
const js = document.getElementById('example-js-source').innerText;
|
||||||
|
const workerContainer = document.getElementById('example-worker-source');
|
||||||
|
const worker = workerContainer ? workerContainer.innerText : undefined;
|
||||||
const pkgJson = document.getElementById('example-pkg-source').innerText;
|
const pkgJson = document.getElementById('example-pkg-source').innerText;
|
||||||
const form = document.getElementById('codepen-form');
|
const form = document.getElementById('codepen-form');
|
||||||
|
|
||||||
@@ -68,8 +70,7 @@
|
|||||||
|
|
||||||
Promise.all(promises)
|
Promise.all(promises)
|
||||||
.then(results => {
|
.then(results => {
|
||||||
const data = {
|
const files = {
|
||||||
files: {
|
|
||||||
'index.html': {
|
'index.html': {
|
||||||
content: html
|
content: html
|
||||||
},
|
},
|
||||||
@@ -82,7 +83,14 @@
|
|||||||
'sandbox.config.json': {
|
'sandbox.config.json': {
|
||||||
content: '{"template": "parcel"}'
|
content: '{"template": "parcel"}'
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
if (worker) {
|
||||||
|
files['worker.js'] = {
|
||||||
|
content: worker
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
const data = {
|
||||||
|
files: files
|
||||||
};
|
};
|
||||||
|
|
||||||
for (let i = 0; i < localResources.length; i++) {
|
for (let i = 0; i < localResources.length; i++) {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ docs: >
|
|||||||
tags: "draw, edit, modify, vector, snap"
|
tags: "draw, edit, modify, vector, snap"
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
<form id="options-form" automplete="off">
|
<form id="options-form" autocomplete="off">
|
||||||
<div class="radio">
|
<div class="radio">
|
||||||
<label>
|
<label>
|
||||||
<input type="radio" name="interaction" value="draw" id="draw" checked>
|
<input type="radio" name="interaction" value="draw" id="draw" checked>
|
||||||
|
|||||||
@@ -17,8 +17,8 @@ register(proj4);
|
|||||||
// and a world extent. These are required for the Graticule.
|
// and a world extent. These are required for the Graticule.
|
||||||
const sphereMollweideProjection = new Projection({
|
const sphereMollweideProjection = new Projection({
|
||||||
code: 'ESRI:53009',
|
code: 'ESRI:53009',
|
||||||
extent: [-9009954.605703328, -9009954.605703328,
|
extent: [-18019909.21177587, -9009954.605703328,
|
||||||
9009954.605703328, 9009954.605703328],
|
18019909.21177587, 9009954.605703328],
|
||||||
worldExtent: [-179, -89.99, 179, 89.99]
|
worldExtent: [-179, -89.99, 179, 89.99]
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -37,6 +37,6 @@ const map = new Map({
|
|||||||
view: new View({
|
view: new View({
|
||||||
center: [0, 0],
|
center: [0, 0],
|
||||||
projection: sphereMollweideProjection,
|
projection: sphereMollweideProjection,
|
||||||
zoom: 0
|
zoom: 1
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -67,9 +67,9 @@
|
|||||||
<link rel="stylesheet" href="./resources/prism/prism.css" type="text/css">
|
<link rel="stylesheet" href="./resources/prism/prism.css" type="text/css">
|
||||||
<link rel="stylesheet" href="./css/ol.css" type="text/css">
|
<link rel="stylesheet" href="./css/ol.css" type="text/css">
|
||||||
<link rel="stylesheet" href="./resources/layout.css" type="text/css">
|
<link rel="stylesheet" href="./resources/layout.css" type="text/css">
|
||||||
|
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"></script>
|
||||||
{{{ extraHead.local }}}
|
{{{ extraHead.local }}}
|
||||||
{{{ css.tag }}}
|
{{{ css.tag }}}
|
||||||
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,requestAnimationFrame,Element.prototype.classList,URL"></script>
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.7.1/clipboard.min.js"></script>
|
||||||
<title>{{ title }}</title>
|
<title>{{ title }}</title>
|
||||||
</head>
|
</head>
|
||||||
@@ -160,6 +160,14 @@
|
|||||||
<pre><legend>index.js</legend><code id="example-js-source" class="language-js">import 'ol/ol.css';
|
<pre><legend>index.js</legend><code id="example-js-source" class="language-js">import 'ol/ol.css';
|
||||||
{{ js.source }}</code></pre>
|
{{ js.source }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
|
{{#if worker.source}}
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="source-controls">
|
||||||
|
<a class="copy-button" id="copy-worker-button" data-clipboard-target="#example-worker-source"><i class="fa fa-clipboard"></i> Copy</a>
|
||||||
|
</div>
|
||||||
|
<pre><legend>worker.js</legend><code id="example-worker-source" class="language-js">{{ worker.source }}</code></pre>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="source-controls">
|
<div class="source-controls">
|
||||||
<a class="copy-button" id="copy-pkg-button" data-clipboard-target="#example-pkg-source"><i class="fa fa-clipboard"></i> Copy</a>
|
<a class="copy-button" id="copy-pkg-button" data-clipboard-target="#example-pkg-source"><i class="fa fa-clipboard"></i> Copy</a>
|
||||||
@@ -167,7 +175,6 @@
|
|||||||
<pre><legend>package.json</legend><code id="example-pkg-source" class="language-js">{{ pkgJson }}</code></pre>
|
<pre><legend>package.json</legend><code id="example-pkg-source" class="language-js">{{ pkgJson }}</code></pre>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="./resources/common.js"></script>
|
<script src="./resources/common.js"></script>
|
||||||
<script src="./resources/prism/prism.min.js"></script>
|
<script src="./resources/prism/prism.min.js"></script>
|
||||||
{{{ js.tag }}}
|
{{{ js.tag }}}
|
||||||
|
|||||||
@@ -1,19 +1,5 @@
|
|||||||
This folder contains example templates. These templates are used to build the examples in the `examples/` folder. The resulting examples are written to the `build/examples` folder.
|
This folder contains example templates. These templates are used to build the examples in the `examples/` folder. The resulting examples are written to the `build/examples` folder.
|
||||||
|
|
||||||
Although the main purpose of these examples is to demonstrate how to use the API, they also serve other purposes in the development cycle, and so are not exactly as they would be in normal application code:
|
The examples are transpiled to es5. If the name of an example starts with `es2015-`, transpilation will be bypassed. This allows for showing es2015+ features in examples.
|
||||||
|
|
||||||
* every time the library changes, they are compiled together with the library as a basic check that they remain in sync with the library
|
|
||||||
|
|
||||||
* they use a special loader script to enable defining at run time which build mode (raw/debug/advanced) to use
|
|
||||||
|
|
||||||
To enable this, examples have the following, not needed in application code:
|
|
||||||
|
|
||||||
* each html file loads `loader.js`; application code would not need this, but would instead load the appropriate library build file, either a hosted version or a custom build
|
|
||||||
|
|
||||||
* each js file starts with `goog.require` functions, used by the compiler; application code would only have these if the code is to be compiled together with the library and/or Closure library
|
|
||||||
|
|
||||||
* some js files use type definitions (comments with @type tags); these are also used by the compiler, and are only needed if the code is to be compiled together with the library
|
|
||||||
|
|
||||||
* html files load `resources/common.js` and some scripts use `common.getRendererFromQueryString()` to set the map renderer; application code would not need these
|
|
||||||
|
|
||||||
At the bottom of each example generated in the `build/examples` folder, a modified version of its source code is shown. That modified version can be run standalone and is usually used as starting point for users to extend examples into their own application.
|
At the bottom of each example generated in the `build/examples` folder, a modified version of its source code is shown. That modified version can be run standalone and is usually used as starting point for users to extend examples into their own application.
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ title: OSM XML
|
|||||||
shortdesc: Example of using the OSM XML source.
|
shortdesc: Example of using the OSM XML source.
|
||||||
docs: >
|
docs: >
|
||||||
OSM XML vector data is loaded dynamically from a the [Overpass API](http://overpass-api.de) using a bbox strategy. Note that panning and zooming will eventually lead to "Too many requests" errors from the Overpass API.
|
OSM XML vector data is loaded dynamically from a the [Overpass API](http://overpass-api.de) using a bbox strategy. Note that panning and zooming will eventually lead to "Too many requests" errors from the Overpass API.
|
||||||
tags: "vector, osmxml, loading, server, strategy, bbox"
|
tags: "vector, osmxml, loading, server, strategy, bbox, maptiler"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import OSMXML from '../src/ol/format/OSMXML.js';
|
|||||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
import {bbox as bboxStrategy} from '../src/ol/loadingstrategy.js';
|
import {bbox as bboxStrategy} from '../src/ol/loadingstrategy.js';
|
||||||
import {transformExtent} from '../src/ol/proj.js';
|
import {transformExtent} from '../src/ol/proj.js';
|
||||||
import BingMaps from '../src/ol/source/BingMaps.js';
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
import VectorSource from '../src/ol/source/Vector.js';
|
import VectorSource from '../src/ol/source/Vector.js';
|
||||||
import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js';
|
import {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js';
|
||||||
|
|
||||||
@@ -85,8 +85,8 @@ const vectorSource = new VectorSource({
|
|||||||
vectorSource.addFeatures(features);
|
vectorSource.addFeatures(features);
|
||||||
});
|
});
|
||||||
const query = '(node(' +
|
const query = '(node(' +
|
||||||
epsg4326Extent[1] + ',' + epsg4326Extent[0] + ',' +
|
epsg4326Extent[1] + ',' + Math.max(epsg4326Extent[0], -180) + ',' +
|
||||||
epsg4326Extent[3] + ',' + epsg4326Extent[2] +
|
epsg4326Extent[3] + ',' + Math.min(epsg4326Extent[2], 180) +
|
||||||
');rel(bn)->.foo;way(bn);node(w)->.foo;rel(bw););out meta;';
|
');rel(bn)->.foo;way(bn);node(w)->.foo;rel(bw););out meta;';
|
||||||
client.send(query);
|
client.send(query);
|
||||||
},
|
},
|
||||||
@@ -110,10 +110,15 @@ const vector = new VectorLayer({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
|
||||||
|
|
||||||
const raster = new TileLayer({
|
const raster = new TileLayer({
|
||||||
source: new BingMaps({
|
source: new XYZ({
|
||||||
imagerySet: 'Aerial',
|
attributions: attributions,
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
|
||||||
|
maxZoom: 20
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ docs: >
|
|||||||
This example generates a `GetFeature` request which uses a `PropertyIsEqualTo`
|
This example generates a `GetFeature` request which uses a `PropertyIsEqualTo`
|
||||||
and a `PropertyIsLike` filter, and then posts the request to load the features
|
and a `PropertyIsLike` filter, and then posts the request to load the features
|
||||||
that match the query.
|
that match the query.
|
||||||
tags: "vector, WFS, GetFeature, filter"
|
tags: "vector, WFS, GetFeature, filter, maptiler"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
} from '../src/ol/format/filter.js';
|
} from '../src/ol/format/filter.js';
|
||||||
import {WFS, GeoJSON} from '../src/ol/format.js';
|
import {WFS, GeoJSON} from '../src/ol/format.js';
|
||||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
import BingMaps from '../src/ol/source/BingMaps.js';
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
import VectorSource from '../src/ol/source/Vector.js';
|
import VectorSource from '../src/ol/source/Vector.js';
|
||||||
import {Stroke, Style} from '../src/ol/style.js';
|
import {Stroke, Style} from '../src/ol/style.js';
|
||||||
|
|
||||||
@@ -23,10 +23,15 @@ const vector = new VectorLayer({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
|
||||||
|
|
||||||
const raster = new TileLayer({
|
const raster = new TileLayer({
|
||||||
source: new BingMaps({
|
source: new XYZ({
|
||||||
imagerySet: 'Aerial',
|
attributions: attributions,
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
|
||||||
|
maxZoom: 20
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ title: WFS
|
|||||||
shortdesc: Example of using WFS with a BBOX strategy.
|
shortdesc: Example of using WFS with a BBOX strategy.
|
||||||
docs: >
|
docs: >
|
||||||
This example loads new features from GeoServer WFS when the view extent changes.
|
This example loads new features from GeoServer WFS when the view extent changes.
|
||||||
tags: "vector, WFS, bbox, loading, server"
|
tags: "vector, WFS, bbox, loading, server, maptiler"
|
||||||
cloak:
|
cloak:
|
||||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import View from '../src/ol/View.js';
|
|||||||
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||||
import {bbox as bboxStrategy} from '../src/ol/loadingstrategy.js';
|
import {bbox as bboxStrategy} from '../src/ol/loadingstrategy.js';
|
||||||
import BingMaps from '../src/ol/source/BingMaps.js';
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
import VectorSource from '../src/ol/source/Vector.js';
|
import VectorSource from '../src/ol/source/Vector.js';
|
||||||
import {Stroke, Style} from '../src/ol/style.js';
|
import {Stroke, Style} from '../src/ol/style.js';
|
||||||
|
|
||||||
@@ -30,10 +30,15 @@ const vector = new VectorLayer({
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const key = 'get_your_own_D6rA4zTHduk6KOKTXzGB';
|
||||||
|
const attributions = '<a href="https://www.maptiler.com/copyright/" target="_blank">© MapTiler</a> ' +
|
||||||
|
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>';
|
||||||
|
|
||||||
const raster = new TileLayer({
|
const raster = new TileLayer({
|
||||||
source: new BingMaps({
|
source: new XYZ({
|
||||||
imagerySet: 'Aerial',
|
attributions: attributions,
|
||||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
|
||||||
|
maxZoom: 20
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ module.exports = {
|
|||||||
stats: 'minimal',
|
stats: 'minimal',
|
||||||
module: {
|
module: {
|
||||||
rules: [{
|
rules: [{
|
||||||
test: /\.js$/,
|
test: /^((?!es2015-)[\s\S])*\.js$/,
|
||||||
use: {
|
use: {
|
||||||
loader: 'buble-loader'
|
loader: 'buble-loader'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -208,6 +208,10 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
|||||||
jsSource = jsSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
jsSource = jsSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Remove worker loader import and modify `new Worker()` to add source
|
||||||
|
jsSource = jsSource.replace(/import Worker from 'worker-loader![^\n]*\n/g, '');
|
||||||
|
jsSource = jsSource.replace('new Worker()', 'new Worker(\'./worker.js\')');
|
||||||
|
|
||||||
data.js = {
|
data.js = {
|
||||||
tag: `<script src="${this.common}.js"></script><script src="${jsName}"></script>`,
|
tag: `<script src="${this.common}.js"></script><script src="${jsName}"></script>`,
|
||||||
source: jsSource
|
source: jsSource
|
||||||
@@ -218,9 +222,33 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
|||||||
data.js.tag = prelude + data.js.tag;
|
data.js.tag = prelude + data.js.tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for worker js
|
||||||
|
const workerName = `${name}.worker.js`;
|
||||||
|
const workerPath = path.join(dir, workerName);
|
||||||
|
let workerSource;
|
||||||
|
try {
|
||||||
|
workerSource = await readFile(workerPath, readOptions);
|
||||||
|
} catch (err) {
|
||||||
|
// pass
|
||||||
|
}
|
||||||
|
if (workerSource) {
|
||||||
|
// remove "../src/" prefix and ".js" to have the same import syntax as the documentation
|
||||||
|
workerSource = workerSource.replace(/'\.\.\/src\//g, '\'');
|
||||||
|
workerSource = workerSource.replace(/\.js';/g, '\';');
|
||||||
|
if (data.cloak) {
|
||||||
|
for (const entry of data.cloak) {
|
||||||
|
workerSource = workerSource.replace(new RegExp(entry.key, 'g'), entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.worker = {
|
||||||
|
source: workerSource
|
||||||
|
};
|
||||||
|
assets[workerName] = workerSource;
|
||||||
|
}
|
||||||
|
|
||||||
data.pkgJson = JSON.stringify({
|
data.pkgJson = JSON.stringify({
|
||||||
name: name,
|
name: name,
|
||||||
dependencies: getDependencies(jsSource),
|
dependencies: getDependencies(jsSource + workerSource ? `\n${workerSource}` : ''),
|
||||||
devDependencies: {
|
devDependencies: {
|
||||||
parcel: '1.11.0'
|
parcel: '1.11.0'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ layout: example.html
|
|||||||
title: ArcGIS REST with 512x512 Tiles
|
title: ArcGIS REST with 512x512 Tiles
|
||||||
shortdesc: Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.
|
shortdesc: Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.
|
||||||
docs: >
|
docs: >
|
||||||
ArcGIS REST tile services with custom tile sizes (here: 512x512 pixels) and projection (here: EPSG:4326) are supported by `ol/source/XYZ`. A custom tile url function is used to handle zoom level offsets.
|
ArcGIS REST tile services with custom tile sizes (here: 512x512 pixels) and projection (here: EPSG:4326) are supported by `ol/source/XYZ`.
|
||||||
tags: "xyz, esri, tilesize, custom projection"
|
tags: "xyz, esri, tilesize, custom projection"
|
||||||
---
|
---
|
||||||
<div id="map" class="map"></div>
|
<div id="map" class="map"></div>
|
||||||
|
|||||||
@@ -3,26 +3,18 @@ import View from '../src/ol/View.js';
|
|||||||
import TileLayer from '../src/ol/layer/Tile.js';
|
import TileLayer from '../src/ol/layer/Tile.js';
|
||||||
import XYZ from '../src/ol/source/XYZ.js';
|
import XYZ from '../src/ol/source/XYZ.js';
|
||||||
|
|
||||||
// The tile size supported by the ArcGIS tile service.
|
|
||||||
const tileSize = 512;
|
|
||||||
|
|
||||||
const urlTemplate = 'https://services.arcgisonline.com/arcgis/rest/services/' +
|
|
||||||
'ESRI_Imagery_World_2D/MapServer/tile/{z}/{y}/{x}';
|
|
||||||
|
|
||||||
const map = new Map({
|
const map = new Map({
|
||||||
target: 'map',
|
target: 'map',
|
||||||
layers: [
|
layers: [
|
||||||
new TileLayer({
|
new TileLayer({
|
||||||
source: new XYZ({
|
source: new XYZ({
|
||||||
attributions: 'Copyright:© 2013 ESRI, i-cubed, GeoEye',
|
attributions: 'Copyright:© 2013 ESRI, i-cubed, GeoEye',
|
||||||
maxZoom: 16,
|
url: 'https://services.arcgisonline.com/arcgis/rest/services/' +
|
||||||
|
'ESRI_Imagery_World_2D/MapServer/tile/{z}/{y}/{x}',
|
||||||
|
maxZoom: 15,
|
||||||
projection: 'EPSG:4326',
|
projection: 'EPSG:4326',
|
||||||
tileSize: tileSize,
|
tileSize: 512, // the tile size supported by the ArcGIS tile service
|
||||||
tileUrlFunction: function(tileCoord) {
|
maxResolution: 180 / 512, // Esri's tile grid fits 180 degrees on one 512 px tile
|
||||||
return urlTemplate.replace('{z}', (tileCoord[0] - 1).toString())
|
|
||||||
.replace('{x}', tileCoord[1].toString())
|
|
||||||
.replace('{y}', tileCoord[2].toString());
|
|
||||||
},
|
|
||||||
wrapX: true
|
wrapX: true
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
2839
package-lock.json
generated
2839
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
35
package.json
35
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ol",
|
"name": "ol",
|
||||||
"version": "6.2.0",
|
"version": "6.3.1",
|
||||||
"description": "OpenLayers mapping library",
|
"description": "OpenLayers mapping library",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"map",
|
"map",
|
||||||
@@ -24,6 +24,7 @@
|
|||||||
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
|
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
|
||||||
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && node tasks/serialize-workers && tsc --project config/tsconfig-build.json",
|
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && node tasks/serialize-workers && tsc --project config/tsconfig-build.json",
|
||||||
"typecheck": "tsc --pretty",
|
"typecheck": "tsc --pretty",
|
||||||
|
"apidoc-debug": "shx rm -rf build/apidoc && node --inspect-brk=9229 ./node_modules/jsdoc/jsdoc.js -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc",
|
||||||
"apidoc": "shx rm -rf build/apidoc && jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
|
"apidoc": "shx rm -rf build/apidoc && jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
|
||||||
},
|
},
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
@@ -35,6 +36,10 @@
|
|||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/openlayers/openlayers/issues"
|
"url": "https://github.com/openlayers/openlayers/issues"
|
||||||
},
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/openlayers"
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"elm-pep": "^1.0.4",
|
"elm-pep": "^1.0.4",
|
||||||
"pbf": "3.2.1",
|
"pbf": "3.2.1",
|
||||||
@@ -50,58 +55,58 @@
|
|||||||
"@types/pbf": "^3.0.2",
|
"@types/pbf": "^3.0.2",
|
||||||
"@types/topojson-specification": "^1.0.1",
|
"@types/topojson-specification": "^1.0.1",
|
||||||
"babel-loader": "^8.0.5",
|
"babel-loader": "^8.0.5",
|
||||||
"buble": "^0.19.7",
|
"buble": "^0.20.0",
|
||||||
"buble-loader": "^0.5.1",
|
"buble-loader": "^0.5.1",
|
||||||
"chaikin-smooth": "^1.0.4",
|
"chaikin-smooth": "^1.0.4",
|
||||||
"clean-css-cli": "4.3.0",
|
"clean-css-cli": "4.3.0",
|
||||||
"copy-webpack-plugin": "^5.0.4",
|
"copy-webpack-plugin": "^5.0.4",
|
||||||
"coveralls": "3.0.9",
|
"coveralls": "3.0.11",
|
||||||
"eslint": "^6.0.0",
|
"eslint": "^6.0.0",
|
||||||
"eslint-config-openlayers": "^13.0.0",
|
"eslint-config-openlayers": "^13.0.0",
|
||||||
"expect.js": "0.3.1",
|
"expect.js": "0.3.1",
|
||||||
"front-matter": "^3.0.2",
|
"front-matter": "^3.0.2",
|
||||||
"fs-extra": "^8.0.0",
|
"fs-extra": "^9.0.0",
|
||||||
"glob": "^7.1.5",
|
"glob": "^7.1.5",
|
||||||
"globby": "^11.0.0",
|
"globby": "^11.0.0",
|
||||||
"handlebars": "4.7.3",
|
"handlebars": "4.7.4",
|
||||||
"html-to-image": "^0.1.0",
|
|
||||||
"istanbul": "0.4.5",
|
"istanbul": "0.4.5",
|
||||||
"istanbul-instrumenter-loader": "^3.0.1",
|
"istanbul-instrumenter-loader": "^3.0.1",
|
||||||
"jquery": "3.4.1",
|
"jquery": "3.4.1",
|
||||||
"jsdoc": "3.6.3",
|
"jsdoc": "3.6.3",
|
||||||
"jsdoc-plugin-typescript": "^2.0.5",
|
"jsdoc-plugin-typescript": "^2.0.5",
|
||||||
|
"json-stringify-safe": "^5.0.1",
|
||||||
"karma": "^4.4.1",
|
"karma": "^4.4.1",
|
||||||
"karma-chrome-launcher": "3.1.0",
|
"karma-chrome-launcher": "3.1.0",
|
||||||
"karma-coverage": "^2.0.1",
|
"karma-coverage-istanbul-reporter": "^2.1.1",
|
||||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
|
||||||
"karma-firefox-launcher": "^1.1.0",
|
"karma-firefox-launcher": "^1.1.0",
|
||||||
"karma-mocha": "1.3.0",
|
"karma-mocha": "1.3.0",
|
||||||
"karma-sourcemap-loader": "^0.3.7",
|
"karma-sourcemap-loader": "^0.3.7",
|
||||||
"karma-webpack": "^4.0.0-rc.2",
|
"karma-webpack": "^4.0.0-rc.2",
|
||||||
"loglevelnext": "^3.0.1",
|
"loglevelnext": "^3.0.1",
|
||||||
"marked": "0.8.0",
|
"marked": "0.8.2",
|
||||||
"mocha": "7.0.1",
|
"mocha": "7.1.1",
|
||||||
"ol-mapbox-style": "^6.0.0",
|
"ol-mapbox-style": "^6.1.1",
|
||||||
"pixelmatch": "^5.1.0",
|
"pixelmatch": "^5.1.0",
|
||||||
"pngjs": "^3.4.0",
|
"pngjs": "^3.4.0",
|
||||||
"proj4": "2.6.0",
|
"proj4": "2.6.1",
|
||||||
"puppeteer": "~2.1.0",
|
"puppeteer": "~2.1.0",
|
||||||
"rollup": "^1.25.1",
|
"rollup": "^2.1.0",
|
||||||
"rollup-plugin-babel": "^4.3.3",
|
"rollup-plugin-babel": "^4.3.3",
|
||||||
"rollup-plugin-commonjs": "^10.0.0",
|
"rollup-plugin-commonjs": "^10.0.0",
|
||||||
"rollup-plugin-node-resolve": "^5.2.0",
|
"rollup-plugin-node-resolve": "^5.2.0",
|
||||||
"rollup-plugin-terser": "^5.0.0",
|
"rollup-plugin-terser": "^5.0.0",
|
||||||
"serve-static": "^1.14.0",
|
"serve-static": "^1.14.0",
|
||||||
"shx": "^0.3.2",
|
"shx": "^0.3.2",
|
||||||
"sinon": "^8.0.0",
|
"sinon": "^9.0.0",
|
||||||
"terser-webpack-plugin": "^2.0.1",
|
"terser-webpack-plugin": "^2.0.1",
|
||||||
"typescript": "3.5.3",
|
"typescript": "3.5.3",
|
||||||
"url-polyfill": "^1.1.5",
|
"url-polyfill": "^1.1.5",
|
||||||
"walk": "^2.3.9",
|
"walk": "^2.3.9",
|
||||||
"webpack": "4.41.5",
|
"webpack": "4.42.1",
|
||||||
"webpack-cli": "^3.3.2",
|
"webpack-cli": "^3.3.2",
|
||||||
"webpack-dev-middleware": "^3.6.2",
|
"webpack-dev-middleware": "^3.6.2",
|
||||||
"webpack-dev-server": "^3.3.1",
|
"webpack-dev-server": "^3.3.1",
|
||||||
|
"worker-loader": "^2.0.0",
|
||||||
"yargs": "^15.0.2"
|
"yargs": "^15.0.2"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|||||||
BIN
rendering/cases/icon-opacity/expected.png
Normal file
BIN
rendering/cases/icon-opacity/expected.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 119 KiB |
47
rendering/cases/icon-opacity/main.js
Normal file
47
rendering/cases/icon-opacity/main.js
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import Map from '../../../src/ol/Map.js';
|
||||||
|
import View from '../../../src/ol/View.js';
|
||||||
|
import {Vector as VectorLayer, Tile as TileLayer} from '../../../src/ol/layer.js';
|
||||||
|
import {Vector as VectorSource, XYZ} from '../../../src/ol/source.js';
|
||||||
|
import Point from '../../../src/ol/geom/Point.js';
|
||||||
|
import Feature from '../../../src/ol/Feature.js';
|
||||||
|
import {fromLonLat} from '../../../src/ol/proj.js';
|
||||||
|
import {Style, Icon} from '../../../src/ol/style.js';
|
||||||
|
|
||||||
|
const center = fromLonLat([8.6, 50.1]);
|
||||||
|
|
||||||
|
new Map({
|
||||||
|
layers: [
|
||||||
|
new TileLayer({
|
||||||
|
source: new XYZ({
|
||||||
|
url: '/data/tiles/satellite/{z}/{x}/{y}.jpg'
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
new VectorLayer({
|
||||||
|
style: function() {
|
||||||
|
return new Style({
|
||||||
|
image: new Icon({
|
||||||
|
opacity: 0.5,
|
||||||
|
src: '/data/icon.png',
|
||||||
|
anchor: [0.5, 46],
|
||||||
|
anchorXUnits: 'fraction',
|
||||||
|
anchorYUnits: 'pixels'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
},
|
||||||
|
source: new VectorSource({
|
||||||
|
features: [
|
||||||
|
new Feature(
|
||||||
|
new Point(center)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
target: 'map',
|
||||||
|
view: new View({
|
||||||
|
center: center,
|
||||||
|
zoom: 3
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
render();
|
||||||
BIN
sponsor-logos/pozi.png
Normal file
BIN
sponsor-logos/pozi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -26,7 +26,7 @@ export class CollectionEvent extends Event {
|
|||||||
/**
|
/**
|
||||||
* @param {CollectionEventType} type Type.
|
* @param {CollectionEventType} type Type.
|
||||||
* @param {*=} opt_element Element.
|
* @param {*=} opt_element Element.
|
||||||
* @param {number} opt_index The index of the added or removed element.
|
* @param {number=} opt_index The index of the added or removed element.
|
||||||
*/
|
*/
|
||||||
constructor(type, opt_element, opt_index) {
|
constructor(type, opt_element, opt_index) {
|
||||||
super(type);
|
super(type);
|
||||||
|
|||||||
@@ -275,7 +275,9 @@ class MapBrowserEventHandler extends EventTarget {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
handleTouchMove_(event) {
|
handleTouchMove_(event) {
|
||||||
if (this.originalPointerMoveEvent_.defaultPrevented) {
|
// Due to https://github.com/mpizenberg/elm-pep/issues/2, `this.originalPointerMoveEvent_`
|
||||||
|
// may not be initialized yet when we get here on a platform without native pointer events.
|
||||||
|
if (!this.originalPointerMoveEvent_ || this.originalPointerMoveEvent_.defaultPrevented) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class Observable extends EventTarget {
|
|||||||
/**
|
/**
|
||||||
* Listen for a certain type of event.
|
* Listen for a certain type of event.
|
||||||
* @param {string|Array<string>} type The event type or array of event types.
|
* @param {string|Array<string>} type The event type or array of event types.
|
||||||
* @param {function(?): ?} listener The listener function.
|
* @param {import("./events.js").ListenerFunction} listener The listener function.
|
||||||
* @return {import("./events.js").EventsKey|Array<import("./events.js").EventsKey>} Unique key for the listener. If
|
* @return {import("./events.js").EventsKey|Array<import("./events.js").EventsKey>} Unique key for the listener. If
|
||||||
* called with an array of event types as the first argument, the return
|
* called with an array of event types as the first argument, the return
|
||||||
* will be an array of keys.
|
* will be an array of keys.
|
||||||
|
|||||||
@@ -37,15 +37,21 @@ import {containsExtent} from './extent.js';
|
|||||||
* container as that of the controls (see the `stopEvent` option) you will
|
* container as that of the controls (see the `stopEvent` option) you will
|
||||||
* probably set `insertFirst` to `true` so the overlay is displayed below the
|
* probably set `insertFirst` to `true` so the overlay is displayed below the
|
||||||
* controls.
|
* controls.
|
||||||
* @property {boolean} [autoPan=false] If set to `true` the map is panned when
|
* @property {PanIntoViewOptions|boolean} [autoPan=false] Pan the map when calling
|
||||||
* calling `setPosition`, so that the overlay is entirely visible in the current
|
* `setPosition`, so that the overlay is entirely visible in the current viewport?
|
||||||
* viewport.
|
* If `true` (deprecated), then `autoPanAnimation` and `autoPanMargin` will be
|
||||||
* @property {PanOptions} [autoPanAnimation] The
|
* used to determine the panning parameters; if an object is supplied then other
|
||||||
* animation options used to pan the overlay into view. This animation is only
|
* parameters are ignored.
|
||||||
* used when `autoPan` is enabled. A `duration` and `easing` may be provided to
|
* @property {PanOptions} [autoPanAnimation] The animation options used to pan
|
||||||
* customize the animation.
|
* the overlay into view. This animation is only used when `autoPan` is enabled.
|
||||||
|
* A `duration` and `easing` may be provided to customize the animation.
|
||||||
|
* Deprecated and ignored if `autoPan` is supplied as an object.
|
||||||
* @property {number} [autoPanMargin=20] The margin (in pixels) between the
|
* @property {number} [autoPanMargin=20] The margin (in pixels) between the
|
||||||
* overlay and the borders of the map when autopanning.
|
* overlay and the borders of the map when autopanning. Deprecated and ignored
|
||||||
|
* if `autoPan` is supplied as an object.
|
||||||
|
* @property {PanIntoViewOptions} [autoPanOptions] The options to use for the
|
||||||
|
* autoPan. This is only used when `autoPan` is enabled and has preference over
|
||||||
|
* the individual `autoPanMargin` and `autoPanOptions`.
|
||||||
* @property {string} [className='ol-overlay-container ol-selectable'] CSS class
|
* @property {string} [className='ol-overlay-container ol-selectable'] CSS class
|
||||||
* name.
|
* name.
|
||||||
*/
|
*/
|
||||||
@@ -60,6 +66,12 @@ import {containsExtent} from './extent.js';
|
|||||||
* Default is {@link module:ol/easing~inAndOut}.
|
* Default is {@link module:ol/easing~inAndOut}.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} PanIntoViewOptions
|
||||||
|
* @property {PanOptions} [animation={}] The animation parameters for the pan
|
||||||
|
* @property {number} [margin=20] The margin (in pixels) between the
|
||||||
|
* overlay and the borders of the map when panning into view.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
@@ -137,38 +149,26 @@ class Overlay extends BaseObject {
|
|||||||
options.className : 'ol-overlay-container ' + CLASS_SELECTABLE;
|
options.className : 'ol-overlay-container ' + CLASS_SELECTABLE;
|
||||||
this.element.style.position = 'absolute';
|
this.element.style.position = 'absolute';
|
||||||
|
|
||||||
|
let autoPan = options.autoPan;
|
||||||
|
if (autoPan && ('object' !== typeof autoPan)) {
|
||||||
|
autoPan = {
|
||||||
|
animation: options.autoPanAnimation,
|
||||||
|
margin: options.autoPanMargin
|
||||||
|
};
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
* @type {boolean}
|
* @type {PanIntoViewOptions|false}
|
||||||
*/
|
*/
|
||||||
this.autoPan = options.autoPan !== undefined ? options.autoPan : false;
|
this.autoPan = /** @type {PanIntoViewOptions} */(autoPan) || false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @protected
|
* @protected
|
||||||
* @type {PanOptions}
|
* @type {{transform_: string,
|
||||||
*/
|
|
||||||
this.autoPanAnimation = options.autoPanAnimation || /** @type {PanOptions} */ ({});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @protected
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
this.autoPanMargin = options.autoPanMargin !== undefined ?
|
|
||||||
options.autoPanMargin : 20;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @protected
|
|
||||||
* @type {{bottom_: string,
|
|
||||||
* left_: string,
|
|
||||||
* right_: string,
|
|
||||||
* top_: string,
|
|
||||||
* visible: boolean}}
|
* visible: boolean}}
|
||||||
*/
|
*/
|
||||||
this.rendered = {
|
this.rendered = {
|
||||||
bottom_: '',
|
transform_: '',
|
||||||
left_: '',
|
|
||||||
right_: '',
|
|
||||||
top_: '',
|
|
||||||
visible: true
|
visible: true
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -300,6 +300,7 @@ class Overlay extends BaseObject {
|
|||||||
} else {
|
} else {
|
||||||
container.appendChild(this.element);
|
container.appendChild(this.element);
|
||||||
}
|
}
|
||||||
|
this.performAutoPan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,9 +323,7 @@ class Overlay extends BaseObject {
|
|||||||
*/
|
*/
|
||||||
handlePositionChanged() {
|
handlePositionChanged() {
|
||||||
this.updatePixelPosition();
|
this.updatePixelPosition();
|
||||||
if (this.get(Property.POSITION) && this.autoPan) {
|
this.performAutoPan();
|
||||||
this.panIntoView();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -378,14 +377,26 @@ class Overlay extends BaseObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pan the map so that the overlay is entirely visible in the current viewport
|
* Pan the map so that the overlay is entirely visisble in the current viewport
|
||||||
* (if necessary).
|
* (if necessary) using the configured autoPan parameters
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
panIntoView() {
|
performAutoPan() {
|
||||||
|
if (this.autoPan) {
|
||||||
|
this.panIntoView(this.autoPan);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pan the map so that the overlay is entirely visible in the current viewport
|
||||||
|
* (if necessary).
|
||||||
|
* @param {PanIntoViewOptions|undefined} panIntoViewOptions Options for the pan action
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
panIntoView(panIntoViewOptions) {
|
||||||
const map = this.getMap();
|
const map = this.getMap();
|
||||||
|
|
||||||
if (!map || !map.getTargetElement()) {
|
if (!map || !map.getTargetElement() || !this.get(Property.POSITION)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,7 +404,7 @@ class Overlay extends BaseObject {
|
|||||||
const element = this.getElement();
|
const element = this.getElement();
|
||||||
const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]);
|
const overlayRect = this.getRect(element, [outerWidth(element), outerHeight(element)]);
|
||||||
|
|
||||||
const margin = this.autoPanMargin;
|
const myMargin = (panIntoViewOptions.margin === undefined) ? 20 : panIntoViewOptions.margin;
|
||||||
if (!containsExtent(mapRect, overlayRect)) {
|
if (!containsExtent(mapRect, overlayRect)) {
|
||||||
// the overlay is not completely inside the viewport, so pan the map
|
// the overlay is not completely inside the viewport, so pan the map
|
||||||
const offsetLeft = overlayRect[0] - mapRect[0];
|
const offsetLeft = overlayRect[0] - mapRect[0];
|
||||||
@@ -404,17 +415,17 @@ class Overlay extends BaseObject {
|
|||||||
const delta = [0, 0];
|
const delta = [0, 0];
|
||||||
if (offsetLeft < 0) {
|
if (offsetLeft < 0) {
|
||||||
// move map to the left
|
// move map to the left
|
||||||
delta[0] = offsetLeft - margin;
|
delta[0] = offsetLeft - myMargin;
|
||||||
} else if (offsetRight < 0) {
|
} else if (offsetRight < 0) {
|
||||||
// move map to the right
|
// move map to the right
|
||||||
delta[0] = Math.abs(offsetRight) + margin;
|
delta[0] = Math.abs(offsetRight) + myMargin;
|
||||||
}
|
}
|
||||||
if (offsetTop < 0) {
|
if (offsetTop < 0) {
|
||||||
// move map up
|
// move map up
|
||||||
delta[1] = offsetTop - margin;
|
delta[1] = offsetTop - myMargin;
|
||||||
} else if (offsetBottom < 0) {
|
} else if (offsetBottom < 0) {
|
||||||
// move map down
|
// move map down
|
||||||
delta[1] = Math.abs(offsetBottom) + margin;
|
delta[1] = Math.abs(offsetBottom) + myMargin;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (delta[0] !== 0 || delta[1] !== 0) {
|
if (delta[0] !== 0 || delta[1] !== 0) {
|
||||||
@@ -425,10 +436,11 @@ class Overlay extends BaseObject {
|
|||||||
centerPx[1] + delta[1]
|
centerPx[1] + delta[1]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const panOptions = panIntoViewOptions.animation || {};
|
||||||
map.getView().animateInternal({
|
map.getView().animateInternal({
|
||||||
center: map.getCoordinateFromPixelInternal(newCenterPx),
|
center: map.getCoordinateFromPixelInternal(newCenterPx),
|
||||||
duration: this.autoPanAnimation.duration,
|
duration: panOptions.duration,
|
||||||
easing: this.autoPanAnimation.easing
|
easing: panOptions.easing
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -506,63 +518,34 @@ class Overlay extends BaseObject {
|
|||||||
|
|
||||||
this.setVisible(true);
|
this.setVisible(true);
|
||||||
|
|
||||||
let offsetX = offset[0];
|
const x = Math.round(pixel[0] + offset[0]) + 'px';
|
||||||
let offsetY = offset[1];
|
const y = Math.round(pixel[1] + offset[1]) + 'px';
|
||||||
|
let posX = '0%';
|
||||||
|
let posY = '0%';
|
||||||
if (positioning == OverlayPositioning.BOTTOM_RIGHT ||
|
if (positioning == OverlayPositioning.BOTTOM_RIGHT ||
|
||||||
positioning == OverlayPositioning.CENTER_RIGHT ||
|
positioning == OverlayPositioning.CENTER_RIGHT ||
|
||||||
positioning == OverlayPositioning.TOP_RIGHT) {
|
positioning == OverlayPositioning.TOP_RIGHT) {
|
||||||
if (this.rendered.left_ !== '') {
|
posX = '-100%';
|
||||||
this.rendered.left_ = '';
|
} else if (positioning == OverlayPositioning.BOTTOM_CENTER ||
|
||||||
style.left = '';
|
|
||||||
}
|
|
||||||
const right = Math.round(mapSize[0] - pixel[0] - offsetX) + 'px';
|
|
||||||
if (this.rendered.right_ != right) {
|
|
||||||
this.rendered.right_ = right;
|
|
||||||
style.right = right;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.rendered.right_ !== '') {
|
|
||||||
this.rendered.right_ = '';
|
|
||||||
style.right = '';
|
|
||||||
}
|
|
||||||
if (positioning == OverlayPositioning.BOTTOM_CENTER ||
|
|
||||||
positioning == OverlayPositioning.CENTER_CENTER ||
|
positioning == OverlayPositioning.CENTER_CENTER ||
|
||||||
positioning == OverlayPositioning.TOP_CENTER) {
|
positioning == OverlayPositioning.TOP_CENTER) {
|
||||||
offsetX -= this.element.offsetWidth / 2;
|
posX = '-50%';
|
||||||
}
|
|
||||||
const left = Math.round(pixel[0] + offsetX) + 'px';
|
|
||||||
if (this.rendered.left_ != left) {
|
|
||||||
this.rendered.left_ = left;
|
|
||||||
style.left = left;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
|
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
|
||||||
positioning == OverlayPositioning.BOTTOM_CENTER ||
|
positioning == OverlayPositioning.BOTTOM_CENTER ||
|
||||||
positioning == OverlayPositioning.BOTTOM_RIGHT) {
|
positioning == OverlayPositioning.BOTTOM_RIGHT) {
|
||||||
if (this.rendered.top_ !== '') {
|
posY = '-100%';
|
||||||
this.rendered.top_ = '';
|
} else if (positioning == OverlayPositioning.CENTER_LEFT ||
|
||||||
style.top = '';
|
|
||||||
}
|
|
||||||
const bottom = Math.round(mapSize[1] - pixel[1] - offsetY) + 'px';
|
|
||||||
if (this.rendered.bottom_ != bottom) {
|
|
||||||
this.rendered.bottom_ = bottom;
|
|
||||||
style.bottom = bottom;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.rendered.bottom_ !== '') {
|
|
||||||
this.rendered.bottom_ = '';
|
|
||||||
style.bottom = '';
|
|
||||||
}
|
|
||||||
if (positioning == OverlayPositioning.CENTER_LEFT ||
|
|
||||||
positioning == OverlayPositioning.CENTER_CENTER ||
|
positioning == OverlayPositioning.CENTER_CENTER ||
|
||||||
positioning == OverlayPositioning.CENTER_RIGHT) {
|
positioning == OverlayPositioning.CENTER_RIGHT) {
|
||||||
offsetY -= this.element.offsetHeight / 2;
|
posY = '-50%';
|
||||||
}
|
|
||||||
const top = Math.round(pixel[1] + offsetY) + 'px';
|
|
||||||
if (this.rendered.top_ != top) {
|
|
||||||
this.rendered.top_ = 'top';
|
|
||||||
style.top = top;
|
|
||||||
}
|
}
|
||||||
|
const transform = `translate(${posX}, ${posY}) translate(${x}, ${y})`;
|
||||||
|
if (this.rendered.transform_ != transform) {
|
||||||
|
this.rendered.transform_ = transform;
|
||||||
|
style.transform = transform;
|
||||||
|
// @ts-ignore IE9
|
||||||
|
style.msTransform = transform;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import MapProperty from './MapProperty.js';
|
|||||||
import RenderEventType from './render/EventType.js';
|
import RenderEventType from './render/EventType.js';
|
||||||
import BaseObject, {getChangeEventType} from './Object.js';
|
import BaseObject, {getChangeEventType} from './Object.js';
|
||||||
import ObjectEventType from './ObjectEventType.js';
|
import ObjectEventType from './ObjectEventType.js';
|
||||||
import TileQueue from './TileQueue.js';
|
import TileQueue, {getTilePriority} from './TileQueue.js';
|
||||||
import View from './View.js';
|
import View from './View.js';
|
||||||
import ViewHint from './ViewHint.js';
|
import ViewHint from './ViewHint.js';
|
||||||
import {assert} from './asserts.js';
|
import {assert} from './asserts.js';
|
||||||
@@ -24,7 +24,6 @@ import {TRUE} from './functions.js';
|
|||||||
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
|
import {DEVICE_PIXEL_RATIO, IMAGE_DECODE, PASSIVE_EVENT_LISTENERS} from './has.js';
|
||||||
import LayerGroup from './layer/Group.js';
|
import LayerGroup from './layer/Group.js';
|
||||||
import {hasArea} from './size.js';
|
import {hasArea} from './size.js';
|
||||||
import {DROP} from './structs/PriorityQueue.js';
|
|
||||||
import {create as createTransform, apply as applyTransform} from './transform.js';
|
import {create as createTransform, apply as applyTransform} from './transform.js';
|
||||||
import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
||||||
|
|
||||||
@@ -891,26 +890,7 @@ class PluggableMap extends BaseObject {
|
|||||||
* @return {number} Tile priority.
|
* @return {number} Tile priority.
|
||||||
*/
|
*/
|
||||||
getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
|
getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
|
||||||
// Filter out tiles at higher zoom levels than the current zoom level, or that
|
return getTilePriority(this.frameState_, tile, tileSourceKey, tileCenter, tileResolution);
|
||||||
// are outside the visible extent.
|
|
||||||
const frameState = this.frameState_;
|
|
||||||
if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
|
|
||||||
return DROP;
|
|
||||||
}
|
|
||||||
if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
|
|
||||||
return DROP;
|
|
||||||
}
|
|
||||||
// Prioritize the highest zoom level tiles closest to the focus.
|
|
||||||
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
|
|
||||||
// Within a zoom level, tiles are prioritized by the distance in pixels between
|
|
||||||
// the center of the tile and the center of the viewport. The factor of 65536
|
|
||||||
// means that the prioritization should behave as desired for tiles up to
|
|
||||||
// 65536 * Math.log(2) = 45426 pixels from the focus.
|
|
||||||
const center = frameState.viewState.center;
|
|
||||||
const deltaX = tileCenter[0] - center[0];
|
|
||||||
const deltaY = tileCenter[1] - center[1];
|
|
||||||
return 65536 * Math.log(tileResolution) +
|
|
||||||
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -934,7 +914,7 @@ class PluggableMap extends BaseObject {
|
|||||||
}
|
}
|
||||||
const target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
const target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
||||||
if (!mapBrowserEvent.dragging) {
|
if (!mapBrowserEvent.dragging) {
|
||||||
if (this.overlayContainerStopEvent_.contains(target) || !document.body.contains(target)) {
|
if (this.overlayContainerStopEvent_.contains(target) || !(document.body.contains(target) || this.viewport_.getRootNode && this.viewport_.getRootNode().contains(target))) {
|
||||||
// Abort if the event target is a child of the container that doesn't allow
|
// Abort if the event target is a child of the container that doesn't allow
|
||||||
// event propagation or is no longer in the page. It's possible for the target to no longer
|
// event propagation or is no longer in the page. It's possible for the target to no longer
|
||||||
// be in the page if it has been removed in an event listener, this might happen in a Control
|
// be in the page if it has been removed in an event listener, this might happen in a Control
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {fromKey, getKey} from './tilecoord.js';
|
|||||||
class TileCache extends LRUCache {
|
class TileCache extends LRUCache {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {!Object<string, import("./TileRange.js").default>} usedTiles Used tiles.
|
* @param {!Object<string, boolean>} usedTiles Used tiles.
|
||||||
*/
|
*/
|
||||||
expireCache(usedTiles) {
|
expireCache(usedTiles) {
|
||||||
while (this.canExpireCache()) {
|
while (this.canExpireCache()) {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import TileState from './TileState.js';
|
import TileState from './TileState.js';
|
||||||
import EventType from './events/EventType.js';
|
import EventType from './events/EventType.js';
|
||||||
import PriorityQueue from './structs/PriorityQueue.js';
|
import PriorityQueue, {DROP} from './structs/PriorityQueue.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -119,3 +119,34 @@ class TileQueue extends PriorityQueue {
|
|||||||
|
|
||||||
|
|
||||||
export default TileQueue;
|
export default TileQueue;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./PluggableMap.js').FrameState} frameState Frame state.
|
||||||
|
* @param {import("./Tile.js").default} tile Tile.
|
||||||
|
* @param {string} tileSourceKey Tile source key.
|
||||||
|
* @param {import("./coordinate.js").Coordinate} tileCenter Tile center.
|
||||||
|
* @param {number} tileResolution Tile resolution.
|
||||||
|
* @return {number} Tile priority.
|
||||||
|
*/
|
||||||
|
export function getTilePriority(frameState, tile, tileSourceKey, tileCenter, tileResolution) {
|
||||||
|
// Filter out tiles at higher zoom levels than the current zoom level, or that
|
||||||
|
// are outside the visible extent.
|
||||||
|
if (!frameState || !(tileSourceKey in frameState.wantedTiles)) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
if (!frameState.wantedTiles[tileSourceKey][tile.getKey()]) {
|
||||||
|
return DROP;
|
||||||
|
}
|
||||||
|
// Prioritize the highest zoom level tiles closest to the focus.
|
||||||
|
// Tiles at higher zoom levels are prioritized using Math.log(tileResolution).
|
||||||
|
// Within a zoom level, tiles are prioritized by the distance in pixels between
|
||||||
|
// the center of the tile and the center of the viewport. The factor of 65536
|
||||||
|
// means that the prioritization should behave as desired for tiles up to
|
||||||
|
// 65536 * Math.log(2) = 45426 pixels from the focus.
|
||||||
|
const center = frameState.viewState.center;
|
||||||
|
const deltaX = tileCenter[0] - center[0];
|
||||||
|
const deltaY = tileCenter[1] - center[1];
|
||||||
|
return 65536 * Math.log(tileResolution) +
|
||||||
|
Math.sqrt(deltaX * deltaX + deltaY * deltaY) / tileResolution;
|
||||||
|
}
|
||||||
|
|||||||
@@ -405,7 +405,6 @@ class View extends BaseObject {
|
|||||||
} else if (options.zoom !== undefined) {
|
} else if (options.zoom !== undefined) {
|
||||||
this.setZoom(options.zoom);
|
this.setZoom(options.zoom);
|
||||||
}
|
}
|
||||||
this.resolveConstraints(0);
|
|
||||||
|
|
||||||
this.setProperties(properties);
|
this.setProperties(properties);
|
||||||
|
|
||||||
@@ -609,10 +608,15 @@ class View extends BaseObject {
|
|||||||
if (series[0].callback) {
|
if (series[0].callback) {
|
||||||
animationCallback(series[0].callback, false);
|
animationCallback(series[0].callback, false);
|
||||||
}
|
}
|
||||||
anchor = anchor ||
|
if (!anchor) {
|
||||||
series.filter(function(animation) {
|
for (let j = 0, jj = series.length; j < jj; ++j) {
|
||||||
return !animation.complete;
|
const animation = series[j];
|
||||||
})[0].anchor;
|
if (!animation.complete) {
|
||||||
|
anchor = animation.anchor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.animations_.length = 0;
|
this.animations_.length = 0;
|
||||||
this.cancelAnchor_ = anchor;
|
this.cancelAnchor_ = anchor;
|
||||||
@@ -762,6 +766,7 @@ class View extends BaseObject {
|
|||||||
*/
|
*/
|
||||||
setViewportSize(opt_size) {
|
setViewportSize(opt_size) {
|
||||||
this.viewportSize_ = Array.isArray(opt_size) ? opt_size.slice() : [100, 100];
|
this.viewportSize_ = Array.isArray(opt_size) ? opt_size.slice() : [100, 100];
|
||||||
|
this.resolveConstraints(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -793,6 +798,13 @@ class View extends BaseObject {
|
|||||||
return this.constraints_;
|
return this.constraints_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {boolean} Resolution constraint is set
|
||||||
|
*/
|
||||||
|
getConstrainResolution() {
|
||||||
|
return this.options_.constrainResolution;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Array<number>=} opt_hints Destination array.
|
* @param {Array<number>=} opt_hints Destination array.
|
||||||
* @return {Array<number>} Hint.
|
* @return {Array<number>} Hint.
|
||||||
|
|||||||
@@ -9,6 +9,29 @@ import EventType from '../events/EventType.js';
|
|||||||
|
|
||||||
const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChange'];
|
const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChange'];
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
const FullScreenEventType = {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered after the map entered fullscreen.
|
||||||
|
* @event FullScreenEventType#enterfullscreen
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ENTERFULLSCREEN: 'enterfullscreen',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggered after the map leave fullscreen.
|
||||||
|
* @event FullScreenEventType#leavefullscreen
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
LEAVEFULLSCREEN: 'leavefullscreen'
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} Options
|
* @typedef {Object} Options
|
||||||
* @property {string} [className='ol-full-screen'] CSS class name.
|
* @property {string} [className='ol-full-screen'] CSS class name.
|
||||||
@@ -38,6 +61,8 @@ const events = ['fullscreenchange', 'webkitfullscreenchange', 'MSFullscreenChang
|
|||||||
* The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to
|
* The [Fullscreen API](http://www.w3.org/TR/fullscreen/) is used to
|
||||||
* toggle the map in full screen mode.
|
* toggle the map in full screen mode.
|
||||||
*
|
*
|
||||||
|
* @fires FullScreenEventType#enterfullscreen
|
||||||
|
* @fires FullScreenEventType#leavefullscreen
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class FullScreen extends Control {
|
class FullScreen extends Control {
|
||||||
@@ -162,9 +187,11 @@ class FullScreen extends Control {
|
|||||||
if (isFullScreen()) {
|
if (isFullScreen()) {
|
||||||
this.setClassName_(this.button_, true);
|
this.setClassName_(this.button_, true);
|
||||||
replaceNode(this.labelActiveNode_, this.labelNode_);
|
replaceNode(this.labelActiveNode_, this.labelNode_);
|
||||||
|
this.dispatchEvent(FullScreenEventType.ENTERFULLSCREEN);
|
||||||
} else {
|
} else {
|
||||||
this.setClassName_(this.button_, false);
|
this.setClassName_(this.button_, false);
|
||||||
replaceNode(this.labelNode_, this.labelActiveNode_);
|
replaceNode(this.labelNode_, this.labelActiveNode_);
|
||||||
|
this.dispatchEvent(FullScreenEventType.LEAVEFULLSCREEN);
|
||||||
}
|
}
|
||||||
if (map) {
|
if (map) {
|
||||||
map.updateSize();
|
map.updateSize();
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {replaceNode} from '../dom.js';
|
|||||||
import {listen, listenOnce} from '../events.js';
|
import {listen, listenOnce} from '../events.js';
|
||||||
import EventType from '../events/EventType.js';
|
import EventType from '../events/EventType.js';
|
||||||
import {containsExtent, equals as equalsExtent, getBottomRight, getTopLeft, scaleFromCenter} from '../extent.js';
|
import {containsExtent, equals as equalsExtent, getBottomRight, getTopLeft, scaleFromCenter} from '../extent.js';
|
||||||
|
import View from '../View.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,8 +60,8 @@ class ControlledMap extends PluggableMap {
|
|||||||
* @property {HTMLElement|string} [target] Specify a target if you want the control
|
* @property {HTMLElement|string} [target] Specify a target if you want the control
|
||||||
* to be rendered outside of the map's viewport.
|
* to be rendered outside of the map's viewport.
|
||||||
* @property {string} [tipLabel='Overview map'] Text label to use for the button tip.
|
* @property {string} [tipLabel='Overview map'] Text label to use for the button tip.
|
||||||
* @property {import("../View.js").default} [view] Custom view for the overview map. If not provided,
|
* @property {View} [view] Custom view for the overview map (should use same projection as main map). If not provided,
|
||||||
* a default view with an EPSG:3857 projection will be used.
|
* a default view with the same projection as the main map will be used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -167,6 +168,13 @@ class OverviewMap extends Control {
|
|||||||
this.ovmapDiv_ = document.createElement('div');
|
this.ovmapDiv_ = document.createElement('div');
|
||||||
this.ovmapDiv_.className = 'ol-overviewmap-map';
|
this.ovmapDiv_.className = 'ol-overviewmap-map';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Explicitly given view to be used instead of a view derived from the main map.
|
||||||
|
* @type {View}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.view_ = options.view;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {ControlledMap}
|
* @type {ControlledMap}
|
||||||
* @private
|
* @private
|
||||||
@@ -303,6 +311,14 @@ class OverviewMap extends Control {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
bindView_(view) {
|
bindView_(view) {
|
||||||
|
if (!this.view_) {
|
||||||
|
// Unless an explicit view definition was given, derive default from whatever main map uses.
|
||||||
|
const newView = new View({
|
||||||
|
projection: view.getProjection()
|
||||||
|
});
|
||||||
|
this.ovmap_.setView(newView);
|
||||||
|
}
|
||||||
|
|
||||||
view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_);
|
view.addEventListener(getChangeEventType(ViewProperty.ROTATION), this.boundHandleRotationChanged_);
|
||||||
// Sync once with the new view
|
// Sync once with the new view
|
||||||
this.handleRotationChanged_();
|
this.handleRotationChanged_();
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import {modulo} from './math.js';
|
import {modulo} from './math.js';
|
||||||
import {padNumber} from './string.js';
|
import {padNumber} from './string.js';
|
||||||
|
import {getWidth} from './extent.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -402,3 +403,23 @@ export function toStringHDMS(coordinate, opt_fractionDigits) {
|
|||||||
export function toStringXY(coordinate, opt_fractionDigits) {
|
export function toStringXY(coordinate, opt_fractionDigits) {
|
||||||
return format(coordinate, '{x}, {y}', opt_fractionDigits);
|
return format(coordinate, '{x}, {y}', opt_fractionDigits);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the provided coordinate in-place to be within the real world
|
||||||
|
* extent. The lower projection extent boundary is inclusive, the upper one
|
||||||
|
* exclusive.
|
||||||
|
*
|
||||||
|
* @param {Coordinate} coordinate Coordinate.
|
||||||
|
* @param {import("./proj/Projection.js").default} projection Projection
|
||||||
|
* @return {Coordinate} The coordinate within the real world extent.
|
||||||
|
*/
|
||||||
|
export function wrapX(coordinate, projection) {
|
||||||
|
const projectionExtent = projection.getExtent();
|
||||||
|
if (projection.canWrapX() && (coordinate[0] < projectionExtent[0] || coordinate[0] >= projectionExtent[2])) {
|
||||||
|
const worldWidth = getWidth(projectionExtent);
|
||||||
|
const worldsAway = Math.floor((coordinate[0] - projectionExtent[0]) / worldWidth);
|
||||||
|
coordinate[0] -= (worldsAway * worldWidth);
|
||||||
|
}
|
||||||
|
return coordinate;
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,9 +4,13 @@
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} FontParameters
|
* @typedef {Object} FontParameters
|
||||||
* @property {Array<string>} families
|
|
||||||
* @property {string} style
|
* @property {string} style
|
||||||
|
* @property {string} variant
|
||||||
* @property {string} weight
|
* @property {string} weight
|
||||||
|
* @property {string} size
|
||||||
|
* @property {string} lineHeight
|
||||||
|
* @property {string} family
|
||||||
|
* @property {Array<string>} families
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
@@ -64,42 +68,52 @@ export const CLASS_CONTROL = 'ol-control';
|
|||||||
*/
|
*/
|
||||||
export const CLASS_COLLAPSED = 'ol-collapsed';
|
export const CLASS_COLLAPSED = 'ol-collapsed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From http://stackoverflow.com/questions/10135697/regex-to-parse-any-css-font
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const fontRegEx = new RegExp([
|
||||||
|
'^\\s*(?=(?:(?:[-a-z]+\\s*){0,2}(italic|oblique))?)',
|
||||||
|
'(?=(?:(?:[-a-z]+\\s*){0,2}(small-caps))?)',
|
||||||
|
'(?=(?:(?:[-a-z]+\\s*){0,2}(bold(?:er)?|lighter|[1-9]00 ))?)',
|
||||||
|
'(?:(?:normal|\\1|\\2|\\3)\\s*){0,3}((?:xx?-)?',
|
||||||
|
'(?:small|large)|medium|smaller|larger|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx]))',
|
||||||
|
'(?:\\s*\\/\\s*(normal|[\\.\\d]+(?:\\%|in|[cem]m|ex|p[ctx])?))',
|
||||||
|
'?\\s*([-,\\"\\\'\\sa-z]+?)\\s*$'
|
||||||
|
].join(''), 'i');
|
||||||
|
const fontRegExMatchIndex = [
|
||||||
|
'style',
|
||||||
|
'variant',
|
||||||
|
'weight',
|
||||||
|
'size',
|
||||||
|
'lineHeight',
|
||||||
|
'family'
|
||||||
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of font families from a font spec. Note that this doesn't work
|
* Get the list of font families from a font spec. Note that this doesn't work
|
||||||
* for font families that have commas in them.
|
* for font families that have commas in them.
|
||||||
* @param {string} The CSS font property.
|
* @param {string} fontSpec The CSS font property.
|
||||||
* @return {FontParameters} The font families (or null if the input spec is invalid).
|
* @return {FontParameters} The font parameters (or null if the input spec is invalid).
|
||||||
*/
|
*/
|
||||||
export const getFontParameters = (function() {
|
export const getFontParameters = function(fontSpec) {
|
||||||
/**
|
const match = fontSpec.match(fontRegEx);
|
||||||
* @type {CSSStyleDeclaration}
|
if (!match) {
|
||||||
*/
|
|
||||||
let style;
|
|
||||||
/**
|
|
||||||
* @type {Object<string, FontParameters>}
|
|
||||||
*/
|
|
||||||
const cache = {};
|
|
||||||
return function(font) {
|
|
||||||
if (!style) {
|
|
||||||
style = document.createElement('div').style;
|
|
||||||
}
|
|
||||||
if (!(font in cache)) {
|
|
||||||
style.font = font;
|
|
||||||
const family = style.fontFamily;
|
|
||||||
const fontWeight = style.fontWeight;
|
|
||||||
const fontStyle = style.fontStyle;
|
|
||||||
style.font = '';
|
|
||||||
if (!family) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const families = family.split(/,\s?/);
|
const style = /** @type {FontParameters} */ ({
|
||||||
cache[font] = {
|
lineHeight: 'normal',
|
||||||
families: families,
|
size: '1.2em',
|
||||||
weight: fontWeight,
|
style: 'normal',
|
||||||
style: fontStyle
|
weight: 'normal',
|
||||||
};
|
variant: 'normal'
|
||||||
|
});
|
||||||
|
for (let i = 0, ii = fontRegExMatchIndex.length; i < ii; ++i) {
|
||||||
|
const value = match[i + 1];
|
||||||
|
if (value !== undefined) {
|
||||||
|
style[fontRegExMatchIndex[i]] = value;
|
||||||
}
|
}
|
||||||
return cache[font];
|
}
|
||||||
|
style.families = style.family.split(/,\s?/);
|
||||||
|
return style;
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
|
import {WORKER_OFFSCREEN_CANVAS} from './has.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @module ol/dom
|
* @module ol/dom
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//FIXME Move this function to the canvas module
|
||||||
/**
|
/**
|
||||||
* Create an html canvas element and returns its 2d context.
|
* Create an html canvas element and returns its 2d context.
|
||||||
* @param {number=} opt_width Canvas width.
|
* @param {number=} opt_width Canvas width.
|
||||||
@@ -12,14 +15,18 @@
|
|||||||
*/
|
*/
|
||||||
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
||||||
const canvas = opt_canvasPool && opt_canvasPool.length ?
|
const canvas = opt_canvasPool && opt_canvasPool.length ?
|
||||||
opt_canvasPool.shift() : document.createElement('canvas');
|
opt_canvasPool.shift() :
|
||||||
|
WORKER_OFFSCREEN_CANVAS ?
|
||||||
|
new OffscreenCanvas(opt_width || 300, opt_height || 300) :
|
||||||
|
document.createElement('canvas');
|
||||||
if (opt_width) {
|
if (opt_width) {
|
||||||
canvas.width = opt_width;
|
canvas.width = opt_width;
|
||||||
}
|
}
|
||||||
if (opt_height) {
|
if (opt_height) {
|
||||||
canvas.height = opt_height;
|
canvas.height = opt_height;
|
||||||
}
|
}
|
||||||
return canvas.getContext('2d');
|
//FIXME Allow OffscreenCanvasRenderingContext2D as return type
|
||||||
|
return /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ import {clear} from './obj.js';
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener function. This function is called with an event object as argument.
|
* Listener function. This function is called with an event object as argument.
|
||||||
* When the function returns `false`, event propagation will stop.
|
* When the function returns `false`, event propagation will stop.
|
||||||
@@ -22,6 +21,14 @@ import {clear} from './obj.js';
|
|||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} ListenerObject
|
||||||
|
* @property {ListenerFunction} handleEvent
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {ListenerFunction|ListenerObject} Listener
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an event listener on an event target. Inspired by
|
* Registers an event listener on an event target. Inspired by
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class Target extends Disposable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {!Object<string, Array<import("../events.js").ListenerFunction>>}
|
* @type {!Object<string, Array<import("../events.js").Listener>>}
|
||||||
*/
|
*/
|
||||||
this.listeners_ = {};
|
this.listeners_ = {};
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ class Target extends Disposable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} type Type.
|
* @param {string} type Type.
|
||||||
* @param {import("../events.js").ListenerFunction} listener Listener.
|
* @param {import("../events.js").Listener} listener Listener.
|
||||||
*/
|
*/
|
||||||
addEventListener(type, listener) {
|
addEventListener(type, listener) {
|
||||||
if (!type || !listener) {
|
if (!type || !listener) {
|
||||||
@@ -85,15 +85,13 @@ class Target extends Disposable {
|
|||||||
* of this type. The event parameter can either be a string or an
|
* of this type. The event parameter can either be a string or an
|
||||||
* Object with a `type` property.
|
* Object with a `type` property.
|
||||||
*
|
*
|
||||||
* @param {{type: string,
|
* @param {import("./Event.js").default|string} event Event object.
|
||||||
* target: (EventTargetLike|undefined),
|
|
||||||
* propagationStopped: (boolean|undefined)}|
|
|
||||||
* import("./Event.js").default|string} event Event object.
|
|
||||||
* @return {boolean|undefined} `false` if anyone called preventDefault on the
|
* @return {boolean|undefined} `false` if anyone called preventDefault on the
|
||||||
* event object or if any of the listeners returned false.
|
* event object or if any of the listeners returned false.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
dispatchEvent(event) {
|
dispatchEvent(event) {
|
||||||
|
/** @type {import("./Event.js").default|Event} */
|
||||||
const evt = typeof event === 'string' ? new Event(event) : event;
|
const evt = typeof event === 'string' ? new Event(event) : event;
|
||||||
const type = evt.type;
|
const type = evt.type;
|
||||||
if (!evt.target) {
|
if (!evt.target) {
|
||||||
@@ -108,7 +106,12 @@ class Target extends Disposable {
|
|||||||
}
|
}
|
||||||
++this.dispatching_[type];
|
++this.dispatching_[type];
|
||||||
for (let i = 0, ii = listeners.length; i < ii; ++i) {
|
for (let i = 0, ii = listeners.length; i < ii; ++i) {
|
||||||
if (listeners[i].call(this, evt) === false || evt.propagationStopped) {
|
if ('handleEvent' in listeners[i]) {
|
||||||
|
propagate = /** @type {import("../events.js").ListenerObject} */ (listeners[i]).handleEvent(evt);
|
||||||
|
} else {
|
||||||
|
propagate = /** @type {import("../events.js").ListenerFunction} */ (listeners[i]).call(this, evt);
|
||||||
|
}
|
||||||
|
if (propagate === false || evt.propagationStopped) {
|
||||||
propagate = false;
|
propagate = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -138,7 +141,7 @@ class Target extends Disposable {
|
|||||||
* order that they will be called in.
|
* order that they will be called in.
|
||||||
*
|
*
|
||||||
* @param {string} type Type.
|
* @param {string} type Type.
|
||||||
* @return {Array<import("../events.js").ListenerFunction>} Listeners.
|
* @return {Array<import("../events.js").Listener>} Listeners.
|
||||||
*/
|
*/
|
||||||
getListeners(type) {
|
getListeners(type) {
|
||||||
return this.listeners_[type];
|
return this.listeners_[type];
|
||||||
@@ -157,7 +160,7 @@ class Target extends Disposable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} type Type.
|
* @param {string} type Type.
|
||||||
* @param {import("../events.js").ListenerFunction} listener Listener.
|
* @param {import("../events.js").Listener} listener Listener.
|
||||||
*/
|
*/
|
||||||
removeEventListener(type, listener) {
|
removeEventListener(type, listener) {
|
||||||
const listeners = this.listeners_[type];
|
const listeners = this.listeners_[type];
|
||||||
|
|||||||
@@ -295,6 +295,18 @@ export function equals(extent1, extent2) {
|
|||||||
extent1[1] == extent2[1] && extent1[3] == extent2[3];
|
extent1[1] == extent2[1] && extent1[3] == extent2[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if two extents are approximately equivalent.
|
||||||
|
* @param {Extent} extent1 Extent 1.
|
||||||
|
* @param {Extent} extent2 Extent 2.
|
||||||
|
* @param {number} tolerance Tolerance in extent coordinate units.
|
||||||
|
* @return {boolean} The two extents differ by less than the tolerance.
|
||||||
|
*/
|
||||||
|
export function approximatelyEquals(extent1, extent2, tolerance) {
|
||||||
|
return Math.abs(extent1[0] - extent2[0]) < tolerance && Math.abs(extent1[2] - extent2[2]) < tolerance &&
|
||||||
|
Math.abs(extent1[1] - extent2[1]) < tolerance && Math.abs(extent1[3] - extent2[3]) < tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modify an extent to include another extent.
|
* Modify an extent to include another extent.
|
||||||
@@ -778,18 +790,60 @@ export function intersectsSegment(extent, start, end) {
|
|||||||
* @param {import("./proj.js").TransformFunction} transformFn Transform function.
|
* @param {import("./proj.js").TransformFunction} transformFn Transform function.
|
||||||
* Called with `[minX, minY, maxX, maxY]` extent coordinates.
|
* Called with `[minX, minY, maxX, maxY]` extent coordinates.
|
||||||
* @param {Extent=} opt_extent Destination extent.
|
* @param {Extent=} opt_extent Destination extent.
|
||||||
|
* @param {number=} opt_stops Number of stops per side used for the transform.
|
||||||
|
* By default only the corners are used.
|
||||||
* @return {Extent} Extent.
|
* @return {Extent} Extent.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
export function applyTransform(extent, transformFn, opt_extent) {
|
export function applyTransform(extent, transformFn, opt_extent, opt_stops) {
|
||||||
const coordinates = [
|
let coordinates = [];
|
||||||
|
if (opt_stops > 1) {
|
||||||
|
const width = extent[2] - extent[0];
|
||||||
|
const height = extent[3] - extent[1];
|
||||||
|
for (let i = 0; i < opt_stops; ++i) {
|
||||||
|
coordinates.push(
|
||||||
|
extent[0] + width * i / opt_stops, extent[1],
|
||||||
|
extent[2], extent[1] + height * i / opt_stops,
|
||||||
|
extent[2] - width * i / opt_stops, extent[3],
|
||||||
|
extent[0], extent[3] - height * i / opt_stops
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
coordinates = [
|
||||||
extent[0], extent[1],
|
extent[0], extent[1],
|
||||||
extent[0], extent[3],
|
|
||||||
extent[2], extent[1],
|
extent[2], extent[1],
|
||||||
extent[2], extent[3]
|
extent[2], extent[3],
|
||||||
|
extent[0], extent[3]
|
||||||
];
|
];
|
||||||
|
}
|
||||||
transformFn(coordinates, coordinates, 2);
|
transformFn(coordinates, coordinates, 2);
|
||||||
const xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];
|
const xs = [];
|
||||||
const ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
|
const ys = [];
|
||||||
|
for (let i = 0, l = coordinates.length; i < l; i += 2) {
|
||||||
|
xs.push(coordinates[i]);
|
||||||
|
ys.push(coordinates[i + 1]);
|
||||||
|
}
|
||||||
return _boundingExtentXYs(xs, ys, opt_extent);
|
return _boundingExtentXYs(xs, ys, opt_extent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modifies the provided extent in-place to be within the real world
|
||||||
|
* extent.
|
||||||
|
*
|
||||||
|
* @param {Extent} extent Extent.
|
||||||
|
* @param {import("./proj/Projection.js").default} projection Projection
|
||||||
|
* @return {Extent} The extent within the real world extent.
|
||||||
|
*/
|
||||||
|
export function wrapX(extent, projection) {
|
||||||
|
const projectionExtent = projection.getExtent();
|
||||||
|
const center = getCenter(extent);
|
||||||
|
if (projection.canWrapX() && (center[0] < projectionExtent[0] || center[0] >= projectionExtent[2])) {
|
||||||
|
const worldWidth = getWidth(projectionExtent);
|
||||||
|
const worldsAway = Math.floor((center[0] - projectionExtent[0]) / worldWidth);
|
||||||
|
const offset = (worldsAway * worldWidth);
|
||||||
|
extent[0] -= offset;
|
||||||
|
extent[2] -= offset;
|
||||||
|
}
|
||||||
|
return extent;
|
||||||
|
}
|
||||||
|
|||||||
@@ -349,7 +349,7 @@ class IIIFInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
|
* @param {PreferredOptions=} opt_preferredOptions Optional options for preferred format and quality.
|
||||||
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
|
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -918,11 +918,7 @@ function createNameStyleFunction(foundStyle, name) {
|
|||||||
|
|
||||||
const nameStyle = new Style({
|
const nameStyle = new Style({
|
||||||
image: imageStyle,
|
image: imageStyle,
|
||||||
text: textStyle,
|
text: textStyle
|
||||||
// although nameStyle will be used only for Point geometries
|
|
||||||
// fill and stroke are included to assist writing of MultiGeometry styles
|
|
||||||
fill: foundStyle.getFill(),
|
|
||||||
stroke: foundStyle.getStroke()
|
|
||||||
});
|
});
|
||||||
return nameStyle;
|
return nameStyle;
|
||||||
}
|
}
|
||||||
@@ -953,7 +949,7 @@ function createFeatureStyleFunction(style, styleUrl, defaultStyle, sharedStyles,
|
|||||||
if (geometry) {
|
if (geometry) {
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
multiGeometryPoints = geometry.getGeometriesArray().filter(function(geometry) {
|
multiGeometryPoints = geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||||
});
|
});
|
||||||
@@ -1171,6 +1167,7 @@ function readStyleMapValue(node, objectStack) {
|
|||||||
const ICON_STYLE_PARSERS = makeStructureNS(
|
const ICON_STYLE_PARSERS = makeStructureNS(
|
||||||
NAMESPACE_URIS, {
|
NAMESPACE_URIS, {
|
||||||
'Icon': makeObjectPropertySetter(readIcon),
|
'Icon': makeObjectPropertySetter(readIcon),
|
||||||
|
'color': makeObjectPropertySetter(readColor),
|
||||||
'heading': makeObjectPropertySetter(readDecimal),
|
'heading': makeObjectPropertySetter(readDecimal),
|
||||||
'hotSpot': makeObjectPropertySetter(readVec2),
|
'hotSpot': makeObjectPropertySetter(readVec2),
|
||||||
'scale': makeObjectPropertySetter(readScale)
|
'scale': makeObjectPropertySetter(readScale)
|
||||||
@@ -1252,6 +1249,9 @@ function iconStyleParser(node, objectStack) {
|
|||||||
let scale = /** @type {number|undefined} */
|
let scale = /** @type {number|undefined} */
|
||||||
(object['scale']);
|
(object['scale']);
|
||||||
|
|
||||||
|
const color = /** @type {Array<number>|undefined} */
|
||||||
|
(object['color']);
|
||||||
|
|
||||||
if (drawIcon) {
|
if (drawIcon) {
|
||||||
if (src == DEFAULT_IMAGE_STYLE_SRC) {
|
if (src == DEFAULT_IMAGE_STYLE_SRC) {
|
||||||
size = DEFAULT_IMAGE_STYLE_SIZE;
|
size = DEFAULT_IMAGE_STYLE_SIZE;
|
||||||
@@ -1271,7 +1271,8 @@ function iconStyleParser(node, objectStack) {
|
|||||||
rotation: rotation,
|
rotation: rotation,
|
||||||
scale: scale,
|
scale: scale,
|
||||||
size: size,
|
size: size,
|
||||||
src: src
|
src: src,
|
||||||
|
color: color
|
||||||
});
|
});
|
||||||
styleObject['imageStyle'] = imageStyle;
|
styleObject['imageStyle'] = imageStyle;
|
||||||
} else {
|
} else {
|
||||||
@@ -1806,7 +1807,7 @@ function readStyle(node, objectStack) {
|
|||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
return new GeometryCollection(
|
return new GeometryCollection(
|
||||||
geometry.getGeometriesArray().filter(function(geometry) {
|
geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
return type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON;
|
return type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON;
|
||||||
})
|
})
|
||||||
@@ -1827,7 +1828,7 @@ function readStyle(node, objectStack) {
|
|||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
return new GeometryCollection(
|
return new GeometryCollection(
|
||||||
geometry.getGeometriesArray().filter(function(geometry) {
|
geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
||||||
})
|
})
|
||||||
@@ -2447,7 +2448,7 @@ function writeIcon(node, icon, objectStack) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const ICON_STYLE_SEQUENCE = makeStructureNS(
|
const ICON_STYLE_SEQUENCE = makeStructureNS(
|
||||||
NAMESPACE_URIS, [
|
NAMESPACE_URIS, [
|
||||||
'scale', 'heading', 'Icon', 'hotSpot'
|
'scale', 'heading', 'Icon', 'color', 'hotSpot'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
@@ -2459,6 +2460,7 @@ const ICON_STYLE_SEQUENCE = makeStructureNS(
|
|||||||
const ICON_STYLE_SERIALIZERS = makeStructureNS(
|
const ICON_STYLE_SERIALIZERS = makeStructureNS(
|
||||||
NAMESPACE_URIS, {
|
NAMESPACE_URIS, {
|
||||||
'Icon': makeChildAppender(writeIcon),
|
'Icon': makeChildAppender(writeIcon),
|
||||||
|
'color': makeChildAppender(writeColorTextNode),
|
||||||
'heading': makeChildAppender(writeDecimalTextNode),
|
'heading': makeChildAppender(writeDecimalTextNode),
|
||||||
'hotSpot': makeChildAppender(writeVec2),
|
'hotSpot': makeChildAppender(writeVec2),
|
||||||
'scale': makeChildAppender(writeScaleTextNode)
|
'scale': makeChildAppender(writeScaleTextNode)
|
||||||
@@ -2514,6 +2516,11 @@ function writeIconStyle(node, style, objectStack) {
|
|||||||
properties['heading'] = rotation; // 0-360
|
properties['heading'] = rotation; // 0-360
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const color = style.getColor();
|
||||||
|
if (color) {
|
||||||
|
properties['color'] = color;
|
||||||
|
}
|
||||||
|
|
||||||
const parentNode = objectStack[objectStack.length - 1].node;
|
const parentNode = objectStack[objectStack.length - 1].node;
|
||||||
const orderedKeys = ICON_STYLE_SEQUENCE[parentNode.namespaceURI];
|
const orderedKeys = ICON_STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||||
const values = makeSequence(properties, orderedKeys);
|
const values = makeSequence(properties, orderedKeys);
|
||||||
@@ -2703,20 +2710,35 @@ function writeMultiGeometry(node, geometry, objectStack) {
|
|||||||
const context = {node: node};
|
const context = {node: node};
|
||||||
const type = geometry.getType();
|
const type = geometry.getType();
|
||||||
/** @type {Array<import("../geom/Geometry.js").default>} */
|
/** @type {Array<import("../geom/Geometry.js").default>} */
|
||||||
let geometries;
|
let geometries = [];
|
||||||
/** @type {function(*, Array<*>, string=): (Node|undefined)} */
|
/** @type {function(*, Array<*>, string=): (Node|undefined)} */
|
||||||
let factory;
|
let factory;
|
||||||
if (type == GeometryType.GEOMETRY_COLLECTION) {
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
geometries = /** @type {GeometryCollection} */ (geometry).getGeometries();
|
/** @type {GeometryCollection} */ (geometry).getGeometriesArrayRecursive().forEach(function(geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
if (type === GeometryType.MULTI_POINT) {
|
||||||
|
geometries = geometries.concat(/** @type {MultiPoint} */ (geometry).getPoints());
|
||||||
|
} else if (type === GeometryType.MULTI_LINE_STRING) {
|
||||||
|
geometries = geometries.concat(/** @type {MultiLineString} */ (geometry).getLineStrings());
|
||||||
|
} else if (type === GeometryType.MULTI_POLYGON) {
|
||||||
|
geometries = geometries.concat(/** @type {MultiPolygon} */ (geometry).getPolygons());
|
||||||
|
} else if (type === GeometryType.POINT
|
||||||
|
|| type === GeometryType.LINE_STRING
|
||||||
|
|| type === GeometryType.POLYGON) {
|
||||||
|
geometries.push(geometry);
|
||||||
|
} else {
|
||||||
|
assert(false, 39); // Unknown geometry type
|
||||||
|
}
|
||||||
|
});
|
||||||
factory = GEOMETRY_NODE_FACTORY;
|
factory = GEOMETRY_NODE_FACTORY;
|
||||||
} else if (type == GeometryType.MULTI_POINT) {
|
} else if (type === GeometryType.MULTI_POINT) {
|
||||||
geometries = /** @type {MultiPoint} */ (geometry).getPoints();
|
geometries = /** @type {MultiPoint} */ (geometry).getPoints();
|
||||||
factory = POINT_NODE_FACTORY;
|
factory = POINT_NODE_FACTORY;
|
||||||
} else if (type == GeometryType.MULTI_LINE_STRING) {
|
} else if (type === GeometryType.MULTI_LINE_STRING) {
|
||||||
geometries =
|
geometries =
|
||||||
(/** @type {MultiLineString} */ (geometry)).getLineStrings();
|
(/** @type {MultiLineString} */ (geometry)).getLineStrings();
|
||||||
factory = LINE_STRING_NODE_FACTORY;
|
factory = LINE_STRING_NODE_FACTORY;
|
||||||
} else if (type == GeometryType.MULTI_POLYGON) {
|
} else if (type === GeometryType.MULTI_POLYGON) {
|
||||||
geometries =
|
geometries =
|
||||||
(/** @type {MultiPolygon} */ (geometry)).getPolygons();
|
(/** @type {MultiPolygon} */ (geometry)).getPolygons();
|
||||||
factory = POLYGON_NODE_FACTORY;
|
factory = POLYGON_NODE_FACTORY;
|
||||||
@@ -2831,16 +2853,64 @@ function writePlacemark(node, feature, objectStack) {
|
|||||||
// resolution-independent here
|
// resolution-independent here
|
||||||
const styles = styleFunction(feature, 0);
|
const styles = styleFunction(feature, 0);
|
||||||
if (styles) {
|
if (styles) {
|
||||||
const style = Array.isArray(styles) ? styles[0] : styles;
|
const styleArray = Array.isArray(styles) ? styles : [styles];
|
||||||
if (this.writeStyles_) {
|
let pointStyles = styleArray;
|
||||||
properties['Style'] = style;
|
if (feature.getGeometry()) {
|
||||||
|
pointStyles = styleArray.filter(function(style) {
|
||||||
|
const geometry = style.getGeometryFunction()(feature);
|
||||||
|
if (geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
|
return /** @type {GeometryCollection} */ (geometry).getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||||
|
}).length;
|
||||||
}
|
}
|
||||||
const textStyle = style.getText();
|
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (this.writeStyles_) {
|
||||||
|
let lineStyles = styleArray;
|
||||||
|
let polyStyles = styleArray;
|
||||||
|
if (feature.getGeometry()) {
|
||||||
|
lineStyles = styleArray.filter(function(style) {
|
||||||
|
const geometry = style.getGeometryFunction()(feature);
|
||||||
|
if (geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
|
return /** @type {GeometryCollection} */ (geometry).getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
return type === GeometryType.LINE_STRING || type === GeometryType.MULTI_LINE_STRING;
|
||||||
|
}).length;
|
||||||
|
}
|
||||||
|
return type === GeometryType.LINE_STRING || type === GeometryType.MULTI_LINE_STRING;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
polyStyles = styleArray.filter(function(style) {
|
||||||
|
const geometry = style.getGeometryFunction()(feature);
|
||||||
|
if (geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||||
|
return /** @type {GeometryCollection} */ (geometry).getGeometriesArrayRecursive().filter(function(geometry) {
|
||||||
|
const type = geometry.getType();
|
||||||
|
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
||||||
|
}).length;
|
||||||
|
}
|
||||||
|
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
properties['Style'] = {pointStyles: pointStyles, lineStyles: lineStyles, polyStyles: polyStyles};
|
||||||
|
}
|
||||||
|
if (pointStyles.length && properties['name'] === undefined) {
|
||||||
|
const textStyle = pointStyles[0].getText();
|
||||||
if (textStyle) {
|
if (textStyle) {
|
||||||
properties['name'] = textStyle.getText();
|
properties['name'] = textStyle.getText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
const parentNode = objectStack[objectStack.length - 1].node;
|
const parentNode = objectStack[objectStack.length - 1].node;
|
||||||
const orderedKeys = PLACEMARK_SEQUENCE[parentNode.namespaceURI];
|
const orderedKeys = PLACEMARK_SEQUENCE[parentNode.namespaceURI];
|
||||||
const values = makeSequence(properties, orderedKeys);
|
const values = makeSequence(properties, orderedKeys);
|
||||||
@@ -2913,6 +2983,17 @@ function writePrimitiveGeometry(node, geometry, objectStack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @const
|
||||||
|
* @type {Object<string, Array<string>>}
|
||||||
|
*/
|
||||||
|
// @ts-ignore
|
||||||
|
const POLY_STYLE_SEQUENCE = makeStructureNS(
|
||||||
|
NAMESPACE_URIS, [
|
||||||
|
'color', 'fill', 'outline'
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @const
|
* @const
|
||||||
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
|
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
|
||||||
@@ -2972,27 +3053,31 @@ function writePolygon(node, polygon, objectStack) {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const POLY_STYLE_SERIALIZERS = makeStructureNS(
|
const POLY_STYLE_SERIALIZERS = makeStructureNS(
|
||||||
NAMESPACE_URIS, {
|
NAMESPACE_URIS, {
|
||||||
'color': makeChildAppender(writeColorTextNode)
|
'color': makeChildAppender(writeColorTextNode),
|
||||||
|
'fill': makeChildAppender(writeBooleanTextNode),
|
||||||
|
'outline': makeChildAppender(writeBooleanTextNode)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A factory for creating coordinates nodes.
|
|
||||||
* @const
|
|
||||||
* @type {function(*, Array<*>, string=): (Node|undefined)}
|
|
||||||
*/
|
|
||||||
const COLOR_NODE_FACTORY = makeSimpleNodeFactory('color');
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Node} node Node.
|
* @param {Node} node Node.
|
||||||
* @param {Fill} style Style.
|
* @param {Style} style Style.
|
||||||
* @param {Array<*>} objectStack Object stack.
|
* @param {Array<*>} objectStack Object stack.
|
||||||
*/
|
*/
|
||||||
function writePolyStyle(node, style, objectStack) {
|
function writePolyStyle(node, style, objectStack) {
|
||||||
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node};
|
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node};
|
||||||
|
const fill = style.getFill();
|
||||||
|
const stroke = style.getStroke();
|
||||||
|
const properties = {
|
||||||
|
'color': fill ? fill.getColor() : undefined,
|
||||||
|
'fill': fill ? undefined : false,
|
||||||
|
'outline': stroke ? undefined : false
|
||||||
|
};
|
||||||
|
const parentNode = objectStack[objectStack.length - 1].node;
|
||||||
|
const orderedKeys = POLY_STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||||
|
const values = makeSequence(properties, orderedKeys);
|
||||||
pushSerializeAndPop(context, POLY_STYLE_SERIALIZERS,
|
pushSerializeAndPop(context, POLY_STYLE_SERIALIZERS,
|
||||||
COLOR_NODE_FACTORY, [style.getColor()], objectStack);
|
OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3034,27 +3119,34 @@ const STYLE_SERIALIZERS = makeStructureNS(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Node} node Node.
|
* @param {Node} node Node.
|
||||||
* @param {Style} style Style.
|
* @param {Object<string, Array<Style>>} styles Styles.
|
||||||
* @param {Array<*>} objectStack Object stack.
|
* @param {Array<*>} objectStack Object stack.
|
||||||
*/
|
*/
|
||||||
function writeStyle(node, style, objectStack) {
|
function writeStyle(node, styles, objectStack) {
|
||||||
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node};
|
const /** @type {import("../xml.js").NodeStackItem} */ context = {node: node};
|
||||||
const properties = {};
|
const properties = {};
|
||||||
const fillStyle = style.getFill();
|
if (styles.pointStyles.length) {
|
||||||
const strokeStyle = style.getStroke();
|
const textStyle = styles.pointStyles[0].getText();
|
||||||
const imageStyle = style.getImage();
|
|
||||||
const textStyle = style.getText();
|
|
||||||
if (imageStyle && typeof /** @type {?} */ (imageStyle).getSrc === 'function') {
|
|
||||||
properties['IconStyle'] = imageStyle;
|
|
||||||
}
|
|
||||||
if (textStyle) {
|
if (textStyle) {
|
||||||
properties['LabelStyle'] = textStyle;
|
properties['LabelStyle'] = textStyle;
|
||||||
}
|
}
|
||||||
|
const imageStyle = styles.pointStyles[0].getImage();
|
||||||
|
if (imageStyle && typeof /** @type {?} */ (imageStyle).getSrc === 'function') {
|
||||||
|
properties['IconStyle'] = imageStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (styles.lineStyles.length) {
|
||||||
|
const strokeStyle = styles.lineStyles[0].getStroke();
|
||||||
if (strokeStyle) {
|
if (strokeStyle) {
|
||||||
properties['LineStyle'] = strokeStyle;
|
properties['LineStyle'] = strokeStyle;
|
||||||
}
|
}
|
||||||
if (fillStyle) {
|
}
|
||||||
properties['PolyStyle'] = fillStyle;
|
if (styles.polyStyles.length) {
|
||||||
|
const strokeStyle = styles.polyStyles[0].getStroke();
|
||||||
|
if (strokeStyle && !properties['LineStyle']) {
|
||||||
|
properties['LineStyle'] = strokeStyle;
|
||||||
|
}
|
||||||
|
properties['PolyStyle'] = styles.polyStyles[0];
|
||||||
}
|
}
|
||||||
const parentNode = objectStack[objectStack.length - 1].node;
|
const parentNode = objectStack[objectStack.length - 1].node;
|
||||||
const orderedKeys = STYLE_SEQUENCE[parentNode.namespaceURI];
|
const orderedKeys = STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||||
|
|||||||
@@ -126,6 +126,23 @@ class GeometryCollection extends Geometry {
|
|||||||
return this.geometries_;
|
return this.geometries_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Array<Geometry>} Geometries.
|
||||||
|
*/
|
||||||
|
getGeometriesArrayRecursive() {
|
||||||
|
/** @type {Array<Geometry>} */
|
||||||
|
let geometriesArray = [];
|
||||||
|
const geometries = this.geometries_;
|
||||||
|
for (let i = 0, ii = geometries.length; i < ii; ++i) {
|
||||||
|
if (geometries[i].getType() === this.getType()) {
|
||||||
|
geometriesArray = geometriesArray.concat(/** @type {GeometryCollection} */ (geometries[i]).getGeometriesArrayRecursive());
|
||||||
|
} else {
|
||||||
|
geometriesArray.push(geometries[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return geometriesArray;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -37,7 +37,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
|
|||||||
* @type {number}
|
* @type {number}
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
|
export const DEVICE_PIXEL_RATIO = typeof devicePixelRatio !== 'undefined' ? devicePixelRatio : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The execution context is a worker with OffscreenCanvas available.
|
||||||
|
* @const
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
export const WORKER_OFFSCREEN_CANVAS = typeof WorkerGlobalScope !== 'undefined' && typeof OffscreenCanvas !== 'undefined' &&
|
||||||
|
self instanceof WorkerGlobalScope; //eslint-disable-line
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Image.prototype.decode() is supported.
|
* Image.prototype.decode() is supported.
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ import PointerInteraction, {centroid as centroidFromPointers} from './Pointer.js
|
|||||||
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
|
* @property {import("../events/condition.js").Condition} [condition] A function that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a boolean
|
||||||
* to indicate whether that event should be handled.
|
* to indicate whether that event should be handled.
|
||||||
* Default is {@link module:ol/events/condition~noModifierKeys} and {@link module:ol/events/condition~primaryAction}.
|
* Default is {@link module:ol/events/condition~noModifierKeys} and {@link module:ol/events/condition~primaryAction}.
|
||||||
|
* In addition, if there is a `tabindex` attribute on the map element,
|
||||||
|
* {@link module:ol/events/condition~focus} will also be applied.
|
||||||
* @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan.
|
* @property {import("../Kinetic.js").default} [kinetic] Kinetic inertia to apply to the pan.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ import {fromUserCoordinate, getUserProjection} from '../proj.js';
|
|||||||
* geometry is the geometry that is returned when the function is called without
|
* geometry is the geometry that is returned when the function is called without
|
||||||
* a second argument.
|
* a second argument.
|
||||||
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=,
|
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=,
|
||||||
* import("../proj/Projection.js").default):
|
* import("../proj/Projection.js").default=):
|
||||||
* import("../geom/SimpleGeometry.js").default} GeometryFunction
|
* import("../geom/SimpleGeometry.js").default} GeometryFunction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -143,7 +143,13 @@ const DrawEventType = {
|
|||||||
* @event DrawEvent#drawend
|
* @event DrawEvent#drawend
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
DRAWEND: 'drawend'
|
DRAWEND: 'drawend',
|
||||||
|
/**
|
||||||
|
* Triggered upon feature draw abortion
|
||||||
|
* @event DrawEvent#drawabort
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
DRAWABORT: 'drawabort'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -584,8 +590,7 @@ class Draw extends PointerInteraction {
|
|||||||
}
|
}
|
||||||
pass = false;
|
pass = false;
|
||||||
} else if (this.freehand_) {
|
} else if (this.freehand_) {
|
||||||
this.finishCoordinate_ = null;
|
this.abortDrawing();
|
||||||
this.abortDrawing_();
|
|
||||||
}
|
}
|
||||||
if (!pass && this.stopClick_) {
|
if (!pass && this.stopClick_) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
@@ -834,7 +839,7 @@ class Draw extends PointerInteraction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (coordinates.length === 0) {
|
if (coordinates.length === 0) {
|
||||||
this.finishCoordinate_ = null;
|
this.abortDrawing();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateSketchFeatures_();
|
this.updateSketchFeatures_();
|
||||||
@@ -901,6 +906,18 @@ class Draw extends PointerInteraction {
|
|||||||
return sketchFeature;
|
return sketchFeature;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop drawing without adding the sketch feature to the target layer.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
abortDrawing() {
|
||||||
|
const sketchFeature = this.abortDrawing_();
|
||||||
|
if (sketchFeature) {
|
||||||
|
this.dispatchEvent(new DrawEvent(DrawEventType.DRAWABORT, sketchFeature));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Append coordinates to the end of the geometry that is currently being drawn.
|
* Append coordinates to the end of the geometry that is currently being drawn.
|
||||||
* This can be used when drawing LineStrings or Polygons. Coordinates will
|
* This can be used when drawing LineStrings or Polygons. Coordinates will
|
||||||
@@ -982,7 +999,7 @@ class Draw extends PointerInteraction {
|
|||||||
const map = this.getMap();
|
const map = this.getMap();
|
||||||
const active = this.getActive();
|
const active = this.getActive();
|
||||||
if (!map || !active) {
|
if (!map || !active) {
|
||||||
this.abortDrawing_();
|
this.abortDrawing();
|
||||||
}
|
}
|
||||||
this.overlay_.setMap(active ? map : null);
|
this.overlay_.setMap(active ? map : null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ import {always, primaryAction, altKeyOnly, singleClick} from '../events/conditio
|
|||||||
import {boundingExtent, buffer as bufferExtent, createOrUpdateFromCoordinate as createExtent} from '../extent.js';
|
import {boundingExtent, buffer as bufferExtent, createOrUpdateFromCoordinate as createExtent} from '../extent.js';
|
||||||
import GeometryType from '../geom/GeometryType.js';
|
import GeometryType from '../geom/GeometryType.js';
|
||||||
import Point from '../geom/Point.js';
|
import Point from '../geom/Point.js';
|
||||||
|
import {fromCircle} from '../geom/Polygon.js';
|
||||||
import PointerInteraction from './Pointer.js';
|
import PointerInteraction from './Pointer.js';
|
||||||
import VectorLayer from '../layer/Vector.js';
|
import VectorLayer from '../layer/Vector.js';
|
||||||
import VectorSource from '../source/Vector.js';
|
import VectorSource from '../source/Vector.js';
|
||||||
import VectorEventType from '../source/VectorEventType.js';
|
import VectorEventType from '../source/VectorEventType.js';
|
||||||
import RBush from '../structs/RBush.js';
|
import RBush from '../structs/RBush.js';
|
||||||
import {createEditingStyle} from '../style/Style.js';
|
import {createEditingStyle} from '../style/Style.js';
|
||||||
import {fromUserExtent, toUserExtent, fromUserCoordinate, toUserCoordinate} from '../proj.js';
|
import {getUserProjection, fromUserExtent, toUserExtent, fromUserCoordinate, toUserCoordinate} from '../proj.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -657,7 +658,14 @@ class Modify extends PointerInteraction {
|
|||||||
centerSegmentData.featureSegments = featureSegments;
|
centerSegmentData.featureSegments = featureSegments;
|
||||||
circumferenceSegmentData.featureSegments = featureSegments;
|
circumferenceSegmentData.featureSegments = featureSegments;
|
||||||
this.rBush_.insert(createExtent(coordinates), centerSegmentData);
|
this.rBush_.insert(createExtent(coordinates), centerSegmentData);
|
||||||
this.rBush_.insert(geometry.getExtent(), circumferenceSegmentData);
|
let circleGeometry = /** @type {import("../geom/Geometry.js").default} */ (geometry);
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection && this.getMap()) {
|
||||||
|
const projection = this.getMap().getView().getProjection();
|
||||||
|
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
|
||||||
|
circleGeometry = fromCircle(/** @type {import("../geom/Circle.js").default} */ (circleGeometry)).transform(projection, userProjection);
|
||||||
|
}
|
||||||
|
this.rBush_.insert(circleGeometry.getExtent(), circumferenceSegmentData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -785,7 +793,16 @@ class Modify extends PointerInteraction {
|
|||||||
this.changingFeature_ = false;
|
this.changingFeature_ = false;
|
||||||
} else { // We're dragging the circle's circumference:
|
} else { // We're dragging the circle's circumference:
|
||||||
this.changingFeature_ = true;
|
this.changingFeature_ = true;
|
||||||
geometry.setRadius(coordinateDistance(geometry.getCenter(), vertex));
|
const projection = evt.map.getView().getProjection();
|
||||||
|
let radius = coordinateDistance(fromUserCoordinate(geometry.getCenter(), projection),
|
||||||
|
fromUserCoordinate(vertex, projection));
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
const circleGeometry = geometry.clone().transform(userProjection, projection);
|
||||||
|
circleGeometry.setRadius(radius);
|
||||||
|
radius = circleGeometry.transform(projection, userProjection).getRadius();
|
||||||
|
}
|
||||||
|
geometry.setRadius(radius);
|
||||||
this.changingFeature_ = false;
|
this.changingFeature_ = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -898,7 +915,14 @@ class Modify extends PointerInteraction {
|
|||||||
circumferenceSegmentData.segment[0] = coordinates;
|
circumferenceSegmentData.segment[0] = coordinates;
|
||||||
circumferenceSegmentData.segment[1] = coordinates;
|
circumferenceSegmentData.segment[1] = coordinates;
|
||||||
this.rBush_.update(createExtent(coordinates), centerSegmentData);
|
this.rBush_.update(createExtent(coordinates), centerSegmentData);
|
||||||
this.rBush_.update(geometry.getExtent(), circumferenceSegmentData);
|
let circleGeometry = geometry;
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
const projection = evt.map.getView().getProjection();
|
||||||
|
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
|
||||||
|
circleGeometry = fromCircle(circleGeometry).transform(projection, userProjection);
|
||||||
|
}
|
||||||
|
this.rBush_.update(circleGeometry.getExtent(), circumferenceSegmentData);
|
||||||
} else {
|
} else {
|
||||||
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
|
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
|
||||||
}
|
}
|
||||||
@@ -1249,11 +1273,15 @@ function projectedDistanceToSegmentDataSquared(pointCoordinates, segmentData, pr
|
|||||||
const geometry = segmentData.geometry;
|
const geometry = segmentData.geometry;
|
||||||
|
|
||||||
if (geometry.getType() === GeometryType.CIRCLE) {
|
if (geometry.getType() === GeometryType.CIRCLE) {
|
||||||
const circleGeometry = /** @type {import("../geom/Circle.js").default} */ (geometry);
|
let circleGeometry = /** @type {import("../geom/Circle.js").default} */ (geometry);
|
||||||
|
|
||||||
if (segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
|
if (segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
circleGeometry = /** @type {import("../geom/Circle.js").default} */ (circleGeometry.clone().transform(userProjection, projection));
|
||||||
|
}
|
||||||
const distanceToCenterSquared =
|
const distanceToCenterSquared =
|
||||||
squaredCoordinateDistance(circleGeometry.getCenter(), pointCoordinates);
|
squaredCoordinateDistance(circleGeometry.getCenter(), fromUserCoordinate(pointCoordinates, projection));
|
||||||
const distanceToCircumference =
|
const distanceToCircumference =
|
||||||
Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius();
|
Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius();
|
||||||
return distanceToCircumference * distanceToCircumference;
|
return distanceToCircumference * distanceToCircumference;
|
||||||
@@ -1280,7 +1308,12 @@ function closestOnSegmentData(pointCoordinates, segmentData, projection) {
|
|||||||
const geometry = segmentData.geometry;
|
const geometry = segmentData.geometry;
|
||||||
|
|
||||||
if (geometry.getType() === GeometryType.CIRCLE && segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
|
if (geometry.getType() === GeometryType.CIRCLE && segmentData.index === CIRCLE_CIRCUMFERENCE_INDEX) {
|
||||||
return geometry.getClosestPoint(pointCoordinates);
|
let circleGeometry = /** @type {import("../geom/Circle.js").default} */ (geometry);
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
circleGeometry = /** @type {import("../geom/Circle.js").default} */ (circleGeometry.clone().transform(userProjection, projection));
|
||||||
|
}
|
||||||
|
return toUserCoordinate(circleGeometry.getClosestPoint(fromUserCoordinate(pointCoordinates, projection)), projection);
|
||||||
}
|
}
|
||||||
const coordinate = fromUserCoordinate(pointCoordinates, projection);
|
const coordinate = fromUserCoordinate(pointCoordinates, projection);
|
||||||
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
|
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
|
||||||
|
|||||||
@@ -4,7 +4,17 @@
|
|||||||
import {always, focus} from '../events/condition.js';
|
import {always, focus} from '../events/condition.js';
|
||||||
import EventType from '../events/EventType.js';
|
import EventType from '../events/EventType.js';
|
||||||
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js';
|
import {DEVICE_PIXEL_RATIO, FIREFOX} from '../has.js';
|
||||||
import Interaction from './Interaction.js';
|
import Interaction, {zoomByDelta} from './Interaction.js';
|
||||||
|
import {clamp} from '../math.js';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @enum {string}
|
||||||
|
*/
|
||||||
|
export const Mode = {
|
||||||
|
TRACKPAD: 'trackpad',
|
||||||
|
WHEEL: 'wheel'
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -13,6 +23,8 @@ import Interaction from './Interaction.js';
|
|||||||
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
* takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||||
* boolean to indicate whether that event should be handled. Default is
|
* boolean to indicate whether that event should be handled. Default is
|
||||||
* {@link module:ol/events/condition~always}.
|
* {@link module:ol/events/condition~always}.
|
||||||
|
* In addition, if there is a `tabindex` attribute on the map element,
|
||||||
|
* {@link module:ol/events/condition~focus} will also be applied.
|
||||||
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
|
* @property {number} [maxDelta=1] Maximum mouse wheel delta.
|
||||||
* @property {number} [duration=250] Animation duration in milliseconds.
|
* @property {number} [duration=250] Animation duration in milliseconds.
|
||||||
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
||||||
@@ -92,16 +104,28 @@ class MouseWheelZoom extends Interaction {
|
|||||||
this.startTime_ = undefined;
|
this.startTime_ = undefined;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Events separated by this delay will be considered separate
|
* @private
|
||||||
|
* @type {?}
|
||||||
|
*/
|
||||||
|
this.timeoutId_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Mode|undefined}
|
||||||
|
*/
|
||||||
|
this.mode_ = undefined;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trackpad events separated by this delay will be considered separate
|
||||||
* interactions.
|
* interactions.
|
||||||
* @type {number}
|
* @type {number}
|
||||||
*/
|
*/
|
||||||
this.eventGap_ = 400;
|
this.trackpadEventGap_ = 400;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {?}
|
* @type {?}
|
||||||
*/
|
*/
|
||||||
this.timeoutId_;
|
this.trackpadTimeoutId_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The number of delta values per zoom level
|
* The number of delta values per zoom level
|
||||||
@@ -130,7 +154,7 @@ class MouseWheelZoom extends Interaction {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
endInteraction_() {
|
endInteraction_() {
|
||||||
this.timeoutId_ = undefined;
|
this.trackpadTimeoutId_ = undefined;
|
||||||
const view = this.getMap().getView();
|
const view = this.getMap().getView();
|
||||||
view.endInteraction(undefined, this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0, this.lastAnchor_);
|
view.endInteraction(undefined, this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0, this.lastAnchor_);
|
||||||
}
|
}
|
||||||
@@ -184,18 +208,61 @@ class MouseWheelZoom extends Interaction {
|
|||||||
this.startTime_ = now;
|
this.startTime_ = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.mode_ || now - this.startTime_ > this.trackpadEventGap_) {
|
||||||
|
this.mode_ = Math.abs(delta) < 4 ?
|
||||||
|
Mode.TRACKPAD :
|
||||||
|
Mode.WHEEL;
|
||||||
|
}
|
||||||
|
|
||||||
const view = map.getView();
|
const view = map.getView();
|
||||||
if (this.timeoutId_) {
|
if (this.mode_ === Mode.TRACKPAD && !view.getConstrainResolution()) {
|
||||||
clearTimeout(this.timeoutId_);
|
if (this.trackpadTimeoutId_) {
|
||||||
|
clearTimeout(this.trackpadTimeoutId_);
|
||||||
} else {
|
} else {
|
||||||
|
if (view.getAnimating()) {
|
||||||
|
view.cancelAnimations();
|
||||||
|
}
|
||||||
view.beginInteraction();
|
view.beginInteraction();
|
||||||
}
|
}
|
||||||
this.timeoutId_ = setTimeout(this.endInteraction_.bind(this), this.eventGap_);
|
this.trackpadTimeoutId_ = setTimeout(this.endInteraction_.bind(this), this.timeout_);
|
||||||
view.adjustZoom(-delta / this.deltaPerZoom_, this.lastAnchor_);
|
view.adjustZoom(-delta / this.deltaPerZoom_, this.lastAnchor_);
|
||||||
this.startTime_ = now;
|
this.startTime_ = now;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.totalDelta_ += delta;
|
||||||
|
|
||||||
|
const timeLeft = Math.max(this.timeout_ - (now - this.startTime_), 0);
|
||||||
|
|
||||||
|
clearTimeout(this.timeoutId_);
|
||||||
|
this.timeoutId_ = setTimeout(this.handleWheelZoom_.bind(this, map), timeLeft);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @param {import("../PluggableMap.js").default} map Map.
|
||||||
|
*/
|
||||||
|
handleWheelZoom_(map) {
|
||||||
|
const view = map.getView();
|
||||||
|
if (view.getAnimating()) {
|
||||||
|
view.cancelAnimations();
|
||||||
|
}
|
||||||
|
let delta = -clamp(this.totalDelta_, -this.maxDelta_ * this.deltaPerZoom_, this.maxDelta_ * this.deltaPerZoom_) / this.deltaPerZoom_;
|
||||||
|
if (view.getConstrainResolution()) {
|
||||||
|
// view has a zoom constraint, zoom by 1
|
||||||
|
delta = delta ? delta > 0 ? 1 : -1 : 0;
|
||||||
|
}
|
||||||
|
zoomByDelta(view, delta, this.lastAnchor_, this.duration_);
|
||||||
|
|
||||||
|
this.mode_ = undefined;
|
||||||
|
this.totalDelta_ = 0;
|
||||||
|
this.lastAnchor_ = null;
|
||||||
|
this.startTime_ = undefined;
|
||||||
|
this.timeoutId_ = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable or disable using the mouse's location as an anchor when zooming
|
* Enable or disable using the mouse's location as an anchor when zooming
|
||||||
* @param {boolean} useAnchor true to zoom to the mouse's location, false
|
* @param {boolean} useAnchor true to zoom to the mouse's location, false
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ const SelectEventType = {
|
|||||||
* @property {import("../style/Style.js").StyleLike} [style]
|
* @property {import("../style/Style.js").StyleLike} [style]
|
||||||
* Style for the selected features. By default the default edit style is used
|
* Style for the selected features. By default the default edit style is used
|
||||||
* (see {@link module:ol/style}).
|
* (see {@link module:ol/style}).
|
||||||
* If set to `false` the selected feature's style will not change.
|
* If set to a falsey value, the selected feature's style will not change.
|
||||||
* @property {import("../events/condition.js").Condition} [removeCondition] A function
|
* @property {import("../events/condition.js").Condition} [removeCondition] A function
|
||||||
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||||
* boolean to indicate whether that event should be handled.
|
* boolean to indicate whether that event should be handled.
|
||||||
@@ -133,6 +133,12 @@ class SelectEvent extends Event {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Original feature styles to reset to when features are no longer selected.
|
||||||
|
* @type {Object.<number, import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction>}
|
||||||
|
*/
|
||||||
|
const originalFeatureStyles = {};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
@@ -161,6 +167,16 @@ class Select extends Interaction {
|
|||||||
|
|
||||||
const options = opt_options ? opt_options : {};
|
const options = opt_options ? opt_options : {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.boundAddFeature_ = this.addFeature_.bind(this);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.boundRemoveFeature_ = this.removeFeature_.bind(this);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {import("../events/condition.js").Condition}
|
* @type {import("../events/condition.js").Condition}
|
||||||
@@ -209,14 +225,6 @@ class Select extends Interaction {
|
|||||||
*/
|
*/
|
||||||
this.style_ = options.style !== undefined ? options.style : getDefaultStyleFunction();
|
this.style_ = options.style !== undefined ? options.style : getDefaultStyleFunction();
|
||||||
|
|
||||||
/**
|
|
||||||
* An association between selected feature (key)
|
|
||||||
* and original style (value)
|
|
||||||
* @private
|
|
||||||
* @type {Object.<number, import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction>}
|
|
||||||
*/
|
|
||||||
this.featureStyleAssociation_ = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @type {import("../Collection.js").default}
|
* @type {import("../Collection.js").default}
|
||||||
@@ -251,10 +259,6 @@ class Select extends Interaction {
|
|||||||
* @type {Object<string, import("../layer/Layer.js").default>}
|
* @type {Object<string, import("../layer/Layer.js").default>}
|
||||||
*/
|
*/
|
||||||
this.featureLayerAssociation_ = {};
|
this.featureLayerAssociation_ = {};
|
||||||
|
|
||||||
const features = this.getFeatures();
|
|
||||||
features.addEventListener(CollectionEventType.ADD, this.addFeature_.bind(this));
|
|
||||||
features.addEventListener(CollectionEventType.REMOVE, this.removeFeature_.bind(this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -319,11 +323,19 @@ class Select extends Interaction {
|
|||||||
setMap(map) {
|
setMap(map) {
|
||||||
const currentMap = this.getMap();
|
const currentMap = this.getMap();
|
||||||
if (currentMap && this.style_) {
|
if (currentMap && this.style_) {
|
||||||
this.features_.forEach(this.removeSelectedStyle_.bind(this));
|
this.features_.forEach(this.restorePreviousStyle_.bind(this));
|
||||||
}
|
}
|
||||||
super.setMap(map);
|
super.setMap(map);
|
||||||
if (map && this.style_) {
|
if (map) {
|
||||||
this.features_.forEach(this.giveSelectedStyle_.bind(this));
|
this.features_.addEventListener(CollectionEventType.ADD, this.boundAddFeature_);
|
||||||
|
this.features_.addEventListener(CollectionEventType.REMOVE, this.boundRemoveFeature_);
|
||||||
|
|
||||||
|
if (this.style_) {
|
||||||
|
this.features_.forEach(this.applySelectedStyle_.bind(this));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.features_.removeEventListener(CollectionEventType.ADD, this.boundAddFeature_);
|
||||||
|
this.features_.removeEventListener(CollectionEventType.REMOVE, this.boundRemoveFeature_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -334,7 +346,7 @@ class Select extends Interaction {
|
|||||||
addFeature_(evt) {
|
addFeature_(evt) {
|
||||||
const feature = evt.element;
|
const feature = evt.element;
|
||||||
if (this.style_) {
|
if (this.style_) {
|
||||||
this.giveSelectedStyle_(feature);
|
this.applySelectedStyle_(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -345,17 +357,26 @@ class Select extends Interaction {
|
|||||||
removeFeature_(evt) {
|
removeFeature_(evt) {
|
||||||
const feature = evt.element;
|
const feature = evt.element;
|
||||||
if (this.style_) {
|
if (this.style_) {
|
||||||
this.removeSelectedStyle_(feature);
|
this.restorePreviousStyle_(feature);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {import("../style/Style.js").default|Array.<import("../style/Style.js").default>|import("../style/Style.js").StyleFunction|null} Select style.
|
||||||
|
*/
|
||||||
|
getStyle() {
|
||||||
|
return this.style_;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../Feature.js").default} feature Feature
|
* @param {import("../Feature.js").default} feature Feature
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
giveSelectedStyle_(feature) {
|
applySelectedStyle_(feature) {
|
||||||
const key = getUid(feature);
|
const key = getUid(feature);
|
||||||
this.featureStyleAssociation_[key] = feature.getStyle();
|
if (!(key in originalFeatureStyles)) {
|
||||||
|
originalFeatureStyles[key] = feature.getStyle();
|
||||||
|
}
|
||||||
feature.setStyle(this.style_);
|
feature.setStyle(this.style_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -363,10 +384,17 @@ class Select extends Interaction {
|
|||||||
* @param {import("../Feature.js").default} feature Feature
|
* @param {import("../Feature.js").default} feature Feature
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
removeSelectedStyle_(feature) {
|
restorePreviousStyle_(feature) {
|
||||||
const key = getUid(feature);
|
const key = getUid(feature);
|
||||||
feature.setStyle(this.featureStyleAssociation_[key]);
|
const selectInteractions = /** @type {Array<Select>} */ (this.getMap().getInteractions().getArray().filter(function(interaction) {
|
||||||
delete this.featureStyleAssociation_[key];
|
return interaction instanceof Select && interaction.getStyle() && interaction.getFeatures().getArray().indexOf(feature) !== -1;
|
||||||
|
}));
|
||||||
|
if (selectInteractions.length > 0) {
|
||||||
|
feature.setStyle(selectInteractions[selectInteractions.length - 1].getStyle());
|
||||||
|
} else {
|
||||||
|
feature.setStyle(originalFeatureStyles[key]);
|
||||||
|
delete originalFeatureStyles[key];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import PointerInteraction from './Pointer.js';
|
|||||||
import {getValues} from '../obj.js';
|
import {getValues} from '../obj.js';
|
||||||
import VectorEventType from '../source/VectorEventType.js';
|
import VectorEventType from '../source/VectorEventType.js';
|
||||||
import RBush from '../structs/RBush.js';
|
import RBush from '../structs/RBush.js';
|
||||||
import {fromUserCoordinate, toUserCoordinate} from '../proj.js';
|
import {getUserProjection, fromUserCoordinate, toUserCoordinate} from '../proj.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -431,8 +431,13 @@ class Snap extends PointerInteraction {
|
|||||||
} else if (this.edge_) {
|
} else if (this.edge_) {
|
||||||
const isCircle = closestSegmentData.feature.getGeometry().getType() === GeometryType.CIRCLE;
|
const isCircle = closestSegmentData.feature.getGeometry().getType() === GeometryType.CIRCLE;
|
||||||
if (isCircle) {
|
if (isCircle) {
|
||||||
vertex = closestOnCircle(pixelCoordinate,
|
let circleGeometry = closestSegmentData.feature.getGeometry();
|
||||||
/** @type {import("../geom/Circle.js").default} */ (closestSegmentData.feature.getGeometry()));
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
circleGeometry = circleGeometry.clone().transform(userProjection, projection);
|
||||||
|
}
|
||||||
|
vertex = toUserCoordinate(closestOnCircle(projectedCoordinate,
|
||||||
|
/** @type {import("../geom/Circle.js").default} */ (circleGeometry)), projection);
|
||||||
} else {
|
} else {
|
||||||
tempSegment[0] = fromUserCoordinate(closestSegment[0], projection);
|
tempSegment[0] = fromUserCoordinate(closestSegment[0], projection);
|
||||||
tempSegment[1] = fromUserCoordinate(closestSegment[1], projection);
|
tempSegment[1] = fromUserCoordinate(closestSegment[1], projection);
|
||||||
@@ -482,7 +487,16 @@ class Snap extends PointerInteraction {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
writeCircleGeometry_(feature, geometry) {
|
writeCircleGeometry_(feature, geometry) {
|
||||||
const polygon = fromCircle(geometry);
|
const projection = this.getMap().getView().getProjection();
|
||||||
|
let circleGeometry = geometry;
|
||||||
|
const userProjection = getUserProjection();
|
||||||
|
if (userProjection) {
|
||||||
|
circleGeometry = /** @type {import("../geom/Circle.js").default} */ (circleGeometry.clone().transform(userProjection, projection));
|
||||||
|
}
|
||||||
|
const polygon = fromCircle(circleGeometry);
|
||||||
|
if (userProjection) {
|
||||||
|
polygon.transform(projection, userProjection);
|
||||||
|
}
|
||||||
const coordinates = polygon.getCoordinates()[0];
|
const coordinates = polygon.getCoordinates()[0];
|
||||||
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||||
const segment = coordinates.slice(i, i + 2);
|
const segment = coordinates.slice(i, i + 2);
|
||||||
|
|||||||
@@ -19,6 +19,10 @@ import Layer from './Layer.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
|
* @property {import("../PluggableMap.js").default} [map] Sets the layer as overlay on a map. The map will not manage
|
||||||
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
|
* this layer in its layers collection, and the layer will be rendered on top. This is useful for
|
||||||
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import {assign} from '../obj.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0`
|
* @property {number} [preload=0] Preload. Load low-resolution tiles up to `preload` levels. `0`
|
||||||
* means no preloading.
|
* means no preloading.
|
||||||
* @property {import("../source/Tile.js").default} [source] Source for this layer.
|
* @property {import("../source/Tile.js").default} [source] Source for this layer.
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||||
* `null` to avoid the sort, but get an undefined draw order.
|
* `null` to avoid the sort, but get an undefined draw order.
|
||||||
|
|||||||
@@ -12,10 +12,22 @@ import VectorSource from '../source/Vector.js';
|
|||||||
import {
|
import {
|
||||||
equivalent as equivalentProjection,
|
equivalent as equivalentProjection,
|
||||||
get as getProjection,
|
get as getProjection,
|
||||||
getTransform,
|
getTransform
|
||||||
transformExtent
|
|
||||||
} from '../proj.js';
|
} from '../proj.js';
|
||||||
import {getCenter, intersects, equals, getIntersection, isEmpty} from '../extent.js';
|
import {
|
||||||
|
applyTransform,
|
||||||
|
containsCoordinate,
|
||||||
|
containsExtent,
|
||||||
|
equals,
|
||||||
|
approximatelyEquals,
|
||||||
|
getCenter,
|
||||||
|
getHeight,
|
||||||
|
getIntersection,
|
||||||
|
getWidth,
|
||||||
|
intersects,
|
||||||
|
isEmpty,
|
||||||
|
wrapX as wrapExtentX
|
||||||
|
} from '../extent.js';
|
||||||
import {clamp} from '../math.js';
|
import {clamp} from '../math.js';
|
||||||
import Style from '../style/Style.js';
|
import Style from '../style/Style.js';
|
||||||
import Feature from '../Feature.js';
|
import Feature from '../Feature.js';
|
||||||
@@ -23,6 +35,8 @@ import {meridian, parallel} from '../geom/flat/geodesic.js';
|
|||||||
import GeometryLayout from '../geom/GeometryLayout.js';
|
import GeometryLayout from '../geom/GeometryLayout.js';
|
||||||
import Point from '../geom/Point.js';
|
import Point from '../geom/Point.js';
|
||||||
import Collection from '../Collection.js';
|
import Collection from '../Collection.js';
|
||||||
|
import {getVectorContext} from '../render.js';
|
||||||
|
import EventType from '../render/EventType.js';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -64,6 +78,10 @@ const INTERVALS = [
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {number} [maxLines=100] The maximum number of meridians and
|
* @property {number} [maxLines=100] The maximum number of meridians and
|
||||||
* parallels from the center of the map. The default value of 100 means that at
|
* parallels from the center of the map. The default value of 100 means that at
|
||||||
* most 200 meridians and 200 parallels will be displayed. The default value is
|
* most 200 meridians and 200 parallels will be displayed. The default value is
|
||||||
@@ -138,7 +156,8 @@ const INTERVALS = [
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @classdesc
|
* @classdesc
|
||||||
* Layer that renders a grid for a coordinate system.
|
* Layer that renders a grid for a coordinate system (currently only EPSG:4326 is supported).
|
||||||
|
* Note that the view projection must define both extent and worldExtent.
|
||||||
*
|
*
|
||||||
* @fires import("../render/Event.js").RenderEvent
|
* @fires import("../render/Event.js").RenderEvent
|
||||||
* @api
|
* @api
|
||||||
@@ -202,25 +221,25 @@ class Graticule extends VectorLayer {
|
|||||||
* @type {number}
|
* @type {number}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.maxLatP_ = Infinity;
|
this.maxX_ = Infinity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.maxLonP_ = Infinity;
|
this.maxY_ = Infinity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.minLatP_ = -Infinity;
|
this.minX_ = -Infinity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
this.minLonP_ = -Infinity;
|
this.minY_ = -Infinity;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {number}
|
* @type {number}
|
||||||
@@ -270,6 +289,30 @@ class Graticule extends VectorLayer {
|
|||||||
*/
|
*/
|
||||||
this.projectionCenterLonLat_ = null;
|
this.projectionCenterLonLat_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../coordinate.js").Coordinate}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.bottomLeft_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../coordinate.js").Coordinate}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.bottomRight_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../coordinate.js").Coordinate}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.topLeft_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {import("../coordinate.js").Coordinate}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.topRight_ = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {Array<GraticuleLabelDataType>}
|
* @type {Array<GraticuleLabelDataType>}
|
||||||
* @private
|
* @private
|
||||||
@@ -378,6 +421,8 @@ class Graticule extends VectorLayer {
|
|||||||
|
|
||||||
this.meridiansLabels_ = [];
|
this.meridiansLabels_ = [];
|
||||||
this.parallelsLabels_ = [];
|
this.parallelsLabels_ = [];
|
||||||
|
|
||||||
|
this.addEventListener(EventType.POSTRENDER, this.drawLabels_.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -415,6 +460,7 @@ class Graticule extends VectorLayer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {?import("../extent.js").Extent}
|
* @type {?import("../extent.js").Extent}
|
||||||
|
* @private
|
||||||
*/
|
*/
|
||||||
this.loadedExtent_ = null;
|
this.loadedExtent_ = null;
|
||||||
|
|
||||||
@@ -435,11 +481,21 @@ class Graticule extends VectorLayer {
|
|||||||
* @return {Array<import("../extent.js").Extent>} Extents.
|
* @return {Array<import("../extent.js").Extent>} Extents.
|
||||||
*/
|
*/
|
||||||
strategyFunction(extent, resolution) {
|
strategyFunction(extent, resolution) {
|
||||||
if (this.loadedExtent_ && !equals(this.loadedExtent_, extent)) {
|
// extents may be passed in different worlds, to avoid endless loop we use only one
|
||||||
|
let realWorldExtent = extent.slice();
|
||||||
|
if (this.projection_ && this.getSource().getWrapX()) {
|
||||||
|
wrapExtentX(realWorldExtent, this.projection_);
|
||||||
|
}
|
||||||
|
if (this.loadedExtent_) {
|
||||||
|
if (approximatelyEquals(this.loadedExtent_, realWorldExtent, resolution)) {
|
||||||
|
// make sure result is exactly equal to previous extent
|
||||||
|
realWorldExtent = this.loadedExtent_.slice();
|
||||||
|
} else {
|
||||||
// we should not keep track of loaded extents
|
// we should not keep track of loaded extents
|
||||||
this.getSource().removeLoadedExtent(this.loadedExtent_);
|
this.getSource().removeLoadedExtent(this.loadedExtent_);
|
||||||
}
|
}
|
||||||
return [extent];
|
}
|
||||||
|
return [realWorldExtent];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -482,10 +538,10 @@ class Graticule extends VectorLayer {
|
|||||||
// first make sure we have enough features in the pool
|
// first make sure we have enough features in the pool
|
||||||
let featureCount = this.meridians_.length + this.parallels_.length;
|
let featureCount = this.meridians_.length + this.parallels_.length;
|
||||||
if (this.meridiansLabels_) {
|
if (this.meridiansLabels_) {
|
||||||
featureCount += this.meridiansLabels_.length;
|
featureCount += this.meridians_.length;
|
||||||
}
|
}
|
||||||
if (this.parallelsLabels_) {
|
if (this.parallelsLabels_) {
|
||||||
featureCount += this.parallelsLabels_.length;
|
featureCount += this.parallels_.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
let feature;
|
let feature;
|
||||||
@@ -512,27 +568,6 @@ class Graticule extends VectorLayer {
|
|||||||
feature.setStyle(this.lineStyle_);
|
feature.setStyle(this.lineStyle_);
|
||||||
featuresColl.push(feature);
|
featuresColl.push(feature);
|
||||||
}
|
}
|
||||||
let labelData;
|
|
||||||
if (this.meridiansLabels_) {
|
|
||||||
for (i = 0, l = this.meridiansLabels_.length; i < l; ++i) {
|
|
||||||
labelData = this.meridiansLabels_[i];
|
|
||||||
feature = this.featurePool_[poolIndex++];
|
|
||||||
feature.setGeometry(labelData.geom);
|
|
||||||
feature.setStyle(this.lonLabelStyle_);
|
|
||||||
feature.set('graticule_label', labelData.text);
|
|
||||||
featuresColl.push(feature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.parallelsLabels_) {
|
|
||||||
for (i = 0, l = this.parallelsLabels_.length; i < l; ++i) {
|
|
||||||
labelData = this.parallelsLabels_[i];
|
|
||||||
feature = this.featurePool_[poolIndex++];
|
|
||||||
feature.setGeometry(labelData.geom);
|
|
||||||
feature.setStyle(this.latLabelStyle_);
|
|
||||||
feature.set('graticule_label', labelData.text);
|
|
||||||
featuresColl.push(feature);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -549,12 +584,16 @@ class Graticule extends VectorLayer {
|
|||||||
const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index);
|
const lineString = this.getMeridian_(lon, minLat, maxLat, squaredTolerance, index);
|
||||||
if (intersects(lineString.getExtent(), extent)) {
|
if (intersects(lineString.getExtent(), extent)) {
|
||||||
if (this.meridiansLabels_) {
|
if (this.meridiansLabels_) {
|
||||||
const textPoint = this.getMeridianPoint_(lineString, extent, index);
|
const text = this.lonLabelFormatter_(lon);
|
||||||
|
if (index in this.meridiansLabels_) {
|
||||||
|
this.meridiansLabels_[index].text = text;
|
||||||
|
} else {
|
||||||
this.meridiansLabels_[index] = {
|
this.meridiansLabels_[index] = {
|
||||||
geom: textPoint,
|
geom: new Point([]),
|
||||||
text: this.lonLabelFormatter_(lon)
|
text: text
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.meridians_[index++] = lineString;
|
this.meridians_[index++] = lineString;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
@@ -574,17 +613,101 @@ class Graticule extends VectorLayer {
|
|||||||
const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index);
|
const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index);
|
||||||
if (intersects(lineString.getExtent(), extent)) {
|
if (intersects(lineString.getExtent(), extent)) {
|
||||||
if (this.parallelsLabels_) {
|
if (this.parallelsLabels_) {
|
||||||
const textPoint = this.getParallelPoint_(lineString, extent, index);
|
const text = this.latLabelFormatter_(lat);
|
||||||
|
if (index in this.parallelsLabels_) {
|
||||||
|
this.parallelsLabels_[index].text = text;
|
||||||
|
} else {
|
||||||
this.parallelsLabels_[index] = {
|
this.parallelsLabels_[index] = {
|
||||||
geom: textPoint,
|
geom: new Point([]),
|
||||||
text: this.latLabelFormatter_(lat)
|
text: text
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
this.parallels_[index++] = lineString;
|
this.parallels_[index++] = lineString;
|
||||||
}
|
}
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import("../render/Event.js").default} event Render event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
drawLabels_(event) {
|
||||||
|
const rotation = event.frameState.viewState.rotation;
|
||||||
|
const extent = event.frameState.extent;
|
||||||
|
const rotationCenter = getCenter(extent);
|
||||||
|
let rotationExtent = extent;
|
||||||
|
if (rotation) {
|
||||||
|
const width = getWidth(extent);
|
||||||
|
const height = getHeight(extent);
|
||||||
|
const cr = Math.abs(Math.cos(rotation));
|
||||||
|
const sr = Math.abs(Math.sin(rotation));
|
||||||
|
const unrotatedWidth = (sr * height - cr * width) / (sr * sr - cr * cr);
|
||||||
|
const unrotatedHeight = (sr * width - cr * height) / (sr * sr - cr * cr);
|
||||||
|
rotationExtent = [
|
||||||
|
rotationCenter[0] - unrotatedWidth / 2, rotationCenter[1] - unrotatedHeight / 2,
|
||||||
|
rotationCenter[0] + unrotatedWidth / 2, rotationCenter[1] + unrotatedHeight / 2
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
let startWorld = 0;
|
||||||
|
let endWorld = 0;
|
||||||
|
let labelsAtStart = this.latLabelPosition_ < 0.5;
|
||||||
|
const projectionExtent = this.projection_.getExtent();
|
||||||
|
const worldWidth = getWidth(projectionExtent);
|
||||||
|
if (this.getSource().getWrapX() && this.projection_.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||||
|
startWorld = Math.floor((extent[0] - projectionExtent[0]) / worldWidth);
|
||||||
|
endWorld = Math.ceil((extent[2] - projectionExtent[2]) / worldWidth);
|
||||||
|
const inverted = Math.abs(rotation) > Math.PI / 2;
|
||||||
|
labelsAtStart = labelsAtStart !== inverted;
|
||||||
|
}
|
||||||
|
const vectorContext = getVectorContext(event);
|
||||||
|
|
||||||
|
for (let world = startWorld; world <= endWorld; ++world) {
|
||||||
|
let poolIndex = this.meridians_.length + this.parallels_.length;
|
||||||
|
let feature, index, l, textPoint;
|
||||||
|
|
||||||
|
if (this.meridiansLabels_) {
|
||||||
|
for (index = 0, l = this.meridiansLabels_.length; index < l; ++index) {
|
||||||
|
const lineString = this.meridians_[index];
|
||||||
|
if (!rotation && world === 0) {
|
||||||
|
textPoint = this.getMeridianPoint_(lineString, extent, index);
|
||||||
|
} else {
|
||||||
|
const clone = lineString.clone();
|
||||||
|
clone.translate(world * worldWidth, 0);
|
||||||
|
clone.rotate(-rotation, rotationCenter);
|
||||||
|
textPoint = this.getMeridianPoint_(clone, rotationExtent, index);
|
||||||
|
textPoint.rotate(rotation, rotationCenter);
|
||||||
|
}
|
||||||
|
feature = this.featurePool_[poolIndex++];
|
||||||
|
feature.setGeometry(textPoint);
|
||||||
|
feature.set('graticule_label', this.meridiansLabels_[index].text);
|
||||||
|
vectorContext.drawFeature(feature, this.lonLabelStyle_(feature));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.parallelsLabels_) {
|
||||||
|
if (world === startWorld && labelsAtStart || world === endWorld && !labelsAtStart) {
|
||||||
|
for (index = 0, l = this.parallels_.length; index < l; ++index) {
|
||||||
|
const lineString = this.parallels_[index];
|
||||||
|
if (!rotation && world === 0) {
|
||||||
|
textPoint = this.getParallelPoint_(lineString, extent, index);
|
||||||
|
} else {
|
||||||
|
const clone = lineString.clone();
|
||||||
|
clone.translate(world * worldWidth, 0);
|
||||||
|
clone.rotate(-rotation, rotationCenter);
|
||||||
|
textPoint = this.getParallelPoint_(clone, rotationExtent, index);
|
||||||
|
textPoint.rotate(rotation, rotationCenter);
|
||||||
|
}
|
||||||
|
feature = this.featurePool_[poolIndex++];
|
||||||
|
feature.setGeometry(textPoint);
|
||||||
|
feature.set('graticule_label', this.parallelsLabels_[index].text);
|
||||||
|
vectorContext.drawFeature(feature, this.latLabelStyle_(feature));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {import("../extent.js").Extent} extent Extent.
|
* @param {import("../extent.js").Extent} extent Extent.
|
||||||
* @param {import("../coordinate.js").Coordinate} center Center.
|
* @param {import("../coordinate.js").Coordinate} center Center.
|
||||||
@@ -606,24 +729,91 @@ class Graticule extends VectorLayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const centerLonLat = this.toLonLatTransform_(center);
|
let wrapX = false;
|
||||||
let centerLon = centerLonLat[0];
|
const projectionExtent = this.projection_.getExtent();
|
||||||
let centerLat = centerLonLat[1];
|
const worldWidth = getWidth(projectionExtent);
|
||||||
|
if (this.getSource().getWrapX() && this.projection_.canWrapX() && !containsExtent(projectionExtent, extent)) {
|
||||||
|
if (getWidth(extent) >= worldWidth) {
|
||||||
|
extent[0] = projectionExtent[0];
|
||||||
|
extent[2] = projectionExtent[2];
|
||||||
|
} else {
|
||||||
|
wrapX = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Constrain the center to fit into the extent available to the graticule
|
||||||
|
|
||||||
|
const validCenterP = [
|
||||||
|
clamp(center[0], this.minX_, this.maxX_),
|
||||||
|
clamp(center[1], this.minY_, this.maxY_)
|
||||||
|
];
|
||||||
|
|
||||||
|
// Transform the center to lon lat
|
||||||
|
// Some projections may have a void area at the poles
|
||||||
|
// so replace any NaN latitudes with the min or max value closest to a pole
|
||||||
|
|
||||||
|
const centerLonLat = this.toLonLatTransform_(validCenterP);
|
||||||
|
if (isNaN(centerLonLat[1])) {
|
||||||
|
centerLonLat[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ?
|
||||||
|
this.maxLat_ : this.minLat_;
|
||||||
|
}
|
||||||
|
let centerLon = clamp(centerLonLat[0], this.minLon_, this.maxLon_);
|
||||||
|
let centerLat = clamp(centerLonLat[1], this.minLat_, this.maxLat_);
|
||||||
const maxLines = this.maxLines_;
|
const maxLines = this.maxLines_;
|
||||||
let cnt, idx, lat, lon;
|
let cnt, idx, lat, lon;
|
||||||
|
|
||||||
let validExtent = [
|
// Limit the extent to fit into the extent available to the graticule
|
||||||
Math.max(extent[0], this.minLonP_),
|
|
||||||
Math.max(extent[1], this.minLatP_),
|
|
||||||
Math.min(extent[2], this.maxLonP_),
|
|
||||||
Math.min(extent[3], this.maxLatP_)
|
|
||||||
];
|
|
||||||
|
|
||||||
validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326');
|
let validExtentP = extent;
|
||||||
const maxLat = validExtent[3];
|
if (!wrapX) {
|
||||||
const maxLon = validExtent[2];
|
validExtentP = [
|
||||||
const minLat = validExtent[1];
|
clamp(extent[0], this.minX_, this.maxX_),
|
||||||
const minLon = validExtent[0];
|
clamp(extent[1], this.minY_, this.maxY_),
|
||||||
|
clamp(extent[2], this.minX_, this.maxX_),
|
||||||
|
clamp(extent[3], this.minY_, this.maxY_)
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform the extent to get the lon lat ranges for the edges of the extent
|
||||||
|
|
||||||
|
const validExtent = applyTransform(validExtentP, this.toLonLatTransform_, undefined, 8);
|
||||||
|
|
||||||
|
let maxLat = validExtent[3];
|
||||||
|
let maxLon = validExtent[2];
|
||||||
|
let minLat = validExtent[1];
|
||||||
|
let minLon = validExtent[0];
|
||||||
|
|
||||||
|
if (!wrapX) {
|
||||||
|
|
||||||
|
// Check if extremities of the world extent lie inside the extent
|
||||||
|
// (for example the pole in a polar projection)
|
||||||
|
// and extend the extent as appropriate
|
||||||
|
|
||||||
|
if (containsCoordinate(validExtentP, this.bottomLeft_)) {
|
||||||
|
minLon = this.minLon_;
|
||||||
|
minLat = this.minLat_;
|
||||||
|
}
|
||||||
|
if (containsCoordinate(validExtentP, this.bottomRight_)) {
|
||||||
|
maxLon = this.maxLon_;
|
||||||
|
minLat = this.minLat_;
|
||||||
|
}
|
||||||
|
if (containsCoordinate(validExtentP, this.topLeft_)) {
|
||||||
|
minLon = this.minLon_;
|
||||||
|
maxLat = this.maxLat_;
|
||||||
|
}
|
||||||
|
if (containsCoordinate(validExtentP, this.topRight_)) {
|
||||||
|
maxLon = this.maxLon_;
|
||||||
|
maxLat = this.maxLat_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The transformed center may also extend the lon lat ranges used for rendering
|
||||||
|
|
||||||
|
maxLat = clamp(maxLat, centerLat, this.maxLat_);
|
||||||
|
maxLon = clamp(maxLon, centerLon, this.maxLon_);
|
||||||
|
minLat = clamp(minLat, this.minLat_, centerLat);
|
||||||
|
minLon = clamp(minLon, this.minLon_, centerLon);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Create meridians
|
// Create meridians
|
||||||
|
|
||||||
@@ -633,18 +823,30 @@ class Graticule extends VectorLayer {
|
|||||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0);
|
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 0);
|
||||||
|
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
|
if (wrapX) {
|
||||||
|
while ((lon -= interval) >= minLon && cnt++ < maxLines) {
|
||||||
|
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
while (lon != this.minLon_ && cnt++ < maxLines) {
|
while (lon != this.minLon_ && cnt++ < maxLines) {
|
||||||
lon = Math.max(lon - interval, this.minLon_);
|
lon = Math.max(lon - interval, this.minLon_);
|
||||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
lon = clamp(centerLon, this.minLon_, this.maxLon_);
|
lon = clamp(centerLon, this.minLon_, this.maxLon_);
|
||||||
|
|
||||||
cnt = 0;
|
cnt = 0;
|
||||||
|
if (wrapX) {
|
||||||
|
while ((lon += interval) <= maxLon && cnt++ < maxLines) {
|
||||||
|
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
while (lon != this.maxLon_ && cnt++ < maxLines) {
|
while (lon != this.maxLon_ && cnt++ < maxLines) {
|
||||||
lon = Math.min(lon + interval, this.maxLon_);
|
lon = Math.min(lon + interval, this.maxLon_);
|
||||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.meridians_.length = idx;
|
this.meridians_.length = idx;
|
||||||
if (this.meridiansLabels_) {
|
if (this.meridiansLabels_) {
|
||||||
@@ -694,11 +896,13 @@ class Graticule extends VectorLayer {
|
|||||||
/** @type {Array<number>} **/
|
/** @type {Array<number>} **/
|
||||||
const p2 = [];
|
const p2 = [];
|
||||||
for (let i = 0, ii = this.intervals_.length; i < ii; ++i) {
|
for (let i = 0, ii = this.intervals_.length; i < ii; ++i) {
|
||||||
const delta = this.intervals_[i] / 2;
|
const delta = clamp(this.intervals_[i] / 2, 0, 90);
|
||||||
|
// Don't attempt to transform latitudes beyond the poles!
|
||||||
|
const clampedLat = clamp(centerLat, -90 + delta, 90 - delta);
|
||||||
p1[0] = centerLon - delta;
|
p1[0] = centerLon - delta;
|
||||||
p1[1] = centerLat - delta;
|
p1[1] = clampedLat - delta;
|
||||||
p2[0] = centerLon + delta;
|
p2[0] = centerLon + delta;
|
||||||
p2[1] = centerLat + delta;
|
p2[1] = clampedLat + delta;
|
||||||
this.fromLonLatTransform_(p1, p1);
|
this.fromLonLatTransform_(p1, p1);
|
||||||
this.fromLonLatTransform_(p2, p2);
|
this.fromLonLatTransform_(p2, p2);
|
||||||
const dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2);
|
const dist = Math.pow(p2[0] - p1[0], 2) + Math.pow(p2[1] - p1[1], 2);
|
||||||
@@ -741,19 +945,23 @@ class Graticule extends VectorLayer {
|
|||||||
*/
|
*/
|
||||||
getMeridianPoint_(lineString, extent, index) {
|
getMeridianPoint_(lineString, extent, index) {
|
||||||
const flatCoordinates = lineString.getFlatCoordinates();
|
const flatCoordinates = lineString.getFlatCoordinates();
|
||||||
const clampedBottom = Math.max(extent[1], flatCoordinates[1]);
|
let bottom = 1;
|
||||||
const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]);
|
let top = flatCoordinates.length - 1;
|
||||||
|
if (flatCoordinates[bottom] > flatCoordinates[top]) {
|
||||||
|
bottom = top;
|
||||||
|
top = 1;
|
||||||
|
}
|
||||||
|
const clampedBottom = Math.max(extent[1], flatCoordinates[bottom]);
|
||||||
|
const clampedTop = Math.min(extent[3], flatCoordinates[top]);
|
||||||
const lat = clamp(
|
const lat = clamp(
|
||||||
extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_,
|
extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_,
|
||||||
clampedBottom, clampedTop);
|
clampedBottom, clampedTop);
|
||||||
const coordinate = [flatCoordinates[0], lat];
|
const coordinate0 = flatCoordinates[bottom - 1] +
|
||||||
let point;
|
(flatCoordinates[top - 1] - flatCoordinates[bottom - 1]) * (lat - flatCoordinates[bottom]) /
|
||||||
if (index in this.meridiansLabels_) {
|
(flatCoordinates[top] - flatCoordinates[bottom]);
|
||||||
point = this.meridiansLabels_[index].geom;
|
const coordinate = [coordinate0, lat];
|
||||||
|
const point = this.meridiansLabels_[index].geom;
|
||||||
point.setCoordinates(coordinate);
|
point.setCoordinates(coordinate);
|
||||||
} else {
|
|
||||||
point = new Point(coordinate);
|
|
||||||
}
|
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -797,19 +1005,23 @@ class Graticule extends VectorLayer {
|
|||||||
*/
|
*/
|
||||||
getParallelPoint_(lineString, extent, index) {
|
getParallelPoint_(lineString, extent, index) {
|
||||||
const flatCoordinates = lineString.getFlatCoordinates();
|
const flatCoordinates = lineString.getFlatCoordinates();
|
||||||
const clampedLeft = Math.max(extent[0], flatCoordinates[0]);
|
let left = 0;
|
||||||
const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]);
|
let right = flatCoordinates.length - 2;
|
||||||
|
if (flatCoordinates[left] > flatCoordinates[right]) {
|
||||||
|
left = right;
|
||||||
|
right = 0;
|
||||||
|
}
|
||||||
|
const clampedLeft = Math.max(extent[0], flatCoordinates[left]);
|
||||||
|
const clampedRight = Math.min(extent[2], flatCoordinates[right]);
|
||||||
const lon = clamp(
|
const lon = clamp(
|
||||||
extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_,
|
extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_,
|
||||||
clampedLeft, clampedRight);
|
clampedLeft, clampedRight);
|
||||||
const coordinate = [lon, flatCoordinates[1]];
|
const coordinate1 = flatCoordinates[left + 1] +
|
||||||
let point;
|
(flatCoordinates[right + 1] - flatCoordinates[left + 1]) * (lon - flatCoordinates[left]) /
|
||||||
if (index in this.parallelsLabels_) {
|
(flatCoordinates[right] - flatCoordinates[left]);
|
||||||
point = this.parallelsLabels_[index].geom;
|
const coordinate = [lon, coordinate1];
|
||||||
|
const point = this.parallelsLabels_[index].geom;
|
||||||
point.setCoordinates(coordinate);
|
point.setCoordinates(coordinate);
|
||||||
} else {
|
|
||||||
point = new Point(coordinate);
|
|
||||||
}
|
|
||||||
return point;
|
return point;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -830,23 +1042,66 @@ class Graticule extends VectorLayer {
|
|||||||
const epsg4326Projection = getProjection('EPSG:4326');
|
const epsg4326Projection = getProjection('EPSG:4326');
|
||||||
|
|
||||||
const worldExtent = projection.getWorldExtent();
|
const worldExtent = projection.getWorldExtent();
|
||||||
const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection);
|
|
||||||
|
|
||||||
this.maxLat_ = worldExtent[3];
|
this.maxLat_ = worldExtent[3];
|
||||||
this.maxLon_ = worldExtent[2];
|
this.maxLon_ = worldExtent[2];
|
||||||
this.minLat_ = worldExtent[1];
|
this.minLat_ = worldExtent[1];
|
||||||
this.minLon_ = worldExtent[0];
|
this.minLon_ = worldExtent[0];
|
||||||
|
|
||||||
this.maxLatP_ = worldExtentP[3];
|
// If the world extent crosses the dateline define a custom transform to
|
||||||
this.maxLonP_ = worldExtentP[2];
|
// return longitudes which wrap the dateline
|
||||||
this.minLatP_ = worldExtentP[1];
|
|
||||||
this.minLonP_ = worldExtentP[0];
|
const toLonLatTransform = getTransform(projection, epsg4326Projection);
|
||||||
|
if (this.minLon_ < this.maxLon_) {
|
||||||
|
this.toLonLatTransform_ = toLonLatTransform;
|
||||||
|
} else {
|
||||||
|
const split = this.minLon_ + this.maxLon_ / 2;
|
||||||
|
this.maxLon_ += 360;
|
||||||
|
this.toLonLatTransform_ = function(coordinates, opt_output, opt_dimension) {
|
||||||
|
const dimension = opt_dimension || 2;
|
||||||
|
const lonLatCoordinates = toLonLatTransform(coordinates, opt_output, dimension);
|
||||||
|
for (let i = 0, l = lonLatCoordinates.length; i < l; i += dimension) {
|
||||||
|
if (lonLatCoordinates[i] < split) {
|
||||||
|
lonLatCoordinates[i] += 360;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lonLatCoordinates;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transform the extent to get the limits of the view projection extent
|
||||||
|
// which should be available to the graticule
|
||||||
|
|
||||||
this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection);
|
this.fromLonLatTransform_ = getTransform(epsg4326Projection, projection);
|
||||||
|
const worldExtentP = applyTransform(
|
||||||
|
[this.minLon_, this.minLat_, this.maxLon_, this.maxLat_],
|
||||||
|
this.fromLonLatTransform_,
|
||||||
|
undefined,
|
||||||
|
8
|
||||||
|
);
|
||||||
|
|
||||||
this.toLonLatTransform_ = getTransform(projection, epsg4326Projection);
|
this.minX_ = worldExtentP[0];
|
||||||
|
this.maxX_ = worldExtentP[2];
|
||||||
|
this.minY_ = worldExtentP[1];
|
||||||
|
this.maxY_ = worldExtentP[3];
|
||||||
|
|
||||||
|
// Determine the view projection coordinates of the extremities of the world extent
|
||||||
|
// as these may lie inside a view extent (for example the pole in a polar projection)
|
||||||
|
|
||||||
|
this.bottomLeft_ = this.fromLonLatTransform_([this.minLon_, this.minLat_]);
|
||||||
|
this.bottomRight_ = this.fromLonLatTransform_([this.maxLon_, this.minLat_]);
|
||||||
|
this.topLeft_ = this.fromLonLatTransform_([this.minLon_, this.maxLat_]);
|
||||||
|
this.topRight_ = this.fromLonLatTransform_([this.maxLon_, this.maxLat_]);
|
||||||
|
|
||||||
|
// Transform the projection center to lon lat
|
||||||
|
// Some projections may have a void area at the poles
|
||||||
|
// so replace any NaN latitudes with the min or max value closest to a pole
|
||||||
|
|
||||||
this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent()));
|
this.projectionCenterLonLat_ = this.toLonLatTransform_(getCenter(projection.getExtent()));
|
||||||
|
if (isNaN(this.projectionCenterLonLat_[1])) {
|
||||||
|
this.projectionCenterLonLat_[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ?
|
||||||
|
this.maxLat_ : this.minLat_;
|
||||||
|
}
|
||||||
|
|
||||||
this.projection_ = projection;
|
this.projection_ = projection;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ import SourceState from '../source/State.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers.
|
* @property {Array<import("./Base.js").default>|import("../Collection.js").default<import("./Base.js").default>} [layers] Child layers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {Array<string>} [gradient=['#00f', '#0ff', '#0f0', '#ff0', '#f00']] The color gradient
|
* @property {Array<string>} [gradient=['#00f', '#0ff', '#0f0', '#ff0', '#f00']] The color gradient
|
||||||
* of the heatmap, specified as an array of CSS color strings.
|
* of the heatmap, specified as an array of CSS color strings.
|
||||||
* @property {number} [radius=8] Radius size in pixels.
|
* @property {number} [radius=8] Radius size in pixels.
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ import {assert} from '../asserts.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../source/Source.js").default} [source] Source for this layer. If not provided to the constructor,
|
* @property {import("../source/Source.js").default} [source] Source for this layer. If not provided to the constructor,
|
||||||
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
|
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
|
||||||
* construction.
|
* construction.
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||||
* `null` to avoid the sort, but get an undefined draw order.
|
* `null` to avoid the sort, but get an undefined draw order.
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ import {assign} from '../obj.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
* @property {import("../render.js").OrderFunction} [renderOrder] Render order. Function to be used when sorting
|
||||||
* features before rendering. By default features are drawn in the order that they are created. Use
|
* features before rendering. By default features are drawn in the order that they are created. Use
|
||||||
* `null` to avoid the sort, but get an undefined draw order.
|
* `null` to avoid the sort, but get an undefined draw order.
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ import Layer from './Layer.js';
|
|||||||
* visible.
|
* visible.
|
||||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||||
* be visible.
|
* be visible.
|
||||||
|
* @property {number} [minZoom] The minimum view zoom level (exclusive) above which this layer will be
|
||||||
|
* visible.
|
||||||
|
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||||
|
* be visible.
|
||||||
* @property {import("../source/Vector.js").default} [source] Source.
|
* @property {import("../source/Vector.js").default} [source] Source.
|
||||||
* @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
|
* @property {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
|
||||||
* prevent all hit detection on the layer.
|
* prevent all hit detection on the layer.
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user