mirror of
https://expo.survex.com/repositories/expoweb/.git/
synced 2024-12-25 01:42:21 +00:00
188 lines
15 KiB
HTML
188 lines
15 KiB
HTML
|
<!DOCTYPE html>
|
|||
|
<html lang="en">
|
|||
|
<head>
|
|||
|
<meta charset="UTF-8">
|
|||
|
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
|
|||
|
<title>OpenLayers - Raster Reprojection</title>
|
|||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/components/prism-core.min.js"></script>
|
|||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/autoloader/prism-autoloader.min.js"></script>
|
|||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/toolbar/prism-toolbar.min.js"></script>
|
|||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.28.0/plugins/copy-to-clipboard/prism-copy-to-clipboard.min.js"></script>
|
|||
|
<link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Quattrocento+Sans:400,400italic,700" crossorigin="anonymous">
|
|||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" crossorigin="anonymous">
|
|||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/fontawesome.min.css" crossorigin="anonymous">
|
|||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/solid.css" crossorigin="anonymous">
|
|||
|
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6.1.2/css/brands.css" crossorigin="anonymous">
|
|||
|
<link rel="stylesheet" type="text/css" href="/theme/ol.css">
|
|||
|
<link rel="stylesheet" type="text/css" href="/theme/site.css">
|
|||
|
<link rel="icon" type="image/svg+xml" href="/theme/img/logo-light.svg" media="(prefers-color-scheme: light)" />
|
|||
|
<link rel="icon" type="image/svg+xml" href="/theme/img/logo-dark.svg" media="(prefers-color-scheme: dark)" />
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<header class="navbar navbar-expand-md navbar-dark mb-3 px-3 py-0 fixed-top" role="navigation">
|
|||
|
<a class="navbar-brand" href="/"><img src="/theme/img/logo-dark.svg" width="70" height="70" alt=""> OpenLayers</a>
|
|||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#olmenu" aria-controls="olmenu" aria-expanded="false" aria-label="Toggle navigation">
|
|||
|
<span class="navbar-toggler-icon"></span>
|
|||
|
</button>
|
|||
|
|
|||
|
<!-- menu items that get hidden below 768px width -->
|
|||
|
<nav class="collapse navbar-collapse" id="olmenu">
|
|||
|
<ul class="nav navbar-nav ms-auto">
|
|||
|
<li class="nav-item dropdown">
|
|||
|
<a class="nav-link dropdown-toggle" href="#" id="docdropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Docs</a>
|
|||
|
<div class="dropdown-menu dropdown-menu-end mb-3" aria-labelledby="docdropdown">
|
|||
|
<a class="dropdown-item" href="/doc/">Docs</a>
|
|||
|
<div class="dropdown-divider"></div>
|
|||
|
<a class="dropdown-item" href="/doc/quickstart.html"><i class="fa fa-check fa-fw me-2 fa-lg"></i>Quick Start</a>
|
|||
|
<a class="dropdown-item" href="/doc/faq.html"><i class="fa fa-question fa-fw me-2 fa-lg"></i>FAQ</a>
|
|||
|
<a class="dropdown-item" href="/doc/tutorials/"><i class="fa fa-book fa-fw me-2 fa-lg"></i>Tutorials</a>
|
|||
|
<a class="dropdown-item" href="/workshop/"><i class="fa fa-graduation-cap fa-fw me-2 fa-lg"></i>Workshop</a>
|
|||
|
<div class="dropdown-divider"></div>
|
|||
|
<a class="dropdown-item" href="https://stackoverflow.com/questions/tagged/openlayers"><i class="fab fa-stack-overflow fa-fw me-2"></i>Ask a Question</a>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li class="nav-item"><a class="nav-link" href="/en/latest/examples/">Examples</a></li>
|
|||
|
<li class="nav-item dropdown">
|
|||
|
<a class="nav-link dropdown-toggle" href="#" id="apidropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
|||
|
<i class="fa fa-sitemap me-1"></i>API
|
|||
|
</a>
|
|||
|
<div class="dropdown-menu dropdown-menu-end mb-3" aria-labelledby="apidropdown">
|
|||
|
<a class="dropdown-item" href="/en/latest/apidoc/"><i class="fa fa-sitemap fa-fw me-2 fa-lg"></i>v9.1.0 (latest)</a>
|
|||
|
<a class="dropdown-item" href="/en/v8.1.0/apidoc/"><i class="fa fa-sitemap a-fw me-2 fa-lg"></i>v8.1.0</a>
|
|||
|
<a class="dropdown-item" href="/en/v7.5.2/apidoc/"><i class="fa fa-sitemap a-fw me-2 fa-lg"></i>v7.5.2</a>
|
|||
|
<a class="dropdown-item" href="/en/v6.15.1/apidoc/"><i class="fa fa-sitemap a-fw me-2 fa-lg"></i>v6.15.1</a>
|
|||
|
<a class="dropdown-item" href="/en/v5.3.0/apidoc/"><i class="fa fa-sitemap fa-fw me-2 fa-lg"></i>v5.3.0</a>
|
|||
|
<a class="dropdown-item" href="/en/v4.6.5/apidoc/"><i class="fa fa-sitemap fa-fw me-2 fa-lg"></i>v4.6.5</a>
|
|||
|
<a class="dropdown-item" href="/en/v3.20.1/apidoc/"><i class="fa fa-sitemap fa-fw me-2 fa-lg"></i>v3.20.1</a>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
<li class="nav-item dropdown">
|
|||
|
<a class="nav-link dropdown-toggle" href="#" id="codedropdown" role="button" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Code</a>
|
|||
|
<div class="dropdown-menu dropdown-menu-end mb-3" aria-labelledby="codedropdown">
|
|||
|
<a class="dropdown-item" href="https://github.com/openlayers/openlayers"><i class="fab fa-github fa-fw me-2 fa-lg"></i>Repository</a>
|
|||
|
<a class="dropdown-item" href="/download/"><i class="fa fa-download fa-fw me-2 fa-lg"></i>Download</a>
|
|||
|
</div>
|
|||
|
</li>
|
|||
|
</ul>
|
|||
|
</nav>
|
|||
|
</header>
|
|||
|
|
|||
|
<div class="container">
|
|||
|
<h1 id="raster-reprojection">Raster Reprojection</h1>
|
|||
|
<p>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.</p>
|
|||
|
<h1 id="usage">Usage</h1>
|
|||
|
<p>The API usage is very simple. Just specify proper projection (e.g. using <a href="https://epsg.io">EPSG</a> code) on <code>ol/View</code>:</p>
|
|||
|
<pre><code class="language-js">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',
|
|||
|
},
|
|||
|
}),
|
|||
|
}),
|
|||
|
],
|
|||
|
});
|
|||
|
</code></pre>
|
|||
|
<p>If a source (based on <code>ol/source/TileImage</code> or <code>ol/source/Image</code>) has a projection different from the current <code>ol/View</code>’s projection then the reprojection happens automatically under the hood.</p>
|
|||
|
<h3 id="examples">Examples</h3>
|
|||
|
<ul>
|
|||
|
<li><a href="/en/latest/examples/reprojection.html">Raster reprojection demo</a></li>
|
|||
|
<li><a href="/en/latest/examples/reprojection-wgs84.html">OpenStreetMap to WGS84 reprojection</a></li>
|
|||
|
<li><a href="/en/latest/examples/reprojection-by-code.html">Reprojection with EPSG.io database search</a></li>
|
|||
|
<li><a href="/en/latest/examples/reprojection-image.html">Image reprojection</a></li>
|
|||
|
</ul>
|
|||
|
<h3 id="custom-projection">Custom projection</h3>
|
|||
|
<p>The easiest way to use a custom projection is to add the <a href="http://proj4js.org/">Proj4js</a> library to your project and then define the projection using a proj4 definition string. It can be installed with</p>
|
|||
|
<pre><code>npm install proj4
|
|||
|
</code></pre>
|
|||
|
<p>Following example shows definition of a <a href="https://epsg.io/27700">British National Grid</a>:</p>
|
|||
|
<pre><code class="language-js">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]);
|
|||
|
</code></pre>
|
|||
|
<h3 id="change-of-the-view-projection">Change of the view projection</h3>
|
|||
|
<p>To switch the projection used to display the map you have to set a new <code>ol/View</code> with selected projection on the <code>ol/Map</code>:</p>
|
|||
|
<pre><code class="language-js">map.setView(new View({
|
|||
|
projection: 'EPSG:27700',
|
|||
|
center: [400000, 650000],
|
|||
|
zoom: 4,
|
|||
|
}));
|
|||
|
</code></pre>
|
|||
|
<h2 id="tilegrid-and-extents">TileGrid and Extents</h2>
|
|||
|
<p>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 <code>ol/tilegrid~getForProjection(projection)</code>.
|
|||
|
The projection should have extent defined (see above) for this to work properly.</p>
|
|||
|
<p>Alternatively, a custom target TileGrid can be constructed manually and set on the source instance using <code>ol/source/TileImage~setTileGridForProjection(projection, tilegrid)</code>.
|
|||
|
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).</p>
|
|||
|
<h1 id="how-it-works">How it works</h1>
|
|||
|
<p>The reprojection process is based on triangles – the target raster is divided into a limited number of triangles with vertices transformed using <code>ol/proj</code> capabilities (<a href="http://proj4js.org/">proj4js</a> 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):</p>
|
|||
|
<img src="raster-reprojection-resources/how-it-works.jpg" alt="How it works" width="600" />
|
|||
|
|
|||
|
<p>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.</p>
|
|||
|
<p>The precision of the reprojection is then limited by the number of triangles.</p>
|
|||
|
<p>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.</p>
|
|||
|
<h3 id="dynamic-triangulation">Dynamic triangulation</h3>
|
|||
|
<p>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.</p>
|
|||
|
<p>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:</p>
|
|||
|
<img src="raster-reprojection-resources/iterative-triangulation.png" alt="Iterative triangulation" width="600" />
|
|||
|
|
|||
|
<p>For debugging, rendering of the reprojection edges can be enabled by <code>ol.source.TileImage#setRenderReprojectionEdges(true)</code>.</p>
|
|||
|
<h1 id="advanced">Advanced</h1>
|
|||
|
<h3 id="triangulation-precision-threshold">Triangulation precision threshold</h3>
|
|||
|
<p>The default <a href="#dynamic-triangulation">triangulation error threshold</a> in pixels is given by <code>ERROR_THRESHOLD</code> (0.5 pixel).
|
|||
|
In case a different threshold needs to be defined for different sources, the <code>reprojectionErrorThreshold</code> option can be passed when constructing the tile image source.</p>
|
|||
|
<h3 id="limiting-visibility-of-reprojected-map-by-extent">Limiting visibility of reprojected map by extent</h3>
|
|||
|
<p>The reprojection algorithm uses inverse transformation (from <em>view projection</em> to <em>data projection</em>).
|
|||
|
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.</p>
|
|||
|
<img src="raster-reprojection-resources/double-occurrence.jpg" alt="Double occurrence of a reprojected map" width="600" />
|
|||
|
|
|||
|
<p>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).</p>
|
|||
|
<p>Therefore a recommended workaround is to define a proper visibility extent on the <code>ol.layer.Tile</code> in the view projection.
|
|||
|
Setting such a limit is demonstrated in the <a href="https://openlayers.org/en/latest/examples/reprojection.html">reprojection demo example</a>.</p>
|
|||
|
<h3 id="resolution-calculation">Resolution calculation</h3>
|
|||
|
<p>When determining source tiles to load, the ideal source resolution needs to be calculated.
|
|||
|
The <code>ol/reproj~calculateSourceResolution(sourceProj, targetProj, targetCenter, targetResolution)</code> 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.</p>
|
|||
|
<p>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).</p>
|
|||
|
|
|||
|
</div>
|
|||
|
<footer>
|
|||
|
Code licensed under the <a href='http://www.tldrlegal.com/license/bsd-2-clause-license-(freebsd)'>2-Clause BSD</a>.
|
|||
|
All documentation <a href='http://creativecommons.org/licenses/by/3.0/'>CC BY 3.0</a>.
|
|||
|
Thanks to our <a href='https://opencollective.com/openlayers'>sponsors</a>.
|
|||
|
<br>
|
|||
|
<a href="https://www.netlify.com">This site is powered by Netlify.</a>
|
|||
|
</footer>
|
|||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"></script>
|
|||
|
</body>
|
|||
|
</html>
|