Compare commits

..

1 Commits

Author SHA1 Message Date
ahocevar
5891aaadf9 Update package version to 6.0.0-beta.6 2019-04-17 18:24:29 -07:00
141 changed files with 1136 additions and 25155 deletions

View File

@@ -27,10 +27,6 @@ jobs:
name: Run Tests
command: npm test
- store_artifacts:
path: coverage/
destination: coverage
- store_artifacts:
path: rendering/cases/
destination: rendering
@@ -42,19 +38,3 @@ jobs:
- store_artifacts:
path: build/examples
destination: examples
- run:
name: Build API Docs
command: npm run apidoc
- store_artifacts:
path: build/apidoc
destination: apidoc
- run:
name: Build Website
command: npm run build-site
- store_artifacts:
path: public
destination: website

2
.gitignore vendored
View File

@@ -2,5 +2,3 @@
/coverage/
/dist/
node_modules/
/.cache/
/public/

View File

@@ -4,28 +4,6 @@
#### Backwards incompatible changes
#### Removal of `GEOLOCATION` constant from `ol/has`
If you were previously using this constant, you can check if `'geolocation'` is define in `navigator` instead.
```js
if ('geolocation' in navigator) {
// ...
}
```
#### Removal of CSS print rules
The CSS media print rules were removed from the `ol.css` file. To get the previous behavior, use the following CSS:
```css
@media print {
.ol-control {
display: none;
}
}
```
#### Removal of optional this arguments
The optional this (i.e. opt_this) arguments were removed from the following methods.
@@ -54,9 +32,9 @@ Previously, this options only constrained the view *center*. This behaviour can
As a side effect, the view `rotate` method is gone and has been replaced with `adjustRotation` which takes a delta as input.
##### The view is constrained so only one world is visible
##### Zoom is constrained so only one world is visible
Previously, maps showed multiple worlds at low zoom levels. In addition, it used to be possible to pan off the north or south edge of the world. Now, the view is restricted to show only one world, and you cannot pan off the edge. To get the previous behavior, configure the `ol/View` with `multiWorld: true`.
Previously, maps showed multiple worlds at low zoom levels. Now, the view is restricted to show only one world. To get the previous behavior, configure the `ol/View` with `multiWorld: true`.
##### Removal of deprecated methods
@@ -120,14 +98,6 @@ If you were previously using `VectorTile` layers with `renderMode: 'vector'`, yo
If you were previously using `Vector` layers with `renderMode: 'image'`, you have to remove this configuration option. Instead, use the new `ol/layer/VectorImage` layer with your `ol/source/Vector`.
##### New declutter behavior
If a map has more than one layer with `declutter` set to true, decluttering now considers all `Vector` and `VectorTile` layers, instead of decluttering each layer separately. Only `VectorImage` layers continue to be decluttered separately. The higher the z-index of a layer, the higher the priority of its decluttered items.
Within a layer, the declutter order has changed. Previously, styles with a lower `zIndex` were prioritized over those with a higher `zIndex`. Now the opposite order is used.
On vector layers, even if decluttered images or texts have a lower z-Index than polygons or lines, they will now be rendered on top of the polygons or lines. For vector tile layers, this was the case already in previous releases.
##### New `prerender` and `postrender` layer events replace old `precompose`, `render` and `postcompose` events
If you were previously registering for `precompose` and `postcompose` events, you should now register for `prerender` and `postrender` events on layers. Instead of the previous `render` event, you should now listen for `postrender`. Layers are no longer composed to a single Canvas element. Instead, they are added to the map viewport as individual elements.

View File

@@ -1,23 +0,0 @@
{
"opts": {
"recurse": true,
"template": "node_modules/jsdoc-json"
},
"tags": {
"allowUnknownTags": true
},
"source": {
"includePattern": "\\.js$",
"include": [
"src/ol"
]
},
"plugins": [
"jsdoc-plugin-typescript",
"config/jsdoc/api-info/plugins/api",
"config/jsdoc/api-info/plugins/module"
],
"typescript": {
"moduleRoot": "src"
}
}

View File

@@ -1,15 +0,0 @@
/**
* Handle the api annotation.
* @param {Object} dictionary The tag dictionary.
*/
exports.defineTags = dictionary => {
dictionary.defineTag('api', {
onTagged: (doclet, tag) => {
doclet.api = true;
}
});
};

View File

@@ -1,170 +0,0 @@
/**
* This plugin adds an `exportMap` property to @module doclets. Each export map
* is an object with properties named like the local identifier and values named
* like the exported identifier.
*
* For example, the code below
*
* export {foo as bar};
*
* would be a map like `{foo: 'bar'}`.
*
* In the case of an export declaration with a source, the export identifier is
* prefixed by the source. For example, this code
*
* export {foo as bar} from 'ol/bam';
*
* would be a map like `{'ol/bam foo': 'bar'}`.
*
* If a default export is a literal or object expression, the local name will be
* an empty string. For example
*
* export default {foo: 'bar'};
*
* would be a map like `{'': 'default'}`.
*/
const assert = require('assert');
const path = require('path');
/**
* A lookup of export maps per source filepath.
*/
const exportMapLookup = {};
function loc(filepath, node) {
return `${filepath}:${node.loc.start.line}`;
}
function nameFromChildIdentifier(filepath, node) {
assert.ok(node.id, `expected identifer in ${loc(filepath, node)}`);
assert.strictEqual(node.id.type, 'Identifier', `expected identifer in ${loc(filepath, node)}`);
return node.id.name;
}
function handleExportNamedDeclaration(filepath, node) {
if (!(filepath in exportMapLookup)) {
exportMapLookup[filepath] = {};
}
const exportMap = exportMapLookup[filepath];
const declaration = node.declaration;
if (declaration) {
// `export class Foo{}` or `export function foo() {}`
if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {
const name = nameFromChildIdentifier(filepath, declaration);
exportMap[name] = name;
return;
}
// `export const foo = 'bar', bam = 42`
if (declaration.type === 'VariableDeclaration') {
const declarations = declaration.declarations;
assert.ok(declarations.length > 0, `expected variable declarations in ${loc(filepath, declaration)}`);
for (const declarator of declarations) {
assert.strictEqual(declarator.type, 'VariableDeclarator', `unexpected "${declarator.type}" in ${loc(filepath, declarator)}`);
const name = nameFromChildIdentifier(filepath, declarator);
exportMap[name] = name;
}
return;
}
throw new Error(`Unexpected named export "${declaration.type}" in ${loc(filepath, declaration)}`);
}
let prefix = '';
const source = node.source;
if (source) {
// `export foo from 'bar'`
assert.strictEqual(source.type, 'Literal', `unexpected export source "${source.type}" in ${loc(filepath, source)}`);
prefix = `${source.value} `;
}
const specifiers = node.specifiers;
assert.ok(specifiers.length > 0, `expected export specifiers in ${loc(filepath, node)}`);
// `export {foo, bar}` or `export {default as Foo} from 'bar'`
for (const specifier of specifiers) {
assert.strictEqual(specifier.type, 'ExportSpecifier', `unexpected export specifier in ${loc(filepath, specifier)}`);
const local = specifier.local;
assert.strictEqual(local.type, 'Identifier', `unexpected local specifier "${local.type} in ${loc(filepath, local)}`);
const exported = specifier.exported;
assert.strictEqual(local.type, 'Identifier', `unexpected exported specifier "${exported.type} in ${loc(filepath, exported)}`);
exportMap[prefix + local.name] = exported.name;
}
}
function handleDefaultDeclaration(filepath, node) {
if (!(filepath in exportMapLookup)) {
exportMapLookup[filepath] = {};
}
const exportMap = exportMapLookup[filepath];
const declaration = node.declaration;
if (declaration) {
// `export default class Foo{}` or `export default function foo () {}`
if (declaration.type === 'ClassDeclaration' || declaration.type === 'FunctionDeclaration') {
const name = nameFromChildIdentifier(filepath, declaration);
exportMap[name] = 'default';
return;
}
// `export default foo`
if (declaration.type === 'Identifier') {
exportMap[declaration.name] = 'default';
return;
}
// `export default {foo: 'bar'}` or `export default 42`
if (declaration.type === 'ObjectExpression' || declaration.type === 'Literal') {
exportMap[''] = 'default';
return;
}
}
throw new Error(`Unexpected default export "${declaration.type}" in ${loc(filepath, declaration)}`);
}
exports.astNodeVisitor = {
visitNode: (node, event, parser, filepath) => {
if (node.type === 'ExportNamedDeclaration') {
return handleExportNamedDeclaration(filepath, node);
}
if (node.type === 'ExportDefaultDeclaration') {
return handleDefaultDeclaration(filepath, node);
}
}
};
const moduleLookup = {};
exports.handlers = {
// create a lookup of @module doclets
newDoclet: event => {
const doclet = event.doclet;
if (doclet.kind === 'module') {
const filepath = path.join(doclet.meta.path, doclet.meta.filename);
assert.ok(!(filepath in moduleLookup), `duplicate @module doc in ${filepath}`);
moduleLookup[filepath] = doclet;
}
},
// assign the `exportMap` property to @module doclets
parseComplete: event => {
for (const filepath in moduleLookup) {
assert.ok(filepath in exportMapLookup, `missing ${filepath} in export map lookup`);
moduleLookup[filepath].exportMap = exportMapLookup[filepath];
}
// make sure there was a @module doclet for each export map
for (const filepath in exportMapLookup) {
assert.ok(filepath in moduleLookup, `missing @module doclet in ${filepath}`);
}
}
};

View File

