diff --git a/map/slippy/index.html b/map/slippy/index.html index f0c4aee30..1da0b073a 100644 --- a/map/slippy/index.html +++ b/map/slippy/index.html @@ -7,7 +7,7 @@ - + diff --git a/map/test/3rd-party/index.html b/map/test/3rd-party/index.html new file mode 100644 index 000000000..afcc2b2e4 --- /dev/null +++ b/map/test/3rd-party/index.html @@ -0,0 +1,203 @@ + + +
+ + ++ The libraries below provide additional functionality by extending OpenLayers or integrating well with it. +
+Library | Description | Maintainer |
---|---|---|
OL-Cesium | +Cesium integration library. | +OpenLayers | +
ol-mapbox-style | +Create OpenLayers maps from Mapbox Style objects. | +OpenLayers | +
OL-LayerSwitcher | +Layer control for OpenLayers. | +Matt Walker | +
OL-Popup | +Basic popup overlay for OpenLayers. | +Matt Walker | +
JSTS | +JavaScript Topology Suite. | +Björn Harrtell | +
OL-Geocoder | +Geocoder Nominatim for OpenLayers. | +Jonatas Walker | +
OL3-Photon | +Photon geocoder for OpenLayers. | +Thomas Gratier | +
ol-opencage-geosearch | +OpenCage GeoSearch for OpenLayers. | +OpenCage | +
OL-ContextMenu | +Custom Context Menu for OpenLayers. | +Jonatas Walker | +
OL-Google-Maps | +Google Maps integration library. | +Mapgears | +
OL3-PanZoom | +PanZoom and PanZoomBar controls for OpenLayers. | +Mapgears | +
OL-Ext | +Miscellaneous classes and functions for OpenLayers. | +Jean-Marc Viglino | +
OL3-Projection-Switcher | +An OpenLayers Control to switch between projections. | +NSIDC | +
Olé | +Integration of OpenLayers and Esri ArcGIS REST services. | +Boundless | +
React OpenLayers | +A minimal React wrapper of OpenLayers 3+ written in TypeScript | +Allen Kim | +
GWT-OpenLayers 3+ | +A GWT wrapper for OpenLayers 3+ written in Java | +Tino Desjardins | +
react-geo | +A set of geo related modules to use in combination with React, Ant Design UI and OpenLayers | +terrestris | +
ol-opacity | +A layer switcher control with opacity sliders for overlays. | +dayjournal | +
ol-grid | +A dynamic grid for OpenLayers - intended for use as part of advanced snapping controls. | +Symbioquine | +
vue3-openlayers | +vue3-openlayers is a components library that brings the power of OpenLayers to the Vue3 reactive world. | +Melih Altıntaş | +
ol-marker-feature | +The OpenLayers Marker Feature plugin provides an easy way to add markers to a map and associate them with popups, e.g. using OL-Popup. | +Andreas Hocevar | +
OL STAC | +STAC support for OpenLayers. Automatically fills an OpenLayers LayerGroup with data extracted from various STAC entities, e.g. geometry and imagery. | +Matthias Mohr | +
The view center is not defined.
+The view resolution is not defined.
+The view rotation is not defined.
+image
and src
cannot be provided at the same time.
imgSize
must be set when image
is provided.
A defined and non-empty src
or image
must be provided.
format
must be set when url
is set.
Unknown serverType
configured.
url
must be configured or set using #setUrl()
.
The default geometryFunction
can only handle ol/geom/Point
geometries.
options.featureTypes
must be an Array.
options.geometryName
must also be provided when options.bbox
is set.
Invalid corner. Valid corners are top-left
, top-right
, bottom-right
and bottom-left
.
Invalid color. Valid colors are all CSS colors.
+Tried to get a value for a key that does not exist in the cache.
+Tried to set a value for a key that is used already.
+resolutions
must be sorted in descending order.
Either origin
or origins
must be configured, never both.
Number of tileSizes
and resolutions
must be equal.
Number of origins
and resolutions
must be equal.
Either tileSize
or tileSizes
must be configured, never both.
Invalid extent or geometry provided as geometry
.
Cannot fit empty extent provided as geometry
.
Features for deletes
must have an id set by the feature reader or ol.Feature#setId()
.
Features for updates
must have an id set by the feature reader or ol.Feature#setId()
.
renderMode
must be 'hybrid'
or 'vector'
.
The passed feature
was already added to the source.
Tried to enqueue an element
that was already added to the queue.
Transformation matrix cannot be inverted.
+Invalid units
. 'degrees'
, 'imperial'
, 'nautical'
, 'metric'
or 'us'
required.
Invalid geometry layout. Must be XY
, XYZ
, XYM
or XYZM
.
Unknown SRS type. Expected "name"
.
Unknown geometry type found. Expected 'Point'
, 'LineString'
, 'Polygon'
or 'GeometryCollection'
.
styleMapValue
has an unknown type.
Unknown geometry type found. Expected 'GeometryCollection'
, 'MultiPoint'
, 'MultiLineString'
or 'MultiPolygon'
.
Expected feature
to have a geometry.
Expected an ol.style.Style
or an array of ol.style.Style
.
Expected layers
to be an array or an ol.Collection
.
Expected controls
to be an array or an ol.Collection
.
Expected interactions
to be an array or an ol.Collection
.
Expected overlays
to be an array or an ol.Collection
.
Cannot determine Rest Service from url.
+Either url
or tileJSON
options must be provided.
Unknown serverType
configured.
Unknown tierSizeCalculation
configured.
The {-y}
placeholder requires a tile grid with extent.
mapBrowserEvent
must originate from a pointer event.
At least 2 conditions are required.
+Duplicate item added to a unique collection. For example, it may be that you tried to add the same layer to a map twice. Check for calls to map.addLayer()
or other places where the map's layer collection is modified.
Invalid command found in the PBF. This indicates that the loaded vector tile may be corrupt.
+Missing or invalid size
.
Cannot determine IIIF Image API version from provided image information JSON.
+A WebGLArrayBuffer
must either be of type ELEMENT_ARRAY_BUFFER
or ARRAY_BUFFER
.
Layer opacity must be a number.
+forEachFeatureAtCoordinate
cannot be used on a WebGL layer if the hit detection logic has not been enabled.
A layer can only be added to the map once. Use either layer.setMap()
or map.addLayer()
, not both.
Data from this source can only be rendered if it has a projection compatible with the view projection.
+ +Certain questions arise more often than others when users ask for help. This +document tries to list some of the common questions that frequently get asked, +e.g. on Stack Overflow.
+If you think a question (and naturally its answer) should be added here, feel +free to ping us or to send a pull request enhancing this document.
+Table of contents:
+Every map that you'll create with OpenLayers will have a view, and every view +will have a projection. As the earth is three-dimensional and round but the 2D +view of a map isn't, we need a mathematical expression to represent it. Enter +projections.
+There isn't only one projection, but there are many common ones. Each projection +has different properties, in that it accurately represents distances, angles or +areas. Certain projections are better suited for different regions in the world.
+Back to the original question: OpenLayers is capable of dealing with most +projections. If you do not explicitly set one, your map is going to use our +default which is the Web Mercator projection (EPSG:3857). The same projection is +used e.g. for the maps of the OpenStreetMap-project and commercial products such +as Bing Maps or Google Maps.
+This projection is a good choice if you want a map which shows the whole world, +and you may need to have this projection if you want to e.g. use the +OpenStreetMap or Bing tiles.
+There is a good chance that you want to change the default projection of +OpenLayers to something more appropriate for your region or your specific data.
+The projection of your map can be set through the view
-property. Here are some
+examples:
import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+
+// OpenLayers comes with support for the World Geodetic System 1984, EPSG:4326:
+const map = new Map({
+ view: new View({
+ projection: 'EPSG:4326'
+ // other view properties like map center etc.
+ })
+ // other properties for your map like layers etc.
+});
+
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import proj4 from 'proj4';
+import {register} from 'ol/proj/proj4.js';
+import {get as getProjection} from 'ol/proj.js';
+
+// To use other projections, you have to register the projection in OpenLayers.
+// This can easily be done with [http://proj4js.org/](proj4)
+//
+// By default OpenLayers does not know about the EPSG:21781 (Swiss) projection.
+// So we create a projection instance for EPSG:21781 and pass it to
+// register to make it available to the library for lookup by its
+// code.
+proj4.defs('EPSG:21781',
+ '+proj=somerc +lat_0=46.95240555555556 +lon_0=7.439583333333333 +k_0=1 ' +
+ '+x_0=600000 +y_0=200000 +ellps=bessel ' +
+ '+towgs84=660.077,13.551,369.344,2.484,1.783,2.939,5.66 +units=m +no_defs');
+register(proj4);
+const swissProjection = getProjection('EPSG:21781');
+
+// we can now use the projection:
+const map = new Map({
+ view: new View({
+ projection: swissProjection
+ // other view properties like map center etc.
+ })
+ // other properties for your map like layers etc.
+});
+
+We recommend to lookup parameters of your projection (like the validity extent) +over at epsg.io.
+If you have set a center in your map view, but don't see a real change in visual +output, chances are that you have provided the coordinates of the map center in +the wrong (a non-matching) projection.
+As the default projection in OpenLayers is Web Mercator (see above), the +coordinates for the center have to be provided in that projection. Chances are +that your map looks like this:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import TileLayer from 'ol/layer/Tile.js';
+import OSM from 'ol/source/OSM.js';
+
+const washingtonLonLat = [-77.036667, 38.895];
+const map = new Map({
+ layers: [
+ new TileLayer({
+ source: new OSM()
+ })
+ ],
+ target: 'map',
+ view: new View({
+ center: washingtonLonLat,
+ zoom: 12
+ })
+});
+
+Here [-77.036667, 38.895]
is provided as the center of the view. But as Web
+Mercator is a metric projection, you are currently telling OpenLayers that the
+center shall be some meters (~77m and ~39m respectively) away from [0, 0]
. In
+the Web Mercator projection the coordinate is right in the gulf of guinea.
The solution is easy: Provide the coordinates projected into Web Mercator. +OpenLayers has some helpful utility methods to assist you:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import TileLayer from 'ol/layer/Tile.js';
+import OSM from 'ol/source/OSM.js';
+import {fromLonLat} from 'ol/proj.js';
+
+const washingtonLonLat = [-77.036667, 38.895];
+const washingtonWebMercator = fromLonLat(washingtonLonLat);
+
+const map = new Map({
+ layers: [
+ new TileLayer({
+ source: new OSM()
+ })
+ ],
+ target: 'map',
+ view: new View({
+ center: washingtonWebMercator,
+ zoom: 8
+ })
+});
+
+The method fromLonLat()
is available from version 3.5 onwards.
If you told OpenLayers about a custom projection (see above), you can use the +following method to transform a coordinate from WGS84 to your projection:
+import {transform} from 'ol/proj.js';
+// assuming that OpenLayers knows about EPSG:21781, see above
+const swissCoord = transform([8.23, 46.86], 'EPSG:4326', 'EPSG:21781');
+
+Because of two different and incompatible conventions. Latitude and longitude
+are normally given in that order. Maps are 2D representations/projections
+of the earth's surface, with coordinates expressed in the x,y
grid of the
+Cartesian system.
+As they are by convention drawn with west on the left and north at the top,
+this means that x
represents longitude, and y
latitude. As stated above,
+OpenLayers is designed to handle all projections, but the default view is in
+projected Cartesian coordinates. It would make no sense to have duplicate
+functions to handle coordinates in both the Cartesian x,y
and lat,lon
+systems, so the degrees of latitude and longitude should be entered as though
+they were Cartesian, in other words, they are lon,lat
.
If you have difficulty remembering which way round it is, use the language code
+for English, en
, as a mnemonic: East before North.
So you want to center your map on a certain place on the earth and obviously you +need to have its coordinates for this. Let's assume you want your map centered +on Schladming, a beautiful place in Austria. Head over to the wikipedia +page for Schladming. In the top-right +corner there is a link to GeoHack, +which effectively tells you the coordinates are:
+WGS84:
+47° 23′ 39″ N, 13° 41′ 21″ E
+47.394167, 13.689167
+
+So the next step would be to put the decimal coordinates into an array and use +it as center:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import TileLayer from 'ol/layer/Tile.js';
+import OSM from 'ol/source/OSM.js';
+import {fromLonLat} from 'ol/proj.js';
+
+const schladming = [47.394167, 13.689167]; // caution partner, read on...
+// since we are using OSM, we have to transform the coordinates...
+const schladmingWebMercator = fromLonLat(schladming);
+
+const map = new Map({
+ layers: [
+ new TileLayer({
+ source: new OSM()
+ })
+ ],
+ target: 'map',
+ view: new View({
+ center: schladmingWebMercator,
+ zoom: 9
+ })
+});
+
+Running the above example will possibly surprise you, since we are not centered +on Schladming, Austria, but instead on Abyan, a region in Yemen (possibly also a +nice place). So what happened?
+Many people mix up the order of longitude and latitude in a coordinate array. +Don't worry if you get it wrong at first, many OpenLayers developers have to +think twice about whether to put the longitude or the latitude first when they +e.g. try to change the map center.
+Ok, then let's flip the coordinates:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import TileLayer from 'ol/layer/Tile.js';
+import OSM from 'ol/source/OSM.js';
+import {fromLonLat} from 'ol/proj.js';
+
+const schladming = [13.689167, 47.394167]; // longitude first, then latitude
+// since we are using OSM, we have to transform the coordinates...
+const schladmingWebMercator = fromLonLat(schladming);
+
+const map = new Map({
+ layers: [
+ new TileLayer({
+ source: new OSM()
+ })
+ ],
+ target: 'map',
+ view: new View({
+ center: schladmingWebMercator,
+ zoom: 9
+ })
+});
+
+Schladming is now correctly displayed in the center of the map.
+So when you deal with EPSG:4326 coordinates in OpenLayers, put the longitude +first, and then the latitude. This behaviour is the same as we had in OpenLayers +2, and it actually makes sense because of the natural axis order in WGS84.
+If you cannot remember the correct order, just have a look at the method name
+we used: fromLonLat
; even there we hint that we expect longitude
+first, and then latitude.
Suppose you want to load a KML file and display the contained features on the +map. Code like the following could be used:
+import VectorLayer from 'ol/layer/Vector.js';
+import KMLSource from 'ol/source/KML.js';
+
+const vector = new VectorLayer({
+ source: new KMLSource({
+ projection: 'EPSG:3857',
+ url: 'data/kml/2012-02-10.kml'
+ })
+});
+
+You may ask yourself how many features are in that KML, and try something like +the following:
+import VectorLayer from 'ol/layer/Vector.js';
+import KMLSource from 'ol/source/KML.js';
+
+const vector = new VectorLayer({
+ source: new KMLSource({
+ projection: 'EPSG:3857',
+ url: 'data/kml/2012-02-10.kml'
+ })
+});
+const numFeatures = vector.getSource().getFeatures().length;
+console.log("Count right after construction: " + numFeatures);
+
+This will log a count of 0
features to be in the source. This is because the
+loading of the KML-file will happen in an asynchronous manner. To get the count
+as soon as possible (right after the file has been fetched and the source has
+been populated with features), you should use an event listener function on the
+source
:
vector.getSource().on('change', function(evt){
+ const source = evt.target;
+ if (source.getState() === 'ready') {
+ const numFeatures = source.getFeatures().length;
+ console.log("Count after change: " + numFeatures);
+ }
+});
+
+This will correctly report the number of features, 1119
in that particular
+case.
Usually the map is automatically re-rendered, once a source changes (for example +when a remote source has loaded).
+If you actually want to manually trigger a rendering, you could use
+map.render();
+
+...or its companion method
+map.renderSync();
+
+You are using Map#forEachFeatureAtPixel
or Map#hasFeatureAtPixel
, but
+it sometimes does not work for large icons or labels? The hit detection only
+checks features that are within a certain distance of the given position. For large
+icons, the actual geometry of a feature might be too far away and is not considered.
In this case, set the renderBuffer
property of VectorLayer
(the default value is 100px):
import VectorLayer from 'ol/layer/Vector.js';
+
+const vectorLayer = new VectorLayer({
+ ...
+ renderBuffer: 200
+});
+
+The recommended value is the size of the largest symbol, line width or label.
+OpenLayers does not update the map when the container element is resized. This can be caused by progressive updates +to CSS styles or manually resizing the map. When that happens, any interaction will become inaccurate: the map would zoom in and out, and end up not being centered on the pointer. This makes it hard to do certain interactions, e.g. selecting the desired feature.
+There is currently no built-in way to react to element's size changes, as Resize Observer API is only implemented in Chrome.
+There is however an easy to use polyfill:
+import Map from 'ol/Map.js';
+import ResizeObserver from 'resize-observer-polyfill';
+
+const mapElement = document.querySelector('#map')
+const map = new Map({
+ target: mapElement
+})
+
+const sizeObserver = new ResizeObserver(() => {
+ map.updateSize()
+})
+sizeObserver.observe(mapElement)
+
+// called when the map is destroyed
+// sizeObserver.disconnect()
+
+
+ If you're eager to get your first OpenLayers map on a page, dive into the quick start.
+For a more in-depth overview of OpenLayers core concepts, check out the tutorials.
+Make sure to also check out the OpenLayers workshop.
+Find additional reference material in the API docs and examples.
+We have put together a document that lists Frequently Asked Questions (FAQ) and our answers. Common problems that may arise when using OpenLayers are explained there, and chances are you'll find an appropriate solution in this document.
+If you cannot find an answer in the documentation or the FAQ, you can search Stack Overflow. If you cannot find an answer there, ask a new question there, using the tag 'openlayers'.
+ +This primer shows you how to put a map on a web page. The development setup uses Node (14 or higher) and requires that you have git
installed.
The easiest way to start building a project with OpenLayers is to run npm create ol-app
:
npm create ol-app my-app
+cd my-app
+npm start
+
+The first command will create a directory called my-app
(you can use a different name if you wish), install OpenLayers and a development server, and set up a basic app with index.html
, main.js
, and style.css
files.
The second command (cd my-app
) changes the working directory to your new my-app
project so you can start working with it.
The third command (npm start
) starts a development server so you can view your application in a browser while working on it. After running npm start
, you'll see output that tells you the URL to open. Open http://localhost:5173/ (or whatever URL is displayed) to see your new application.
An OpenLayers application is composed of three basic parts:
+index.html
)main.js
)style.css
)Open the index.html
file in a text editor. It should look something like this:
<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Quick Start</title>
+ </head>
+ <body>
+ <div id="map"></div>
+ <script type="module" src="./main.js"></script>
+ </body>
+</html>
+
+The two important parts in the markup are the <div>
element to contain the map and the <script>
tag to pull in the JavaScript. The map container or target should be a block level element (like a <div>
) and it must appear in the document before the <script>
tag that initializes the map.
Open the main.js
file in a text editor. It should look something like this:
import './style.css';
+import Map from 'ol/Map.js';
+import OSM from 'ol/source/OSM.js';
+import TileLayer from 'ol/layer/Tile.js';
+import View from 'ol/View.js';
+
+const map = new Map({
+ target: 'map',
+ layers: [
+ new TileLayer({
+ source: new OSM(),
+ }),
+ ],
+ view: new View({
+ center: [0, 0],
+ zoom: 2,
+ }),
+});
+
+OpenLayers is packaged as a collection of ES modules. The import
lines are used to pull in the modules that your application needs. Take a look through the examples and API docs to understand which modules you might want to use.
The import './style.css';
line might be a bit unexpected. In this example, we're using Vite as a development server. Vite allows CSS to be imported from JavaScript modules. If you were using a different development server, you might include the style.css
in a <link>
tag in the index.html
instead.
The main.js
module serves as an entry point for your application. It initializes a new map, giving it a single layer with an OSM source and a view describing the center and zoom level. Read through the Basic Concepts tutorial to learn more about Map
, View
, Layer
, and Source
components.
Open the style.css
file in a text editor. It should look something like this:
@import "node_modules/ol/ol.css";
+
+html,
+body {
+ margin: 0;
+ height: 100%;
+}
+
+#map {
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+}
+
+The first line imports the ol.css
file that comes with the ol
package (OpenLayers is published as the ol
package in the npm registry). The ol
package was installed in the npm create ol-app
step above. If you were starting with an existing application instead of using npm create ol-app
, you would install the package with npm install ol
. The ol.css
stylesheet includes styles for the elements that OpenLayers creates – things like buttons for zooming in and out.
The remaining rules in the style.css
file make it so the <div id="map">
element that contains the map fills the entire page.
You can make edits to the index.html
, main.js
, or style.css
files and see the resulting change in your browser while running the development server (with npm start
). After you have finished making edits, it is time to bundle or build your application so that it can be deployed as a static website (without needing to run a development server like Vite).
To build your application, run the following:
+npm run build
+
+This will create a dist
directory with a new index.html
and assets that make up your application. These dist
files can be deployed with your production website.
OpenLayers is a modular, high-performance, feature-packed library for displaying and interacting with maps and geospatial data.
+The library comes with built-in support for a wide range of commercial and free image and vector tile sources, and the most popular open and proprietary vector data formats. With OpenLayers's map projection support, data can be in any projection.
+OpenLayers is available as ol
npm package, which provides all modules of the officially supported API.
OpenLayers runs on all modern browsers (with greater than 1% global usage). This includes Chrome, Firefox, Safari and Edge. For older browsers, polyfills will likely need to be added.
+The library is intended for use on both desktop/laptop and mobile devices, and supports pointer and touch interactions.
+OpenLayers modules with CamelCase names provide classes as default exports, and may contain additional constants or functions as named exports:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+
+Class hierarchies grouped by their parent are provided in a subfolder of the package, e.g. layer/
.
For convenience, these are also available as named exports, e.g.
+import {Map, View} from 'ol';
+import {Tile, Vector} from 'ol/layer.js';
+
+In addition to these re-exported classes, modules with lowercase names also provide constants or functions as named exports:
+import {getUid} from 'ol';
+import {fromLonLat} from 'ol/proj.js';
+
+
+ The core component of OpenLayers is the map (from the ol/Map
module). It is rendered to a target
container (e.g. a div
element on the web page that contains the map). All map properties can either be configured at construction time, or by using setter methods, e.g. setTarget()
.
The markup below could be used to create a <div>
that contains your map.
<div id="map" style="width: 100%; height: 400px"></div>
+
+The script below constructs a map that is rendered in the <div>
above, using the map
id of the element as a selector.
import Map from 'ol/Map.js';
+
+const map = new Map({target: 'map'});
+
+The map is not responsible for things like center, zoom level and projection of the map. Instead, these are properties of a ol/View
instance.
import View from 'ol/View.js';
+
+map.setView(new View({
+ center: [0, 0],
+ zoom: 2,
+}));
+
+A View
also has a projection
. The projection determines the coordinate system of the center
and the units for map resolution calculations. If not specified (like in the above snippet), the default projection is Spherical Mercator (EPSG:3857), with meters as map units.
The zoom
option is a convenient way to specify the map resolution. The available zoom levels are determined by maxZoom
(default: 28), zoomFactor
(default: 2) and maxResolution
(default is calculated in such a way that the projection's validity extent fits in a 256x256 pixel tile). Starting at zoom level 0 with a resolution of maxResolution
units per pixel, subsequent zoom levels are calculated by dividing the previous zoom level's resolution by zoomFactor
, until zoom level maxZoom
is reached.
To get remote data for a layer, OpenLayers uses ol/source/Source
subclasses. These are available for free and commercial map tile services like OpenStreetMap or Bing, for OGC sources like WMS or WMTS, and for vector data in formats like GeoJSON or KML.
import OSM from 'ol/source/OSM.js';
+
+const source = OSM();
+
+A layer is a visual representation of data from a source. OpenLayers has four basic types of layers:
+ol/layer/Tile
- Renders sources that provide tiled images in grids that are organized by zoom levels for specific resolutions.ol/layer/Image
- Renders sources that provide map images at arbitrary extents and resolutions.ol/layer/Vector
- Renders vector data client-side.ol/layer/VectorTile
- Renders data that is provided as vector tiles.import TileLayer from 'ol/layer/Tile.js';
+
+// ...
+const layer = new TileLayer({source: source});
+map.addLayer(layer);
+
+The above snippets can be combined into a single script that renders a map with a single tile layer:
+import Map from 'ol/Map.js';
+import View from 'ol/View.js';
+import OSM from 'ol/source/OSM.js';
+import TileLayer from 'ol/layer/Tile.js';
+
+new Map({
+ layers: [
+ new TileLayer({source: new OSM()}),
+ ],
+ view: new View({
+ center: [0, 0],
+ zoom: 2,
+ }),
+ target: 'map',
+});
+
+
+ OpenLayers has an ability to display raster data from WMS, WMTS, static images and many other sources in a different coordinate system than delivered from the server. +Transformation of the map projections of the image happens directly in a web browser. +The view in any Proj4js supported coordinate reference system is possible and previously incompatible layers can now be combined and overlaid.
+The API usage is very simple. Just specify proper projection (e.g. using EPSG code) on ol/View
:
import Map from 'ol/Map.js';
+import TileLayer from 'ol/layer/Tile.js';
+import TileWMS from 'ol/source/TileWMS.js';
+import View from 'ol/View.js';
+
+const map = new Map({
+ target: 'map',
+ view: new View({
+ projection: 'EPSG:3857', // here is the view projection
+ center: [0, 0],
+ zoom: 2,
+ }),
+ layers: [
+ new TileLayer({
+ source: new TileWMS({
+ projection: 'EPSG:4326', // here is the source projection
+ url: 'https://ahocevar.com/geoserver/wms',
+ params: {
+ 'LAYERS': 'ne:NE1_HR_LC_SR_W_DR',
+ },
+ }),
+ }),
+ ],
+});
+
+If a source (based on ol/source/TileImage
or ol/source/Image
) has a projection different from the current ol/View
’s projection then the reprojection happens automatically under the hood.
The easiest way to use a custom projection is to add the Proj4js library to your project and then define the projection using a proj4 definition string. It can be installed with
+npm install proj4
+
+Following example shows definition of a British National Grid:
+import proj4 from 'proj4';
+import {get as getProjection} from 'ol/proj.js';
+import {register} from 'ol/proj/proj4.js';
+
+proj4.defs('EPSG:27700', '+proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 ' +
+ '+x_0=400000 +y_0=-100000 +ellps=airy ' +
+ '+towgs84=446.448,-125.157,542.06,0.15,0.247,0.842,-20.489 ' +
+ '+units=m +no_defs');
+register(proj4);
+const proj27700 = getProjection('EPSG:27700');
+proj27700.setExtent([0, 0, 700000, 1300000]);
+
+To switch the projection used to display the map you have to set a new ol/View
with selected projection on the ol/Map
:
map.setView(new View({
+ projection: 'EPSG:27700',
+ center: [400000, 650000],
+ zoom: 4,
+}));
+
+When reprojection is needed, new tiles (in the target projection) are under the hood created from the original source tiles.
+The TileGrid of the reprojected tiles is by default internally constructed using ol/tilegrid~getForProjection(projection)
.
+The projection should have extent defined (see above) for this to work properly.
Alternatively, a custom target TileGrid can be constructed manually and set on the source instance using ol/source/TileImage~setTileGridForProjection(projection, tilegrid)
.
+This TileGrid will then be used when reprojecting to the specified projection instead of creating the default one.
+In certain cases, this can be used to optimize performance (by tweaking tile sizes) or visual quality (by specifying resolutions).
The reprojection process is based on triangles – the target raster is divided into a limited number of triangles with vertices transformed using ol/proj
capabilities (proj4js is usually utilized to define custom transformations).
+The reprojection of pixels inside the triangle is approximated with an affine transformation (with rendering hardware-accelerated by the canvas 2d context):
This way we can support a wide range of projections from proj4js (or even custom transformation functions) on almost any hardware (with canvas 2d support) with a relatively small number of actual transformation calculations.
+The precision of the reprojection is then limited by the number of triangles.
+The reprojection process preserves transparency on the raster data supplied from the source (png or gif) and the gaps and no-data pixels generated by reprojection are automatically transparent.
+The above image above shows a noticeable error (especially on the edges) when the original image (left; EPSG:27700) is transformed with only a limited number of triangles (right; EPSG:3857). +The error can be minimized by increasing the number of triangles used.
+Since some transformations require a more detail triangulation network, the dynamic triangulation process automatically measures reprojection error and iteratively subdivides to meet a specific error threshold:
+ + +For debugging, rendering of the reprojection edges can be enabled by ol.source.TileImage#setRenderReprojectionEdges(true)
.
The default triangulation error threshold in pixels is given by ERROR_THRESHOLD
(0.5 pixel).
+In case a different threshold needs to be defined for different sources, the reprojectionErrorThreshold
option can be passed when constructing the tile image source.
The reprojection algorithm uses inverse transformation (from view projection to data projection). +For certain coordinate systems this can result in a "double occurrence" of the source data on a map. +For example, when reprojecting a map of Switzerland from EPSG:21781 to EPSG:3857, it is displayed twice: once at the proper place in Europe, but also in the Pacific Ocean near New Zealand, on the opposite side of the globe.
+ + +Although this is mathematically correct behavior of the inverse transformation, visibility of the layer on multiple places is not expected by users. +A possible general solution would be to calculate the forward transformation for every vertex as well - but this would significantly decrease performance (especially for computationally expensive transformations).
+Therefore a recommended workaround is to define a proper visibility extent on the ol.layer.Tile
in the view projection.
+Setting such a limit is demonstrated in the reprojection demo example.
When determining source tiles to load, the ideal source resolution needs to be calculated.
+The ol/reproj~calculateSourceResolution(sourceProj, targetProj, targetCenter, targetResolution)
function calculates the ideal value in order to achieve pixel mapping as close as possible to 1:1 during reprojection, which is then used to select proper zoom level from the source.
It is, however, generally not practical to use the same source zoom level for the whole target zoom level -- different projections can have significantly different resolutions in different parts of the world (e.g. polar regions in EPSG:3857 vs EPSG:4326) and enforcing a single resolution for the whole zoom level would result in some tiles being scaled up/down, possibly requiring a huge number of source tiles to be loaded. +Therefore, the resolution mapping is calculated separately for each reprojected tile (in the middle of the tile extent).
+ +ol
package
+ The recommended way to use OpenLayers is to work with the ol
package.
+
+ To add OpenLayers to an existing project, install the latest with npm
:
+
npm install ol
+
+ + If you are starting a new project from scratch, see the quick start docs for more information. +
++ If you want to try out OpenLayers without downloading anything (not recommended for production), include the following in the head of your html page: +
<script src="https://cdn.jsdelivr.net/npm/ol@v9.1.0/dist/ol.js"></script>
+<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v9.1.0/ol.css">
+
+
+
+ The full build of the library does not include all dependencies: the geotiff
and ol-mapbox-style
packages are omitted. If you use these, you'll need to add additional script tags.
+
Archive | Description |
---|---|
v9.1.0-site.zip | +Includes examples and documentation. | +
v9.1.0-package.zip | +Includes sources and the full build of the library. | +
+ See the v9.1.0 release page for a changelog and any special upgrade notes. +
++ For archives of previous releases, see the complete list of releases. +
+++A high-performance, feature-packed library for all your mapping needs.
+
OpenLayers v9.1.0 is here! Check out the docs and the examples to get started. The full distribution can be downloaded from the release page.
+OpenLayers makes it easy to put a dynamic map in any web page. It can display map tiles, vector data and markers loaded from any source. OpenLayers has been developed to further the use of geographic information of all kinds. It is completely free, Open Source JavaScript, released under the 2-clause BSD License (also known as the FreeBSD).
+Pull tiles from OSM, Bing, MapBox, Stadia Maps, and any other XYZ source you can find. OGC mapping services and untiled layers also supported.
+Render vector data from GeoJSON, TopoJSON, KML, GML, Mapbox vector tiles, and other formats.
+Leverages Canvas 2D, WebGL, and all the latest greatness from HTML5. Mobile support out of the box. Build lightweight custom profiles with just the components you need.
+Style your map controls with straight-forward CSS. Hook into different levels of the API or use 3rd party libraries to customize and extend functionality.
+Seen enough already? Go here to get started.
+Get the latest release or dig through the archives.
+Spend time learning the basics and graduate up to advanced mapping techniques.
+Want to learn OpenLayers hands-on? Get started with the workshop.
+Browse through the API docs for details on code usage.
+In case you are not ready (yet) for the latest version of OpenLayers, we provide links to selected resources of older major versions of the software.
+Please consider upgrading to benefit from the latest features and bug fixes. Get better performance and usability for free by using recent versions of OpenLayers.
+0&&f>p)?(a=i,l=c):(s[o++]=a,s[o++]=l,h=a,u=l,a=i,l=c)}return s[o++]=a,s[o++]=l,o}function mn(t,e,i,n,r,s,o,a){for(let l=0,h=i.length;l0}function Dn(t,e,i,n,r){r=void 0!==r&&r;for(let s=0,o=i.length;s0?o/s.duration:1;a>=1?(s.complete=!0,a=1):r=!1;const l=s.easing(a);if(s.sourceCenter){const t=s.sourceCenter[0],e=s.sourceCenter[1],i=s.targetCenter[0],n=s.targetCenter[1];this.nextCenter_=s.targetCenter;const r=t+l*(i-t),o=e+l*(n-e);this.targetCenter_=[r,o]}if(s.sourceResolution&&s.targetResolution){const t=1===l?s.targetResolution:s.sourceResolution+l*(s.targetResolution-s.sourceResolution);if(s.anchor){const e=this.getViewportSize_(this.getRotation()),i=this.constraints_.resolution(t,0,e,!0);this.targetCenter_=this.calculateCenterZoom(i,s.anchor)}this.nextResolution_=s.targetResolution,this.targetResolution_=t,this.applyTargetState_(!0)}if(void 0!==s.sourceRotation&&void 0!==s.targetRotation){const t=1===l?Gt(s.targetRotation+Math.PI,2*Math.PI)-Math.PI:s.sourceRotation+l*(s.targetRotation-s.sourceRotation);if(s.anchor){const e=this.constraints_.rotation(t,!0);this.targetCenter_=this.calculateCenterRotate(e,s.anchor)}this.nextRotation_=s.targetRotation,this.targetRotation_=t}if(this.applyTargetState_(!0),e=!0,!s.complete)break}if(r){this.animations_[i]=null,this.setHint(Kt,-1),this.nextCenter_=null,this.nextResolution_=NaN,this.nextRotation_=NaN;const t=n[0].callback;t&&Xn(t,!0)}}this.animations_=this.animations_.filter(Boolean),e&&void 0===this.updateAnimationKey_&&(this.updateAnimationKey_=requestAnimationFrame(this.updateAnimations_.bind(this)))}calculateCenterRotate(t,e){let i;const n=this.getCenterInternal();var r,s;return void 0!==n&&(i=[n[0]-e[0],n[1]-e[1]],ii(i,t-this.getRotation()),s=e,(r=i)[0]+=+s[0],r[1]+=+s[1]),i}calculateCenterZoom(t,e){let i;const n=this.getCenterInternal(),r=this.getResolution();if(void 0!==n&&void 0!==r){i=[e[0]-t*(e[0]-n[0])/r,e[1]-t*(e[1]-n[1])/r]}return i}getViewportSize_(t){const e=this.viewportSize_;if(t){const i=e[0],n=e[1];return[Math.abs(i*Math.cos(t))+Math.abs(n*Math.sin(t)),Math.abs(i*Math.sin(t))+Math.abs(n*Math.cos(t))]}return e}setViewportSize(t){this.viewportSize_=Array.isArray(t)?t.slice():[100,100],this.getAnimating()||this.resolveConstraints(0)}getCenter(){const t=this.getCenterInternal();return t?bi(t,this.getProjection()):t}getCenterInternal(){return this.get(Jt.CENTER)}getConstraints(){return this.constraints_}getConstrainResolution(){return this.get("constrainResolution")}getHints(t){return void 0!==t?(t[0]=this.hints_[0],t[1]=this.hints_[1],t):this.hints_.slice()}calculateExtent(t){return Si(this.calculateExtentInternal(t),this.getProjection())}calculateExtentInternal(t){t=t||this.getViewportSizeMinusPadding_();const e=this.getCenterInternal();At(e,"The view center is not defined");const i=this.getResolution();At(void 0!==i,"The view resolution is not defined");const n=this.getRotation();return At(void 0!==n,"The view rotation is not defined"),Be(e,i,n,t)}getMaxResolution(){return this.maxResolution_}getMinResolution(){return this.minResolution_}getMaxZoom(){return this.getZoomForResolution(this.minResolution_)}setMaxZoom(t){this.applyOptions_(this.getUpdatedOptions_({maxZoom:t}))}getMinZoom(){return this.getZoomForResolution(this.maxResolution_)}setMinZoom(t){this.applyOptions_(this.getUpdatedOptions_({minZoom:t}))}setConstrainResolution(t){this.applyOptions_(this.getUpdatedOptions_({constrainResolution:t}))}getProjection(){return this.projection_}getResolution(){return this.get(Jt.RESOLUTION)}getResolutions(){return this.resolutions_}getResolutionForExtent(t,e){return this.getResolutionForExtentInternal(Ei(t,this.getProjection()),e)}getResolutionForExtentInternal(t,e){e=e||this.getViewportSizeMinusPadding_();const i=Ke(t)/e[0],n=Ye(t)/e[1];return Math.max(i,n)}getResolutionForValueFunction(t){t=t||2;const e=this.getConstrainedResolution(this.maxResolution_),i=this.minResolution_,n=Math.log(e/i)/Math.log(t);return function(i){return e/Math.pow(t,i*n)}}getRotation(){return this.get(Jt.ROTATION)}getValueForResolutionFunction(t){const e=Math.log(t||2),i=this.getConstrainedResolution(this.maxResolution_),n=this.minResolution_,r=Math.log(i/n)/e;return function(t){return Math.log(i/t)/e/r}}getViewportSizeMinusPadding_(t){let e=this.getViewportSize_(t);const i=this.padding_;return i&&(e=[e[0]-i[1]-i[3],e[1]-i[0]-i[2]]),e}getState(){const t=this.getProjection(),e=this.getResolution(),i=this.getRotation();let n=this.getCenterInternal();const r=this.padding_;if(r){const t=this.getViewportSizeMinusPadding_();n=Vn(n,this.getViewportSize_(),[t[0]/2+r[3],t[1]/2+r[0]],e,i)}return{center:n.slice(0),projection:void 0!==t?t:null,resolution:e,nextCenter:this.nextCenter_,nextResolution:this.nextResolution_,nextRotation:this.nextRotation_,rotation:i,zoom:this.getZoom()}}getViewStateAndExtent(){return{viewState:this.getState(),extent:this.calculateExtent()}}getZoom(){let t;const e=this.getResolution();return void 0!==e&&(t=this.getZoomForResolution(e)),t}getZoomForResolution(t){let e,i,n=this.minZoom_||0;if(this.resolutions_){const s=r(this.resolutions_,t,1);n=s,e=this.resolutions_[s],i=s==this.resolutions_.length-1?2:e/this.resolutions_[s+1]}else e=this.maxResolution_,i=this.zoomFactor_;return n+Math.log(e/t)/Math.log(i)}getResolutionForZoom(t){if(this.resolutions_){if(this.resolutions_.length<=1)return 0;const e=Dt(Math.floor(t),0,this.resolutions_.length-2),i=this.resolutions_[e]/this.resolutions_[e+1];return this.resolutions_[e]/Math.pow(i,Dt(t-e,0,1))}return this.maxResolution_/Math.pow(this.zoomFactor_,t-this.minZoom_)}fit(t,e){let i;if(At(Array.isArray(t)||"function"==typeof t.getSimplifiedGeometry,"Invalid extent or geometry provided as `geometry`"),Array.isArray(t)){At(!Je(t),"Cannot fit empty extent provided as `geometry`");i=qn(Ei(t,this.getProjection()))}else if("Circle"===t.getType()){const e=Ei(t.getExtent(),this.getProjection());i=qn(e),i.rotate(this.getRotation(),We(e))}else i=t;this.fitInternal(i,e)}rotatedExtentForGeometry(t){const e=this.getRotation(),i=Math.cos(e),n=Math.sin(-e),r=t.getFlatCoordinates(),s=t.getStride();let o=1/0,a=1/0,l=-1/0,h=-1/0;for(let t=0,e=r.length;t