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:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/node:latest-browsers
|
||||
- image: circleci/node:current-browsers
|
||||
|
||||
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 [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
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
```json
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true,
|
||||
// Point to the JSDoc typed sources when using modules from the ol package
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"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.
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
## 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
|
||||
|
||||
#### 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.
|
||||
*/
|
||||
|
||||
const api = [];
|
||||
const api = {};
|
||||
const classes = {};
|
||||
const types = {};
|
||||
const modules = {};
|
||||
|
||||
function hasApiMembers(doclet) {
|
||||
return doclet.longname.split('#')[0] == this.longname;
|
||||
}
|
||||
|
||||
function includeAugments(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.
|
||||
@@ -77,9 +73,6 @@ function includeAugments(doclet) {
|
||||
});
|
||||
}
|
||||
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 = {
|
||||
|
||||
newDoclet: function(e) {
|
||||
const doclet = e.doclet;
|
||||
if (doclet.stability) {
|
||||
modules[doclet.longname.split(/[~\.]/).shift()] = true;
|
||||
api.push(doclet);
|
||||
api[doclet.longname.split('#')[0]] = true;
|
||||
}
|
||||
if (doclet.kind == 'class') {
|
||||
if (!(doclet.longname in classes)) {
|
||||
@@ -133,22 +154,14 @@ exports.handlers = {
|
||||
|
||||
parseComplete: function(e) {
|
||||
const doclets = e.doclets;
|
||||
const byLongname = doclets.index.longname;
|
||||
for (let i = doclets.length - 1; i >= 0; --i) {
|
||||
const doclet = doclets[i];
|
||||
if (doclet.stability) {
|
||||
if (doclet.kind == 'class') {
|
||||
includeAugments(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;
|
||||
});
|
||||
}
|
||||
sortOtherMembers(doclet);
|
||||
// Always document namespaces and items with stability annotation
|
||||
continue;
|
||||
}
|
||||
@@ -159,20 +172,32 @@ exports.handlers = {
|
||||
if (doclet.isEnum || doclet.kind == 'typedef') {
|
||||
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.
|
||||
// This is used in ../template/tmpl/container.tmpl to hide the
|
||||
// constructor from the docs.
|
||||
doclet._hideConstructor = true;
|
||||
includeAugments(doclet);
|
||||
} else if (!doclet._hideConstructor && !(doclet.kind == 'typedef' && doclet.longname in types)) {
|
||||
sortOtherMembers(doclet);
|
||||
} else if (!doclet._hideConstructor) {
|
||||
// Remove all other undocumented symbols
|
||||
doclet.undocumented = true;
|
||||
}
|
||||
if (doclet._documented) {
|
||||
delete doclet.undocumented;
|
||||
if (doclet.memberof && byLongname[doclet.memberof] &&
|
||||
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) {
|
||||
let ancestors, candidate, candidates, doclet, i, j, k, l, key;
|
||||
let incompleteDoclet, stability, incomplete, incompletes;
|
||||
let stability, incomplete, incompletes;
|
||||
const doclets = e.doclets;
|
||||
for (i = doclets.length - 1; i >= 0; --i) {
|
||||
doclet = doclets[i];
|
||||
if (doclet.augments) {
|
||||
ancestors = [].concat(doclet.augments);
|
||||
}
|
||||
incompletes = incompleteByClass[doclet.longname];
|
||||
if (ancestors && incompletes) {
|
||||
if (!doclet.augments || !incompletes) {
|
||||
continue;
|
||||
}
|
||||
ancestors = doclet.augments.slice();
|
||||
// collect ancestors from the whole hierarchy
|
||||
for (j = 0; j < ancestors.length; ++j) {
|
||||
candidates = lookup[ancestors[j]];
|
||||
@@ -58,12 +58,13 @@ exports.handlers = {
|
||||
for (k = candidates.length - 1; k >= 0; --k) {
|
||||
candidate = candidates[k];
|
||||
if (candidate.augments) {
|
||||
ancestors = ancestors.concat(candidate.augments);
|
||||
Array.prototype.push.apply(ancestors, candidate.augments);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// walk through all inheritDoc members
|
||||
let incompleteDoclet;
|
||||
for (j = incompletes.length - 1; j >= 0; --j) {
|
||||
incomplete = incompletes[j];
|
||||
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 handle = require('jsdoc/lib/jsdoc/util/error').handle;
|
||||
const helper = require('jsdoc/lib/jsdoc/util/templateHelper');
|
||||
const _ = require('underscore');
|
||||
const htmlsafe = helper.htmlsafe;
|
||||
const linkto = helper.linkto;
|
||||
const resolveAuthorLinks = helper.resolveAuthorLinks;
|
||||
@@ -188,10 +187,12 @@ function attachModuleSymbols(doclets, modules) {
|
||||
});
|
||||
}
|
||||
|
||||
function getPrettyName(longname) {
|
||||
return longname
|
||||
.split('~')[0]
|
||||
.replace('module:', '');
|
||||
function getPrettyName(doclet) {
|
||||
const fullname = doclet.longname.replace('module:', '');
|
||||
if (doclet.isDefaultExport) {
|
||||
return fullname.split('~')[0];
|
||||
}
|
||||
return fullname;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -209,27 +210,13 @@ function getPrettyName(longname) {
|
||||
*/
|
||||
function buildNav(members) {
|
||||
const nav = [];
|
||||
// merge namespaces and classes, then sort
|
||||
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) {
|
||||
members.classes.forEach(function(v) {
|
||||
// exclude interfaces from sidebar
|
||||
if (v.interface !== true && v.kind === 'class') {
|
||||
if (v.interface !== true) {
|
||||
nav.push({
|
||||
type: 'class',
|
||||
longname: v.longname,
|
||||
prettyname: getPrettyName(v.longname),
|
||||
prettyname: getPrettyName(v),
|
||||
name: v.name,
|
||||
module: find({
|
||||
kind: 'module',
|
||||
@@ -253,7 +240,9 @@ function buildNav(members) {
|
||||
memberof: v.longname
|
||||
})
|
||||
});
|
||||
} else if (v.kind == 'module') {
|
||||
}
|
||||
});
|
||||
members.modules.forEach(function(v) {
|
||||
const classes = find({
|
||||
kind: 'class',
|
||||
memberof: v.longname
|
||||
@@ -274,13 +263,13 @@ function buildNav(members) {
|
||||
kind: 'event',
|
||||
memberof: v.longname
|
||||
});
|
||||
// only add modules that have more to show than just classes
|
||||
const numItems = classes.length - 1 + members.length + methods.length + typedefs.length + events.length;
|
||||
if (!classes.length || (numItems > 0 && numItems !== classes.length)) {
|
||||
// Only add modules that contain more than just classes with their
|
||||
// associated Options typedef
|
||||
if (typedefs.length > classes.length || members.length + methods.length > 0) {
|
||||
nav.push({
|
||||
type: 'module',
|
||||
longname: v.longname,
|
||||
prettyname: getPrettyName(v.longname),
|
||||
prettyname: getPrettyName(v),
|
||||
name: v.name,
|
||||
members: members,
|
||||
methods: methods,
|
||||
@@ -289,7 +278,18 @@ function buildNav(members) {
|
||||
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;
|
||||
}
|
||||
@@ -477,6 +477,7 @@ exports.publish = function(taffyData, opts, tutorials) {
|
||||
// index page displays information from package.json and lists files
|
||||
const files = find({kind: 'file'});
|
||||
|
||||
view.navigationHtml = helper.resolveLinks(view.partial('navigation.tmpl'));
|
||||
generate('Index',
|
||||
[{kind: 'mainpage', readme: opts.readme, longname: (opts.mainpagetitle) ? opts.mainpagetitle : 'Main Page'}].concat(files),
|
||||
indexUrl);
|
||||
|
||||
@@ -1,99 +1,258 @@
|
||||
$(function () {
|
||||
// Search Items
|
||||
$('#include_modules').change(function (e) {
|
||||
console.log('change');
|
||||
if ($(this).is(':checked')) {
|
||||
'use strict';
|
||||
|
||||
} 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) {
|
||||
let weight = 0;
|
||||
function getWeightFunction(searchTerm, allowRegex) {
|
||||
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
|
||||
if ($matchedItem.data('shortname')
|
||||
&& $matchedItem.data('shortname').toLowerCase() === searchTerm.toLowerCase()) {
|
||||
weight++;
|
||||
const name = matchedItem.dataset.name;
|
||||
if (beginOnly) {
|
||||
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;
|
||||
};
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// Toggle when click an item element
|
||||
$('.navigation').on('click', '.toggle', function (e) {
|
||||
$(this).parent().parent().find('.itemMembers').toggle();
|
||||
});
|
||||
const search = (function () {
|
||||
const $navList = $('.navigation-list');
|
||||
const navListNode = $navList.get(0);
|
||||
let $classItems;
|
||||
let $members;
|
||||
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
|
||||
var filename = $('.page-title').data('filename')
|
||||
const longname = $('.page-title').data('filename')
|
||||
.replace(/\.[a-z]+$/, '')
|
||||
.replace('module-', 'module:')
|
||||
.replace(/_/g, '/')
|
||||
.replace(/-/g, '~');
|
||||
var $currentItem = $('.navigation .item[data-name*="' + filename + '"]:eq(0)');
|
||||
|
||||
if ($currentItem.length) {
|
||||
$currentItem
|
||||
.remove()
|
||||
.prependTo('.navigation .list')
|
||||
.show()
|
||||
.find('.itemMembers')
|
||||
.show();
|
||||
const currentItem = navListNode.querySelector('.item[data-longname="' + longname + '"]');
|
||||
if (currentItem) {
|
||||
$navList.prepend(currentItem);
|
||||
}
|
||||
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
|
||||
var _onResize = function () {
|
||||
var height = $(window).height();
|
||||
var $el = $('.navigation');
|
||||
|
||||
$el.height(height).find('.list').height(height - 133);
|
||||
$el.height(height).find('.navigation-list').height(height - 133);
|
||||
};
|
||||
|
||||
$(window).on('resize', _onResize);
|
||||
@@ -137,22 +296,4 @@ $(function () {
|
||||
'<a href="' + link + textParts[1].replace('line ', '#L') + '">' +
|
||||
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;
|
||||
height: 0px;
|
||||
}
|
||||
.nameContainer .anchor.highlighted + h4 {
|
||||
/* Highlighting current anchor */
|
||||
.nameContainer .anchor:target + h4 {
|
||||
background-color: #faebcc;
|
||||
}
|
||||
a {
|
||||
@@ -123,7 +124,7 @@ li {
|
||||
color: #fff;
|
||||
border-color: #555;
|
||||
}
|
||||
.navigation .list {
|
||||
.navigation .navigation-list {
|
||||
padding: 10px 15px 0 15px;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
@@ -135,6 +136,24 @@ li {
|
||||
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 {
|
||||
border: 5px solid orange;
|
||||
}
|
||||
@@ -147,8 +166,8 @@ li {
|
||||
}
|
||||
.navigation li.item .title {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
display: block;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.navigation li.item .title a {
|
||||
@@ -183,10 +202,42 @@ li {
|
||||
padding-left: 8px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.navigation li.item .itemMembers {
|
||||
display: none;
|
||||
.navigation li.item .member-list {
|
||||
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 {
|
||||
padding: 20px 20px;
|
||||
margin-left: 250px;
|
||||
|
||||
@@ -92,7 +92,7 @@ var version = obj.packageInfo.version;
|
||||
</div>
|
||||
|
||||
<div id="wrap" class="clearfix">
|
||||
<?js= this.partial('navigation.tmpl', this) ?>
|
||||
<?js= this.navigationHtml ?>
|
||||
<div class="main">
|
||||
<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">
|
||||
|
||||
@@ -64,7 +64,7 @@ var self = this;
|
||||
<ul><?js fires.forEach(function(f) {
|
||||
var parts = f.split(/#?event:/);
|
||||
var type = parts.pop();
|
||||
var eventClassName = parts[0];
|
||||
var eventClass = self.find({longname: parts[0]})[0];
|
||||
parts = type.split(' ');
|
||||
type = parts.shift();
|
||||
var description = parts.length ? parts.join(' ') : '';
|
||||
@@ -74,13 +74,10 @@ var self = this;
|
||||
}
|
||||
?>
|
||||
<li class="<?js= (eventDoclet || data).stability !== 'stable' ? 'unstable' : '' ?>">
|
||||
<code><?js= eventClassName ? self.linkto(f, type) : type ?></code>
|
||||
<?js if (eventClassName) {
|
||||
var eventClass = self.find({longname: eventClassName})[0];
|
||||
if (eventClass) { ?>
|
||||
<code><?js= eventClass ? self.linkto(f, type) : type ?></code>
|
||||
<?js if (eventClass) { ?>
|
||||
(<?js= self.linkto(eventClass.longname) ?>)
|
||||
<?js } ?>
|
||||
<?js } ?>
|
||||
<?js= self.partial('stability.tmpl', eventDoclet || (data.stability ? data : {})) ?>
|
||||
<?js if (description) { ?> -
|
||||
<?js= description ?>
|
||||
|
||||
@@ -1,95 +1,59 @@
|
||||
<?js
|
||||
var self = this;
|
||||
|
||||
function toShortName(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="search">
|
||||
<input id="search" type="text" class="form-control input-sm" placeholder="Search Documentation">
|
||||
</div>
|
||||
<ul class="list">
|
||||
<?js
|
||||
this.nav.forEach(function (item) {
|
||||
?>
|
||||
<li class="item" data-name="<?js= item.longname ?>" data-shortname="<?js= item.name.toLowerCase() ?>">
|
||||
<span class="title">
|
||||
<?js if (item.type === 'module') { ?>
|
||||
<span class="glyphicon glyphicon-plus toggle"></span>
|
||||
<?js } else if (item.type === 'class') { ?>
|
||||
<span class="glyphicon glyphicon-chevron-right toggle"></span>
|
||||
<?js } ?>
|
||||
<?js= self.linkto(item.longname, item.prettyname) ?>
|
||||
<?js if (item.type === 'namespace' &&
|
||||
(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 class="navigation-list search-empty"><?js
|
||||
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">
|
||||
<span class="glyphicon <?js= getItemCssClass(item.type) ?>"></span>
|
||||
<span><?js= self.linkto(item.longname, item.prettyname.replace(/[.~]/g, '\u200b$&')) ?></span>
|
||||
</span><?js
|
||||
listContent(item, 'Members', printList);
|
||||
listContent(item, 'Typedefs', printListWithStability);
|
||||
listContent(item, 'Methods', printListWithStability);
|
||||
if (item.fires) {
|
||||
const fires = item.fires.map(v => self.find({longname: v})[0] || {longname: v, name: v.split(/#?event:/)[1]});
|
||||
listContent({fires: fires}, 'Fires', printListWithStability)
|
||||
}
|
||||
?>
|
||||
</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>
|
||||
</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>
|
||||
tags: "bing, bing-maps"
|
||||
cloak:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -18,7 +18,7 @@ for (i = 0, ii = styles.length; i < ii; ++i) {
|
||||
visible: false,
|
||||
preload: Infinity,
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||
imagerySet: styles[i]
|
||||
// use maxZoom 19 to see stretched tiles instead of the BingMaps
|
||||
// "no photos at this zoom level" tiles
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<rect width="20" height="20" style="fill:#fff" />
|
||||
<rect width="20" height="20" style="fill:#fff; stroke-width:4px; stroke:#000" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 225 B |
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: example.html
|
||||
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: >
|
||||
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"
|
||||
|
||||
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
|
||||
shortdesc: Example of exporting a map as a PNG image.
|
||||
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>
|
||||
library.
|
||||
Example of exporting a map as a PNG image.
|
||||
tags: "export, png, openstreetmap"
|
||||
---
|
||||
<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="image-download" download="map.png"></a>
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.js';
|
||||
import Overlay from '../src/ol/Overlay.js';
|
||||
import GeoJSON from '../src/ol/format/GeoJSON.js';
|
||||
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
|
||||
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
|
||||
|
||||
import {toPng} from 'html-to-image';
|
||||
|
||||
const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
@@ -16,7 +13,8 @@ const map = new Map({
|
||||
source: new VectorSource({
|
||||
url: 'data/geojson/countries.geojson',
|
||||
format: new GeoJSON()
|
||||
})
|
||||
}),
|
||||
opacity: 0.5
|
||||
})
|
||||
],
|
||||
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() {
|
||||
map.once('rendercomplete', function() {
|
||||
toPng(map.getTargetElement(), exportOptions)
|
||||
.then(function(dataURL) {
|
||||
const link = document.getElementById('image-download');
|
||||
link.href = dataURL;
|
||||
link.click();
|
||||
const mapCanvas = document.createElement('canvas');
|
||||
const size = map.getSize();
|
||||
mapCanvas.width = size[0];
|
||||
mapCanvas.height = size[1];
|
||||
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();
|
||||
});
|
||||
|
||||
@@ -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.
|
||||
tags: "export, pdf, openstreetmap"
|
||||
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="span12">
|
||||
|
||||
@@ -18,7 +18,8 @@ feature.getGeometry().transform('EPSG:4326', 'EPSG:3857');
|
||||
const vector = new VectorLayer({
|
||||
source: new VectorSource({
|
||||
features: [feature]
|
||||
})
|
||||
}),
|
||||
opacity: 0.5
|
||||
});
|
||||
|
||||
|
||||
@@ -62,8 +63,10 @@ exportButton.addEventListener('click', function() {
|
||||
mapCanvas.width = width;
|
||||
mapCanvas.height = height;
|
||||
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) {
|
||||
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);
|
||||
|
||||
@@ -147,6 +147,7 @@ function updateView() {
|
||||
view.setCenter(getCenterWithHeading(c, -c[2], view.getResolution()));
|
||||
view.setRotation(-c[2]);
|
||||
marker.setPosition(c);
|
||||
map.render();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ rome.setStyle(new Style({
|
||||
image: new Icon({
|
||||
color: '#8959A8',
|
||||
crossOrigin: 'anonymous',
|
||||
imgSize: [20, 20],
|
||||
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
|
||||
shortdesc: Example of using ol-mapbox-style with tiles from maptiler.com.
|
||||
docs: >
|
||||
@@ -10,27 +10,4 @@ cloak:
|
||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||
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>
|
||||
<script src="common.js"></script>
|
||||
<script src="mapbox-style.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
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.
|
||||
tags: "fullscreen, geolocation, mobile"
|
||||
cloak:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
---
|
||||
<!doctype html>
|
||||
|
||||
@@ -14,7 +14,7 @@ const map = new Map({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||
imagerySet: 'RoadOnDemand'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -232,6 +232,7 @@ const map = new Map({
|
||||
target: 'map',
|
||||
view: new View({
|
||||
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>
|
||||
tags: "preload, bing"
|
||||
cloak:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
- key: ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
---
|
||||
<div id="map1" class="map"></div>
|
||||
|
||||
@@ -14,7 +14,7 @@ const map1 = new Map({
|
||||
new TileLayer({
|
||||
preload: Infinity,
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||
imagerySet: 'Aerial'
|
||||
})
|
||||
})
|
||||
@@ -28,7 +28,7 @@ const map2 = new Map({
|
||||
new TileLayer({
|
||||
preload: 0, // default value
|
||||
source: new BingMaps({
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5',
|
||||
key: 'ApTJzdkyN1DdFKkRAE6QIDtzihNaf6IWJsT-nQ_2eMoO4PN__0Tzhl2-WgJtXFSp ',
|
||||
imagerySet: 'AerialWithLabelsOnDemand'
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,12 +6,12 @@ docs: >
|
||||
This example shows client-side raster reprojection capabilities from
|
||||
OSM (EPSG:3857) to arbitrary projection by searching
|
||||
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>
|
||||
<form class="form-inline">
|
||||
<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>
|
||||
<span id="epsg-result"></span>
|
||||
<div>
|
||||
@@ -19,5 +19,9 @@ tags: "reprojection, projection, proj4js, epsg.io"
|
||||
Render reprojection edges
|
||||
<input type="checkbox" id="render-edges">
|
||||
</label>
|
||||
<label for="show-graticule">
|
||||
Show graticule
|
||||
<input type="checkbox" id="show-graticule" />
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,19 +1,34 @@
|
||||
import Map from '../src/ol/Map.js';
|
||||
import View from '../src/ol/View.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 {get as getProjection, getTransform} from '../src/ol/proj.js';
|
||||
import {register} from '../src/ol/proj/proj4.js';
|
||||
import OSM from '../src/ol/source/OSM.js';
|
||||
import TileImage from '../src/ol/source/TileImage.js';
|
||||
import Stroke from '../src/ol/style/Stroke.js';
|
||||
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({
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new OSM()
|
||||
})
|
||||
}),
|
||||
graticule
|
||||
],
|
||||
target: 'map',
|
||||
view: new View({
|
||||
@@ -28,6 +43,7 @@ const queryInput = document.getElementById('epsg-query');
|
||||
const searchButton = document.getElementById('epsg-search');
|
||||
const resultSpan = document.getElementById('epsg-result');
|
||||
const renderEdgesCheckbox = document.getElementById('render-edges');
|
||||
const showGraticuleCheckbox = document.getElementById('show-graticule');
|
||||
|
||||
function setProjection(code, name, proj4def, bbox) {
|
||||
if (code === null || name === null || proj4def === null || bbox === null) {
|
||||
@@ -48,9 +64,15 @@ function setProjection(code, name, proj4def, bbox) {
|
||||
const newProj = getProjection(newProjCode);
|
||||
const fromLonLat = getTransform('EPSG:4326', newProj);
|
||||
|
||||
// very approximate calculation of projection extent
|
||||
const extent = applyTransform(
|
||||
[bbox[1], bbox[2], bbox[3], bbox[0]], fromLonLat);
|
||||
let worldExtent = [bbox[1], bbox[2], bbox[3], bbox[0]];
|
||||
newProj.setWorldExtent(worldExtent);
|
||||
|
||||
// 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);
|
||||
const newView = new View({
|
||||
projection: newProj
|
||||
@@ -98,7 +120,7 @@ searchButton.onclick = function(event) {
|
||||
|
||||
|
||||
/**
|
||||
* Handle change event.
|
||||
* Handle checkbox change event.
|
||||
*/
|
||||
renderEdgesCheckbox.onchange = function() {
|
||||
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();
|
||||
const html = document.getElementById('example-html-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 form = document.getElementById('codepen-form');
|
||||
|
||||
@@ -68,8 +70,7 @@
|
||||
|
||||
Promise.all(promises)
|
||||
.then(results => {
|
||||
const data = {
|
||||
files: {
|
||||
const files = {
|
||||
'index.html': {
|
||||
content: html
|
||||
},
|
||||
@@ -82,7 +83,14 @@
|
||||
'sandbox.config.json': {
|
||||
content: '{"template": "parcel"}'
|
||||
}
|
||||
};
|
||||
if (worker) {
|
||||
files['worker.js'] = {
|
||||
content: worker
|
||||
}
|
||||
}
|
||||
const data = {
|
||||
files: files
|
||||
};
|
||||
|
||||
for (let i = 0; i < localResources.length; i++) {
|
||||
|
||||
@@ -10,7 +10,7 @@ docs: >
|
||||
tags: "draw, edit, modify, vector, snap"
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
<form id="options-form" automplete="off">
|
||||
<form id="options-form" autocomplete="off">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<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.
|
||||
const sphereMollweideProjection = new Projection({
|
||||
code: 'ESRI:53009',
|
||||
extent: [-9009954.605703328, -9009954.605703328,
|
||||
9009954.605703328, 9009954.605703328],
|
||||
extent: [-18019909.21177587, -9009954.605703328,
|
||||
18019909.21177587, 9009954.605703328],
|
||||
worldExtent: [-179, -89.99, 179, 89.99]
|
||||
});
|
||||
|
||||
@@ -37,6 +37,6 @@ const map = new Map({
|
||||
view: new View({
|
||||
center: [0, 0],
|
||||
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="./css/ol.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 }}}
|
||||
{{{ 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>
|
||||
<title>{{ title }}</title>
|
||||
</head>
|
||||
@@ -160,6 +160,14 @@
|
||||
<pre><legend>index.js</legend><code id="example-js-source" class="language-js">import 'ol/ol.css';
|
||||
{{ js.source }}</code></pre>
|
||||
</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="source-controls">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="./resources/common.js"></script>
|
||||
<script src="./resources/prism/prism.min.js"></script>
|
||||
{{{ 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.
|
||||
|
||||
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:
|
||||
|
||||
* 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
|
||||
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.
|
||||
|
||||
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.
|
||||
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.
|
||||
tags: "vector, osmxml, loading, server, strategy, bbox"
|
||||
tags: "vector, osmxml, loading, server, strategy, bbox, maptiler"
|
||||
cloak:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||
---
|
||||
<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 {bbox as bboxStrategy} from '../src/ol/loadingstrategy.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 {Circle as CircleStyle, Fill, Stroke, Style} from '../src/ol/style.js';
|
||||
|
||||
@@ -85,8 +85,8 @@ const vectorSource = new VectorSource({
|
||||
vectorSource.addFeatures(features);
|
||||
});
|
||||
const query = '(node(' +
|
||||
epsg4326Extent[1] + ',' + epsg4326Extent[0] + ',' +
|
||||
epsg4326Extent[3] + ',' + epsg4326Extent[2] +
|
||||
epsg4326Extent[1] + ',' + Math.max(epsg4326Extent[0], -180) + ',' +
|
||||
epsg4326Extent[3] + ',' + Math.min(epsg4326Extent[2], 180) +
|
||||
');rel(bn)->.foo;way(bn);node(w)->.foo;rel(bw););out meta;';
|
||||
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({
|
||||
source: new BingMaps({
|
||||
imagerySet: 'Aerial',
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
||||
source: new XYZ({
|
||||
attributions: attributions,
|
||||
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`
|
||||
and a `PropertyIsLike` filter, and then posts the request to load the features
|
||||
that match the query.
|
||||
tags: "vector, WFS, GetFeature, filter"
|
||||
tags: "vector, WFS, GetFeature, filter, maptiler"
|
||||
cloak:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||
---
|
||||
<div id="map" class="map"></div>
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
} from '../src/ol/format/filter.js';
|
||||
import {WFS, GeoJSON} from '../src/ol/format.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 {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({
|
||||
source: new BingMaps({
|
||||
imagerySet: 'Aerial',
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
||||
source: new XYZ({
|
||||
attributions: attributions,
|
||||
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.
|
||||
docs: >
|
||||
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:
|
||||
- key: As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5
|
||||
value: Your Bing Maps Key from http://www.bingmapsportal.com/ here
|
||||
- key: get_your_own_D6rA4zTHduk6KOKTXzGB
|
||||
value: Get your own API key at https://www.maptiler.com/cloud/
|
||||
---
|
||||
<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 {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.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 {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({
|
||||
source: new BingMaps({
|
||||
imagerySet: 'Aerial',
|
||||
key: 'As1HiMj1PvLPlqc_gtM7AqZfBL8ZL3VrjaS3zIb22Uvb9WKhuJObROC-qUpa81U5'
|
||||
source: new XYZ({
|
||||
attributions: attributions,
|
||||
url: 'https://api.maptiler.com/tiles/satellite/{z}/{x}/{y}.jpg?key=' + key,
|
||||
maxZoom: 20
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ module.exports = {
|
||||
stats: 'minimal',
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js$/,
|
||||
test: /^((?!es2015-)[\s\S])*\.js$/,
|
||||
use: {
|
||||
loader: 'buble-loader'
|
||||
},
|
||||
|
||||
@@ -208,6 +208,10 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
||||
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 = {
|
||||
tag: `<script src="${this.common}.js"></script><script src="${jsName}"></script>`,
|
||||
source: jsSource
|
||||
@@ -218,9 +222,33 @@ ExampleBuilder.prototype.render = async function(dir, chunk) {
|
||||
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({
|
||||
name: name,
|
||||
dependencies: getDependencies(jsSource),
|
||||
dependencies: getDependencies(jsSource + workerSource ? `\n${workerSource}` : ''),
|
||||
devDependencies: {
|
||||
parcel: '1.11.0'
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ layout: example.html
|
||||
title: ArcGIS REST with 512x512 Tiles
|
||||
shortdesc: Example of a XYZ source in EPSG:4326 using Esri 512x512 tiles.
|
||||
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"
|
||||
---
|
||||
<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 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({
|
||||
target: 'map',
|
||||
layers: [
|
||||
new TileLayer({
|
||||
source: new XYZ({
|
||||
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',
|
||||
tileSize: tileSize,
|
||||
tileUrlFunction: function(tileCoord) {
|
||||
return urlTemplate.replace('{z}', (tileCoord[0] - 1).toString())
|
||||
.replace('{x}', tileCoord[1].toString())
|
||||
.replace('{y}', tileCoord[2].toString());
|
||||
},
|
||||
tileSize: 512, // the tile size supported by the ArcGIS tile service
|
||||
maxResolution: 180 / 512, // Esri's tile grid fits 180 degrees on one 512 px tile
|
||||
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",
|
||||
"version": "6.2.0",
|
||||
"version": "6.3.1",
|
||||
"description": "OpenLayers mapping library",
|
||||
"keywords": [
|
||||
"map",
|
||||
@@ -24,6 +24,7 @@
|
||||
"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",
|
||||
"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"
|
||||
},
|
||||
"main": "index.js",
|
||||
@@ -35,6 +36,10 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/openlayers/openlayers/issues"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/openlayers"
|
||||
},
|
||||
"dependencies": {
|
||||
"elm-pep": "^1.0.4",
|
||||
"pbf": "3.2.1",
|
||||
@@ -50,58 +55,58 @@
|
||||
"@types/pbf": "^3.0.2",
|
||||
"@types/topojson-specification": "^1.0.1",
|
||||
"babel-loader": "^8.0.5",
|
||||
"buble": "^0.19.7",
|
||||
"buble": "^0.20.0",
|
||||
"buble-loader": "^0.5.1",
|
||||
"chaikin-smooth": "^1.0.4",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"copy-webpack-plugin": "^5.0.4",
|
||||
"coveralls": "3.0.9",
|
||||
"coveralls": "3.0.11",
|
||||
"eslint": "^6.0.0",
|
||||
"eslint-config-openlayers": "^13.0.0",
|
||||
"expect.js": "0.3.1",
|
||||
"front-matter": "^3.0.2",
|
||||
"fs-extra": "^8.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"glob": "^7.1.5",
|
||||
"globby": "^11.0.0",
|
||||
"handlebars": "4.7.3",
|
||||
"html-to-image": "^0.1.0",
|
||||
"handlebars": "4.7.4",
|
||||
"istanbul": "0.4.5",
|
||||
"istanbul-instrumenter-loader": "^3.0.1",
|
||||
"jquery": "3.4.1",
|
||||
"jsdoc": "3.6.3",
|
||||
"jsdoc-plugin-typescript": "^2.0.5",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"karma": "^4.4.1",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage": "^2.0.1",
|
||||
"karma-coverage-istanbul-reporter": "^2.1.0",
|
||||
"karma-coverage-istanbul-reporter": "^2.1.1",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-mocha": "1.3.0",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^4.0.0-rc.2",
|
||||
"loglevelnext": "^3.0.1",
|
||||
"marked": "0.8.0",
|
||||
"mocha": "7.0.1",
|
||||
"ol-mapbox-style": "^6.0.0",
|
||||
"marked": "0.8.2",
|
||||
"mocha": "7.1.1",
|
||||
"ol-mapbox-style": "^6.1.1",
|
||||
"pixelmatch": "^5.1.0",
|
||||
"pngjs": "^3.4.0",
|
||||
"proj4": "2.6.0",
|
||||
"proj4": "2.6.1",
|
||||
"puppeteer": "~2.1.0",
|
||||
"rollup": "^1.25.1",
|
||||
"rollup": "^2.1.0",
|
||||
"rollup-plugin-babel": "^4.3.3",
|
||||
"rollup-plugin-commonjs": "^10.0.0",
|
||||
"rollup-plugin-node-resolve": "^5.2.0",
|
||||
"rollup-plugin-terser": "^5.0.0",
|
||||
"serve-static": "^1.14.0",
|
||||
"shx": "^0.3.2",
|
||||
"sinon": "^8.0.0",
|
||||
"sinon": "^9.0.0",
|
||||
"terser-webpack-plugin": "^2.0.1",
|
||||
"typescript": "3.5.3",
|
||||
"url-polyfill": "^1.1.5",
|
||||
"walk": "^2.3.9",
|
||||
"webpack": "4.41.5",
|
||||
"webpack": "4.42.1",
|
||||
"webpack-cli": "^3.3.2",
|
||||
"webpack-dev-middleware": "^3.6.2",
|
||||
"webpack-dev-server": "^3.3.1",
|
||||
"worker-loader": "^2.0.0",
|
||||
"yargs": "^15.0.2"
|
||||
},
|
||||
"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 {*=} 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) {
|
||||
super(type);
|
||||
|
||||
@@ -275,7 +275,9 @@ class MapBrowserEventHandler extends EventTarget {
|
||||
* @private
|
||||
*/
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ class Observable extends EventTarget {
|
||||
/**
|
||||
* Listen for a certain type of event.
|
||||
* @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
|
||||
* called with an array of event types as the first argument, the return
|
||||
* 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
|
||||
* probably set `insertFirst` to `true` so the overlay is displayed below the
|
||||
* controls.
|
||||
* @property {boolean} [autoPan=false] If set to `true` the map is panned when
|
||||
* calling `setPosition`, so that the overlay is entirely visible in the current
|
||||
* viewport.
|
||||
* @property {PanOptions} [autoPanAnimation] The
|
||||
* animation options used to pan the overlay into view. This animation is only
|
||||
* used when `autoPan` is enabled. A `duration` and `easing` may be provided to
|
||||
* customize the animation.
|
||||
* @property {PanIntoViewOptions|boolean} [autoPan=false] Pan the map when calling
|
||||
* `setPosition`, so that the overlay is entirely visible in the current viewport?
|
||||
* If `true` (deprecated), then `autoPanAnimation` and `autoPanMargin` will be
|
||||
* used to determine the panning parameters; if an object is supplied then other
|
||||
* parameters are ignored.
|
||||
* @property {PanOptions} [autoPanAnimation] The animation options used to pan
|
||||
* 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
|
||||
* 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
|
||||
* name.
|
||||
*/
|
||||
@@ -60,6 +66,12 @@ import {containsExtent} from './extent.js';
|
||||
* 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}
|
||||
@@ -137,38 +149,26 @@ class Overlay extends BaseObject {
|
||||
options.className : 'ol-overlay-container ' + CLASS_SELECTABLE;
|
||||
this.element.style.position = 'absolute';
|
||||
|
||||
let autoPan = options.autoPan;
|
||||
if (autoPan && ('object' !== typeof autoPan)) {
|
||||
autoPan = {
|
||||
animation: options.autoPanAnimation,
|
||||
margin: options.autoPanMargin
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @protected
|
||||
* @type {boolean}
|
||||
* @type {PanIntoViewOptions|false}
|
||||
*/
|
||||
this.autoPan = options.autoPan !== undefined ? options.autoPan : false;
|
||||
this.autoPan = /** @type {PanIntoViewOptions} */(autoPan) || false;
|
||||
|
||||
/**
|
||||
* @protected
|
||||
* @type {PanOptions}
|
||||
*/
|
||||
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,
|
||||
* @type {{transform_: string,
|
||||
* visible: boolean}}
|
||||
*/
|
||||
this.rendered = {
|
||||
bottom_: '',
|
||||
left_: '',
|
||||
right_: '',
|
||||
top_: '',
|
||||
transform_: '',
|
||||
visible: true
|
||||
};
|
||||
|
||||
@@ -300,6 +300,7 @@ class Overlay extends BaseObject {
|
||||
} else {
|
||||
container.appendChild(this.element);
|
||||
}
|
||||
this.performAutoPan();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,9 +323,7 @@ class Overlay extends BaseObject {
|
||||
*/
|
||||
handlePositionChanged() {
|
||||
this.updatePixelPosition();
|
||||
if (this.get(Property.POSITION) && this.autoPan) {
|
||||
this.panIntoView();
|
||||
}
|
||||
this.performAutoPan();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -378,14 +377,26 @@ class Overlay extends BaseObject {
|
||||
}
|
||||
|
||||
/**
|
||||
* Pan the map so that the overlay is entirely visible in the current viewport
|
||||
* (if necessary).
|
||||
* Pan the map so that the overlay is entirely visisble in the current viewport
|
||||
* (if necessary) using the configured autoPan parameters
|
||||
* @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();
|
||||
|
||||
if (!map || !map.getTargetElement()) {
|
||||
if (!map || !map.getTargetElement() || !this.get(Property.POSITION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -393,7 +404,7 @@ class Overlay extends BaseObject {
|
||||
const element = this.getElement();
|
||||
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)) {
|
||||
// the overlay is not completely inside the viewport, so pan the map
|
||||
const offsetLeft = overlayRect[0] - mapRect[0];
|
||||
@@ -404,17 +415,17 @@ class Overlay extends BaseObject {
|
||||
const delta = [0, 0];
|
||||
if (offsetLeft < 0) {
|
||||
// move map to the left
|
||||
delta[0] = offsetLeft - margin;
|
||||
delta[0] = offsetLeft - myMargin;
|
||||
} else if (offsetRight < 0) {
|
||||
// move map to the right
|
||||
delta[0] = Math.abs(offsetRight) + margin;
|
||||
delta[0] = Math.abs(offsetRight) + myMargin;
|
||||
}
|
||||
if (offsetTop < 0) {
|
||||
// move map up
|
||||
delta[1] = offsetTop - margin;
|
||||
delta[1] = offsetTop - myMargin;
|
||||
} else if (offsetBottom < 0) {
|
||||
// move map down
|
||||
delta[1] = Math.abs(offsetBottom) + margin;
|
||||
delta[1] = Math.abs(offsetBottom) + myMargin;
|
||||
}
|
||||
|
||||
if (delta[0] !== 0 || delta[1] !== 0) {
|
||||
@@ -425,10 +436,11 @@ class Overlay extends BaseObject {
|
||||
centerPx[1] + delta[1]
|
||||
];
|
||||
|
||||
const panOptions = panIntoViewOptions.animation || {};
|
||||
map.getView().animateInternal({
|
||||
center: map.getCoordinateFromPixelInternal(newCenterPx),
|
||||
duration: this.autoPanAnimation.duration,
|
||||
easing: this.autoPanAnimation.easing
|
||||
duration: panOptions.duration,
|
||||
easing: panOptions.easing
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -506,63 +518,34 @@ class Overlay extends BaseObject {
|
||||
|
||||
this.setVisible(true);
|
||||
|
||||
let offsetX = offset[0];
|
||||
let offsetY = offset[1];
|
||||
const x = Math.round(pixel[0] + offset[0]) + 'px';
|
||||
const y = Math.round(pixel[1] + offset[1]) + 'px';
|
||||
let posX = '0%';
|
||||
let posY = '0%';
|
||||
if (positioning == OverlayPositioning.BOTTOM_RIGHT ||
|
||||
positioning == OverlayPositioning.CENTER_RIGHT ||
|
||||
positioning == OverlayPositioning.TOP_RIGHT) {
|
||||
if (this.rendered.left_ !== '') {
|
||||
this.rendered.left_ = '';
|
||||
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 ||
|
||||
posX = '-100%';
|
||||
} else if (positioning == OverlayPositioning.BOTTOM_CENTER ||
|
||||
positioning == OverlayPositioning.CENTER_CENTER ||
|
||||
positioning == OverlayPositioning.TOP_CENTER) {
|
||||
offsetX -= this.element.offsetWidth / 2;
|
||||
}
|
||||
const left = Math.round(pixel[0] + offsetX) + 'px';
|
||||
if (this.rendered.left_ != left) {
|
||||
this.rendered.left_ = left;
|
||||
style.left = left;
|
||||
}
|
||||
posX = '-50%';
|
||||
}
|
||||
if (positioning == OverlayPositioning.BOTTOM_LEFT ||
|
||||
positioning == OverlayPositioning.BOTTOM_CENTER ||
|
||||
positioning == OverlayPositioning.BOTTOM_RIGHT) {
|
||||
if (this.rendered.top_ !== '') {
|
||||
this.rendered.top_ = '';
|
||||
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 ||
|
||||
posY = '-100%';
|
||||
} else if (positioning == OverlayPositioning.CENTER_LEFT ||
|
||||
positioning == OverlayPositioning.CENTER_CENTER ||
|
||||
positioning == OverlayPositioning.CENTER_RIGHT) {
|
||||
offsetY -= this.element.offsetHeight / 2;
|
||||
}
|
||||
const top = Math.round(pixel[1] + offsetY) + 'px';
|
||||
if (this.rendered.top_ != top) {
|
||||
this.rendered.top_ = 'top';
|
||||
style.top = top;
|
||||
posY = '-50%';
|
||||
}
|
||||
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 BaseObject, {getChangeEventType} from './Object.js';
|
||||
import ObjectEventType from './ObjectEventType.js';
|
||||
import TileQueue from './TileQueue.js';
|
||||
import TileQueue, {getTilePriority} from './TileQueue.js';
|
||||
import View from './View.js';
|
||||
import ViewHint from './ViewHint.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 LayerGroup from './layer/Group.js';
|
||||
import {hasArea} from './size.js';
|
||||
import {DROP} from './structs/PriorityQueue.js';
|
||||
import {create as createTransform, apply as applyTransform} from './transform.js';
|
||||
import {toUserCoordinate, fromUserCoordinate} from './proj.js';
|
||||
|
||||
@@ -891,26 +890,7 @@ class PluggableMap extends BaseObject {
|
||||
* @return {number} Tile priority.
|
||||
*/
|
||||
getTilePriority(tile, tileSourceKey, tileCenter, tileResolution) {
|
||||
// Filter out tiles at higher zoom levels than the current zoom level, or that
|
||||
// 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;
|
||||
return getTilePriority(this.frameState_, tile, tileSourceKey, tileCenter, tileResolution);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -934,7 +914,7 @@ class PluggableMap extends BaseObject {
|
||||
}
|
||||
const target = /** @type {Node} */ (mapBrowserEvent.originalEvent.target);
|
||||
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
|
||||
// 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
|
||||
|
||||
@@ -7,7 +7,7 @@ import {fromKey, getKey} from './tilecoord.js';
|
||||
class TileCache extends LRUCache {
|
||||
|
||||
/**
|
||||
* @param {!Object<string, import("./TileRange.js").default>} usedTiles Used tiles.
|
||||
* @param {!Object<string, boolean>} usedTiles Used tiles.
|
||||
*/
|
||||
expireCache(usedTiles) {
|
||||
while (this.canExpireCache()) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
import TileState from './TileState.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;
|
||||
|
||||
|
||||
/**
|
||||
* @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) {
|
||||
this.setZoom(options.zoom);
|
||||
}
|
||||
this.resolveConstraints(0);
|
||||
|
||||
this.setProperties(properties);
|
||||
|
||||
@@ -609,10 +608,15 @@ class View extends BaseObject {
|
||||
if (series[0].callback) {
|
||||
animationCallback(series[0].callback, false);
|
||||
}
|
||||
anchor = anchor ||
|
||||
series.filter(function(animation) {
|
||||
return !animation.complete;
|
||||
})[0].anchor;
|
||||
if (!anchor) {
|
||||
for (let j = 0, jj = series.length; j < jj; ++j) {
|
||||
const animation = series[j];
|
||||
if (!animation.complete) {
|
||||
anchor = animation.anchor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
this.animations_.length = 0;
|
||||
this.cancelAnchor_ = anchor;
|
||||
@@ -762,6 +766,7 @@ class View extends BaseObject {
|
||||
*/
|
||||
setViewportSize(opt_size) {
|
||||
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 {boolean} Resolution constraint is set
|
||||
*/
|
||||
getConstrainResolution() {
|
||||
return this.options_.constrainResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<number>=} opt_hints Destination array.
|
||||
* @return {Array<number>} Hint.
|
||||
|
||||
@@ -9,6 +9,29 @@ import EventType from '../events/EventType.js';
|
||||
|
||||
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
|
||||
* @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
|
||||
* toggle the map in full screen mode.
|
||||
*
|
||||
* @fires FullScreenEventType#enterfullscreen
|
||||
* @fires FullScreenEventType#leavefullscreen
|
||||
* @api
|
||||
*/
|
||||
class FullScreen extends Control {
|
||||
@@ -162,9 +187,11 @@ class FullScreen extends Control {
|
||||
if (isFullScreen()) {
|
||||
this.setClassName_(this.button_, true);
|
||||
replaceNode(this.labelActiveNode_, this.labelNode_);
|
||||
this.dispatchEvent(FullScreenEventType.ENTERFULLSCREEN);
|
||||
} else {
|
||||
this.setClassName_(this.button_, false);
|
||||
replaceNode(this.labelNode_, this.labelActiveNode_);
|
||||
this.dispatchEvent(FullScreenEventType.LEAVEFULLSCREEN);
|
||||
}
|
||||
if (map) {
|
||||
map.updateSize();
|
||||
|
||||
@@ -17,6 +17,7 @@ import {replaceNode} from '../dom.js';
|
||||
import {listen, listenOnce} from '../events.js';
|
||||
import EventType from '../events/EventType.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
|
||||
* to be rendered outside of the map's viewport.
|
||||
* @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,
|
||||
* a default view with an EPSG:3857 projection will be used.
|
||||
* @property {View} [view] Custom view for the overview map (should use same projection as main map). If not provided,
|
||||
* 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_.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}
|
||||
* @private
|
||||
@@ -303,6 +311,14 @@ class OverviewMap extends Control {
|
||||
* @private
|
||||
*/
|
||||
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_);
|
||||
// Sync once with the new view
|
||||
this.handleRotationChanged_();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
import {modulo} from './math.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) {
|
||||
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
|
||||
* @property {Array<string>} families
|
||||
* @property {string} style
|
||||
* @property {string} variant
|
||||
* @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';
|
||||
|
||||
/**
|
||||
* 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
|
||||
* for font families that have commas in them.
|
||||
* @param {string} The CSS font property.
|
||||
* @return {FontParameters} The font families (or null if the input spec is invalid).
|
||||
* @param {string} fontSpec The CSS font property.
|
||||
* @return {FontParameters} The font parameters (or null if the input spec is invalid).
|
||||
*/
|
||||
export const getFontParameters = (function() {
|
||||
/**
|
||||
* @type {CSSStyleDeclaration}
|
||||
*/
|
||||
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) {
|
||||
export const getFontParameters = function(fontSpec) {
|
||||
const match = fontSpec.match(fontRegEx);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const families = family.split(/,\s?/);
|
||||
cache[font] = {
|
||||
families: families,
|
||||
weight: fontWeight,
|
||||
style: fontStyle
|
||||
};
|
||||
const style = /** @type {FontParameters} */ ({
|
||||
lineHeight: 'normal',
|
||||
size: '1.2em',
|
||||
style: 'normal',
|
||||
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
|
||||
*/
|
||||
|
||||
|
||||
//FIXME Move this function to the canvas module
|
||||
/**
|
||||
* Create an html canvas element and returns its 2d context.
|
||||
* @param {number=} opt_width Canvas width.
|
||||
@@ -12,14 +15,18 @@
|
||||
*/
|
||||
export function createCanvasContext2D(opt_width, opt_height, opt_canvasPool) {
|
||||
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) {
|
||||
canvas.width = opt_width;
|
||||
}
|
||||
if (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
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Listener function. This function is called with an event object as argument.
|
||||
* When the function returns `false`, event propagation will stop.
|
||||
@@ -22,6 +21,14 @@ import {clear} from './obj.js';
|
||||
* @api
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} ListenerObject
|
||||
* @property {ListenerFunction} handleEvent
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {ListenerFunction|ListenerObject} Listener
|
||||
*/
|
||||
|
||||
/**
|
||||
* Registers an event listener on an event target. Inspired by
|
||||
|
||||
@@ -56,7 +56,7 @@ class Target extends Disposable {
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {!Object<string, Array<import("../events.js").ListenerFunction>>}
|
||||
* @type {!Object<string, Array<import("../events.js").Listener>>}
|
||||
*/
|
||||
this.listeners_ = {};
|
||||
|
||||
@@ -64,7 +64,7 @@ class Target extends Disposable {
|
||||
|
||||
/**
|
||||
* @param {string} type Type.
|
||||
* @param {import("../events.js").ListenerFunction} listener Listener.
|
||||
* @param {import("../events.js").Listener} listener Listener.
|
||||
*/
|
||||
addEventListener(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
|
||||
* Object with a `type` property.
|
||||
*
|
||||
* @param {{type: string,
|
||||
* target: (EventTargetLike|undefined),
|
||||
* propagationStopped: (boolean|undefined)}|
|
||||
* import("./Event.js").default|string} event Event object.
|
||||
* @param {import("./Event.js").default|string} event Event object.
|
||||
* @return {boolean|undefined} `false` if anyone called preventDefault on the
|
||||
* event object or if any of the listeners returned false.
|
||||
* @api
|
||||
*/
|
||||
dispatchEvent(event) {
|
||||
/** @type {import("./Event.js").default|Event} */
|
||||
const evt = typeof event === 'string' ? new Event(event) : event;
|
||||
const type = evt.type;
|
||||
if (!evt.target) {
|
||||
@@ -108,7 +106,12 @@ class Target extends Disposable {
|
||||
}
|
||||
++this.dispatching_[type];
|
||||
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;
|
||||
break;
|
||||
}
|
||||
@@ -138,7 +141,7 @@ class Target extends Disposable {
|
||||
* order that they will be called in.
|
||||
*
|
||||
* @param {string} type Type.
|
||||
* @return {Array<import("../events.js").ListenerFunction>} Listeners.
|
||||
* @return {Array<import("../events.js").Listener>} Listeners.
|
||||
*/
|
||||
getListeners(type) {
|
||||
return this.listeners_[type];
|
||||
@@ -157,7 +160,7 @@ class Target extends Disposable {
|
||||
|
||||
/**
|
||||
* @param {string} type Type.
|
||||
* @param {import("../events.js").ListenerFunction} listener Listener.
|
||||
* @param {import("../events.js").Listener} listener Listener.
|
||||
*/
|
||||
removeEventListener(type, listener) {
|
||||
const listeners = this.listeners_[type];
|
||||
|
||||
@@ -295,6 +295,18 @@ export function equals(extent1, extent2) {
|
||||
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.
|
||||
@@ -778,18 +790,60 @@ export function intersectsSegment(extent, start, end) {
|
||||
* @param {import("./proj.js").TransformFunction} transformFn Transform function.
|
||||
* Called with `[minX, minY, maxX, maxY]` extent coordinates.
|
||||
* @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.
|
||||
* @api
|
||||
*/
|
||||
export function applyTransform(extent, transformFn, opt_extent) {
|
||||
const coordinates = [
|
||||
export function applyTransform(extent, transformFn, opt_extent, opt_stops) {
|
||||
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[3],
|
||||
extent[2], extent[1],
|
||||
extent[2], extent[3]
|
||||
extent[2], extent[3],
|
||||
extent[0], extent[3]
|
||||
];
|
||||
}
|
||||
transformFn(coordinates, coordinates, 2);
|
||||
const xs = [coordinates[0], coordinates[2], coordinates[4], coordinates[6]];
|
||||
const ys = [coordinates[1], coordinates[3], coordinates[5], coordinates[7]];
|
||||
const xs = [];
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @api
|
||||
*/
|
||||
|
||||
@@ -918,11 +918,7 @@ function createNameStyleFunction(foundStyle, name) {
|
||||
|
||||
const nameStyle = new Style({
|
||||
image: imageStyle,
|
||||
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()
|
||||
text: textStyle
|
||||
});
|
||||
return nameStyle;
|
||||
}
|
||||
@@ -953,7 +949,7 @@ function createFeatureStyleFunction(style, styleUrl, defaultStyle, sharedStyles,
|
||||
if (geometry) {
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
multiGeometryPoints = geometry.getGeometriesArray().filter(function(geometry) {
|
||||
multiGeometryPoints = geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type === GeometryType.POINT || type === GeometryType.MULTI_POINT;
|
||||
});
|
||||
@@ -1171,6 +1167,7 @@ function readStyleMapValue(node, objectStack) {
|
||||
const ICON_STYLE_PARSERS = makeStructureNS(
|
||||
NAMESPACE_URIS, {
|
||||
'Icon': makeObjectPropertySetter(readIcon),
|
||||
'color': makeObjectPropertySetter(readColor),
|
||||
'heading': makeObjectPropertySetter(readDecimal),
|
||||
'hotSpot': makeObjectPropertySetter(readVec2),
|
||||
'scale': makeObjectPropertySetter(readScale)
|
||||
@@ -1252,6 +1249,9 @@ function iconStyleParser(node, objectStack) {
|
||||
let scale = /** @type {number|undefined} */
|
||||
(object['scale']);
|
||||
|
||||
const color = /** @type {Array<number>|undefined} */
|
||||
(object['color']);
|
||||
|
||||
if (drawIcon) {
|
||||
if (src == DEFAULT_IMAGE_STYLE_SRC) {
|
||||
size = DEFAULT_IMAGE_STYLE_SIZE;
|
||||
@@ -1271,7 +1271,8 @@ function iconStyleParser(node, objectStack) {
|
||||
rotation: rotation,
|
||||
scale: scale,
|
||||
size: size,
|
||||
src: src
|
||||
src: src,
|
||||
color: color
|
||||
});
|
||||
styleObject['imageStyle'] = imageStyle;
|
||||
} else {
|
||||
@@ -1806,7 +1807,7 @@ function readStyle(node, objectStack) {
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
return new GeometryCollection(
|
||||
geometry.getGeometriesArray().filter(function(geometry) {
|
||||
geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type !== GeometryType.POLYGON && type !== GeometryType.MULTI_POLYGON;
|
||||
})
|
||||
@@ -1827,7 +1828,7 @@ function readStyle(node, objectStack) {
|
||||
const type = geometry.getType();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
return new GeometryCollection(
|
||||
geometry.getGeometriesArray().filter(function(geometry) {
|
||||
geometry.getGeometriesArrayRecursive().filter(function(geometry) {
|
||||
const type = geometry.getType();
|
||||
return type === GeometryType.POLYGON || type === GeometryType.MULTI_POLYGON;
|
||||
})
|
||||
@@ -2447,7 +2448,7 @@ function writeIcon(node, icon, objectStack) {
|
||||
// @ts-ignore
|
||||
const ICON_STYLE_SEQUENCE = makeStructureNS(
|
||||
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(
|
||||
NAMESPACE_URIS, {
|
||||
'Icon': makeChildAppender(writeIcon),
|
||||
'color': makeChildAppender(writeColorTextNode),
|
||||
'heading': makeChildAppender(writeDecimalTextNode),
|
||||
'hotSpot': makeChildAppender(writeVec2),
|
||||
'scale': makeChildAppender(writeScaleTextNode)
|
||||
@@ -2514,6 +2516,11 @@ function writeIconStyle(node, style, objectStack) {
|
||||
properties['heading'] = rotation; // 0-360
|
||||
}
|
||||
|
||||
const color = style.getColor();
|
||||
if (color) {
|
||||
properties['color'] = color;
|
||||
}
|
||||
|
||||
const parentNode = objectStack[objectStack.length - 1].node;
|
||||
const orderedKeys = ICON_STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||
const values = makeSequence(properties, orderedKeys);
|
||||
@@ -2703,20 +2710,35 @@ function writeMultiGeometry(node, geometry, objectStack) {
|
||||
const context = {node: node};
|
||||
const type = geometry.getType();
|
||||
/** @type {Array<import("../geom/Geometry.js").default>} */
|
||||
let geometries;
|
||||
let geometries = [];
|
||||
/** @type {function(*, Array<*>, string=): (Node|undefined)} */
|
||||
let factory;
|
||||
if (type == GeometryType.GEOMETRY_COLLECTION) {
|
||||
geometries = /** @type {GeometryCollection} */ (geometry).getGeometries();
|
||||
if (type === GeometryType.GEOMETRY_COLLECTION) {
|
||||
/** @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;
|
||||
} else if (type == GeometryType.MULTI_POINT) {
|
||||
} else if (type === GeometryType.MULTI_POINT) {
|
||||
geometries = /** @type {MultiPoint} */ (geometry).getPoints();
|
||||
factory = POINT_NODE_FACTORY;
|
||||
} else if (type == GeometryType.MULTI_LINE_STRING) {
|
||||
} else if (type === GeometryType.MULTI_LINE_STRING) {
|
||||
geometries =
|
||||
(/** @type {MultiLineString} */ (geometry)).getLineStrings();
|
||||
factory = LINE_STRING_NODE_FACTORY;
|
||||
} else if (type == GeometryType.MULTI_POLYGON) {
|
||||
} else if (type === GeometryType.MULTI_POLYGON) {
|
||||
geometries =
|
||||
(/** @type {MultiPolygon} */ (geometry)).getPolygons();
|
||||
factory = POLYGON_NODE_FACTORY;
|
||||
@@ -2831,16 +2853,64 @@ function writePlacemark(node, feature, objectStack) {
|
||||
// resolution-independent here
|
||||
const styles = styleFunction(feature, 0);
|
||||
if (styles) {
|
||||
const style = Array.isArray(styles) ? styles[0] : styles;
|
||||
if (this.writeStyles_) {
|
||||
properties['Style'] = style;
|
||||
const styleArray = Array.isArray(styles) ? styles : [styles];
|
||||
let pointStyles = styleArray;
|
||||
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) {
|
||||
properties['name'] = textStyle.getText();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const parentNode = objectStack[objectStack.length - 1].node;
|
||||
const orderedKeys = PLACEMARK_SEQUENCE[parentNode.namespaceURI];
|
||||
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
|
||||
* @type {Object<string, Object<string, import("../xml.js").Serializer>>}
|
||||
@@ -2972,27 +3053,31 @@ function writePolygon(node, polygon, objectStack) {
|
||||
// @ts-ignore
|
||||
const POLY_STYLE_SERIALIZERS = makeStructureNS(
|
||||
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 {Fill} style Style.
|
||||
* @param {Style} style Style.
|
||||
* @param {Array<*>} objectStack Object stack.
|
||||
*/
|
||||
function writePolyStyle(node, style, objectStack) {
|
||||
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,
|
||||
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 {Style} style Style.
|
||||
* @param {Object<string, Array<Style>>} styles Styles.
|
||||
* @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 properties = {};
|
||||
const fillStyle = style.getFill();
|
||||
const strokeStyle = style.getStroke();
|
||||
const imageStyle = style.getImage();
|
||||
const textStyle = style.getText();
|
||||
if (imageStyle && typeof /** @type {?} */ (imageStyle).getSrc === 'function') {
|
||||
properties['IconStyle'] = imageStyle;
|
||||
}
|
||||
if (styles.pointStyles.length) {
|
||||
const textStyle = styles.pointStyles[0].getText();
|
||||
if (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) {
|
||||
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 orderedKeys = STYLE_SEQUENCE[parentNode.namespaceURI];
|
||||
|
||||
@@ -126,6 +126,23 @@ class GeometryCollection extends Geometry {
|
||||
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
|
||||
*/
|
||||
|
||||
@@ -37,7 +37,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
|
||||
* @type {number}
|
||||
* @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.
|
||||
|
||||
@@ -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
|
||||
* to indicate whether that event should be handled.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ import {fromUserCoordinate, getUserProjection} from '../proj.js';
|
||||
* geometry is the geometry that is returned when the function is called without
|
||||
* a second argument.
|
||||
* @typedef {function(!SketchCoordType, import("../geom/SimpleGeometry.js").default=,
|
||||
* import("../proj/Projection.js").default):
|
||||
* import("../proj/Projection.js").default=):
|
||||
* import("../geom/SimpleGeometry.js").default} GeometryFunction
|
||||
*/
|
||||
|
||||
@@ -143,7 +143,13 @@ const DrawEventType = {
|
||||
* @event DrawEvent#drawend
|
||||
* @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;
|
||||
} else if (this.freehand_) {
|
||||
this.finishCoordinate_ = null;
|
||||
this.abortDrawing_();
|
||||
this.abortDrawing();
|
||||
}
|
||||
if (!pass && this.stopClick_) {
|
||||
event.stopPropagation();
|
||||
@@ -834,7 +839,7 @@ class Draw extends PointerInteraction {
|
||||
}
|
||||
|
||||
if (coordinates.length === 0) {
|
||||
this.finishCoordinate_ = null;
|
||||
this.abortDrawing();
|
||||
}
|
||||
|
||||
this.updateSketchFeatures_();
|
||||
@@ -901,6 +906,18 @@ class Draw extends PointerInteraction {
|
||||
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.
|
||||
* This can be used when drawing LineStrings or Polygons. Coordinates will
|
||||
@@ -982,7 +999,7 @@ class Draw extends PointerInteraction {
|
||||
const map = this.getMap();
|
||||
const active = this.getActive();
|
||||
if (!map || !active) {
|
||||
this.abortDrawing_();
|
||||
this.abortDrawing();
|
||||
}
|
||||
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 GeometryType from '../geom/GeometryType.js';
|
||||
import Point from '../geom/Point.js';
|
||||
import {fromCircle} from '../geom/Polygon.js';
|
||||
import PointerInteraction from './Pointer.js';
|
||||
import VectorLayer from '../layer/Vector.js';
|
||||
import VectorSource from '../source/Vector.js';
|
||||
import VectorEventType from '../source/VectorEventType.js';
|
||||
import RBush from '../structs/RBush.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;
|
||||
circumferenceSegmentData.featureSegments = featureSegments;
|
||||
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;
|
||||
} else { // We're dragging the circle's circumference:
|
||||
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;
|
||||
}
|
||||
break;
|
||||
@@ -898,7 +915,14 @@ class Modify extends PointerInteraction {
|
||||
circumferenceSegmentData.segment[0] = coordinates;
|
||||
circumferenceSegmentData.segment[1] = coordinates;
|
||||
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 {
|
||||
this.rBush_.update(boundingExtent(segmentData.segment), segmentData);
|
||||
}
|
||||
@@ -1249,11 +1273,15 @@ function projectedDistanceToSegmentDataSquared(pointCoordinates, segmentData, pr
|
||||
const geometry = segmentData.geometry;
|
||||
|
||||
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) {
|
||||
const userProjection = getUserProjection();
|
||||
if (userProjection) {
|
||||
circleGeometry = /** @type {import("../geom/Circle.js").default} */ (circleGeometry.clone().transform(userProjection, projection));
|
||||
}
|
||||
const distanceToCenterSquared =
|
||||
squaredCoordinateDistance(circleGeometry.getCenter(), pointCoordinates);
|
||||
squaredCoordinateDistance(circleGeometry.getCenter(), fromUserCoordinate(pointCoordinates, projection));
|
||||
const distanceToCircumference =
|
||||
Math.sqrt(distanceToCenterSquared) - circleGeometry.getRadius();
|
||||
return distanceToCircumference * distanceToCircumference;
|
||||
@@ -1280,7 +1308,12 @@ function closestOnSegmentData(pointCoordinates, segmentData, projection) {
|
||||
const geometry = segmentData.geometry;
|
||||
|
||||
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);
|
||||
tempSegment[0] = fromUserCoordinate(segmentData.segment[0], projection);
|
||||
|
||||
@@ -4,7 +4,17 @@
|
||||
import {always, focus} from '../events/condition.js';
|
||||
import EventType from '../events/EventType.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
|
||||
* boolean to indicate whether that event should be handled. Default is
|
||||
* {@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} [duration=250] Animation duration in milliseconds.
|
||||
* @property {number} [timeout=80] Mouse wheel timeout duration in milliseconds.
|
||||
@@ -92,16 +104,28 @@ class MouseWheelZoom extends Interaction {
|
||||
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.
|
||||
* @type {number}
|
||||
*/
|
||||
this.eventGap_ = 400;
|
||||
this.trackpadEventGap_ = 400;
|
||||
|
||||
/**
|
||||
* @type {?}
|
||||
*/
|
||||
this.timeoutId_;
|
||||
this.trackpadTimeoutId_;
|
||||
|
||||
/**
|
||||
* The number of delta values per zoom level
|
||||
@@ -130,7 +154,7 @@ class MouseWheelZoom extends Interaction {
|
||||
* @private
|
||||
*/
|
||||
endInteraction_() {
|
||||
this.timeoutId_ = undefined;
|
||||
this.trackpadTimeoutId_ = undefined;
|
||||
const view = this.getMap().getView();
|
||||
view.endInteraction(undefined, this.lastDelta_ ? (this.lastDelta_ > 0 ? 1 : -1) : 0, this.lastAnchor_);
|
||||
}
|
||||
@@ -184,18 +208,61 @@ class MouseWheelZoom extends Interaction {
|
||||
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();
|
||||
if (this.timeoutId_) {
|
||||
clearTimeout(this.timeoutId_);
|
||||
if (this.mode_ === Mode.TRACKPAD && !view.getConstrainResolution()) {
|
||||
if (this.trackpadTimeoutId_) {
|
||||
clearTimeout(this.trackpadTimeoutId_);
|
||||
} else {
|
||||
if (view.getAnimating()) {
|
||||
view.cancelAnimations();
|
||||
}
|
||||
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_);
|
||||
this.startTime_ = now;
|
||||
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
|
||||
* @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]
|
||||
* Style for the selected features. By default the default edit style is used
|
||||
* (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
|
||||
* that takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
|
||||
* 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
|
||||
@@ -161,6 +167,16 @@ class Select extends Interaction {
|
||||
|
||||
const options = opt_options ? opt_options : {};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.boundAddFeature_ = this.addFeature_.bind(this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
this.boundRemoveFeature_ = this.removeFeature_.bind(this);
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @type {import("../events/condition.js").Condition}
|
||||
@@ -209,14 +225,6 @@ class Select extends Interaction {
|
||||
*/
|
||||
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
|
||||
* @type {import("../Collection.js").default}
|
||||
@@ -251,10 +259,6 @@ class Select extends Interaction {
|
||||
* @type {Object<string, import("../layer/Layer.js").default>}
|
||||
*/
|
||||
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) {
|
||||
const currentMap = this.getMap();
|
||||
if (currentMap && this.style_) {
|
||||
this.features_.forEach(this.removeSelectedStyle_.bind(this));
|
||||
this.features_.forEach(this.restorePreviousStyle_.bind(this));
|
||||
}
|
||||
super.setMap(map);
|
||||
if (map && this.style_) {
|
||||
this.features_.forEach(this.giveSelectedStyle_.bind(this));
|
||||
if (map) {
|
||||
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) {
|
||||
const feature = evt.element;
|
||||
if (this.style_) {
|
||||
this.giveSelectedStyle_(feature);
|
||||
this.applySelectedStyle_(feature);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,17 +357,26 @@ class Select extends Interaction {
|
||||
removeFeature_(evt) {
|
||||
const feature = evt.element;
|
||||
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
|
||||
* @private
|
||||
*/
|
||||
giveSelectedStyle_(feature) {
|
||||
applySelectedStyle_(feature) {
|
||||
const key = getUid(feature);
|
||||
this.featureStyleAssociation_[key] = feature.getStyle();
|
||||
if (!(key in originalFeatureStyles)) {
|
||||
originalFeatureStyles[key] = feature.getStyle();
|
||||
}
|
||||
feature.setStyle(this.style_);
|
||||
}
|
||||
|
||||
@@ -363,10 +384,17 @@ class Select extends Interaction {
|
||||
* @param {import("../Feature.js").default} feature Feature
|
||||
* @private
|
||||
*/
|
||||
removeSelectedStyle_(feature) {
|
||||
restorePreviousStyle_(feature) {
|
||||
const key = getUid(feature);
|
||||
feature.setStyle(this.featureStyleAssociation_[key]);
|
||||
delete this.featureStyleAssociation_[key];
|
||||
const selectInteractions = /** @type {Array<Select>} */ (this.getMap().getInteractions().getArray().filter(function(interaction) {
|
||||
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 VectorEventType from '../source/VectorEventType.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_) {
|
||||
const isCircle = closestSegmentData.feature.getGeometry().getType() === GeometryType.CIRCLE;
|
||||
if (isCircle) {
|
||||
vertex = closestOnCircle(pixelCoordinate,
|
||||
/** @type {import("../geom/Circle.js").default} */ (closestSegmentData.feature.getGeometry()));
|
||||
let circleGeometry = 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 {
|
||||
tempSegment[0] = fromUserCoordinate(closestSegment[0], projection);
|
||||
tempSegment[1] = fromUserCoordinate(closestSegment[1], projection);
|
||||
@@ -482,7 +487,16 @@ class Snap extends PointerInteraction {
|
||||
* @private
|
||||
*/
|
||||
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];
|
||||
for (let i = 0, ii = coordinates.length - 1; i < ii; ++i) {
|
||||
const segment = coordinates.slice(i, i + 2);
|
||||
|
||||
@@ -19,6 +19,10 @@ import Layer from './Layer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* 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
|
||||
|
||||
@@ -21,6 +21,10 @@ import {assign} from '../obj.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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`
|
||||
* means no preloading.
|
||||
* @property {import("../source/Tile.js").default} [source] Source for this layer.
|
||||
|
||||
@@ -21,6 +21,10 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* 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.
|
||||
|
||||
@@ -12,10 +12,22 @@ import VectorSource from '../source/Vector.js';
|
||||
import {
|
||||
equivalent as equivalentProjection,
|
||||
get as getProjection,
|
||||
getTransform,
|
||||
transformExtent
|
||||
getTransform
|
||||
} 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 Style from '../style/Style.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 Point from '../geom/Point.js';
|
||||
import Collection from '../Collection.js';
|
||||
import {getVectorContext} from '../render.js';
|
||||
import EventType from '../render/EventType.js';
|
||||
|
||||
|
||||
/**
|
||||
@@ -64,6 +78,10 @@ const INTERVALS = [
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* 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
|
||||
@@ -138,7 +156,8 @@ const INTERVALS = [
|
||||
|
||||
/**
|
||||
* @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
|
||||
* @api
|
||||
@@ -202,25 +221,25 @@ class Graticule extends VectorLayer {
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.maxLatP_ = Infinity;
|
||||
this.maxX_ = Infinity;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.maxLonP_ = Infinity;
|
||||
this.maxY_ = Infinity;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.minLatP_ = -Infinity;
|
||||
this.minX_ = -Infinity;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
this.minLonP_ = -Infinity;
|
||||
this.minY_ = -Infinity;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
@@ -270,6 +289,30 @@ class Graticule extends VectorLayer {
|
||||
*/
|
||||
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>}
|
||||
* @private
|
||||
@@ -378,6 +421,8 @@ class Graticule extends VectorLayer {
|
||||
|
||||
this.meridiansLabels_ = [];
|
||||
this.parallelsLabels_ = [];
|
||||
|
||||
this.addEventListener(EventType.POSTRENDER, this.drawLabels_.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -415,6 +460,7 @@ class Graticule extends VectorLayer {
|
||||
|
||||
/**
|
||||
* @type {?import("../extent.js").Extent}
|
||||
* @private
|
||||
*/
|
||||
this.loadedExtent_ = null;
|
||||
|
||||
@@ -435,11 +481,21 @@ class Graticule extends VectorLayer {
|
||||
* @return {Array<import("../extent.js").Extent>} Extents.
|
||||
*/
|
||||
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
|
||||
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
|
||||
let featureCount = this.meridians_.length + this.parallels_.length;
|
||||
if (this.meridiansLabels_) {
|
||||
featureCount += this.meridiansLabels_.length;
|
||||
featureCount += this.meridians_.length;
|
||||
}
|
||||
if (this.parallelsLabels_) {
|
||||
featureCount += this.parallelsLabels_.length;
|
||||
featureCount += this.parallels_.length;
|
||||
}
|
||||
|
||||
let feature;
|
||||
@@ -512,27 +568,6 @@ class Graticule extends VectorLayer {
|
||||
feature.setStyle(this.lineStyle_);
|
||||
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);
|
||||
if (intersects(lineString.getExtent(), extent)) {
|
||||
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] = {
|
||||
geom: textPoint,
|
||||
text: this.lonLabelFormatter_(lon)
|
||||
geom: new Point([]),
|
||||
text: text
|
||||
};
|
||||
}
|
||||
}
|
||||
this.meridians_[index++] = lineString;
|
||||
}
|
||||
return index;
|
||||
@@ -574,17 +613,101 @@ class Graticule extends VectorLayer {
|
||||
const lineString = this.getParallel_(lat, minLon, maxLon, squaredTolerance, index);
|
||||
if (intersects(lineString.getExtent(), extent)) {
|
||||
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] = {
|
||||
geom: textPoint,
|
||||
text: this.latLabelFormatter_(lat)
|
||||
geom: new Point([]),
|
||||
text: text
|
||||
};
|
||||
}
|
||||
}
|
||||
this.parallels_[index++] = lineString;
|
||||
}
|
||||
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("../coordinate.js").Coordinate} center Center.
|
||||
@@ -606,24 +729,91 @@ class Graticule extends VectorLayer {
|
||||
return;
|
||||
}
|
||||
|
||||
const centerLonLat = this.toLonLatTransform_(center);
|
||||
let centerLon = centerLonLat[0];
|
||||
let centerLat = centerLonLat[1];
|
||||
let wrapX = false;
|
||||
const projectionExtent = this.projection_.getExtent();
|
||||
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_;
|
||||
let cnt, idx, lat, lon;
|
||||
|
||||
let validExtent = [
|
||||
Math.max(extent[0], this.minLonP_),
|
||||
Math.max(extent[1], this.minLatP_),
|
||||
Math.min(extent[2], this.maxLonP_),
|
||||
Math.min(extent[3], this.maxLatP_)
|
||||
];
|
||||
// Limit the extent to fit into the extent available to the graticule
|
||||
|
||||
validExtent = transformExtent(validExtent, this.projection_, 'EPSG:4326');
|
||||
const maxLat = validExtent[3];
|
||||
const maxLon = validExtent[2];
|
||||
const minLat = validExtent[1];
|
||||
const minLon = validExtent[0];
|
||||
let validExtentP = extent;
|
||||
if (!wrapX) {
|
||||
validExtentP = [
|
||||
clamp(extent[0], this.minX_, this.maxX_),
|
||||
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
|
||||
|
||||
@@ -633,18 +823,30 @@ class Graticule extends VectorLayer {
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, 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) {
|
||||
lon = Math.max(lon - interval, this.minLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
}
|
||||
|
||||
lon = clamp(centerLon, this.minLon_, this.maxLon_);
|
||||
|
||||
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) {
|
||||
lon = Math.min(lon + interval, this.maxLon_);
|
||||
idx = this.addMeridian_(lon, minLat, maxLat, squaredTolerance, extent, idx);
|
||||
}
|
||||
}
|
||||
|
||||
this.meridians_.length = idx;
|
||||
if (this.meridiansLabels_) {
|
||||
@@ -694,11 +896,13 @@ class Graticule extends VectorLayer {
|
||||
/** @type {Array<number>} **/
|
||||
const p2 = [];
|
||||
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[1] = centerLat - delta;
|
||||
p1[1] = clampedLat - delta;
|
||||
p2[0] = centerLon + delta;
|
||||
p2[1] = centerLat + delta;
|
||||
p2[1] = clampedLat + delta;
|
||||
this.fromLonLatTransform_(p1, p1);
|
||||
this.fromLonLatTransform_(p2, p2);
|
||||
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) {
|
||||
const flatCoordinates = lineString.getFlatCoordinates();
|
||||
const clampedBottom = Math.max(extent[1], flatCoordinates[1]);
|
||||
const clampedTop = Math.min(extent[3], flatCoordinates[flatCoordinates.length - 1]);
|
||||
let bottom = 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(
|
||||
extent[1] + Math.abs(extent[1] - extent[3]) * this.lonLabelPosition_,
|
||||
clampedBottom, clampedTop);
|
||||
const coordinate = [flatCoordinates[0], lat];
|
||||
let point;
|
||||
if (index in this.meridiansLabels_) {
|
||||
point = this.meridiansLabels_[index].geom;
|
||||
const coordinate0 = flatCoordinates[bottom - 1] +
|
||||
(flatCoordinates[top - 1] - flatCoordinates[bottom - 1]) * (lat - flatCoordinates[bottom]) /
|
||||
(flatCoordinates[top] - flatCoordinates[bottom]);
|
||||
const coordinate = [coordinate0, lat];
|
||||
const point = this.meridiansLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
} else {
|
||||
point = new Point(coordinate);
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
@@ -797,19 +1005,23 @@ class Graticule extends VectorLayer {
|
||||
*/
|
||||
getParallelPoint_(lineString, extent, index) {
|
||||
const flatCoordinates = lineString.getFlatCoordinates();
|
||||
const clampedLeft = Math.max(extent[0], flatCoordinates[0]);
|
||||
const clampedRight = Math.min(extent[2], flatCoordinates[flatCoordinates.length - 2]);
|
||||
let left = 0;
|
||||
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(
|
||||
extent[0] + Math.abs(extent[0] - extent[2]) * this.latLabelPosition_,
|
||||
clampedLeft, clampedRight);
|
||||
const coordinate = [lon, flatCoordinates[1]];
|
||||
let point;
|
||||
if (index in this.parallelsLabels_) {
|
||||
point = this.parallelsLabels_[index].geom;
|
||||
const coordinate1 = flatCoordinates[left + 1] +
|
||||
(flatCoordinates[right + 1] - flatCoordinates[left + 1]) * (lon - flatCoordinates[left]) /
|
||||
(flatCoordinates[right] - flatCoordinates[left]);
|
||||
const coordinate = [lon, coordinate1];
|
||||
const point = this.parallelsLabels_[index].geom;
|
||||
point.setCoordinates(coordinate);
|
||||
} else {
|
||||
point = new Point(coordinate);
|
||||
}
|
||||
return point;
|
||||
}
|
||||
|
||||
@@ -830,23 +1042,66 @@ class Graticule extends VectorLayer {
|
||||
const epsg4326Projection = getProjection('EPSG:4326');
|
||||
|
||||
const worldExtent = projection.getWorldExtent();
|
||||
const worldExtentP = transformExtent(worldExtent, epsg4326Projection, projection);
|
||||
|
||||
this.maxLat_ = worldExtent[3];
|
||||
this.maxLon_ = worldExtent[2];
|
||||
this.minLat_ = worldExtent[1];
|
||||
this.minLon_ = worldExtent[0];
|
||||
|
||||
this.maxLatP_ = worldExtentP[3];
|
||||
this.maxLonP_ = worldExtentP[2];
|
||||
this.minLatP_ = worldExtentP[1];
|
||||
this.minLonP_ = worldExtentP[0];
|
||||
// If the world extent crosses the dateline define a custom transform to
|
||||
// return longitudes which wrap the dateline
|
||||
|
||||
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);
|
||||
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()));
|
||||
if (isNaN(this.projectionCenterLonLat_[1])) {
|
||||
this.projectionCenterLonLat_[1] = Math.abs(this.maxLat_) >= Math.abs(this.minLat_) ?
|
||||
this.maxLat_ : this.minLat_;
|
||||
}
|
||||
|
||||
this.projection_ = projection;
|
||||
}
|
||||
|
||||
@@ -33,6 +33,10 @@ import SourceState from '../source/State.js';
|
||||
* visible.
|
||||
* @property {number} [maxZoom] The maximum view zoom level (inclusive) at which this layer will
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
@@ -24,6 +24,10 @@ import WebGLPointsLayerRenderer from '../renderer/webgl/PointsLayer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* of the heatmap, specified as an array of CSS color strings.
|
||||
* @property {number} [radius=8] Radius size in pixels.
|
||||
|
||||
@@ -31,6 +31,10 @@ import {assert} from '../asserts.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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,
|
||||
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
|
||||
* construction.
|
||||
|
||||
@@ -20,6 +20,10 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* 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.
|
||||
|
||||
@@ -24,6 +24,10 @@ import {assign} from '../obj.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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
|
||||
* 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.
|
||||
|
||||
@@ -23,6 +23,10 @@ import Layer from './Layer.js';
|
||||
* visible.
|
||||
* @property {number} [maxResolution] The maximum resolution (exclusive) below which this layer will
|
||||
* 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 {boolean} [disableHitDetection=false] Setting this to true will provide a slight performance boost, but will
|
||||
* 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