@@ -1,43 +1,43 @@
<table><tr>
<th width="33.3%">Map</th><th width="33.3%">View</th><th width="33.3%">Layers</th>
</tr><tr>
<td><p>A <a href="module-ol_Map-Map.html">map</a> is made of <a href="module-ol_layer_Base-BaseLayer.html">layers</a>, a <a href="module-ol_View-View.html">view</a> to visualize them, <a href="module-ol_interaction_Interaction-Interaction.html">interactions</a> to modify map content and <a href="module-ol_control_Control-Control.html">controls</a> with UI components.</p>
<a href="module-ol_Map-Map.html">Overview</a><br>
<a href="module-ol_Map-Map.html#Map">Creation</a><br>
<a href="module-ol_MapBrowserEvent-MapBrowserEvent.html">Events</a></td>
<td><p>A [map](module-ol_Map-Map.html) is made of [layers](module-ol_layer_Base-BaseLayer.html), a [view](module-ol_View-View.html) to visualize them, [interactions](module-ol_interaction_Interaction-Interaction.html) to modify map content and [controls](module-ol_control_Control-Control.html) with UI components.</p>
[Overview](module-ol_Map-Map.html)<br>
[Creation](module-ol_Map-Map.html#Map)<br>
[Events](module-ol_MapBrowserEvent-MapBrowserEvent.html)</td>
<td><p>The view manages the visual parameters of the map view, like resolution or rotation.</p>
<a href="module-ol_View-View.html">View</a> with center, projection, resolution and rotation</td>
<td><p>Layers are lightweight containers that get their data from <a href="module-ol_source_Source-Source.html">sources</a>.</p>
<a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a><br>
<a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a><br>
<a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a><br>
<a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a></td>
[View](module-ol_View-View.html) with center, projection, resolution and rotation</td>
<td><p>Layers are lightweight containers that get their data from [sources](module-ol_source_Source-Source.html).</p>
[ol/layer/Tile](module-ol_layer_Tile-TileLayer.html)<br>
[ol/layer/Image](module-ol_layer_Image-ImageLayer.html)<br>
[ol/layer/Vector](module-ol_layer_Vector-VectorLayer.html)<br>
[ol/layer/VectorTile](module-ol_layer_VectorTile-VectorTileLayer.html)</td>
</tr><tr>
<th>Controls</th><th>Interactions</th><th>Sources and formats</th>
</tr><tr>
<td><a href="module-ol_control_util.html#.defaults">Map default controls</a><br>
<a href="module-ol_control_Control-Control.html">All controls</a>
<td>[Map default controls](module-ol_control_util.html#.defaults)<br>
[All controls](module-ol_control_Control-Control.html)
</td>
<td>
<a href="module-ol_interaction.html#~defaults">Map default interactions</a><br>
Interactions for <a href="module-ol_Feature-Feature.html">vector features</a>
<ul><li><a href="module-ol_interaction_Select-Select.html">ol/interaction/Select</a></li>
<li><a href="module-ol_interaction_Draw-Draw.html">ol/interaction/Draw</a></li>
<li><a href="module-ol_interaction_Modify-Modify.html">ol/interaction/Modify</a></li></ul>
<a href="module-ol_interaction_Interaction-Interaction.html">All interactions</a></td>
<td><a href="module-ol_source_Tile-TileSource.html">Tile sources</a> for <a href="module-ol_layer_Tile-TileLayer.html">ol/layer/Tile</a>
<br><a href="module-ol_source_Image-ImageSource.html">Image sources</a> for <a href="module-ol_layer_Image-ImageLayer.html">ol/layer/Image</a>
<br><a href="module-ol_source_Vector-VectorSource.html">Vector sources</a> for <a href="module-ol_layer_Vector-VectorLayer.html">ol/layer/Vector</a>
<br><a href="module-ol_source_VectorTile-VectorTile.html">Vector tile sources</a> for <a href="module-ol_layer_VectorTile-VectorTileLayer.html">ol/layer/VectorTile</a>
<br><a href="module-ol_format_Feature-FeatureFormat.html">Formats</a> for reading/writing vector data
<br><a href="module-ol_format_WMSCapabilities-WMSCapabilities.html">ol/format/WMSCapabilities</a></td></tr>
[Map default interactions](module-ol_interaction.html#~defaults)<br>
Interactions for [vector features](module-ol_Feature-Feature.html)
<ul><li>[ol/interaction/Select](module-ol_interaction_Select-Select.html)</li>
<li>[ol/interaction/Draw](module-ol_interaction_Draw-Draw.html)</li>
<li>[ol/interaction/Modify](module-ol_interaction_Modify-Modify.html)</li></ul>
[All interactions](module-ol_interaction_Interaction-Interaction.html)</td>
<td>[Tile sources](module-ol_source_Tile-TileSource.html) for [ol/layer/Tile](module-ol_layer_Tile-TileLayer.html)
<br>[Image sources](module-ol_source_Image-ImageSource.html) for [ol/layer/Image](module-ol_layer_Image-ImageLayer.html)
<br>[Vector sources](module-ol_source_Vector-VectorSource.html) for [ol/layer/Vector](module-ol_layer_Vector-VectorLayer.html)
<br>[Vector tile sources](module-ol_source_VectorTile-VectorTile.html) for [ol/layer/VectorTile](module-ol_layer_VectorTile-VectorTileLayer.html)
<br>[Formats](module-ol_format_Feature-FeatureFormat.html) for reading/writing vector data
<br>[ol/format/WMSCapabilities](module-ol_format_WMSCapabilities-WMSCapabilities.html)</td></tr>
<tr><th>Projections</th><th>Observable objects</th><th>Other components</th></tr>
<tr><td><p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform, use <a href="module-ol_proj.html#.transform">ol/proj#transform()</a> and <a href="module-ol_proj.html#.transformExtent">ol/proj#transformExtent()</a>.</p>
<a href="module-ol_proj.html">ol/proj</a></td>
<td><p>Changes to all <a href="module-ol_Object-BaseObject.html">ol/Object</a>s can be observed by calling the <a href="module-ol_Object-BaseObject.html#on">object.on('propertychange')</a> method. Listeners receive an <a href="module-ol_Object-ObjectEvent.html">ol/Object.ObjectEvent</a> with information on the changed property and old value.</p>
<tr><td><p>All coordinates and extents need to be provided in view projection (default: EPSG:3857). To transform, use [ol/proj#transform()](module-ol_proj.html#.transform) and [ol/proj#transformExtent()](module-ol_proj.html#.transformExtent).</p>
[ol/proj](module-ol_proj.html)</td>
<td><p>Changes to all [ol/Object](module-ol_Object-BaseObject.html)s can be observed by calling the [object.on('propertychange')](module-ol_Object-BaseObject.html#on) method. Listeners receive an [ol/Object.ObjectEvent](module-ol_Object-ObjectEvent.html) with information on the changed property and old value.</p>
<td>
<a href="module-ol_Geolocation.html">ol/Geolocation</a><br>
<a href="module-ol_Overlay-Overlay.html">ol/Overlay</a><br></td>
[ol/Geolocation](module-ol_Geolocation.html)<br>
[ol/Overlay](module-ol_Overlay-Overlay.html)<br></td>
</tr></table>
&nbsp;

View File

@@ -1,29 +1,32 @@
const events = {};
const classes = {};
exports.handlers = {
newDoclet: function(e) {
const doclet = e.doclet;
if (doclet.kind !== 'event') {
return;
let cls;
if (doclet.kind == 'event') {
cls = doclet.longname.split('#')[0];
if (!(cls in events)) {
events[cls] = [];
}
events[cls].push(doclet.longname);
} else if (doclet.kind == 'class' && !(doclet.longname in classes)) {
classes[doclet.longname] = doclet;
}
const cls = doclet.longname.split('#')[0];
if (!(cls in events)) {
events[cls] = [];
}
events[cls].push(doclet.longname);
},
parseComplete: function(e) {
const doclets = e.doclets;
for (let i = 0, ii = doclets.length - 1; i < ii; ++i) {
const doclet = doclets[i];
let doclet, i, ii, j, jj, event, fires;
for (i = 0, ii = doclets.length - 1; i < ii; ++i) {
doclet = doclets[i];
if (doclet.fires) {
if (doclet.kind == 'class') {
const fires = [];
for (let j = 0, jj = doclet.fires.length; j < jj; ++j) {
const event = doclet.fires[j].replace('event:', '');
fires = [];
for (j = 0, jj = doclet.fires.length; j < jj; ++j) {
event = doclet.fires[j].replace('event:', '');
if (events[event]) {
fires.push.apply(fires, events[event]);
} else if (doclet.fires[j] !== 'event:ObjectEvent') {

View File

@@ -220,11 +220,3 @@ Duplicate item added to a unique collection. For example, it may be that you tr
### 59
Invalid command found in the PBF. This indicates that the loaded vector tile may be corrupt.
### 60
Missing or invalid `size`.
### 61
Cannot determine IIIF Image API version from provided image information JSON.

View File

@@ -5,6 +5,7 @@
"common": false,
"createMapboxStreetsV6Style": false,
"d3": false,
"domtoimage": false,
"geojsonvt": false,
"GyroNorm": false,
"jsPDF": false,

View File

@@ -6,7 +6,7 @@ docs: >
The example loads TopoJSON geometries and uses d3 (<code>d3.geo.path</code>) to render these geometries to a SVG element.
tags: "d3"
resources:
- https://unpkg.com/d3@5.9.2/dist/d3.js
- https://unpkg.com/d3@4.12.0/build/d3.js
- https://unpkg.com/topojson@3.0.2/dist/topojson.js
---
<div id="map" class="map"></div>

2
examples/d3.js vendored
View File

@@ -85,7 +85,7 @@ const map = new Map({
/**
* Load the topojson data and create an ol/layer/Image for that data.
*/
d3.json('data/topojson/us.json').then(function(us) {
d3.json('data/topojson/us.json', function(error, us) {
const layer = new CanvasLayer({
features: topojson.feature(us, us.objects.counties)

View File

@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg">
<g>
<rect width="20" height="20" style="fill:#fff" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 194 B

View File

@@ -40,6 +40,6 @@ gn.init().then(function() {
center[0] -= resolution * gamma * 25;
center[1] += resolution * beta * 25;
view.setCenter(center);
view.setCenter(view.constrainCenter(center));
});
});

View File

@@ -1,6 +0,0 @@
.overlay {
background-color: yellow;
border-radius: 6px;
padding: 4px;
white-space: nowrap;
}

View File

@@ -3,13 +3,12 @@ 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>
Example of exporting a map as a PNG image. This example use the <a href="https://www.npmjs.com/package/dom-to-image-more">dom-to-image-more</a>
library.
tags: "export, png, openstreetmap"
resources:
- https://unpkg.com/dom-to-image-more@2.7.1/dist/dom-to-image-more.min.js
---
<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>

View File

@@ -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({
@@ -26,23 +23,9 @@ 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)
domtoimage.toPng(map.getViewport().querySelector('.ol-layers'))
.then(function(dataURL) {
const link = document.getElementById('image-download');
link.href = dataURL;

View File

@@ -7,6 +7,7 @@ docs: >
tags: "export, pdf, openstreetmap"
resources:
- https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.2.61/jspdf.min.js
- https://unpkg.com/dom-to-image-more@2.7.1/dist/dom-to-image-more.min.js
---
<div class="row-fluid">
<div class="span12">

View File

@@ -4,8 +4,6 @@ import WKT from '../src/ol/format/WKT.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
import {OSM, Vector as VectorSource} from '../src/ol/source.js';
import {toJpeg} from 'html-to-image';
const raster = new TileLayer({
source: new OSM()
});
@@ -43,15 +41,6 @@ const dims = {
a5: [210, 148]
};
// export options for html-to-image.
// See: https://github.com/bubkoo/html-to-image#options
const exportOptions = {
filter: function(element) {
return element.className.indexOf('ol-control') === -1;
}
};
const exportButton = document.getElementById('export-pdf');
exportButton.addEventListener('click', function() {
@@ -68,14 +57,15 @@ exportButton.addEventListener('click', function() {
const extent = map.getView().calculateExtent(size);
map.once('rendercomplete', function() {
toJpeg(map.getTargetElement(), exportOptions).then(function(dataUrl) {
domtoimage.toJpeg(map.getViewport().querySelector('.ol-layers')).then(function(dataUrl) {
const pdf = new jsPDF('landscape', undefined, format);
pdf.addImage(dataUrl, 'JPEG', 0, 0, dim[0], dim[1]);
pdf.save('map.pdf');
// Reset original map size
map.setSize(size);
map.getView().fit(extent, {
size: size
size: size,
constrainResolution: false
});
exportButton.disabled = false;
document.body.style.cursor = 'auto';

View File

@@ -2,7 +2,7 @@ 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';
import {defaults as defaultControls} from '../src/ol/control.js';
import {defaults as defaultControls} from '../src/ol/control/util';
import ZoomSlider from '../src/ol/control/ZoomSlider';
const view = new View({

View File

@@ -1,5 +1,6 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import {platformModifierKeyOnly} from '../src/ol/events/condition.js';
import GeoJSON from '../src/ol/format/GeoJSON.js';
import ExtentInteraction from '../src/ol/interaction/Extent.js';
import {Tile as TileLayer, Vector as VectorLayer} from '../src/ol/layer.js';
@@ -26,7 +27,9 @@ const map = new Map({
})
});
const extent = new ExtentInteraction();
const extent = new ExtentInteraction({
condition: platformModifierKeyOnly
});
map.addInteraction(extent);
extent.setActive(false);

View File

@@ -10,7 +10,9 @@ import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer';
import {clamp, lerp} from '../src/ol/math';
import Stamen from '../src/ol/source/Stamen';
const features = [];
const vectorSource = new Vector({
features: [],
attributions: 'NASA'
});
@@ -36,16 +38,15 @@ updateStatusText();
class WebglPointsLayer extends VectorLayer {
createRenderer() {
return new WebGLPointsLayerRenderer(this, {
colorCallback: function(feature, color) {
colorCallback: function(feature, vertex, component) {
// component at index 3 is alpha
if (component === 3) {
return 1;
}
// color is interpolated based on year
const ratio = clamp((feature.get('year') - 1800) / (2013 - 1800), 0, 1);
color[0] = lerp(oldColor[0], newColor[0], ratio) / 255;
color[1] = lerp(oldColor[1], newColor[1], ratio) / 255;
color[2] = lerp(oldColor[2], newColor[2], ratio) / 255;
color[3] = 1;
return color;
return lerp(oldColor[component], newColor[component], ratio) / 255;
},
sizeCallback: function(feature) {
return 18 * clamp(feature.get('mass') / 200000, 0, 1) + 8;
@@ -108,28 +109,26 @@ function loadData() {
client.open('GET', 'data/csv/meteorite_landings.csv');
client.onload = function() {
const csv = client.responseText;
const features = [];
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
let curIndex;
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
let prevIndex = 0;
let line;
while ((curIndex = csv.indexOf('\n', prevIndex)) > 0) {
line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
prevIndex = curIndex + 1;
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
if (isNaN(coords[0]) || isNaN(coords[1])) {
// guard against bad data
// skip header
if (prevIndex === 0) {
continue;
}
const coords = fromLonLat([parseFloat(line[4]), parseFloat(line[3])]);
features.push(new Feature({
mass: parseFloat(line[1]) || 0,
year: parseInt(line[2]) || 0,
geometry: new Point(coords)
}));
}
vectorSource.addFeatures(features);
};
client.send();

View File

@@ -1,6 +1,5 @@
.map:-webkit-full-screen {
height: 100%;
margin: 0;
}
.map:-ms-fullscreen {
height: 100%;

View File

@@ -1,6 +1,5 @@
.fullscreen:-webkit-full-screen {
height: 100%;
margin: 0;
}
.fullscreen:-ms-fullscreen {
height: 100%;

View File

@@ -1,6 +1,5 @@
.map:-webkit-full-screen {
height: 100%;
margin: 0;
}
.map:-ms-fullscreen {
height: 100%;

View File

@@ -4,7 +4,7 @@ title: Earthquakes Heatmap
shortdesc: Demonstrates the use of a heatmap layer.
docs: >
This example parses a KML file and renders the features as a <code>ol/layer/Heatmap</code> layer.
tags: "heatmap, kml, vector, style, webgl"
tags: "heatmap, kml, vector, style"
---
<div id="map" class="map"></div>
<form>

View File

@@ -25,7 +25,7 @@ rome.setStyle(new Style({
image: new Icon({
color: '#8959A8',
crossOrigin: 'anonymous',
src: 'data/square.svg'
src: 'data/dot.png'
})
}));

View File

@@ -10,6 +10,7 @@ import {fromLonLat} from '../src/ol/proj';
import WebGLPointsLayerRenderer from '../src/ol/renderer/webgl/PointsLayer';
import {lerp} from '../src/ol/math';
const features = [];
const vectorSource = new Vector({
features: [],
attributions: 'National UFO Reporting Center'
@@ -38,17 +39,16 @@ class WebglPointsLayer extends VectorLayer {
createRenderer() {
return new WebGLPointsLayerRenderer(this, {
texture: texture,
colorCallback: function(feature, color) {
colorCallback: function(feature, vertex, component) {
// component at index 3 is alpha
if (component === 3) {
return 1;
}
// color is interpolated based on year (min is 1910, max is 2013)
// please note: most values are between 2000-2013
const ratio = (feature.get('year') - 1950) / (2013 - 1950);
color[0] = lerp(oldColor[0], newColor[0], ratio * ratio) / 255;
color[1] = lerp(oldColor[1], newColor[1], ratio * ratio) / 255;
color[2] = lerp(oldColor[2], newColor[2], ratio * ratio) / 255;
color[3] = 1;
return color;
return lerp(oldColor[component], newColor[component], ratio * ratio) / 255;
},
texCoordCallback: function(feature, component) {
let coords = shapeTextureCoords[feature.get('shape')];
@@ -70,15 +70,18 @@ function loadData() {
client.open('GET', 'data/csv/ufo_sighting_data.csv');
client.onload = function() {
const csv = client.responseText;
const features = [];
let prevIndex = csv.indexOf('\n') + 1; // scan past the header line
let curIndex;
while ((curIndex = csv.indexOf('\n', prevIndex)) != -1) {
const line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
let prevIndex = 0;
let line;
while ((curIndex = csv.indexOf('\n', prevIndex)) > 0) {
line = csv.substr(prevIndex, curIndex - prevIndex).split(',');
prevIndex = curIndex + 1;
// skip header
if (prevIndex === 0) {
continue;
}
const coords = fromLonLat([parseFloat(line[5]), parseFloat(line[4])]);
// only keep valid points

View File

@@ -1,16 +0,0 @@
---
layout: example.html
title: IIIF Image API
shortdesc: Example of a IIIF Image API source.
docs: >
Example of a tile source for an International Image Interoperability Framework (IIIF) Image Service.
Try any Image API version 1 or 2 service.
tags: "IIIF, IIIF Image API, tile source"
---
<div id="map" class="map"></div>
<div class="controls">
<div id="iiif-notification">&nbsp;</div>
Enter <code>info.json</code> URL:
<input type="text" id="imageInfoUrl" value="https://iiif.ub.uni-leipzig.de/iiif/j2k/0000/0107/0000010732/00000072.jpx/info.json">
<button id="display">Display image</button>
</div>

View File

@@ -1,46 +0,0 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import TileLayer from '../src/ol/layer/Tile.js';
import IIIF from '../src/ol/source/IIIF.js';
import IIIFInfo from '../src/ol/format/IIIFInfo.js';
const layer = new TileLayer(),
map = new Map({
layers: [layer],
target: 'map'
}),
notifyDiv = document.getElementById('iiif-notification'),
urlInput = document.getElementById('imageInfoUrl'),
displayButton = document.getElementById('display');
function refreshMap(imageInfoUrl) {
fetch(imageInfoUrl).then(function(response) {
response.json().then(function(imageInfo) {
const options = new IIIFInfo(imageInfo).getTileSourceOptions();
if (options === undefined || options.version === undefined) {
notifyDiv.textContent = 'Data seems to be no valid IIIF image information.';
return;
}
options.zDirection = -1;
const iiifTileSource = new IIIF(options);
layer.setSource(iiifTileSource);
map.setView(new View({
resolutions: iiifTileSource.getTileGrid().getResolutions(),
extent: iiifTileSource.getTileGrid().getExtent(),
constrainOnlyCenter: true
}));
map.getView().fit(iiifTileSource.getTileGrid().getExtent());
notifyDiv.textContent = '';
}).catch(function(body) {
notifyDiv.textContent = 'Could not read image info json. ' + body;
});
}).catch(function() {
notifyDiv.textContent = 'Could not read data from URL.';
});
}
displayButton.addEventListener('click', function() {
refreshMap(urlInput.value);
});
refreshMap(urlInput.value);

View File

@@ -11,25 +11,9 @@
body {
padding-top: 70px;
}
img.header-logo {
padding-left: 18px;
}
input.search-query {
color: #333;
}
@media (max-width: 480px) {
input.search-query {
width: 110px;
}
#count {
display: none;
}
}
@media (max-width: 374px) {
input.search-query {
display: none;
}
}
.example {
display: block;
padding: 10px;
@@ -198,9 +182,9 @@
<body>
<header class="navbar navbar-fixed-top" role="navigation">
<div class="container-fluid">
<div class="container">
<div class="display-table pull-left">
<a class="navbar-brand" href="./"><img class="header-logo" src="./resources/logo-70x70.png">&nbsp;OpenLayers</a>
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png">&nbsp;OpenLayers Examples</a>
<form class="navbar-form" role="search">
<input name="q" type="text" id="keywords" class="search-query" placeholder="Search" autofocus>
<span id="count"></span>

View File

@@ -6,10 +6,9 @@ docs: >
Show how to add a mapbox-gl-js layer in an openlayers map. **Note**: Make sure to get your own Mapbox API key when using this example. No map will be visible when the API key has expired.
tags: "simple, mapbox, vector, tiles"
resources:
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.js
- https://unpkg.com/mapbox-gl@0.54.0/dist/mapbox-gl.css
- https://unpkg.com/mapbox-gl@0.51.0/dist/mapbox-gl.js
cloak:
- key: ER67WIiPdCQvhgsUjoWK
value: Your Mapbox access token from https://mapbox.com/ here
value: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>

View File

@@ -1,67 +1,188 @@
import Map from '../src/ol/Map.js';
import View from '../src/ol/View.js';
import Layer from '../src/ol/layer/Layer';
import {toLonLat, fromLonLat} from '../src/ol/proj';
import {assign} from '../src/ol/obj';
import {getTransform} from '../src/ol/proj';
import SourceState from '../src/ol/source/State';
import {Stroke, Style} from '../src/ol/style.js';
import VectorLayer from '../src/ol/layer/Vector.js';
import VectorSource from '../src/ol/source/Vector.js';
import GeoJSON from '../src/ol/format/GeoJSON.js';
const center = [-98.8, 37.9];
const key = 'ER67WIiPdCQvhgsUjoWK';
class Mapbox extends Layer {
const mbMap = new mapboxgl.Map({
style: 'https://maps.tilehosting.com/styles/bright/style.json?key=' + key,
attributionControl: false,
boxZoom: false,
center: center,
container: 'map',
doubleClickZoom: false,
dragPan: false,
dragRotate: false,
interactive: false,
keyboard: false,
pitchWithRotate: false,
scrollZoom: false,
touchZoomRotate: false
});
/**
* @param {import('./Base.js').Options} options Layer options.
*/
constructor(options) {
const baseOptions = assign({}, options);
super(baseOptions);
const mbLayer = new Layer({
render: function(frameState) {
const canvas = mbMap.getCanvas();
const viewState = frameState.viewState;
this.baseOptions = baseOptions;
const visible = mbLayer.getVisible();
canvas.style.display = visible ? 'block' : 'none';
/**
* @private
* @type boolean
*/
this.loaded = false;
const opacity = mbLayer.getOpacity();
canvas.style.opacity = opacity;
this.initMap();
}
// adjust view parameters in mapbox
const rotation = viewState.rotation;
initMap() {
const map = this.map_;
const view = map.getView();
const transformToLatLng = getTransform(view.getProjection(), 'EPSG:4326');
const center = transformToLatLng(view.getCenter());
this.centerLastRender = view.getCenter();
this.zoomLastRender = view.getZoom();
this.centerLastRender = view.getCenter();
this.zoomLastRender = view.getZoom();
const options = assign(this.baseOptions, {
attributionControl: false,
boxZoom: false,
center,
container: map.getTargetElement(),
doubleClickZoom: false,
dragPan: false,
dragRotate: false,
interactive: false,
keyboard: false,
pitchWithRotate: false,
scrollZoom: false,
touchZoomRotate: false,
zoom: view.getZoom() - 1
});
this.mbmap = new mapboxgl.Map(options);
this.mbmap.on('load', function() {
this.mbmap.getCanvas().remove();
this.loaded = true;
this.map_.render();
[
'mapboxgl-control-container'
].forEach(className => document.getElementsByClassName(className)[0].remove());
}.bind(this));
this.mbmap.on('render', function() {
// Reset offset
if (this.centerNextRender) {
this.centerLastRender = this.centerNextRender;
}
if (this.zoomNextRender) {
this.zoomLastRender = this.zoomNextRender;
}
this.updateRenderedPosition([0, 0], 1);
}.bind(this));
}
/**
*
* @inheritDoc
*/
render(frameState) {
const map = this.map_;
const view = map.getView();
const transformToLatLng = getTransform(view.getProjection(), 'EPSG:4326');
this.centerNextRender = view.getCenter();
const lastRender = map.getPixelFromCoordinate(this.centerLastRender);
const nextRender = map.getPixelFromCoordinate(this.centerNextRender);
const centerOffset = [lastRender[0] - nextRender[0], lastRender[1] - nextRender[1]];
this.zoomNextRender = view.getZoom();
const zoomOffset = Math.pow(2, this.zoomNextRender - this.zoomLastRender);
this.updateRenderedPosition(centerOffset, zoomOffset);
const rotation = frameState.viewState.rotation;
if (rotation) {
mbMap.rotateTo(-rotation * 180 / Math.PI, {
this.mbmap.rotateTo(-rotation * 180 / Math.PI, {
animate: false
});
}
mbMap.jumpTo({
center: toLonLat(viewState.center),
zoom: viewState.zoom - 1,
animate: false
// Re-render mbmap
const center = transformToLatLng(this.centerNextRender);
const zoom = view.getZoom() - 1;
this.mbmap.jumpTo({
center: center,
zoom: zoom
});
// cancel the scheduled update & trigger synchronous redraw
// see https://github.com/mapbox/mapbox-gl-js/issues/7893#issue-408992184
// NOTE: THIS MIGHT BREAK WHEN UPDATING MAPBOX
if (mbMap._frame) {
mbMap._frame.cancel();
mbMap._frame = null;
}
mbMap._render();
return canvas;
return this.mbmap.getCanvas();
}
});
updateRenderedPosition(centerOffset, zoomOffset) {
const style = this.mbmap.getCanvas().style;
style.left = Math.round(centerOffset[0]) + 'px';
style.top = Math.round(centerOffset[1]) + 'px';
style.transform = 'scale(' + zoomOffset + ')';
}
setVisible(visible) {
super.setVisible(visible);
const canvas = this.mbmap.getCanvas();
canvas.style.display = visible ? 'block' : 'none';
}
setOpacity(opacity) {
super.setOpacity(opacity);
const canvas = this.mbmap.getCanvas();
canvas.style.opacity = opacity;
}
setZIndex(zindex) {
super.setZIndex(zindex);
const canvas = this.mbmap.getCanvas();
canvas.style.zIndex = zindex;
}
/**
* @inheritDoc
*/
getSourceState() {
return this.loaded ? SourceState.READY : SourceState.UNDEFINED;
}
setMap(map) {
this.map_ = map;
}
}
mapboxgl.Map.prototype._setupContainer = function _setupContainer() {
const container = this._container;
container.classList.add('mapboxgl-map');
const canvasContainer = this._canvasContainer = container.firstChild;
this._canvas = document.createElement('canvas');
canvasContainer.insertBefore(this._canvas, canvasContainer.firstChild);
this._canvas.style.position = 'absolute';
this._canvas.addEventListener('webglcontextlost', this._contextLost, false);
this._canvas.addEventListener('webglcontextrestored', this._contextRestored, false);
this._canvas.setAttribute('tabindex', '0');
this._canvas.setAttribute('aria-label', 'Map');
this._canvas.className = 'mapboxgl-canvas';
const dimensions = this._containerDimensions();
this._resizeCanvas(dimensions[0], dimensions[1]);
this._controlContainer = canvasContainer;
const controlContainer = this._controlContainer = document.createElement('div');
controlContainer.className = 'mapboxgl-control-container';
container.appendChild(controlContainer);
const positions = this._controlPositions = {};
['top-left', 'top-right', 'bottom-left', 'bottom-right'].forEach(function(positionName) {
const elem = document.createElement('div');
elem.className = 'mapboxgl-ctrl-' + positionName;
controlContainer.appendChild(elem);
positions[positionName] = elem;
});
};
const style = new Style({
stroke: new Stroke({
@@ -81,8 +202,21 @@ const vectorLayer = new VectorLayer({
const map = new Map({
target: 'map',
view: new View({
center: fromLonLat(center),
zoom: 4
}),
layers: [mbLayer, vectorLayer]
center: [-10997148, 4569099],
zoom: 4,
minZoom: 1,
extent: [-Infinity, -20048966.10, Infinity, 20048966.10],
smoothExtentConstraint: false,
smoothResolutionConstraint: false
})
});
const key = 'ER67WIiPdCQvhgsUjoWK';
const mbLayer = new Mapbox({
map: map,
container: map.getTarget(),
style: 'https://maps.tilehosting.com/styles/bright/style.json?key=' + key
});
map.addLayer(mbLayer);
map.addLayer(vectorLayer);

View File

@@ -9,6 +9,6 @@ resources:
- resources/mapbox-streets-v6-style.js
cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from https://mapbox.com/ here
value: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>

View File

@@ -9,6 +9,6 @@ resources:
- resources/mapbox-streets-v6-style.js
cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from https://mapbox.com/ here
value: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>

View File

@@ -3,9 +3,9 @@ import View from '../src/ol/View.js';
import ImageLayer from '../src/ol/layer/Image.js';
import ImageMapGuide from '../src/ol/source/ImageMapGuide.js';
const mdf = 'Library://Samples/Sheboygan/Maps/Sheboygan.MapDefinition';
const mdf = 'Library://Public/Samples/Sheboygan/Maps/Sheboygan.MapDefinition';
const agentUrl =
'http://138.197.230.93:8008/mapguide/mapagent/mapagent.fcgi?';
'http://www.buoyshark.com/mapguide/mapagent/mapagent.fcgi?';
const bounds = [
-87.865114442365922,
43.665065564837931,
@@ -24,9 +24,8 @@ const map = new Map({
params: {
MAPDEFINITION: mdf,
FORMAT: 'PNG',
VERSION: '3.0.0',
USERNAME: 'OLGuest',
PASSWORD: 'olguest'
USERNAME: 'OpenLayers',
PASSWORD: 'OpenLayers'
},
ratio: 2
})

View File

@@ -11,7 +11,7 @@ docs: >
tags: "raster, pixel operation, flood"
cloak:
- key: pk.eyJ1IjoidHNjaGF1YiIsImEiOiJjaW5zYW5lNHkxMTNmdWttM3JyOHZtMmNtIn0.CDIBD8H-G2Gf-cPkIuWtRg
value: Your Mapbox access token from https://mapbox.com/ here
value: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>
<label>

View File

@@ -78,7 +78,7 @@
<header class="navbar" role="navigation">
<div class="container">
<div class="display-table pull-left" id="navbar-logo-container">
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png">&nbsp;OpenLayers</a>
<a class="navbar-brand" href="./"><img src="./resources/logo-70x70.png">&nbsp;OpenLayers Examples</a>
</div>
<!-- menu items that get hidden below 768px width -->
<nav class='collapse navbar-collapse navbar-responsive-collapse'>
@@ -122,7 +122,7 @@
<a class="copy-button" id="copy-html-button" data-clipboard-target="#example-html-source"><i class="fa fa-clipboard"></i> Copy</a>
</div>
<pre><legend>index.html</legend><code id="example-html-source" class="language-markup">&lt;!DOCTYPE html&gt;
&lt;html lang="en"&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;{{ title }}&lt;/title&gt;
&lt;!-- The line below is only needed for old environments like Internet Explorer and Android 4.x --&gt;

View File

@@ -8,7 +8,7 @@ docs: >
tags: "utfgrid, tilejson"
cloak:
- key: pk.eyJ1IjoiYWhvY2V2YXIiLCJhIjoiY2pzbmg0Nmk5MGF5NzQzbzRnbDNoeHJrbiJ9.7_-_gL8ur7ZtEiNwRfCy7Q
value: Your Mapbox access token from https://mapbox.com/ here
value: Your Mapbox access token from http://mapbox.com/ here
---
<div id="map" class="map"></div>
<div style="display: none;">

View File

@@ -19,25 +19,16 @@ module.exports = {
context: src,
target: 'web',
entry: entry,
stats: 'minimal',
module: {
rules: [{
test: /\.js$/,
use: {
loader: 'buble-loader'
},
test: /\.js$/,
include: [
path.join(__dirname, '..', '..', 'src'),
path.join(__dirname, '..')
]
}, {
test: /\.js$/,
use: {
loader: path.join(__dirname, './worker-loader.js')
},
include: [
path.join(__dirname, '../../src/ol/worker')
]
}]
},
optimization: {

View File

@@ -1,24 +0,0 @@
const build = require('../../tasks/serialize-workers').build;
function loader() {
const callback = this.async();
let minify = false;
// TODO: remove when https://github.com/webpack/webpack/issues/6496 is addressed
const compilation = this._compilation;
if (compilation) {
minify = compilation.compiler.options.mode === 'production';
}
build(this.resource, {minify})
.then(chunk => {
for (const filePath in chunk.modules) {
this.addDependency(filePath);
}
callback(null, chunk.code);
})
.catch(callback);
}
module.exports = loader;

View File

@@ -46,7 +46,7 @@ function updateInfo() {
function setTime() {
startDate.setMinutes(startDate.getMinutes() + 15);
if (startDate > new Date()) {
if (startDate > Date.now()) {
startDate = threeHoursAgo();
}
layers[1].getSource().updateParams({'TIME': startDate.toISOString()});

View File

@@ -1,10 +0,0 @@
---
layout: example.html
title: Worker
shortdesc: This example should be deleted.
docs: >
When you move the map, a message is sent to a worker. In response, the woker sends a
message back with the version identifier.
tags: "worker"
---
<div id="map" class="map"></div>

View File

@@ -1,35 +0,0 @@
/* eslint-disable no-console */
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';
import {create as createVersionWorker} from '../src/ol/worker/version';
const map = new Map({
layers: [
new TileLayer({
source: new OSM()
})
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2
})
});
const worker = createVersionWorker();
worker.addEventListener('error', function(error) {
console.error('worker error', error);
});
worker.addEventListener('message', function(event) {
console.log('message from worker:', event.data);
});
map.on('moveend', function(event) {
const state = event.frameState.viewState;
worker.postMessage({zoom: state.zoom, center: state.center});
});

View File

@@ -11,6 +11,5 @@ tags: "zoomify, deep zoom, IIP, pixel, projection"
<select id="zoomifyProtocol">
<option value="zoomify">Zoomify</option>
<option value="iip">IIP</option>
<option value="zoomifyretina">Zoomify Retina</option>
</select>
</div>

View File

@@ -12,8 +12,6 @@ const iipUrl = 'http://vips.vtech.fr/cgi-bin/iipsrv.fcgi?FIF=' + '/mnt/MD1/AD00/
const layer = new TileLayer({
source: new Zoomify({
tileSize: 256,
tilePixelRatio: 1,
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
@@ -22,15 +20,12 @@ const layer = new TileLayer({
const extent = [0, -imgHeight, imgWidth, 0];
const resolutions = layer.getSource().getTileGrid().getResolutions();
const map = new Map({
layers: [layer],
target: 'map',
view: new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
resolutions: layer.getSource().getTileGrid().getResolutions(),
// constrain the center: center cannot be set outside this extent
extent: extent
})
@@ -41,73 +36,17 @@ const control = document.getElementById('zoomifyProtocol');
control.addEventListener('change', function(event) {
const value = event.currentTarget.value;
if (value === 'iip') {
const extent = [0, -imgHeight, imgWidth, 0];
layer.setSource(
new Zoomify({
tileSize: 256,
tilePixelRatio: 1,
url: iipUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
layer.setSource(new Zoomify({
url: iipUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
}));
} else if (value === 'zoomify') {
const extent = [0, -imgHeight, imgWidth, 0];
layer.setSource(
new Zoomify({
tileSize: 256,
tilePixelRatio: 1,
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1],
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
} else if (value === 'zoomifyretina') {
const pixelRatio = 4;
// Be careful! Image extent will be modified by pixel ratio
const extent = [0, -imgHeight / pixelRatio, imgWidth / pixelRatio, 0];
layer.setSource(
new Zoomify({
tileSize: 256 / pixelRatio,
tilePixelRatio: pixelRatio,
url: zoomifyUrl,
size: [imgWidth / pixelRatio, imgHeight / pixelRatio],
crossOrigin: 'anonymous'
})
);
const resolutions = layer.getSource().getTileGrid().getResolutions();
map.setView(
new View({
// adjust zoom levels to those provided by the source
minResolution: resolutions[resolutions.length - 1] / pixelRatio,
maxResolution: resolutions[0],
// constrain the center: center cannot be set outside this extent
extent: extent
})
);
map.getView().fit(extent);
layer.setSource(new Zoomify({
url: zoomifyUrl,
size: [imgWidth, imgHeight],
crossOrigin: 'anonymous'
}));
}
});

View File

@@ -1,11 +0,0 @@
module.exports = {
plugins: [
'gatsby-plugin-emotion',
{
resolve: 'gatsby-plugin-typography',
options: {
pathToConfigModule: 'site/util/typography'
}
}
]
};

View File

@@ -1,23 +0,0 @@
function getDocs() {
// TODO: build if not present
const info = require('./build/api-info.json');
return info.docs.filter(doc => !doc.ignore && (doc.api || doc.kind === 'module' || doc.kind === 'typedef'));
}
function createPages({actions: {createPage}}) {
createPage({
path: '/api',
component: require.resolve('./site/pages/API.js'),
context: {docs: getDocs()}
});
createPage({
path: '/info',
component: require.resolve('./site/pages/Info.js'),
context: {docs: getDocs()}
});
}
exports.createPages = createPages;

20668
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "ol",
"version": "6.0.0-beta.8",
"version": "6.0.0-beta.6",
"description": "OpenLayers mapping library",
"keywords": [
"map",
@@ -22,12 +22,9 @@
"build-index": "npm run build-package && node tasks/generate-index",
"build-legacy": "shx rm -rf build && npm run build-index && webpack --config config/webpack-config-legacy-build.js && cleancss --source-map src/ol/ol.css -o build/legacy/ol.css",
"copy-css": "shx cp src/ol/ol.css build/ol/ol.css",
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && node tasks/serialize-workers && tsc --project config/tsconfig-build.json",
"transpile": "shx rm -rf build/ol && shx mkdir -p build/ol && shx cp -rf src/ol build/ol/src && tsc --project config/tsconfig-build.json",
"typecheck": "tsc --pretty",
"apidoc": "jsdoc -R config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc",
"api-info": "jsdoc --configure config/jsdoc/api-info/conf.json --destination build/api-info.json",
"serve-site": "gatsby develop",
"build-site": "npm run api-info && gatsby build"
"apidoc": "jsdoc config/jsdoc/api/index.md -c config/jsdoc/api/conf.json -P package.json -d build/apidoc"
},
"main": "index.js",
"repository": {
@@ -41,83 +38,57 @@
"dependencies": {
"pbf": "3.2.0",
"pixelworks": "1.1.0",
"rbush": "^3.0.0"
"rbush": "2.0.2"
},
"devDependencies": {
"@babel/core": "^7.4.0",
"@babel/preset-env": "^7.4.4",
"@emotion/core": "^10.0.10",
"@emotion/styled": "^10.0.11",
"@openlayers/eslint-plugin": "^4.0.0-beta.2",
"@types/arcgis-rest-api": "^10.4.4",
"@types/geojson": "^7946.0.7",
"@types/pbf": "^3.0.1",
"@types/rbush": "^2.0.2",
"@types/topojson-specification": "^1.0.1",
"babel-loader": "^8.0.5",
"buble": "^0.19.7",
"buble-loader": "^0.5.1",
"chaikin-smooth": "^1.0.4",
"clean-css-cli": "4.3.0",
"copy-webpack-plugin": "^5.0.3",
"copy-webpack-plugin": "^5.0.2",
"coveralls": "3.0.3",
"eslint": "^5.16.0",
"eslint-config-openlayers": "^11.0.0",
"eslint-config-tschaub": "^13.1.0",
"eslint-plugin-react": "^7.13.0",
"expect.js": "0.3.1",
"front-matter": "^3.0.2",
"fs-extra": "^8.0.0",
"gatsby": "^2.4.3",
"gatsby-plugin-emotion": "^4.0.6",
"gatsby-plugin-typography": "^2.2.13",
"glob": "^7.1.4",
"front-matter": "^3.0.1",
"fs-extra": "^7.0.1",
"glob": "^7.1.2",
"globby": "^9.2.0",
"handlebars": "4.1.2",
"html-to-image": "^0.1.0",
"istanbul": "0.4.5",
"istanbul-instrumenter-loader": "^3.0.1",
"jquery": "3.4.1",
"jsdoc": "3.6.2",
"jsdoc-json": "^2.0.2",
"jsdoc-plugin-typescript": "^2.0.1",
"karma": "^4.1.0",
"jquery": "3.4.0",
"jsdoc": "3.5.5",
"jsdoc-plugin-typescript": "^1.0.7",
"karma": "^4.0.1",
"karma-chrome-launcher": "2.2.0",
"karma-coverage": "^1.1.2",
"karma-coverage-istanbul-reporter": "^2.0.5",
"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",
"loglevelnext": "^3.0.0",
"marked": "0.6.2",
"mocha": "6.1.4",
"ol-mapbox-style": "^5.0.0-beta.2",
"mocha": "6.1.3",
"ol-mapbox-style": "^4.3.1",
"pixelmatch": "^4.0.2",
"pngjs": "^3.4.0",
"proj4": "2.5.0",
"prop-types": "^15.7.2",
"puppeteer": "~1.16.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-markdown": "^4.0.8",
"react-syntax-highlighter": "^10.2.1",
"react-typography": "^0.16.19",
"rollup": "^1.12.0",
"rollup-plugin-babel": "^4.3.2",
"rollup-plugin-commonjs": "^10.0.0",
"rollup-plugin-node-resolve": "^5.0.0",
"rollup-plugin-terser": "^4.0.4",
"serve-static": "^1.14.0",
"puppeteer": "~1.14.0",
"serve-static": "^1.13.2",
"shx": "^0.3.2",
"sinon": "^7.3.2",
"sinon": "^7.3.1",
"terser-webpack-plugin": "^1.2.3",
"typescript": "^3.4.5",
"typography": "^0.16.19",
"typography-theme-alton": "^0.16.19",
"typescript": "^3.2.2",
"url-polyfill": "^1.1.5",
"walk": "^2.3.9",
"webpack": "4.31.0",
"webpack-cli": "^3.3.2",
"webpack": "4.30.0",
"webpack-cli": "^3.3.0",
"webpack-dev-middleware": "^3.6.2",
"webpack-dev-server": "^3.3.1",
"yargs": "^13.2.2"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -7,7 +7,6 @@ import Point from '../../../src/ol/geom/Point.js';
import Style from '../../../src/ol/style/Style.js';
import Text from '../../../src/ol/style/Text.js';
import CircleStyle from '../../../src/ol/style/Circle.js';
import Fill from '../../../src/ol/style/Fill.js';
import Stroke from '../../../src/ol/style/Stroke.js';
import LineString from '../../../src/ol/geom/LineString.js';
@@ -60,10 +59,9 @@ source1.addFeature(new Feature({
}));
layer1.setStyle(function(feature) {
return new Style({
zIndex: feature.get('zIndex'),
image: new CircleStyle({
radius: 15,
fill: new Fill({
stroke: new Stroke({
color: 'blue'
})
})
@@ -71,7 +69,7 @@ layer1.setStyle(function(feature) {
});
map.addLayer(layer1);
center = [center[0] + 500, center[1] + 700];
center = [center[0] + 500, center[1] + 500];
const feature2 = new Feature({
geometry: new Point(center),
text: 'center',
@@ -90,16 +88,15 @@ source2.addFeature(new Feature({
}));
layer2.setStyle(function(feature) {
return new Style({
zIndex: feature.get('zIndex'),
text: new Text({
text: feature.get('text'),
font: 'italic bold 18px Ubuntu'
font: '16px Ubuntu'
})
});
});
map.addLayer(layer2);
center = [center[0] + 500, center[1] + 300];
center = [center[0] + 500, center[1] + 500];
source3.addFeature(new Feature({
geometry: new Point(center),
text: 'center'
@@ -115,17 +112,16 @@ source3.addFeature(new Feature({
layer3.setStyle(function(feature) {
return new Style({
image: new CircleStyle({
radius: 10,
radius: 5,
stroke: new Stroke({
color: 'red',
width: 8
color: 'red'
})
}),
text: new Text({
text: feature.get('text'),
font: 'italic bold 18px Ubuntu',
font: '16px Ubuntu',
textBaseline: 'bottom',
offsetY: -12
offsetY: -5
})
});
});
@@ -134,12 +130,11 @@ map.addLayer(layer3);
center = [center[0] - 2000, center[1] - 2000];
const point = new Feature(new Point(center));
point.setStyle(new Style({
zIndex: 1,
zIndex: 2,
image: new CircleStyle({
radius: 8,
stroke: new Stroke({
color: 'blue',
width: 4
color: 'blue'
})
})
}));
@@ -148,7 +143,7 @@ const line = new Feature(new LineString([
[center[0] + 650, center[1] - 200]
]));
line.setStyle(new Style({
zIndex: 2,
zIndex: 1,
stroke: new Stroke({
color: '#CCC',
width: 12
@@ -156,7 +151,7 @@ line.setStyle(new Style({
text: new Text({
placement: 'line',
text: 'east-west',
font: 'italic bold 18px Ubuntu',
font: '16px Ubuntu',
overflow: true
})
}));
@@ -164,4 +159,4 @@ source4.addFeature(point);
source4.addFeature(line);
map.addLayer(layer4);
render({tolerance: 0.007});
render({tolerance: 0.02});

View File

@@ -42,8 +42,7 @@ const layer = new VectorLayer({
const view = new View({
center: [-9.5, 78],
zoom: 2,
projection: 'EPSG:4326',
multiWorld: true
projection: 'EPSG:4326'
});
new Map({
pixelRatio: 1,

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

@@ -1,154 +1,55 @@
import Feature from '../../../src/ol/Feature.js';
import Map from '../../../src/ol/Map.js';
import View from '../../../src/ol/View.js';
import VectorSource from '../../../src/ol/source/Vector.js';
import VectorImageLayer from '../../../src/ol/layer/VectorImage.js';
import Feature from '../../../src/ol/Feature.js';
import Point from '../../../src/ol/geom/Point.js';
import Style from '../../../src/ol/style/Style.js';
import Text from '../../../src/ol/style/Text.js';
import CircleStyle from '../../../src/ol/style/Circle.js';
import Fill from '../../../src/ol/style/Fill.js';
import Stroke from '../../../src/ol/style/Stroke.js';
import VectorImageLayer from '../../../src/ol/layer/VectorImage.js';
import CircleStyle from '../../../src/ol/style/Circle.js';
import Point from '../../../src/ol/geom/Point.js';
import LineString from '../../../src/ol/geom/LineString.js';
import Text from '../../../src/ol/style/Text.js';
let center = [1825927.7316762917, 6143091.089223046];
const map = new Map({
pixelRatio: 1,
target: 'map',
view: new View({
center: center,
zoom: 13
})
});
const center = [1825927.7316762917, 6143091.089223046];
const source1 = new VectorSource();
const layer1 = new VectorImageLayer({
declutter: true,
source: source1
});
const source2 = new VectorSource();
const layer2 = new VectorImageLayer({
declutter: true,
source: source2
});
const source3 = new VectorSource();
const layer3 = new VectorImageLayer({
declutter: true,
source: source3
});
const source4 = new VectorSource();
const layer4 = new VectorImageLayer({
declutter: true,
source: source4
});
const feature1 = new Feature({
geometry: new Point(center),
zIndex: 2
});
source1.addFeature(feature1);
source1.addFeature(new Feature({
geometry: new Point([center[0] - 540, center[1]]),
zIndex: 3
}));
source1.addFeature(new Feature({
geometry: new Point([center[0] + 540, center[1]]),
zIndex: 1
}));
layer1.setStyle(function(feature) {
return new Style({
zIndex: feature.get('zIndex'),
image: new CircleStyle({
radius: 15,
fill: new Fill({
color: 'blue'
const source = new VectorSource();
const vectorLayer1 = new VectorImageLayer({
source: source,
style: function(feature) {
return new Style({
image: new CircleStyle({
radius: 15,
stroke: new Stroke({
color: 'blue'
})
}),
text: new Text({
text: feature.get('text'),
font: '16px Ubuntu'
})
})
});
});
}
});
map.addLayer(layer1);
center = [center[0] + 500, center[1] + 700];
const feature2 = new Feature({
geometry: new Point(center),
text: 'center',
zIndex: 2
});
source2.addFeature(feature2);
source2.addFeature(new Feature({
geometry: new Point([center[0] - 540, center[1]]),
text: 'west',
zIndex: 3
}));
source2.addFeature(new Feature({
geometry: new Point([center[0] + 540, center[1]]),
text: 'east',
zIndex: 1
}));
layer2.setStyle(function(feature) {
return new Style({
zIndex: feature.get('zIndex'),
text: new Text({
text: feature.get('text'),
font: 'italic bold 18px Ubuntu'
})
});
});
map.addLayer(layer2);
center = [center[0] + 500, center[1] + 300];
source3.addFeature(new Feature({
const centerFeature = new Feature({
geometry: new Point(center),
text: 'center'
}));
source3.addFeature(new Feature({
});
source.addFeature(centerFeature);
source.addFeature(new Feature({
geometry: new Point([center[0] - 540, center[1]]),
text: 'west'
}));
source3.addFeature(new Feature({
source.addFeature(new Feature({
geometry: new Point([center[0] + 540, center[1]]),
text: 'east'
}));
layer3.setStyle(function(feature) {
return new Style({
image: new CircleStyle({
radius: 10,
stroke: new Stroke({
color: 'red',
width: 8
})
}),
text: new Text({
text: feature.get('text'),
font: 'italic bold 18px Ubuntu',
textBaseline: 'bottom',
offsetY: -12
})
});
});
map.addLayer(layer3);
center = [center[0] - 2000, center[1] - 2000];
const point = new Feature(new Point(center));
point.setStyle(new Style({
zIndex: 1,
image: new CircleStyle({
radius: 8,
stroke: new Stroke({
color: 'blue',
width: 4
})
})
}));
const line = new Feature(new LineString([
[center[0] - 650, center[1] - 200],
[center[0] + 650, center[1] - 200]
]));
line.setStyle(new Style({
zIndex: 2,
stroke: new Stroke({
color: '#CCC',
width: 12
@@ -156,12 +57,23 @@ line.setStyle(new Style({
text: new Text({
placement: 'line',
text: 'east-west',
font: 'italic bold 18px Ubuntu',
overflow: true
font: '16px Ubuntu'
})
}));
source4.addFeature(point);
source4.addFeature(line);
map.addLayer(layer4);
source.addFeature(line);
render({tolerance: 0.007});
const map = new Map({
pixelRatio: 1,
layers: [
vectorLayer1
],
target: 'map',
view: new View({
center: center,
zoom: 13
})
});
map.getView().fit(source.getExtent());
render({tolerance: 0.02});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 KiB

After

Width:  |  Height:  |  Size: 101 KiB

View File

@@ -4,10 +4,8 @@ import {Vector as VectorLayer, Tile as TileLayer} from '../../../src/ol/layer.js
import {Vector as VectorSource, XYZ} from '../../../src/ol/source.js';
import GeoJSON from '../../../src/ol/format/GeoJSON.js';
import {Style, Stroke} from '../../../src/ol/style.js';
import Feature from '../../../src/ol/Feature.js';
import Point from '../../../src/ol/geom/Point.js';
const map = new Map({
new Map({
layers: [
new TileLayer({
source: new XYZ({
@@ -16,7 +14,6 @@ const map = new Map({
})
}),
new VectorLayer({
zIndex: 1,
style: new Style({
stroke: new Stroke({
color: 'rgba(255,255,255,0.5)',
@@ -36,11 +33,4 @@ const map = new Map({
})
});
const unmanaged = new VectorLayer({
source: new VectorSource({
features: [new Feature(new Point([0, 0]))]
})
});
unmanaged.setMap(map);
render();

View File

@@ -1,3 +0,0 @@
{
"extends": "tschaub/react"
}

View File

@@ -1,35 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import Markdown from 'react-markdown';
import Code from './Code';
import Parameter from './Parameter';
function Class({cls, module, helper}) {
const exportedName = module.getExportedName(cls.name);
let importCode;
if (exportedName === 'default') {
importCode = `import ${cls.name} from '${module.id}';`;
} else {
importCode = `import {${exportedName}} from '${module.id}';`;
}
return (
<div>
<h3>{cls.name}</h3>
<Code value={importCode} />
<Markdown source={cls.doc.classdesc} renderers={{code: Code}} />
<h6>Parameters</h6>
<ul>
{cls.doc.params && cls.doc.params.map(param => <Parameter param={param} module={module} helper={helper} />)}
</ul>
</div>
);
}
Class.propTypes = {
cls: object.isRequired,
module: object.isRequired,
helper: object.isRequired
};
export default Class;

View File

@@ -1,26 +0,0 @@
import React, {PureComponent} from 'react';
import {string} from 'prop-types';
import {Prism as SyntaxHighlighter} from 'react-syntax-highlighter';
import {coy} from 'react-syntax-highlighter/dist/styles/prism';
class Code extends PureComponent {
render() {
let language = this.props.language;
if (!language) {
language = 'js';
}
return (
<SyntaxHighlighter language={language} style={coy}>
{this.props.value}
</SyntaxHighlighter>
);
}
}
Code.propTypes = {
value: string.isRequired,
language: string
};
export default Code;

View File

@@ -1,35 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import Markdown from 'react-markdown';
import Code from './Code';
import Parameter from './Parameter';
function Func({func, module, helper}) {
const exportedName = module.getExportedName(func.name);
let importCode;
if (exportedName === 'default') {
importCode = `import ${func.name} from '${module.id}';`;
} else {
importCode = `import {${exportedName}} from '${module.id}';`;
}
return (
<div>
<h3>{func.name}</h3>
<Code value={importCode} />
<Markdown source={func.doc.description} renderers={{code: Code}} />
<h6>Parameters</h6>
<ul>
{func.doc.params && func.doc.params.map(param => <Parameter param={param} module={module} helper={helper} />)}
</ul>
</div>
);
}
Func.propTypes = {
func: object.isRequired,
module: object.isRequired,
helper: object.isRequired
};
export default Func;

View File

@@ -1,26 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import Class from './Class';
import Func from './Func';
function Module({module, helper}) {
return (
<div>
<hr />
<h2>{module.id}</h2>
{module.classes.map(cls => (
<Class key={cls.name} cls={cls} module={module} helper={helper} />
))}
{module.functions.map(func => (
<Func key={func.name} func={func} module={module} helper={helper} />
))}
</div>
);
}
Module.propTypes = {
module: object.isRequired,
helper: object.isRequired
};
export default Module;

View File

@@ -1,20 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import Type from './Type';
function Parameter({param, module, helper}) {
return (
<li>
<code>{param.name}</code> - {param.description} {param.optional && <span>(optional)</span>}<br/>
{param.type.names.map(longName => <Type longName={longName} module={module} helper={helper} />)}
</li>
);
}
Parameter.propTypes = {
param: object.isRequired,
module: object.isRequired,
helper: object.isRequired
};
export default Parameter;

View File

@@ -1,27 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import Parameter from './Parameter';
function Type({longName, module, helper}) {
const type = helper.getTypeDef(longName);
if (!type) {
return <code>{longName}</code>;
}
return (
<div>
<code>{type.doc.type.names}</code>
<ul>
{type.doc.properties && type.doc.properties.map(prop => <Parameter param={prop} module={module} helper={helper} />)}
</ul>
</div>
);
}
Type.propTypes = {
longName: object.isRequired,
module: object.isRequired,
helper: object.isRequired
};
export default Type;

View File

@@ -1,8 +0,0 @@
import styled from '@emotion/styled';
import {baseSpacingPx} from '../util/typography';
export const Page = styled.div({
display: 'flex',
flexDirection: 'column',
margin: 2 * baseSpacingPx
});

View File

@@ -1,26 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import {Page} from '../components/layout';
import Module from '../components/Module';
import {getHelper} from '../util/api';
function API({pageContext: {docs}}) {
const helper = getHelper(docs);
return (
<Page>
<h1>API</h1>
{helper.modules
.filter(module => module.visible)
.map(module => (
<Module key={module.id} module={module} helper={helper} />
))}
</Page>
);
}
API.propTypes = {
pageContext: object.isRequired
};
export default API;

View File

@@ -1,33 +0,0 @@
import {object} from 'prop-types';
import React from 'react';
import {Page} from '../components/layout';
function Info({pageContext: {docs}}) {
return (
<Page>
<h1>API</h1>
<table>
<tbody>
<tr>
<th>kind</th>
<th>longname</th>
<th>memberof</th>
</tr>
{docs.map(doc => (
<tr key={doc.longname}>
<td>{doc.kind}</td>
<td>{doc.longname}</td>
<td>{doc.memberof}</td>
</tr>
))}
</tbody>
</table>
</Page>
);
}
Info.propTypes = {
pageContext: object.isRequired
};
export default Info;

View File

@@ -1,205 +0,0 @@
class FunctionDoc {
constructor(doc) {
this.name = doc.name;
this.doc = doc;
}
}
class TypedefDoc {
constructor(doc) {
this.name = doc.name;
this.doc = doc;
}
}
class ClassDoc {
constructor(name) {
this.name = name;
}
processDoc(doc) {
if (doc.kind === 'class') {
this.doc = doc;
}
}
}
class ModuleDoc {
constructor(id) {
this.id = id;
this.classLookup = {};
this.classes = [];
this.functionLookup = {};
this.functions = [];
}
processDoc(doc) {
if (doc.kind === 'module') {
this.doc = doc;
//console.log('processing module: ' + doc.longname)
return;
}
if (doc.kind === 'class') {
const name = nameFromLongname(doc.longname);
if (!(name in this.classLookup)) {
const cls = new ClassDoc(name);
this.classLookup[name] = cls;
this.classes.push(cls);
}
this.classLookup[name].processDoc(doc);
return;
}
if (doc.kind === 'function') {
if (nameFromLongname(doc.memberof)) {
// belongs to a class or other
return;
}
if (doc.name in this.functionLookup) {
throw new Error(`Duplicate function ${doc.name} in ${this.id}`);
}
const func = new FunctionDoc(doc);
this.functionLookup[doc.name] = func;
this.functions.push(func);
return;
}
}
finalize() {
this.classes.sort(byName);
this.functions.sort(byName);
this.visible = this.classes.length > 0 || this.functions.length > 0;
}
getExportedName(localName) {
if (!this.doc || !this.doc.exportMap) {
throw new Error(`Expected to find export map in module doc: ${this.id}`);
}
if (!(localName in this.doc.exportMap)) {
throw new Error(
`No local name "${localName}" in export map for module: ${this.id}`
);
}
return this.doc.exportMap[localName];
}
}
const longnameRE = /^module:(?<module>.*?)([~\.](?<name>\w+)(#(?<member>\w+))?(:(?<type>\w+))?)?$/;
function moduleIDFromLongname(longname) {
const match = longname.match(longnameRE);
if (!match) {
throw new Error(`could not match module id in longname: ${longname}`);
}
return match.groups.module;
}
export function nameFromLongname(longname) {
const match = longname.match(longnameRE);
if (!match) {
throw new Error(`could not match name in longname: ${longname}`);
}
return match.groups.name;
}
function memberFromLongname(longname) {
const match = longname.match(longnameRE);
if (!match) {
throw new Error(`could not match member in longname: ${longname}`);
}
return match.groups.member;
}
function byName(a, b) {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
}
function byModuleId(a, b) {
const aParts = a.id.split('/');
const bParts = b.id.split('/');
const len = Math.max(aParts.length, bParts.length);
for (let i = 0; i < len; ++i) {
if (aParts[i] && bParts[i]) {
if (aParts[i] < bParts[i]) {
return -1;
}
if (aParts[i] > bParts[i]) {
return 1;
}
} else if (!aParts[i]) {
return -1;
} else {
return 1;
}
}
return 0;
}
class DocHelper {
constructor(docs) {
this.moduleLookup = {};
this.modules = [];
this.typedefLookup = {};
docs.forEach(doc => {
// typedef are indexed by long name
if (doc.kind === 'typedef') {
if (doc.name in this.typedefLookup) {
throw new Error(`Duplicate type definition ${doc.name} in ${this.id}`);
}
const type = new TypedefDoc(doc);
this.typedefLookup[doc.longname] = type;
return;
}
const moduleID = moduleIDFromLongname(doc.longname);
if (!(moduleID in this.moduleLookup)) {
const module = new ModuleDoc(moduleID);
this.moduleLookup[moduleID] = module;
this.modules.push(module);
}
const module = this.moduleLookup[moduleID];
module.processDoc(doc);
});
this.modules.sort(byModuleId);
this.modules.forEach(module => module.finalize());
}
getTypeDef(longName) {
this.typedefLookup[longName] && console.log(this.typedefLookup[longName]);
return this.typedefLookup[longName];
}
}
let cachedDocs;
let cachedHelper;
export function getHelper(docs) {
if (docs !== cachedDocs) {
if (cachedDocs) {
console.warn('creating new doc helper'); // eslint-disable-line
}
cachedHelper = new DocHelper(docs);
cachedDocs = docs;
}
return cachedHelper;
}

View File

@@ -1,9 +0,0 @@
import Typography from 'typography';
import theme from 'typography-theme-alton';
const typography = new Typography(theme);
export const baseSpacingPx = parseInt(theme.baseFontSize, 10);
export {theme};
export default typography;

View File

@@ -6,6 +6,7 @@ import {listen} from './events.js';
import Event from './events/Event.js';
import EventType from './events/EventType.js';
import {circular as circularPolygon} from './geom/Polygon.js';
import {GEOLOCATION} from './has.js';
import {toRadians} from './math.js';
import {get as getProjection, getTransformFromProjections, identityTransform} from './proj.js';
@@ -82,7 +83,7 @@ class GeolocationError extends Event {
* window.console.log(geolocation.getPosition());
* });
*
* @fires module:ol/events/Event~Event#event:error
* @fires error
* @api
*/
class Geolocation extends BaseObject {
@@ -159,7 +160,7 @@ class Geolocation extends BaseObject {
* @private
*/
handleTrackingChanged_() {
if ('geolocation' in navigator) {
if (GEOLOCATION) {
const tracking = this.getTracking();
if (tracking && this.watchId_ === undefined) {
this.watchId_ = navigator.geolocation.watchPosition(
@@ -203,6 +204,12 @@ class Geolocation extends BaseObject {
this.changed();
}
/**
* Triggered when the Geolocation returns an error.
* @event error
* @api
*/
/**
* @private
* @param {PositionError} error error object.

View File

@@ -58,9 +58,9 @@ class ImageWrapper extends ImageBase {
/**
* @private
* @type {function():void}
* @type {Array<import("./events.js").EventsKey>}
*/
this.unlisten_ = null;
this.imageListenerKeys_ = null;
/**
* @protected
@@ -120,12 +120,13 @@ class ImageWrapper extends ImageBase {
if (this.state == ImageState.IDLE || this.state == ImageState.ERROR) {
this.state = ImageState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.imageLoadFunction_(this, this.src_);
this.unlisten_ = listenImage(
this.image_,
this.handleImageLoad_.bind(this),
this.handleImageError_.bind(this)
);
}
}
@@ -142,47 +143,10 @@ class ImageWrapper extends ImageBase {
* @private
*/
unlistenImage_() {
if (this.unlisten_) {
this.unlisten_();
this.unlisten_ = null;
}
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
}
}
/**
* @param {HTMLCanvasElement|HTMLImageElement|HTMLVideoElement} image Image element.
* @param {function():any} loadHandler Load callback function.
* @param {function():any} errorHandler Error callback function.
* @return {function():void} Callback to stop listening.
*/
export function listenImage(image, loadHandler, errorHandler) {
const img = /** @type {HTMLImageElement} */ (image);
if (img.decode) {
const promise = img.decode();
let listening = true;
const unlisten = function() {
listening = false;
};
promise.then(function() {
if (listening) {
loadHandler();
}
}).catch(function(error) {
if (listening) {
errorHandler();
}
});
return unlisten;
}
const listenerKeys = [
listenOnce(img, EventType.LOAD, loadHandler),
listenOnce(img, EventType.ERROR, errorHandler)
];
return function unlisten() {
listenerKeys.forEach(unlistenByKey);
};
}
export default ImageWrapper;

View File

@@ -4,7 +4,8 @@
import Tile from './Tile.js';
import TileState from './TileState.js';
import {createCanvasContext2D} from './dom.js';
import {listenImage} from './Image.js';
import {listenOnce, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
class ImageTile extends Tile {
@@ -46,9 +47,9 @@ class ImageTile extends Tile {
/**
* @private
* @type {function():void}
* @type {Array<import("./events.js").EventsKey>}
*/
this.unlisten_ = null;
this.imageListenerKeys_ = null;
/**
* @private
@@ -133,12 +134,13 @@ class ImageTile extends Tile {
if (this.state == TileState.IDLE) {
this.state = TileState.LOADING;
this.changed();
this.imageListenerKeys_ = [
listenOnce(this.image_, EventType.ERROR,
this.handleImageError_, this),
listenOnce(this.image_, EventType.LOAD,
this.handleImageLoad_, this)
];
this.tileLoadFunction_(this, this.src_);
this.unlisten_ = listenImage(
this.image_,
this.handleImageLoad_.bind(this),
this.handleImageError_.bind(this)
);
}
}
@@ -148,10 +150,8 @@ class ImageTile extends Tile {
* @private
*/
unlistenImage_() {
if (this.unlisten_) {
this.unlisten_();
this.unlisten_ = null;
}
this.imageListenerKeys_.forEach(unlistenByKey);
this.imageListenerKeys_ = null;
}
}

View File

@@ -2,7 +2,7 @@
* @module ol/Map
*/
import PluggableMap from './PluggableMap.js';
import {defaults as defaultControls} from './control.js';
import {defaults as defaultControls} from './control/util.js';
import {defaults as defaultInteractions} from './interaction.js';
import {assign} from './obj.js';
import CompositeMapRenderer from './renderer/Composite.js';

View File

@@ -39,7 +39,6 @@ import {create as createTransform, apply as applyTransform} from './transform.js
* @property {boolean} animate
* @property {import("./transform.js").Transform} coordinateToPixelTransform
* @property {null|import("./extent.js").Extent} extent
* @property {Array<*>} declutterItems
* @property {import("./coordinate.js").Coordinate} focus
* @property {number} index
* @property {Array<import("./layer/Layer.js").State>} layerStatesArray
@@ -293,11 +292,6 @@ class PluggableMap extends BaseObject {
*/
this.interactions = optionsInternal.interactions || new Collection();
/**
* @type {import("./events.js").EventsKey}
*/
this.labelCacheListenerKey_;
/**
* @type {Collection<import("./Overlay.js").default>}
* @private
@@ -503,24 +497,6 @@ class PluggableMap extends BaseObject {
overlay.setMap(this);
}
/**
* Attach a label cache for listening to font changes.
* @param {import("./events/Target.js").default} labelCache Label cache.
*/
attachLabelCache(labelCache) {
this.detachLabelCache();
this.labelCacheListenerKey_ = listen(labelCache, EventType.CLEAR, this.redrawText.bind(this));
}
/**
* Detach the label cache, i.e. no longer listen to font changes.
*/
detachLabelCache() {
if (this.labelCacheListenerKey_) {
unlistenByKey(this.labelCacheListenerKey_);
}
}
/**
*
* @inheritDoc
@@ -538,7 +514,6 @@ class PluggableMap extends BaseObject {
cancelAnimationFrame(this.animationDelayKey_);
this.animationDelayKey_ = undefined;
}
this.detachLabelCache();
this.setTarget(null);
super.disposeInternal();
}
@@ -787,21 +762,6 @@ class PluggableMap extends BaseObject {
return layers;
}
/**
* @return {boolean} Layers have sources that are still loading.
*/
getLoading() {
const layerStatesArray = this.getLayerGroup().getLayerStatesArray();
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layer = layerStatesArray[i].layer;
const source = /** @type {import("./layer/Layer.js").default} */ (layer).getSource();
if (source && source.loading) {
return true;
}
}
return false;
}
/**
* Get the pixel for a coordinate. This takes a coordinate in the map view
* projection and returns the corresponding pixel.
@@ -989,7 +949,7 @@ class PluggableMap extends BaseObject {
}
if (frameState && this.hasListener(RenderEventType.RENDERCOMPLETE) && !frameState.animate &&
!this.tileQueue_.getTilesLoading() && !this.getLoading()) {
!this.tileQueue_.getTilesLoading() && !getLoading(this.getLayers().getArray())) {
this.renderer_.dispatchRenderEvent(RenderEventType.RENDERCOMPLETE, frameState);
}
@@ -1033,6 +993,7 @@ class PluggableMap extends BaseObject {
}
if (!targetElement) {
this.renderer_.removeLayerRenderers();
removeNode(this.viewport_);
if (this.handleResize_ !== undefined) {
removeEventListener(EventType.RESIZE, this.handleResize_, false);
@@ -1140,19 +1101,6 @@ class PluggableMap extends BaseObject {
this.animationDelay_();
}
/**
* Redraws all text after new fonts have loaded
*/
redrawText() {
const layerStates = this.getLayerGroup().getLayerStatesArray();
for (let i = 0, ii = layerStates.length; i < ii; ++i) {
const layer = layerStates[i].layer;
if (layer.hasRenderer()) {
layer.getRenderer().handleFontsChanged();
}
}
}
/**
* Request a map rendering (at the next animation frame).
* @api
@@ -1227,7 +1175,6 @@ class PluggableMap extends BaseObject {
frameState = /** @type {FrameState} */ ({
animate: false,
coordinateToPixelTransform: this.coordinateToPixelTransform_,
declutterItems: previousFrameState ? previousFrameState.declutterItems : [],
extent: extent,
focus: this.focus_ ? this.focus_ : viewState.center,
index: this.frameIndex_++,
@@ -1449,3 +1396,23 @@ function createOptionsInternal(options) {
}
export default PluggableMap;
/**
* @param {Array<import("./layer/Base.js").default>} layers Layers.
* @return {boolean} Layers have sources that are still loading.
*/
function getLoading(layers) {
for (let i = 0, ii = layers.length; i < ii; ++i) {
const layer = layers[i];
if (typeof /** @type {?} */ (layer).getLayers === 'function') {
return getLoading(/** @type {LayerGroup} */ (layer).getLayers().getArray());
} else {
const source = /** @type {import("./layer/Layer.js").default} */ (
layer).getSource();
if (source && source.loading) {
return true;
}
}
}
return false;
}

View File

@@ -191,11 +191,6 @@ const DEFAULT_MIN_ZOOM = 0;
* This is the object to act upon to change the center, resolution,
* and rotation of the map.
*
* A View has a `projection`. The projection determines the
* coordinate system of the center, and its units determine the units of the
* resolution (projection units per pixel). The default projection is
* Spherical Mercator (EPSG:3857).
*
* ### The view states
*
* An View is determined by three states: `center`, `resolution`,
@@ -207,6 +202,11 @@ const DEFAULT_MIN_ZOOM = 0;
* methods are available, as well as `getResolutionForZoom` and
* `getZoomForResolution` to switch from one system to the other.
*
* A View has a `projection`. The projection determines the
* coordinate system of the center, and its units determine the units of the
* resolution (projection units per pixel). The default projection is
* Spherical Mercator (EPSG:3857).
*
* ### The constraints
*
* `setCenter`, `setResolution` and `setRotation` can be used to change the
@@ -218,7 +218,7 @@ const DEFAULT_MIN_ZOOM = 0;
*
* The *resolution constraint* typically restricts min/max values and
* snaps to specific resolutions. It is determined by the following
* options: `resolutions`, `maxResolution`, `maxZoom` and `zoomFactor`.
* options: `resolutions`, `maxResolution`, `maxZoom`, and `zoomFactor`.
* If `resolutions` is set, the other three options are ignored. See
* documentation for each option for more information. By default, the view
* only has a min/max restriction and allow intermediary zoom levels when
@@ -226,7 +226,7 @@ const DEFAULT_MIN_ZOOM = 0;
*
* The *rotation constraint* snaps to specific angles. It is determined
* by the following options: `enableRotation` and `constrainRotation`.
* By default rotation is allowed and its value is snapped to zero when approaching the
* By default the rotation value is snapped to zero when approaching the
* horizontal.
*
* The *center constraint* is determined by the `extent` option. By
@@ -1409,19 +1409,11 @@ function animationCallback(callback, returnValue) {
*/
export function createCenterConstraint(options) {
if (options.extent !== undefined) {
const smooth = options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true;
return createExtent(options.extent, options.constrainOnlyCenter, smooth);
return createExtent(options.extent, options.constrainOnlyCenter,
options.smoothExtentConstraint !== undefined ? options.smoothExtentConstraint : true);
} else {
return centerNone;
}
const projection = createProjection(options.projection, 'EPSG:3857');
if (options.multiWorld !== true && projection.isGlobal()) {
const extent = projection.getExtent().slice();
extent[0] = -Infinity;
extent[2] = Infinity;
return createExtent(extent, false, false);
}
return centerNone;
}

View File

@@ -1,10 +1,6 @@
/**
* @module ol/control
*/
import Collection from './Collection.js';
import Attribution from './control/Attribution.js';
import Rotate from './control/Rotate.js';
import Zoom from './control/Zoom.js';
export {default as Attribution} from './control/Attribution.js';
export {default as Control} from './control/Control.js';
@@ -16,59 +12,4 @@ export {default as ScaleLine} from './control/ScaleLine.js';
export {default as Zoom} from './control/Zoom.js';
export {default as ZoomSlider} from './control/ZoomSlider.js';
export {default as ZoomToExtent} from './control/ZoomToExtent.js';
/**
* @typedef {Object} DefaultsOptions
* @property {boolean} [attribution=true] Include
* {@link module:ol/control/Attribution~Attribution}.
* @property {import("./control/Attribution.js").Options} [attributionOptions]
* Options for {@link module:ol/control/Attribution~Attribution}.
* @property {boolean} [rotate=true] Include
* {@link module:ol/control/Rotate~Rotate}.
* @property {import("./control/Rotate.js").Options} [rotateOptions] Options
* for {@link module:ol/control/Rotate~Rotate}.
* @property {boolean} [zoom] Include {@link module:ol/control/Zoom~Zoom}.
* @property {import("./control/Zoom.js").Options} [zoomOptions] Options for
* {@link module:ol/control/Zoom~Zoom}.
* @api
*/
/**
* Set of controls included in maps by default. Unless configured otherwise,
* this returns a collection containing an instance of each of the following
* controls:
* * {@link module:ol/control/Zoom~Zoom}
* * {@link module:ol/control/Rotate~Rotate}
* * {@link module:ol/control/Attribution~Attribution}
*
* @param {DefaultsOptions=} opt_options
* Defaults options.
* @return {Collection<import("./control/Control.js").default>}
* Controls.
* @api
*/
export function defaults(opt_options) {
const options = opt_options ? opt_options : {};
const controls = new Collection();
const zoomControl = options.zoom !== undefined ? options.zoom : true;
if (zoomControl) {
controls.push(new Zoom(options.zoomOptions));
}
const rotateControl = options.rotate !== undefined ? options.rotate : true;
if (rotateControl) {
controls.push(new Rotate(options.rotateOptions));
}
const attributionControl = options.attribution !== undefined ?
options.attribution : true;
if (attributionControl) {
controls.push(new Attribution(options.attributionOptions));
}
return controls;
}
export {defaults} from './control/util.js';

View File

@@ -1,8 +1,8 @@
/**
* @module ol/control/OverviewMap
*/
import PluggableMap from '../PluggableMap.js';
import CompositeMapRenderer from '../renderer/Composite.js';
import Collection from '../Collection.js';
import Map from '../Map.js';
import MapEventType from '../MapEventType.js';
import MapProperty from '../MapProperty.js';
import {getChangeEventType} from '../Object.js';
@@ -35,13 +35,6 @@ const MAX_RATIO = 0.75;
const MIN_RATIO = 0.1;
class ControlledMap extends PluggableMap {
createRenderer() {
return new CompositeMapRenderer(this);
}
}
/**
* @typedef {Object} Options
* @property {string} [className='ol-overviewmap'] CSS class name.
@@ -150,10 +143,12 @@ class OverviewMap extends Control {
this.ovmapDiv_.className = 'ol-overviewmap-map';
/**
* @type {ControlledMap}
* @type {import("../Map.js").default}
* @private
*/
this.ovmap_ = new ControlledMap({
this.ovmap_ = new Map({
controls: new Collection(),
interactions: new Collection(),
view: options.view
});
const ovmap = this.ovmap_;

65
src/ol/control/util.js Normal file
View File

@@ -0,0 +1,65 @@
/**
* @module ol/control/util
*/
import Collection from '../Collection.js';
import Attribution from './Attribution.js';
import Rotate from './Rotate.js';
import Zoom from './Zoom.js';
/**
* @typedef {Object} DefaultsOptions
* @property {boolean} [attribution=true] Include
* {@link module:ol/control/Attribution~Attribution}.
* @property {import("./Attribution.js").Options} [attributionOptions]
* Options for {@link module:ol/control/Attribution~Attribution}.
* @property {boolean} [rotate=true] Include
* {@link module:ol/control/Rotate~Rotate}.
* @property {import("./Rotate.js").Options} [rotateOptions] Options
* for {@link module:ol/control/Rotate~Rotate}.
* @property {boolean} [zoom] Include {@link module:ol/control/Zoom~Zoom}.
* @property {import("./Zoom.js").Options} [zoomOptions] Options for
* {@link module:ol/control/Zoom~Zoom}.
* @api
*/
/**
* Set of controls included in maps by default. Unless configured otherwise,
* this returns a collection containing an instance of each of the following
* controls:
* * {@link module:ol/control/Zoom~Zoom}
* * {@link module:ol/control/Rotate~Rotate}
* * {@link module:ol/control/Attribution~Attribution}
*
* @param {DefaultsOptions=} opt_options
* Defaults options.
* @return {Collection<import("./Control.js").default>}
* Controls.
* @function module:ol/control.defaults
* @api
*/
export function defaults(opt_options) {
const options = opt_options ? opt_options : {};
const controls = new Collection();
const zoomControl = options.zoom !== undefined ? options.zoom : true;
if (zoomControl) {
controls.push(new Zoom(options.zoomOptions));
}
const rotateControl = options.rotate !== undefined ? options.rotate : true;
if (rotateControl) {
controls.push(new Rotate(options.rotateOptions));
}
const attributionControl = options.attribution !== undefined ?
options.attribution : true;
if (attributionControl) {
controls.push(new Attribution(options.attributionOptions));
}
return controls;
}

View File

@@ -14,13 +14,6 @@ export default {
*/
CHANGE: 'change',
/**
* Generic error event. Triggered when an error occurs.
* @event module:ol/events/Event~Event#error
* @api
*/
ERROR: 'error',
CLEAR: 'clear',
CONTEXTMENU: 'contextmenu',
CLICK: 'click',
@@ -28,6 +21,7 @@ export default {
DRAGENTER: 'dragenter',
DRAGOVER: 'dragover',
DROP: 'drop',
ERROR: 'error',
KEYDOWN: 'keydown',
KEYPRESS: 'keypress',
LOAD: 'load',

View File

@@ -7,7 +7,6 @@ export {default as GeoJSON} from './format/GeoJSON.js';
export {default as GML} from './format/GML.js';
export {default as GPX} from './format/GPX.js';
export {default as IGC} from './format/IGC.js';
export {default as IIIFInfo} from './format/IIIFInfo.js';
export {default as KML} from './format/KML.js';
export {default as MVT} from './format/MVT.js';
export {default as OWS} from './format/OWS.js';

View File

@@ -1,431 +0,0 @@
/**
* @module ol/format/IIIFInfo
*/
import {assert} from '../asserts.js';
/**
* @typedef {Object} PreferredOptions
* @property {string} [format] Preferred image format. Will be used if the image information
* indicates support for that format.
* @property {string} [quality] IIIF image qualitiy. Will be used if the image information
* indicates support for that quality.
*/
/**
* @typedef {Object} SupportedFeatures
* @property {Array<string>} [supports] Supported IIIF image size and region
* calculation features.
* @property {Array<string>} [formats] Supported image formats.
* @property {Array<string>} [qualities] Supported IIIF image qualities.
*/
/**
* @typedef {Object} ImageInformationResponse1_0
* @property {string} identifier
* @property {number} width
* @property {number} height
* @property {Array<number>} [scale_factors] Resolution scaling factors.
* @property {number} [tile_width]
* @property {number} [tile_height]
* @property {Array<string>} [formats] Supported image formats.
* @property {string} [profile] Compliance level URI.
*/
/**
* @typedef {Object} ImageInformationResponse1_1
* @property {string} "@id" The base URI of the image service.
* @property {string} "@context" JSON-LD context URI.
* @property {number} width Full image width.
* @property {number} height Full image height.
* @property {Array<number>} [scale_factors] Resolution scaling factors.
* @property {number} [tile_width]
* @property {number} [tile_height]
* @property {Array<string>} [formats] Supported image formats.
* @property {string} [profile] Compliance level URI.
*/
/**
* @typedef {Object} TileInfo
* @property {Array<number>} scaleFactors Supported resolution scaling factors.
* @property {number} width Tile width in pixels.
* @property {number} [height] Tile height in pixels. Same as tile width if height is
* not given.
*/
/**
* @typedef {Object} IiifProfile
* @property {Array<string>} [formats] Supported image formats for the image service.
* @property {Array<string>} [qualities] Supported IIIF image qualities.
* @property {Array<string>} [supports] Supported features.
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
* @property {number} [maxHeight] Maximum height.
* @property {number} [maxWidth] Maximum width.
*/
/**
* @typedef {Object} ImageInformationResponse2
* @property {string} "@id" The base URI of the image service.
* @property {string} "@context" JSON-LD context IRI
* @property {number} width Full image width.
* @property {number} height Full image height.
* @property {Array<string|IiifProfile>} profile Additional informations about the image
* service's capabilities.
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
*/
/**
* @typedef {Object} ImageInformationResponse3
* @property {string} id The base URI of the image service.
* @property {string} "@context" JSON-LD context IRI
* @property {number} width Full image width.
* @property {number} height Full image height.
* @property {string} profile Compliance level, one of 'level0', 'level1' or 'level2'
* @property {Array<Object<string, number>>} [sizes] Supported full image dimensions.
* @property {Array<TileInfo>} [tiles] Supported tile sizes and resolution scaling factors.
* @property {number} [maxArea] Maximum area (pixels) available for this image service.
* @property {number} [maxHeight] Maximum height.
* @property {number} [maxWidth] Maximum width.
* @property {Array<string>} [extraQualities] IIIF image qualities supported by the
* image service additional to the ones indicated by the compliance level.
* @property {Array<string>} [extraFormats] Image formats supported by the
* image service additional to the ones indicated by the compliance level.
* @property {Array<string>} [extraFeatures] Additional supported features whose support
* is not indicated by the compliance level.
*/
/**
* Enum representing the major IIIF Image API versions
* @enum {string}
*/
const Versions = {
VERSION1: 'version1',
VERSION2: 'version2',
VERSION3: 'version3'
};
/**
* Supported image formats, qualities and supported region / size calculation features
* for different image API versions and compliance levels
* @const
* @type {Object<string, Object<string, SupportedFeatures>>}
*/
const IIIF_PROFILE_VALUES = {};
IIIF_PROFILE_VALUES[Versions.VERSION1] = {
'level0': {
supports: [],
formats: [],
qualities: ['native']
},
'level1': {
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
formats: ['jpg'],
qualities: ['native']
},
'level2': {
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
'sizeByConfinedWh', 'sizeByWh'],
formats: ['jpg', 'png'],
qualities: ['native', 'color', 'grey', 'bitonal']
}
};
IIIF_PROFILE_VALUES[Versions.VERSION2] = {
'level0': {
supports: [],
formats: ['jpg'],
qualities: ['default']
},
'level1': {
supports: ['regionByPx', 'sizeByW', 'sizeByH', 'sizeByPct'],
formats: ['jpg'],
qualities: ['default']
},
'level2': {
supports: ['regionByPx', 'regionByPct', 'sizeByW', 'sizeByH', 'sizeByPct',
'sizeByConfinedWh', 'sizeByDistortedWh', 'sizeByWh'],
formats: ['jpg', 'png'],
qualities: ['default', 'bitonal']
}
};
IIIF_PROFILE_VALUES[Versions.VERSION3] = {
'level0': {
supports: [],
formats: ['jpg'],
qualities: ['default']
},
'level1': {
supports: ['regionByPx', 'regionSquare', 'sizeByW', 'sizeByH'],
formats: ['jpg'],
qualities: ['default']
},
'level2': {
supports: ['regionByPx', 'regionSquare', 'regionByPct',
'sizeByW', 'sizeByH', 'sizeByPct', 'sizeByConfinedWh', 'sizeByWh'],
formats: ['jpg'],
qualities: ['default', 'bitonal']
}
};
IIIF_PROFILE_VALUES['none'] = {
'none': {
supports: [],
formats: [],
qualities: []
}
};
const COMPLIANCE_VERSION1 = new RegExp('^https?\:\/\/library\.stanford\.edu\/iiif\/image-api\/(1\.1\/)?compliance\.html#level[0-2]$');
const COMPLIANCE_VERSION2 = new RegExp('^https?\:\/\/iiif\.io\/api\/image\/2\/level[0-2](\.json)?$');
const COMPLIANCE_VERSION3 = new RegExp('(^https?\:\/\/iiif\.io\/api\/image\/3\/level[0-2](\.json)?$)|(^level[0-2]$)');
function generateVersion1Options(iiifInfo) {
let levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
// Version 1.0 and 1.1 do not require a profile.
if (levelProfile === undefined) {
levelProfile = IIIF_PROFILE_VALUES[Versions.VERSION1]['level0'];
}
return {
url: iiifInfo.imageInfo['@id'] === undefined ? undefined : iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
supports: levelProfile.supports,
formats: [...levelProfile.formats, iiifInfo.imageInfo.formats === undefined ?
[] : iiifInfo.imageInfo.formats
],
qualities: [...levelProfile.qualities, iiifInfo.imageInfo.qualities === undefined ?
[] : iiifInfo.imageInfo.qualities
],
resolutions: iiifInfo.imageInfo.scale_factors,
tileSize: iiifInfo.imageInfo.tile_width !== undefined ? (iiifInfo.imageInfo.tile_height !== undefined ?
[iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_height] : [iiifInfo.imageInfo.tile_width, iiifInfo.imageInfo.tile_width]) :
(iiifInfo.imageInfo.tile_height != undefined ? [iiifInfo.imageInfo.tile_height, iiifInfo.imageInfo.tile_height] : undefined)
};
}
function generateVersion2Options(iiifInfo) {
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures(),
additionalProfile = Array.isArray(iiifInfo.imageInfo.profile) && iiifInfo.imageInfo.profile.length > 1,
profileSupports = additionalProfile && iiifInfo.imageInfo.profile[1].supports ? iiifInfo.imageInfo.profile[1].supports : [],
profileFormats = additionalProfile && iiifInfo.imageInfo.profile[1].formats ? iiifInfo.imageInfo.profile[1].formats : [],
profileQualities = additionalProfile && iiifInfo.imageInfo.profile[1].qualities ? iiifInfo.imageInfo.profile[1].qualities : [];
return {
url: iiifInfo.imageInfo['@id'].replace(/\/?(info.json)?$/g, ''),
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
return [size.width, size.height];
}),
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.width;
})[0],
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.height === undefined ? tile.width : tile.height;
})[0]
],
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.scaleFactors;
})[0],
supports: [...levelProfile.supports, ...profileSupports],
formats: [...levelProfile.formats, ...profileFormats],
qualities: [...levelProfile.qualities, ...profileQualities]
};
}
function generateVersion3Options(iiifInfo) {
const levelProfile = iiifInfo.getComplianceLevelSupportedFeatures();
return {
url: iiifInfo.imageInfo['id'],
sizes: iiifInfo.imageInfo.sizes === undefined ? undefined : iiifInfo.imageInfo.sizes.map(function(size) {
return [size.width, size.height];
}),
tileSize: iiifInfo.imageInfo.tiles === undefined ? undefined : [
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.width;
})[0],
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.height;
})[0]
],
resolutions: iiifInfo.imageInfo.tiles === undefined ? undefined :
iiifInfo.imageInfo.tiles.map(function(tile) {
return tile.scaleFactors;
})[0],
supports: iiifInfo.imageInfo.extraFeatures === undefined ? levelProfile.supports :
[...levelProfile.supports, ...iiifInfo.imageInfo.extraFeatures],
formats: iiifInfo.imageInfo.extraFormats === undefined ? levelProfile.formats :
[...levelProfile.formats, ...iiifInfo.imageInfo.extraFormats],
qualities: iiifInfo.imageInfo.extraQualities === undefined ? levelProfile.qualities :
[...levelProfile.supports, ...iiifInfo.imageInfo.extraQualities],
maxWidth: undefined,
maxHeight: undefined,
maxArea: undefined
};
}
const versionFunctions = {};
versionFunctions[Versions.VERSION1] = generateVersion1Options;
versionFunctions[Versions.VERSION2] = generateVersion2Options;
versionFunctions[Versions.VERSION3] = generateVersion3Options;
/**
* @classdesc
* Format for transforming IIIF Image API image information responses into
* IIIF tile source ready options
*
* @api
*/
class IIIFInfo {
/**
* @param {string|ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3} imageInfo
* Deserialized image information JSON response object or JSON response as string
*/
constructor(imageInfo) {
this.setImageInfo(imageInfo);
}
/**
* @param {string|ImageInformationResponse1_0|ImageInformationResponse1_1|ImageInformationResponse2|ImageInformationResponse3} imageInfo
* Deserialized image information JSON response object or JSON response as string
* @api
*/
setImageInfo(imageInfo) {
if (typeof imageInfo == 'string') {
this.imageInfo = JSON.parse(imageInfo);
} else {
this.imageInfo = imageInfo;
}
}
/**
* @returns {Versions} Major IIIF version.
* @api
*/
getImageApiVersion() {
if (this.imageInfo === undefined) {
return;
}
let context = this.imageInfo['@context'] || 'ol-no-context';
if (typeof context == 'string') {
context = [context];
}
for (let i = 0; i < context.length; i++) {
switch (context[i]) {
case 'http://library.stanford.edu/iiif/image-api/1.1/context.json':
case 'http://iiif.io/api/image/1/context.json':
return Versions.VERSION1;
case 'http://iiif.io/api/image/2/context.json':
return Versions.VERSION2;
case 'http://iiif.io/api/image/3/context.json':
return Versions.VERSION3;
case 'ol-no-context':
// Image API 1.0 has no '@context'
if (this.getComplianceLevelEntryFromProfile(Versions.VERSION1) && this.imageInfo.identifier) {
return Versions.VERSION1;
}
break;
default:
}
}
assert(false, 61);
}
/**
* @param {Versions} version Optional IIIF image API version
* @returns {string} Compliance level as it appears in the IIIF image information
* response.
*/
getComplianceLevelEntryFromProfile(version) {
if (this.imageInfo === undefined || this.imageInfo.profile === undefined) {
return;
}
if (version === undefined) {
version = this.getImageApiVersion();
}
switch (version) {
case Versions.VERSION1:
if (COMPLIANCE_VERSION1.test(this.imageInfo.profile)) {
return this.imageInfo.profile;
}
break;
case Versions.VERSION3:
if (COMPLIANCE_VERSION3.test(this.imageInfo.profile)) {
return this.imageInfo.profile;
}
break;
case Versions.VERSION2:
if (typeof this.imageInfo.profile === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile)) {
return this.imageInfo.profile;
}
if (Array.isArray(this.imageInfo.profile) && this.imageInfo.profile.length > 0
&& typeof this.imageInfo.profile[0] === 'string' && COMPLIANCE_VERSION2.test(this.imageInfo.profile[0])) {
return this.imageInfo.profile[0];
}
break;
default:
}
}
/**
* @param {Versions} version Optional IIIF image API version
* @returns {string} Compliance level, on of 'level0', 'level1' or 'level2' or undefined
*/
getComplianceLevelFromProfile(version) {
const complianceLevel = this.getComplianceLevelEntryFromProfile(version);
if (complianceLevel === undefined) {
return undefined;
}
const level = complianceLevel.match(/level[0-2](\.json)?$/g);
return Array.isArray(level) ? level[0].replace('.json', '') : undefined;
}
/**
* @returns {SupportedFeatures} Image formats, qualities and region / size calculation
* methods that are supported by the IIIF service.
*/
getComplianceLevelSupportedFeatures() {
if (this.imageInfo === undefined) {
return;
}
const version = this.getImageApiVersion();
const level = this.getComplianceLevelFromProfile(version);
if (level === undefined) {
return IIIF_PROFILE_VALUES['none']['none'];
}
return IIIF_PROFILE_VALUES[version][level];
}
/**
* @param {PreferredOptions} opt_preferredOptions Optional options for preferred format and quality.
* @returns {import("../source/IIIF.js").Options} IIIF tile source ready constructor options.
* @api
*/
getTileSourceOptions(opt_preferredOptions) {
const options = opt_preferredOptions || {},
version = this.getImageApiVersion();
if (version === undefined) {
return;
}
const imageOptions = version === undefined ? undefined : versionFunctions[version](this);
if (imageOptions === undefined) {
return;
}
return {
url: imageOptions.url,
version: version,
size: [this.imageInfo.width, this.imageInfo.height],
sizes: imageOptions.sizes,
format: imageOptions.formats.includes(options.format) ? options.format : 'jpg',
supports: imageOptions.supports,
quality: options.quality && imageOptions.qualities.includes(options.quality) ?
options.quality : imageOptions.qualities.includes('native') ? 'native' : 'default',
resolutions: Array.isArray(imageOptions.resolutions) ? imageOptions.resolutions.sort(function(a, b) {
return b - a;
}) : undefined,
tileSize: imageOptions.tileSize
};
}
}
export default IIIFInfo;
export {Versions};

View File

@@ -2684,6 +2684,13 @@ function writePlacemark(node, feature, objectStack) {
return !filter[v];
});
if (keys.length > 0) {
const sequence = makeSequence(properties, keys);
const namesAndValues = {names: keys, values: sequence};
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
EXTENDEDDATA_NODE_FACTORY, [namesAndValues], objectStack);
}
const styleFunction = feature.getStyleFunction();
if (styleFunction) {
// FIXME the styles returned by the style function are supposed to be
@@ -2706,13 +2713,6 @@ function writePlacemark(node, feature, objectStack) {
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
OBJECT_PROPERTY_NODE_FACTORY, values, objectStack, orderedKeys);
if (keys.length > 0) {
const sequence = makeSequence(properties, keys);
const namesAndValues = {names: keys, values: sequence};
pushSerializeAndPop(context, PLACEMARK_SERIALIZERS,
EXTENDEDDATA_NODE_FACTORY, [namesAndValues], objectStack);
}
// serialize geometry
const options = /** @type {import("./Feature.js").WriteOptions} */ (objectStack[0]);
let geometry = feature.getGeometry();

View File

@@ -16,8 +16,8 @@ import {lerp} from '../../math.js';
* @param {function(string, string, Object<string, number>):number} measureAndCacheTextWidth Measure and cache text width.
* @param {string} font The font.
* @param {Object<string, number>} cache A cache of measured widths.
* @return {Array<Array<*>>} The result array (or null if `maxAngle` was
* exceeded). Entries of the array are x, y, anchorX, angle, chunk.
* @return {Array<Array<*>>} The result array of null if `maxAngle` was
* exceeded. Entries of the array are x, y, anchorX, angle, chunk.
*/
export function drawTextOnPath(
flatCoordinates, offset, end, stride, text, startM, maxAngle, scale, measureAndCacheTextWidth, font, cache) {
@@ -35,13 +35,16 @@ export function drawTextOnPath(
let y2 = flatCoordinates[offset + 1];
let segmentM = 0;
let segmentLength = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
let angleChanged = false;
let index, previousAngle;
let chunk = '';
let chunkLength = 0;
let data, index, previousAngle;
for (let i = 0; i < numChars; ++i) {
index = reverse ? numChars - i - 1 : i;
const char = text[index];
const charLength = scale * measureAndCacheTextWidth(font, char, cache);
const char = text.charAt(index);
chunk = reverse ? char + chunk : chunk + char;
const charLength = scale * measureAndCacheTextWidth(font, chunk, cache) - chunkLength;
chunkLength += charLength;
const charM = startM + charLength / 2;
while (offset < end - stride && segmentM + segmentLength < charM) {
x1 = x2;
@@ -59,18 +62,33 @@ export function drawTextOnPath(
}
if (previousAngle !== undefined) {
let delta = angle - previousAngle;
angleChanged = angleChanged || delta !== 0;
delta += (delta > Math.PI) ? -2 * Math.PI : (delta < -Math.PI) ? 2 * Math.PI : 0;
if (Math.abs(delta) > maxAngle) {
return null;
}
}
previousAngle = angle;
const interpolate = segmentPos / segmentLength;
const x = lerp(x1, x2, interpolate);
const y = lerp(y1, y2, interpolate);
result[index] = [x, y, charLength / 2, angle, char];
if (previousAngle == angle) {
if (reverse) {
data[0] = x;
data[1] = y;
data[2] = charLength / 2;
}
data[4] = chunk;
} else {
chunk = char;
chunkLength = charLength;
data = [x, y, charLength / 2, angle, chunk];
if (reverse) {
result.unshift(data);
} else {
result.push(data);
}
previousAngle = angle;
}
startM += charLength;
}
return angleChanged ? result : [[result[0][0], result[0][1], result[0][2], result[0][3], text]];
return result;
}

View File

@@ -40,6 +40,15 @@ export const MAC = ua.indexOf('macintosh') !== -1;
export const DEVICE_PIXEL_RATIO = window.devicePixelRatio || 1;
/**
* Is HTML5 geolocation supported in the current browser?
* @const
* @type {boolean}
* @api
*/
export const GEOLOCATION = 'geolocation' in navigator;
/**
* True if browser supports touch events.
* @const

View File

@@ -73,10 +73,6 @@ class DragPan extends PointerInteraction {
* @inheritDoc
*/
handleDragEvent(mapBrowserEvent) {
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().beginInteraction();
}
const targetPointers = this.targetPointers;
const centroid = centroidFromPointers(targetPointers);
if (targetPointers.length == this.lastPointersCount_) {
@@ -153,6 +149,10 @@ class DragPan extends PointerInteraction {
if (view.getAnimating()) {
view.cancelAnimations();
}
if (!this.panning_) {
this.panning_ = true;
this.getMap().getView().beginInteraction();
}
if (this.kinetic_) {
this.kinetic_.begin();
}

View File

@@ -38,7 +38,7 @@ import {createEditingStyle} from '../style/Style.js';
const ExtentEventType = {
/**
* Triggered after the extent is changed
* @event ExtentEvent#extentchanged
* @event ExtentEventType#extentchanged
* @api
*/
EXTENTCHANGED: 'extentchanged'
@@ -47,10 +47,10 @@ const ExtentEventType = {
/**
* @classdesc
* Events emitted by {@link module:ol/interaction/Extent~Extent} instances are
* Events emitted by {@link module:ol/interaction/Extent~ExtentInteraction} instances are
* instances of this type.
*/
class ExtentEvent extends Event {
class ExtentInteractionEvent extends Event {
/**
* @param {import("../extent.js").Extent} extent the new extent
@@ -75,10 +75,10 @@ class ExtentEvent extends Event {
* Once drawn, the vector box can be modified by dragging its vertices or edges.
* This interaction is only supported for mouse devices.
*
* @fires ExtentEvent
* @fires Event
* @api
*/
class Extent extends PointerInteraction {
class ExtentInteraction extends PointerInteraction {
/**
* @param {Options=} opt_options Options.
*/
@@ -399,7 +399,7 @@ class Extent extends PointerInteraction {
//Null extent means no bbox
this.extent_ = extent ? extent : null;
this.createOrUpdateExtentFeature_(extent);
this.dispatchEvent(new ExtentEvent(this.extent_));
this.dispatchEvent(new ExtentInteractionEvent(this.extent_));
}
}
@@ -470,4 +470,4 @@ function getSegments(extent) {
}
export default Extent;
export default ExtentInteraction;

View File

@@ -83,20 +83,19 @@ class BaseLayer extends BaseObject {
}
/**
* @param {boolean=} opt_managed Layer is managed.
* @return {import("./Layer.js").State} Layer state.
*/
getLayerState(opt_managed) {
getLayerState() {
/** @type {import("./Layer.js").State} */
const state = this.state_ || /** @type {?} */ ({
layer: this,
managed: opt_managed === undefined ? true : opt_managed
managed: true
});
state.opacity = clamp(Math.round(this.getOpacity() * 100) / 100, 0, 1);
state.sourceState = this.getSourceState();
state.visible = this.getVisible();
state.extent = this.getExtent();
state.zIndex = this.getZIndex() || (state.managed === false ? Infinity : 0);
state.zIndex = this.getZIndex() || 0;
state.maxResolution = this.getMaxResolution();
state.minResolution = Math.max(this.getMinResolution(), 0);
this.state_ = state;

View File

@@ -8,7 +8,6 @@ import {assign} from '../obj.js';
/**
* @typedef {Object} Options
* @property {string} [className='ol-layer'] A CSS class name to set to the layer element.
* @property {number} [opacity=1] Opacity (0, 1).
* @property {boolean} [visible=true] Visibility.
* @property {import("../extent.js").Extent} [extent] The bounding extent for layer rendering. The layer will not be

View File

@@ -32,10 +32,8 @@ import {createDefaultStyle, toFunction as toStyleFunction} from '../style/Style.
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
* use {@link module:ol/Map#addLayer}.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority
* is defined by the z-index of the layer, the `zIndex` of the style and the render order of features.
* Higher z-index means higher priority. Within the same z-index, a feature rendered before another has
* higher priority.
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority.
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will

View File

@@ -10,10 +10,6 @@ import {assign} from '../obj.js';
import RenderEventType from '../render/EventType.js';
import SourceState from '../source/State.js';
/**
* @typedef {function(import("../PluggableMap.js").FrameState):HTMLElement} RenderFunction
*/
/**
* @typedef {Object} Options
@@ -33,8 +29,6 @@ import SourceState from '../source/State.js';
* the source can be set by calling {@link module:ol/layer/Layer#setSource layer.setSource(source)} after
* construction.
* @property {import("../PluggableMap.js").default} [map] Map.
* @property {RenderFunction} [render] Render function. Takes the frame state as input and is expected to return an
* HTML element. Will overwrite the default rendering for the layer.
*/
@@ -53,10 +47,8 @@ import SourceState from '../source/State.js';
/**
* @classdesc
* Base class from which all layer types are derived. This should only be instantiated
* in the case where a custom layer is be added to the map with a custom `render` function.
* Such a function can be specified in the `options` object, and is expected to return an HTML element.
*
* Abstract base class; normally only used for creating subclasses and not
* instantiated in apps.
* A visual representation of raster or vector map data.
* Layers group together those properties that pertain to how the data is to be
* displayed, irrespective of the source of that data.
@@ -72,7 +64,6 @@ import SourceState from '../source/State.js';
* @fires import("../render/Event.js").RenderEvent#postrender
*
* @template {import("../source/Source.js").default} SourceType
* @api
*/
class Layer extends BaseLayer {
/**
@@ -109,11 +100,6 @@ class Layer extends BaseLayer {
*/
this.renderer_ = null;
// Overwrite default render method with a custom one
if (options.render) {
this.render = options.render;
}
if (options.map) {
this.setMap(options.map);
}
@@ -226,7 +212,12 @@ class Layer extends BaseLayer {
if (map) {
this.mapPrecomposeKey_ = listen(map, RenderEventType.PRECOMPOSE, function(evt) {
const renderEvent = /** @type {import("../render/Event.js").default} */ (evt);
renderEvent.frameState.layerStatesArray.push(this.getLayerState(false));
const layerState = this.getLayerState();
layerState.managed = false;
if (this.getZIndex() === undefined) {
layerState.zIndex = Infinity;
}
renderEvent.frameState.layerStatesArray.push(layerState);
}, this);
this.mapRenderKey_ = listen(this, EventType.CHANGE, map.render, map);
this.changed();
@@ -254,13 +245,6 @@ class Layer extends BaseLayer {
return this.renderer_;
}
/**
* @return {boolean} The layer has a renderer.
*/
hasRenderer() {
return !!this.renderer_;
}
/**
* Create a renderer for this layer.
* @return {import("../renderer/Layer.js").default} A layer renderer.

View File

@@ -30,9 +30,9 @@ import CanvasVectorImageLayerRenderer from '../renderer/canvas/VectorImageLayer.
* 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
* use {@link module:ol/Map#addLayer}.
* @property {boolean} [declutter=false] Declutter images and text on this layer. The priority is defined
* by the `zIndex` of the style and the render order of features. Higher z-index means higher priority.
* Within the same z-index, a feature rendered before another has higher priority.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority.
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will

View File

@@ -46,10 +46,8 @@ import {assign} from '../obj.js';
* temporary layers. The standard way to add a layer to a map and have it managed by the map is to
* use {@link module:ol/Map#addLayer}.
* @property {boolean} [declutter=false] Declutter images and text. Decluttering is applied to all
* image and text styles of all Vector and VectorTile layers that have set this to `true`. The priority
* is defined by the z-index of the layer, the `zIndex` of the style and the render order of features.
* Higher z-index means higher priority. Within the same z-index, a feature rendered before another has
* higher priority.
* image and text styles, and the priority is defined by the z-index of the style. Lower z-index
* means higher priority.
* @property {import("../style/Style.js").StyleLike} [style] Layer style. See
* {@link module:ol/style} for default style which will be used if this is not defined.
* @property {boolean} [updateWhileAnimating=false] When set to `true`, feature batches will be

View File

@@ -1,6 +1,7 @@
/**
* @module ol/math
*/
import {assert} from './asserts.js';
/**
* Takes a number and clamps it to within the provided bounds.
@@ -42,6 +43,16 @@ export const cosh = (function() {
}());
/**
* @param {number} x X.
* @return {number} The smallest power of two greater than or equal to x.
*/
export function roundUpToPowerOfTwo(x) {
assert(0 < x, 29); // `x` must be greater than `0`
return Math.pow(2, Math.ceil(Math.log(x) / Math.LN2));
}
/**
* Returns the square of the closest distance between the point (x, y) and the
* line segment (x1, y1) to (x2, y2).

View File

@@ -127,6 +127,11 @@
right: .5em;
top: .5em;
}
@media print {
.ol-control {
display: none;
}
}
.ol-control button {
display: block;

View File

@@ -110,23 +110,3 @@ export function getRenderPixel(event, pixel) {
applyTransform(event.inversePixelTransform.slice(), result);
return result;
}
/**
* @param {import("./PluggableMap.js").FrameState} frameState Frame state.
* @param {?} declutterTree Declutter tree.
* @returns {?} Declutter tree.
*/
export function renderDeclutterItems(frameState, declutterTree) {
if (declutterTree) {
declutterTree.clear();
}
const items = frameState.declutterItems;
for (let z = items.length - 1; z >= 0; --z) {
const zIndexItems = items[z];
for (let i = 0, ii = zIndexItems.length; i < ii; i += 3) {
declutterTree = zIndexItems[i].renderDeclutter(zIndexItems[i + 1], zIndexItems[i + 2], declutterTree);
}
}
items.length = 0;
return declutterTree;
}

View File

@@ -21,7 +21,6 @@ import {
import {createCanvasContext2D} from '../../dom.js';
import {labelCache, defaultTextAlign, measureTextHeight, measureAndCacheTextWidth, measureTextWidths} from '../canvas.js';
import Disposable from '../../Disposable.js';
import RBush from 'rbush';
/**
@@ -59,10 +58,15 @@ class Executor extends Disposable {
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @param {boolean} overlaps The replay can have overlapping geometries.
* @param {?} declutterTree Declutter tree.
* @param {SerializableInstructions} instructions The serializable instructions
*/
constructor(resolution, pixelRatio, overlaps, instructions) {
constructor(resolution, pixelRatio, overlaps, declutterTree, instructions) {
super();
/**
* @type {?}
*/
this.declutterTree = declutterTree;
/**
* @protected
@@ -89,11 +93,6 @@ class Executor extends Disposable {
*/
this.alignFill_;
/**
* @type {Array<*>}
*/
this.declutterItems = [];
/**
* @protected
* @type {Array<*>}
@@ -193,10 +192,9 @@ class Executor extends Disposable {
const width = measureTextWidths(textState.font, lines, widths);
const lineHeight = measureTextHeight(textState.font);
const height = lineHeight * numLines;
const renderWidth = width + strokeWidth;
const renderWidth = (width + strokeWidth);
const context = createCanvasContext2D(
// make canvas 2 pixels wider to account for italic text width measurement errors
Math.ceil((renderWidth + 2) * scale),
Math.ceil(renderWidth * scale),
Math.ceil((height + strokeWidth) * scale));
label = context.canvas;
labelCache.set(key, label);
@@ -221,7 +219,7 @@ class Executor extends Disposable {
context.textBaseline = 'middle';
context.textAlign = 'center';
const leftRight = (0.5 - align);
const x = align * renderWidth + leftRight * strokeWidth;
const x = align * label.width / scale + leftRight * strokeWidth;
let i;
if (strokeKey) {
for (i = 0; i < numLines; ++i) {
@@ -414,10 +412,8 @@ class Executor extends Disposable {
/**
* @param {import("../canvas.js").DeclutterGroup} declutterGroup Declutter group.
* @param {import("../../Feature.js").FeatureLike} feature Feature.
* @param {?} declutterTree Declutter tree.
* @return {?} Declutter tree.
*/
renderDeclutter(declutterGroup, feature, declutterTree) {
renderDeclutter_(declutterGroup, feature) {
if (declutterGroup && declutterGroup.length > 5) {
const groupCount = declutterGroup[4];
if (groupCount == 1 || groupCount == declutterGroup.length - 5) {
@@ -429,11 +425,8 @@ class Executor extends Disposable {
maxY: /** @type {number} */ (declutterGroup[3]),
value: feature
};
if (!declutterTree) {
declutterTree = new RBush(9);
}
if (!declutterTree.collides(box)) {
declutterTree.insert(box);
if (!this.declutterTree.collides(box)) {
this.declutterTree.insert(box);
for (let j = 5, jj = declutterGroup.length; j < jj; ++j) {
const declutterData = /** @type {Array} */ (declutterGroup[j]);
if (declutterData) {
@@ -450,7 +443,6 @@ class Executor extends Disposable {
createOrUpdateEmpty(declutterGroup);
}
}
return declutterTree;
}
/**
@@ -472,9 +464,7 @@ class Executor extends Disposable {
const baseline = TEXT_ALIGN[textState.textBaseline || defaultTextBaseline];
const strokeWidth = strokeState && strokeState.lineWidth ? strokeState.lineWidth : 0;
// Remove the 2 pixels we added in getTextImage() for the anchor
const width = label.width / pixelRatio - 2 * textState.scale;
const anchorX = align * width + 2 * (0.5 - align) * strokeWidth;
const anchorX = align * label.width / pixelRatio + 2 * (0.5 - align) * strokeWidth;
const anchorY = baseline * label.height / pixelRatio + 2 * (0.5 - baseline) * strokeWidth;
return {
@@ -507,7 +497,6 @@ class Executor extends Disposable {
featureCallback,
opt_hitExtent
) {
this.declutterItems.length = 0;
/** @type {Array<number>} */
let pixelCoordinates;
if (this.pixelCoordinates_ && equals(transform, this.renderedTransform_)) {
@@ -681,7 +670,7 @@ class Executor extends Disposable {
backgroundFill ? /** @type {Array<*>} */ (lastFillInstruction) : null,
backgroundStroke ? /** @type {Array<*>} */ (lastStrokeInstruction) : null);
}
this.declutterItems.push(this, declutterGroup, feature);
this.renderDeclutter_(declutterGroup, feature);
++i;
break;
case CanvasInstruction.DRAW_CHARS:
@@ -750,7 +739,7 @@ class Executor extends Disposable {
}
}
}
this.declutterItems.push(this, declutterGroup, feature);
this.renderDeclutter_(declutterGroup, feature);
++i;
break;
case CanvasInstruction.END_GEOMETRY:

View File

@@ -35,12 +35,18 @@ class ExecutorGroup extends Disposable {
* @param {number} resolution Resolution.
* @param {number} pixelRatio Pixel ratio.
* @param {boolean} overlaps The executor group can have overlapping geometries.
* @param {?} declutterTree Declutter tree for declutter processing in postrender.
* @param {!Object<string, !Object<BuilderType, import("./Builder.js").SerializableInstructions>>} allInstructions
* The serializable instructions.
* @param {number=} opt_renderBuffer Optional rendering buffer.
*/
constructor(maxExtent, resolution, pixelRatio, overlaps, allInstructions, opt_renderBuffer) {
constructor(maxExtent, resolution, pixelRatio, overlaps, declutterTree, allInstructions, opt_renderBuffer) {
super();
/**
* Declutter tree.
* @private
*/
this.declutterTree_ = declutterTree;
/**
* @private
@@ -82,7 +88,7 @@ class ExecutorGroup extends Disposable {
* @private
* @type {CanvasRenderingContext2D}
*/
this.hitDetectionContext_ = null;
this.hitDetectionContext_ = createCanvasContext2D(1, 1);
/**
* @private
@@ -122,7 +128,7 @@ class ExecutorGroup extends Disposable {
for (const builderType in instructionByZindex) {
const instructions = instructionByZindex[builderType];
executors[builderType] = new Executor(
this.resolution_, this.pixelRatio_, this.overlaps_, instructions);
this.resolution_, this.pixelRatio_, this.overlaps_, this.declutterTree_, instructions);
}
}
}
@@ -137,11 +143,8 @@ class ExecutorGroup extends Disposable {
executors[key].disposeInternal();
}
}
if (this.hitDetectionContext_) {
const canvas = this.hitDetectionContext_.canvas;
canvas.width = canvas.height = 0;
}
const canvas = this.hitDetectionContext_.canvas;
canvas.width = canvas.height = 0;
super.disposeInternal();
}
@@ -169,7 +172,7 @@ class ExecutorGroup extends Disposable {
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {Object<string, boolean>} skippedFeaturesHash Ids of features to skip.
* @param {function(import("../../Feature.js").FeatureLike): T} callback Feature callback.
* @param {Array<import("../../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
* @param {Object<string, import("../canvas.js").DeclutterGroup>} declutterReplays Declutter replays.
* @return {T|undefined} Callback result.
* @template T
*/
@@ -180,7 +183,7 @@ class ExecutorGroup extends Disposable {
hitTolerance,
skippedFeaturesHash,
callback,
declutteredFeatures
declutterReplays
) {
hitTolerance = Math.round(hitTolerance);
@@ -190,10 +193,6 @@ class ExecutorGroup extends Disposable {
1 / resolution, -1 / resolution,
-rotation,
-coordinate[0], -coordinate[1]);
if (!this.hitDetectionContext_) {
this.hitDetectionContext_ = createCanvasContext2D(contextSize, contextSize);
}
const context = this.hitDetectionContext_;
if (context.canvas.width !== contextSize || context.canvas.height !== contextSize) {
@@ -214,6 +213,12 @@ class ExecutorGroup extends Disposable {
}
const mask = getCircleArray(hitTolerance);
let declutteredFeatures;
if (this.declutterTree_) {
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
return entry.value;
});
}
let builderType;
@@ -256,10 +261,20 @@ class ExecutorGroup extends Disposable {
builderType = ORDER[j];
executor = executors[builderType];
if (executor !== undefined) {
result = executor.executeHitDetection(context, transform, rotation,
skippedFeaturesHash, featureCallback, hitExtent);
if (result) {
return result;
if (declutterReplays &&
(builderType == BuilderType.IMAGE || builderType == BuilderType.TEXT)) {
const declutter = declutterReplays[zIndexKey];
if (!declutter) {
declutterReplays[zIndexKey] = [executor, transform.slice(0)];
} else {
declutter.push(executor, transform.slice(0));
}
} else {
result = executor.executeHitDetection(context, transform, rotation,
skippedFeaturesHash, featureCallback, hitExtent);
if (result) {
return result;
}
}
}
}
@@ -431,20 +446,14 @@ export function getCircleArray(radius) {
* @param {CanvasRenderingContext2D} context Context.
* @param {number} rotation Rotation.
* @param {boolean} snapToPixel Snap point symbols and text to integer pixels.
* @param {Array<Array<*>>} declutterItems Declutter items.
*/
export function replayDeclutter(declutterReplays, context, rotation, snapToPixel, declutterItems) {
export function replayDeclutter(declutterReplays, context, rotation, snapToPixel) {
const zs = Object.keys(declutterReplays).map(Number).sort(numberSafeCompareFunction);
const skippedFeatureUids = {};
for (let z = 0, zz = zs.length; z < zz; ++z) {
const executorData = declutterReplays[zs[z].toString()];
let currentExecutor;
for (let i = 0, ii = executorData.length; i < ii;) {
const executor = executorData[i++];
if (executor !== currentExecutor) {
currentExecutor = executor;
declutterItems.push(executor.declutterItems);
}
const transform = executorData[i++];
executor.execute(context, transform, rotation, skippedFeatureUids, snapToPixel);
}

View File

@@ -8,7 +8,6 @@ import RenderEventType from '../render/EventType.js';
import MapRenderer from './Map.js';
import SourceState from '../source/State.js';
import {replaceChildren} from '../dom.js';
import {labelCache} from '../render/canvas.js';
/**
@@ -23,7 +22,6 @@ class CompositeMapRenderer extends MapRenderer {
*/
constructor(map) {
super(map);
map.attachLabelCache(labelCache);
/**
* @private
@@ -87,8 +85,7 @@ class CompositeMapRenderer extends MapRenderer {
this.children_.length = 0;
for (let i = 0, ii = layerStatesArray.length; i < ii; ++i) {
const layerState = layerStatesArray[i];
if (!visibleAtResolution(layerState, viewResolution) ||
(layerState.sourceState != SourceState.READY && layerState.sourceState != SourceState.UNDEFINED)) {
if (!visibleAtResolution(layerState, viewResolution) || layerState.sourceState != SourceState.READY) {
continue;
}
@@ -97,12 +94,11 @@ class CompositeMapRenderer extends MapRenderer {
if (element) {
const zIndex = layerState.zIndex;
if (zIndex !== element.style.zIndex) {
element.style.zIndex = zIndex === Infinity ? Number.MAX_SAFE_INTEGER : zIndex;
element.style.zIndex = zIndex;
}
this.children_.push(element);
}
}
super.renderFrame(frameState);
replaceChildren(this.element_, this.children_);
@@ -113,6 +109,7 @@ class CompositeMapRenderer extends MapRenderer {
this.renderedVisible_ = true;
}
this.scheduleRemoveUnusedLayerRenderers(frameState);
this.scheduleExpireIconCache(frameState);
}
@@ -129,8 +126,11 @@ class CompositeMapRenderer extends MapRenderer {
for (let i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = layerState.layer;
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) {
const layerRenderer = layer.getRenderer();
if (visibleAtResolution(layerState, viewResolution) && layerFilter(layer)) {
const layerRenderer = this.getLayerRenderer(layer);
if (!layerRenderer) {
continue;
}
const data = layerRenderer.getDataAtPixel(pixel, frameState, hitTolerance);
if (data) {
const result = callback(layer, data);

View File

@@ -89,11 +89,10 @@ class LayerRenderer extends Observable {
* @param {import("../PluggableMap.js").FrameState} frameState Frame state.
* @param {number} hitTolerance Hit tolerance in pixels.
* @param {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default): T} callback Feature callback.
* @param {Array<import("../Feature.js").FeatureLike>} declutteredFeatures Decluttered features.
* @return {T|void} Callback result.
* @template T
*/
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback, declutteredFeatures) {}
forEachFeatureAtCoordinate(coordinate, frameState, hitTolerance, callback) {}
/**
* @abstract
@@ -115,12 +114,6 @@ class LayerRenderer extends Observable {
return this.layer_;
}
/**
* Perform action necessary to get the layer rendered after new fonts have loaded
* @abstract
*/
handleFontsChanged() {}
/**
* Handle changes in image state.
* @param {import("../events/Event.js").default} event Image change event.

View File

@@ -3,12 +3,13 @@
*/
import {abstract, getUid} from '../util.js';
import Disposable from '../Disposable.js';
import {listen, unlistenByKey} from '../events.js';
import EventType from '../events/EventType.js';
import {getWidth} from '../extent.js';
import {TRUE} from '../functions.js';
import {visibleAtResolution} from '../layer/Layer.js';
import {shared as iconImageCache} from '../style/IconImageCache.js';
import {compose as composeTransform, makeInverse} from '../transform.js';
import {renderDeclutterItems} from '../render.js';
/**
* @abstract
@@ -29,8 +30,15 @@ class MapRenderer extends Disposable {
/**
* @private
* @type {!Object<string, import("./Layer.js").default>}
*/
this.declutterTree_ = null;
this.layerRenderers_ = {};
/**
* @private
* @type {Object<string, import("../events.js").EventsKey>}
*/
this.layerRendererListeners_ = {};
}
@@ -61,6 +69,15 @@ class MapRenderer extends Disposable {
makeInverse(pixelToCoordinateTransform, coordinateToPixelTransform);
}
/**
* Removes all layer renderers.
*/
removeLayerRenderers() {
for (const key in this.layerRenderers_) {
this.removeLayerRendererByKey_(key).dispose();
}
}
/**
* @param {import("../coordinate.js").Coordinate} coordinate Coordinate.
* @param {import("../PluggableMap.js").FrameState} frameState FrameState.
@@ -116,24 +133,18 @@ class MapRenderer extends Disposable {
const layerStates = frameState.layerStatesArray;
const numLayers = layerStates.length;
let declutteredFeatures;
if (this.declutterTree_) {
declutteredFeatures = this.declutterTree_.all().map(function(entry) {
return entry.value;
});
}
let i;
for (i = numLayers - 1; i >= 0; --i) {
const layerState = layerStates[i];
const layer = /** @type {import("../layer/Layer.js").default} */ (layerState.layer);
if (layer.hasRenderer() && visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = layer.getRenderer();
if (visibleAtResolution(layerState, viewResolution) && layerFilter.call(thisArg2, layer)) {
const layerRenderer = this.getLayerRenderer(layer);
const source = layer.getSource();
if (layerRenderer && source) {
const callback = forEachFeatureAtCoordinate.bind(null, layerState.managed);
result = layerRenderer.forEachFeatureAtCoordinate(
source.getWrapX() ? translatedCoordinate : coordinate,
frameState, hitTolerance, callback, declutteredFeatures);
frameState, hitTolerance, callback);
}
if (result) {
return result;
@@ -180,6 +191,35 @@ class MapRenderer extends Disposable {
return hasFeature !== undefined;
}
/**
* @param {import("../layer/Layer.js").default} layer Layer.
* @protected
* @return {import("./Layer.js").default} Layer renderer. May return null.
*/
getLayerRenderer(layer) {
const layerKey = getUid(layer);
if (layerKey in this.layerRenderers_) {
return this.layerRenderers_[layerKey];
}
const renderer = layer.getRenderer();
if (!renderer) {
return null;
}
this.layerRenderers_[layerKey] = renderer;
this.layerRendererListeners_[layerKey] = listen(renderer, EventType.CHANGE, this.handleLayerRendererChange_, this);
return renderer;
}
/**
* @protected
* @return {Object<string, import("./Layer.js").default>} Layer renderers.
*/
getLayerRenderers() {
return this.layerRenderers_;
}
/**
* @return {import("../PluggableMap.js").default} Map.
*/
@@ -187,12 +227,36 @@ class MapRenderer extends Disposable {
return this.map_;
}
/**
* Handle changes in a layer renderer.
* @private
*/
handleLayerRendererChange_() {
this.map_.render();
}
/**
* @param {string} layerKey Layer key.
* @return {import("./Layer.js").default} Layer renderer.
* @private
*/
removeLayerRendererByKey_(layerKey) {
const layerRenderer = this.layerRenderers_[layerKey];
delete this.layerRenderers_[layerKey];
unlistenByKey(this.layerRendererListeners_[layerKey]);
delete this.layerRendererListeners_[layerKey];
return layerRenderer;
}
/**
* Render.
* @abstract
* @param {?import("../PluggableMap.js").FrameState} frameState Frame state.
*/
renderFrame(frameState) {
this.declutterTree_ = renderDeclutterItems(frameState, this.declutterTree_);
abstract();
}
/**
@@ -204,6 +268,21 @@ class MapRenderer extends Disposable {
frameState.postRenderFunctions.push(expireIconCache);
}
}
/**
* @param {!import("../PluggableMap.js").FrameState} frameState Frame state.
* @protected
*/
scheduleRemoveUnusedLayerRenderers(frameState) {
const layerStatesMap = getLayerStatesMap(frameState.layerStatesArray);
for (const layerKey in this.layerRenderers_) {
if (!(layerKey in layerStatesMap)) {
frameState.postRenderFunctions.push(function() {
this.removeLayerRendererByKey_(layerKey).dispose();
}.bind(this));
}
}
}
}
@@ -215,4 +294,15 @@ function expireIconCache(map, frameState) {
iconImageCache.expire();
}
/**
* @param {Array<import("../layer/Layer.js").State>} layerStatesArray Layer states array.
* @return {Object<string, import("../layer/Layer.js").State>} States mapped by layer uid.
*/
function getLayerStatesMap(layerStatesArray) {
return layerStatesArray.reduce(function(acc, state) {
acc[getUid(state.layer)] = state;
return acc;
}, {});
}
export default MapRenderer;

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