Merge pull request #4122 from klokantech/rasterreproj
Raster reprojection
26
examples/reprojection-by-code.html
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
template: example.html
|
||||||
|
title: Reprojection with EPSG.io database search
|
||||||
|
shortdesc: Demonstrates client-side raster reprojection of MapQuest OSM to arbitrary projection
|
||||||
|
docs: >
|
||||||
|
This example shows client-side raster reprojection capabilities from
|
||||||
|
MapQuest OSM (EPSG:3857) to arbitrary projection by searching
|
||||||
|
in <a href="http://epsg.io/">EPSG.io</a> database.
|
||||||
|
tags: "reprojection, projection, proj4js, mapquest, epsg.io"
|
||||||
|
resources:
|
||||||
|
- http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js
|
||||||
|
---
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
</div>
|
||||||
|
<form class="form-inline">
|
||||||
|
<label for="epsg-query">Search projection:</label>
|
||||||
|
<input type="text" id="epsg-query" placeholder="4326, 27700, US National Atlas, Swiss, France, ..." class="form-control" size="50" />
|
||||||
|
<button id="epsg-search" class="btn">Search</button>
|
||||||
|
<span id="epsg-result"></span>
|
||||||
|
<div>
|
||||||
|
<label for="render-edges"><input type="checkbox" id="render-edges" />Render reprojection edges</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
116
examples/reprojection-by-code.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.View');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.source.MapQuest');
|
||||||
|
goog.require('ol.source.TileImage');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
layers: [
|
||||||
|
new ol.layer.Tile({
|
||||||
|
source: new ol.source.MapQuest({layer: 'osm'})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
renderer: common.getRendererFromQueryString(),
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 1
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var queryInput = document.getElementById('epsg-query');
|
||||||
|
var searchButton = document.getElementById('epsg-search');
|
||||||
|
var resultSpan = document.getElementById('epsg-result');
|
||||||
|
var renderEdgesCheckbox = document.getElementById('render-edges');
|
||||||
|
|
||||||
|
function setProjection(code, name, proj4def, bbox) {
|
||||||
|
if (code === null || name === null || proj4def === null || bbox === null) {
|
||||||
|
resultSpan.innerHTML = 'Nothing usable found, using EPSG:3857...';
|
||||||
|
map.setView(new ol.View({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 1
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultSpan.innerHTML = '(' + code + ') ' + name;
|
||||||
|
|
||||||
|
var newProjCode = 'EPSG:' + code;
|
||||||
|
proj4.defs(newProjCode, proj4def);
|
||||||
|
var newProj = ol.proj.get(newProjCode);
|
||||||
|
var fromLonLat = ol.proj.getTransform('EPSG:4326', newProj);
|
||||||
|
|
||||||
|
// very approximate calculation of projection extent
|
||||||
|
var extent = ol.extent.applyTransform(
|
||||||
|
[bbox[1], bbox[2], bbox[3], bbox[0]], fromLonLat);
|
||||||
|
newProj.setExtent(extent);
|
||||||
|
var newView = new ol.View({
|
||||||
|
projection: newProj
|
||||||
|
});
|
||||||
|
map.setView(newView);
|
||||||
|
|
||||||
|
var size = map.getSize();
|
||||||
|
if (size) {
|
||||||
|
newView.fit(extent, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function search(query) {
|
||||||
|
resultSpan.innerHTML = 'Searching...';
|
||||||
|
$.ajax({
|
||||||
|
url: 'http://epsg.io/?format=json&q=' + query,
|
||||||
|
dataType: 'jsonp',
|
||||||
|
success: function(response) {
|
||||||
|
if (response) {
|
||||||
|
var results = response['results'];
|
||||||
|
if (results && results.length > 0) {
|
||||||
|
for (var i = 0, ii = results.length; i < ii; i++) {
|
||||||
|
var result = results[i];
|
||||||
|
if (result) {
|
||||||
|
var code = result['code'], name = result['name'],
|
||||||
|
proj4def = result['proj4'], bbox = result['bbox'];
|
||||||
|
if (code && code.length > 0 && proj4def && proj4def.length > 0 &&
|
||||||
|
bbox && bbox.length == 4) {
|
||||||
|
setProjection(code, name, proj4def, bbox);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setProjection(null, null, null, null);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
searchButton.onclick = function(e) {
|
||||||
|
search(queryInput.value);
|
||||||
|
e.preventDefault();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
renderEdgesCheckbox.onchange = function(e) {
|
||||||
|
map.getLayers().forEach(function(layer) {
|
||||||
|
if (layer instanceof ol.layer.Tile) {
|
||||||
|
var source = layer.getSource();
|
||||||
|
if (source instanceof ol.source.TileImage) {
|
||||||
|
source.setRenderReprojectionEdges(renderEdgesCheckbox.checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
15
examples/reprojection-image.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
template: example.html
|
||||||
|
title: Image reprojection example
|
||||||
|
shortdesc: Demonstrates client-side reprojection of single image source.
|
||||||
|
docs: >
|
||||||
|
This example shows client-side reprojection of single image source.
|
||||||
|
tags: "reprojection, projection, proj4js, mapquest, image, imagestatic"
|
||||||
|
resources:
|
||||||
|
- http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js
|
||||||
|
---
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
39
examples/reprojection-image.js
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.View');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.layer.Image');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.source.ImageStatic');
|
||||||
|
goog.require('ol.source.MapQuest');
|
||||||
|
|
||||||
|
|
||||||
|
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');
|
||||||
|
var imageExtent = [0, 0, 700000, 1300000];
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
layers: [
|
||||||
|
new ol.layer.Tile({
|
||||||
|
source: new ol.source.MapQuest({layer: 'osm'})
|
||||||
|
}),
|
||||||
|
new ol.layer.Image({
|
||||||
|
source: new ol.source.ImageStatic({
|
||||||
|
url: 'http://upload.wikimedia.org/wikipedia/commons/thumb/1/18/' +
|
||||||
|
'British_National_Grid.svg/2000px-British_National_Grid.svg.png',
|
||||||
|
crossOrigin: '',
|
||||||
|
projection: 'EPSG:27700',
|
||||||
|
imageExtent: imageExtent
|
||||||
|
})
|
||||||
|
})
|
||||||
|
],
|
||||||
|
renderer: common.getRendererFromQueryString(),
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View({
|
||||||
|
center: ol.proj.transform(ol.extent.getCenter(imageExtent),
|
||||||
|
'EPSG:27700', 'EPSG:3857'),
|
||||||
|
zoom: 4
|
||||||
|
})
|
||||||
|
});
|
||||||
13
examples/reprojection-wgs84.html
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
template: example.html
|
||||||
|
title: OpenStreetMap reprojection example
|
||||||
|
shortdesc: Demonstrates client-side reprojection of OpenStreetMap in WGS84.
|
||||||
|
docs: >
|
||||||
|
This example shows client-side reprojection of OpenStreetMap in WGS84.
|
||||||
|
tags: "reprojection, projection, openstreetmap, wgs84, tile"
|
||||||
|
---
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
19
examples/reprojection-wgs84.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.View');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.source.OSM');
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
layers: [
|
||||||
|
new ol.layer.Tile({
|
||||||
|
source: new ol.source.OSM()
|
||||||
|
})
|
||||||
|
],
|
||||||
|
renderer: common.getRendererFromQueryString(),
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View({
|
||||||
|
projection: 'EPSG:4326',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 1
|
||||||
|
})
|
||||||
|
});
|
||||||
48
examples/reprojection.html
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
template: example.html
|
||||||
|
title: Raster reprojection example
|
||||||
|
shortdesc: Demonstrates client-side raster reprojection between various projections.
|
||||||
|
docs: >
|
||||||
|
This example shows client-side raster reprojection between various projections.
|
||||||
|
tags: "reprojection, projection, proj4js, mapquest, wms, wmts, hidpi"
|
||||||
|
resources:
|
||||||
|
- http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js
|
||||||
|
---
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<div id="map" class="map"></div>
|
||||||
|
</div>
|
||||||
|
<form class="form-inline">
|
||||||
|
<div class="col-md-3">
|
||||||
|
<label>Base map:</label>
|
||||||
|
<select id="base-layer">
|
||||||
|
<option value="mapquest">MapQuest (EPSG:3857)</option>
|
||||||
|
<option value="wms4326">WMS (EPSG:4326)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label>Overlay map:</label>
|
||||||
|
<select id="overlay-layer">
|
||||||
|
<option value="bng">British National Grid (EPSG:27700)</option>
|
||||||
|
<option value="wms21781">Swisstopo WMS (EPSG:21781)</option>
|
||||||
|
<option value="wmts3413">NASA Arctic WMTS (EPSG:3413)</option>
|
||||||
|
<option value="grandcanyon">Grand Canyon HiDPI (EPSG:3857)</option>
|
||||||
|
<option value="states">United States (EPSG:3857)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-5">
|
||||||
|
<label>View projection:</label>
|
||||||
|
<select id="view-projection">
|
||||||
|
<option value="EPSG:3857">Spherical Mercator (EPSG:3857)</option>
|
||||||
|
<option value="EPSG:4326">WGS 84 (EPSG:4326)</option>
|
||||||
|
<option value="ESRI:54009">Mollweide (ESRI:54009)</option>
|
||||||
|
<option value="EPSG:27700">British National Grid (EPSG:27700)</option>
|
||||||
|
<option value="EPSG:23032">ED50 / UTM zone 32N (EPSG:23032)</option>
|
||||||
|
<option value="EPSG:2163">US National Atlas Equal Area (EPSG:2163)</option>
|
||||||
|
<option value="EPSG:3413">NSIDC Polar Stereographic North (EPSG:3413)</option>
|
||||||
|
<option value="EPSG:5479">RSRGD2000 / MSLC2000 (EPSG:5479)</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<label for="render-edges"><input type="checkbox" id="render-edges" />Render reprojection edges</label> (only displayed on reprojected data)
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
244
examples/reprojection.js
Normal file
@@ -0,0 +1,244 @@
|
|||||||
|
goog.require('ol.Attribution');
|
||||||
|
goog.require('ol.Map');
|
||||||
|
goog.require('ol.View');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.format.WMTSCapabilities');
|
||||||
|
goog.require('ol.layer.Tile');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.source.MapQuest');
|
||||||
|
goog.require('ol.source.TileImage');
|
||||||
|
goog.require('ol.source.TileWMS');
|
||||||
|
goog.require('ol.source.WMTS');
|
||||||
|
goog.require('ol.source.XYZ');
|
||||||
|
goog.require('ol.tilegrid.TileGrid');
|
||||||
|
|
||||||
|
|
||||||
|
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');
|
||||||
|
var proj27700 = ol.proj.get('EPSG:27700');
|
||||||
|
proj27700.setExtent([0, 0, 700000, 1300000]);
|
||||||
|
|
||||||
|
proj4.defs('EPSG:23032', '+proj=utm +zone=32 +ellps=intl ' +
|
||||||
|
'+towgs84=-87,-98,-121,0,0,0,0 +units=m +no_defs');
|
||||||
|
var proj23032 = ol.proj.get('EPSG:23032');
|
||||||
|
proj23032.setExtent([-1206118.71, 4021309.92, 1295389.00, 8051813.28]);
|
||||||
|
|
||||||
|
proj4.defs('EPSG:5479', '+proj=lcc +lat_1=-76.66666666666667 +lat_2=' +
|
||||||
|
'-79.33333333333333 +lat_0=-78 +lon_0=163 +x_0=7000000 +y_0=5000000 ' +
|
||||||
|
'+ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
|
||||||
|
var proj5479 = ol.proj.get('EPSG:5479');
|
||||||
|
proj5479.setExtent([6825737.53, 4189159.80, 9633741.96, 5782472.71]);
|
||||||
|
|
||||||
|
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=674.4,15.1,405.3,0,0,0,0 +units=m +no_defs');
|
||||||
|
var proj21781 = ol.proj.get('EPSG:21781');
|
||||||
|
proj21781.setExtent([485071.54, 75346.36, 828515.78, 299941.84]);
|
||||||
|
|
||||||
|
proj4.defs('EPSG:3413', '+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 +k=1 ' +
|
||||||
|
'+x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
|
||||||
|
var proj3413 = ol.proj.get('EPSG:3413');
|
||||||
|
proj3413.setExtent([-4194304, -4194304, 4194304, 4194304]);
|
||||||
|
|
||||||
|
proj4.defs('EPSG:2163', '+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 ' +
|
||||||
|
'+a=6370997 +b=6370997 +units=m +no_defs');
|
||||||
|
var proj2163 = ol.proj.get('EPSG:2163');
|
||||||
|
proj2163.setExtent([-8040784.5135, -2577524.9210, 3668901.4484, 4785105.1096]);
|
||||||
|
|
||||||
|
proj4.defs('ESRI:54009', '+proj=moll +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 ' +
|
||||||
|
'+units=m +no_defs');
|
||||||
|
var proj54009 = ol.proj.get('ESRI:54009');
|
||||||
|
proj54009.setExtent([-18e6, -9e6, 18e6, 9e6]);
|
||||||
|
|
||||||
|
|
||||||
|
var layers = [];
|
||||||
|
|
||||||
|
layers['bng'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:27700',
|
||||||
|
url: 'http://tileserver.maptiler.com/miniscale/{z}/{x}/{y}.png',
|
||||||
|
crossOrigin: '',
|
||||||
|
maxZoom: 6
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
layers['mapquest'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.MapQuest({layer: 'osm'})
|
||||||
|
});
|
||||||
|
|
||||||
|
layers['wms4326'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.TileWMS({
|
||||||
|
url: 'http://demo.boundlessgeo.com/geoserver/wms',
|
||||||
|
crossOrigin: '',
|
||||||
|
params: {
|
||||||
|
'LAYERS': 'ne:NE1_HR_LC_SR_W_DR'
|
||||||
|
},
|
||||||
|
projection: 'EPSG:4326'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
layers['wms21781'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.TileWMS({
|
||||||
|
attributions: [new ol.Attribution({
|
||||||
|
html: '© ' +
|
||||||
|
'<a href="http://www.geo.admin.ch/internet/geoportal/' +
|
||||||
|
'en/home.html">' +
|
||||||
|
'Pixelmap 1:1000000 / geo.admin.ch</a>'
|
||||||
|
})],
|
||||||
|
crossOrigin: 'anonymous',
|
||||||
|
params: {
|
||||||
|
'LAYERS': 'ch.swisstopo.pixelkarte-farbe-pk1000.noscale',
|
||||||
|
'FORMAT': 'image/jpeg'
|
||||||
|
},
|
||||||
|
url: 'http://wms.geo.admin.ch/',
|
||||||
|
projection: 'EPSG:21781'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var parser = new ol.format.WMTSCapabilities();
|
||||||
|
$.ajax('http://map1.vis.earthdata.nasa.gov/wmts-arctic/' +
|
||||||
|
'wmts.cgi?SERVICE=WMTS&request=GetCapabilities').then(function(response) {
|
||||||
|
var result = parser.read(response);
|
||||||
|
var options = ol.source.WMTS.optionsFromCapabilities(result,
|
||||||
|
{layer: 'OSM_Land_Mask', matrixSet: 'EPSG3413_250m'});
|
||||||
|
options.crossOrigin = '';
|
||||||
|
options.projection = 'EPSG:3413';
|
||||||
|
options.wrapX = false;
|
||||||
|
layers['wmts3413'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.WMTS(options)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
layers['grandcanyon'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.XYZ({
|
||||||
|
url: 'http://tileserver.maptiler.com/grandcanyon@2x/{z}/{x}/{y}.png',
|
||||||
|
crossOrigin: '',
|
||||||
|
tilePixelRatio: 2,
|
||||||
|
maxZoom: 15,
|
||||||
|
attributions: [new ol.Attribution({
|
||||||
|
html: 'Tiles © USGS, rendered with ' +
|
||||||
|
'<a href="http://www.maptiler.com/">MapTiler</a>'
|
||||||
|
})]
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
var startResolution =
|
||||||
|
ol.extent.getWidth(ol.proj.get('EPSG:3857').getExtent()) / 256;
|
||||||
|
var resolutions = new Array(22);
|
||||||
|
for (var i = 0, ii = resolutions.length; i < ii; ++i) {
|
||||||
|
resolutions[i] = startResolution / Math.pow(2, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
layers['states'] = new ol.layer.Tile({
|
||||||
|
source: new ol.source.TileWMS({
|
||||||
|
url: 'http://demo.boundlessgeo.com/geoserver/wms',
|
||||||
|
crossOrigin: '',
|
||||||
|
params: {'LAYERS': 'topp:states', 'TILED': true},
|
||||||
|
serverType: 'geoserver',
|
||||||
|
tileGrid: new ol.tilegrid.TileGrid({
|
||||||
|
extent: [-13884991, 2870341, -7455066, 6338219],
|
||||||
|
resolutions: resolutions,
|
||||||
|
tileSize: [512, 256]
|
||||||
|
}),
|
||||||
|
projection: 'EPSG:3857'
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var map = new ol.Map({
|
||||||
|
layers: [
|
||||||
|
layers['mapquest'],
|
||||||
|
layers['bng']
|
||||||
|
],
|
||||||
|
renderer: common.getRendererFromQueryString(),
|
||||||
|
target: 'map',
|
||||||
|
view: new ol.View({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
center: [0, 0],
|
||||||
|
zoom: 2
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var baseLayerSelect = document.getElementById('base-layer');
|
||||||
|
var overlayLayerSelect = document.getElementById('overlay-layer');
|
||||||
|
var viewProjSelect = document.getElementById('view-projection');
|
||||||
|
var renderEdgesCheckbox = document.getElementById('render-edges');
|
||||||
|
var renderEdges = false;
|
||||||
|
|
||||||
|
function updateViewProjection() {
|
||||||
|
var newProj = ol.proj.get(viewProjSelect.value);
|
||||||
|
var newProjExtent = newProj.getExtent();
|
||||||
|
var newView = new ol.View({
|
||||||
|
projection: newProj,
|
||||||
|
center: ol.extent.getCenter(newProjExtent || [0, 0, 0, 0]),
|
||||||
|
zoom: 0,
|
||||||
|
extent: newProjExtent || undefined
|
||||||
|
});
|
||||||
|
map.setView(newView);
|
||||||
|
|
||||||
|
// Example how to prevent double occurence of map by limiting layer extent
|
||||||
|
if (newProj == ol.proj.get('EPSG:3857')) {
|
||||||
|
layers['bng'].setExtent([-1057216, 6405988, 404315, 8759696]);
|
||||||
|
} else {
|
||||||
|
layers['bng'].setExtent(undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
viewProjSelect.onchange = function(e) {
|
||||||
|
updateViewProjection();
|
||||||
|
};
|
||||||
|
|
||||||
|
updateViewProjection();
|
||||||
|
|
||||||
|
var updateRenderEdgesOnLayer = function(layer) {
|
||||||
|
if (layer instanceof ol.layer.Tile) {
|
||||||
|
var source = layer.getSource();
|
||||||
|
if (source instanceof ol.source.TileImage) {
|
||||||
|
source.setRenderReprojectionEdges(renderEdges);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
baseLayerSelect.onchange = function(e) {
|
||||||
|
var layer = layers[baseLayerSelect.value];
|
||||||
|
if (layer) {
|
||||||
|
layer.setOpacity(1);
|
||||||
|
updateRenderEdgesOnLayer(layer);
|
||||||
|
map.getLayers().setAt(0, layer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
overlayLayerSelect.onchange = function(e) {
|
||||||
|
var layer = layers[overlayLayerSelect.value];
|
||||||
|
if (layer) {
|
||||||
|
layer.setOpacity(0.7);
|
||||||
|
updateRenderEdgesOnLayer(layer);
|
||||||
|
map.getLayers().setAt(1, layer);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} e Change event.
|
||||||
|
*/
|
||||||
|
renderEdgesCheckbox.onchange = function(e) {
|
||||||
|
renderEdges = renderEdgesCheckbox.checked;
|
||||||
|
map.getLayers().forEach(function(layer) {
|
||||||
|
updateRenderEdgesOnLayer(layer);
|
||||||
|
});
|
||||||
|
};
|
||||||
110
externs/olx.js
@@ -3602,6 +3602,7 @@ olx.source;
|
|||||||
* key: string,
|
* key: string,
|
||||||
* imagerySet: string,
|
* imagerySet: string,
|
||||||
* maxZoom: (number|undefined),
|
* maxZoom: (number|undefined),
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* wrapX: (boolean|undefined)}}
|
* wrapX: (boolean|undefined)}}
|
||||||
* @api
|
* @api
|
||||||
@@ -3642,6 +3643,15 @@ olx.source.BingMapsOptions.prototype.imagerySet;
|
|||||||
olx.source.BingMapsOptions.prototype.maxZoom;
|
olx.source.BingMapsOptions.prototype.maxZoom;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.BingMapsOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -3761,6 +3771,7 @@ olx.source.TileUTFGridOptions.prototype.url;
|
|||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
* opaque: (boolean|undefined),
|
* opaque: (boolean|undefined),
|
||||||
* projection: ol.proj.ProjectionLike,
|
* projection: ol.proj.ProjectionLike,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* state: (ol.source.State|string|undefined),
|
* state: (ol.source.State|string|undefined),
|
||||||
* tileClass: (function(new: ol.ImageTile, ol.TileCoord,
|
* tileClass: (function(new: ol.ImageTile, ol.TileCoord,
|
||||||
* ol.TileState, string, ?string,
|
* ol.TileState, string, ?string,
|
||||||
@@ -3819,6 +3830,15 @@ olx.source.TileImageOptions.prototype.opaque;
|
|||||||
olx.source.TileImageOptions.prototype.projection;
|
olx.source.TileImageOptions.prototype.projection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.TileImageOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Source state.
|
* Source state.
|
||||||
* @type {ol.source.State|string|undefined}
|
* @type {ol.source.State|string|undefined}
|
||||||
@@ -4076,6 +4096,7 @@ olx.source.ImageMapGuideOptions.prototype.params;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {{layer: string,
|
* @typedef {{layer: string,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: (string|undefined)}}
|
* url: (string|undefined)}}
|
||||||
* @api
|
* @api
|
||||||
@@ -4091,6 +4112,15 @@ olx.source.MapQuestOptions;
|
|||||||
olx.source.MapQuestOptions.prototype.layer;
|
olx.source.MapQuestOptions.prototype.layer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.MapQuestOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -4144,6 +4174,7 @@ olx.source.TileDebugOptions.prototype.wrapX;
|
|||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* crossOrigin: (null|string|undefined),
|
* crossOrigin: (null|string|undefined),
|
||||||
* maxZoom: (number|undefined),
|
* maxZoom: (number|undefined),
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: (string|undefined),
|
* url: (string|undefined),
|
||||||
* wrapX: (boolean|undefined)}}
|
* wrapX: (boolean|undefined)}}
|
||||||
@@ -4182,6 +4213,15 @@ olx.source.OSMOptions.prototype.crossOrigin;
|
|||||||
olx.source.OSMOptions.prototype.maxZoom;
|
olx.source.OSMOptions.prototype.maxZoom;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.OSMOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -4538,6 +4578,7 @@ olx.source.ImageWMSOptions.prototype.url;
|
|||||||
* minZoom: (number|undefined),
|
* minZoom: (number|undefined),
|
||||||
* maxZoom: (number|undefined),
|
* maxZoom: (number|undefined),
|
||||||
* opaque: (boolean|undefined),
|
* opaque: (boolean|undefined),
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: (string|undefined)}}
|
* url: (string|undefined)}}
|
||||||
* @api
|
* @api
|
||||||
@@ -4577,6 +4618,15 @@ olx.source.StamenOptions.prototype.maxZoom;
|
|||||||
olx.source.StamenOptions.prototype.opaque;
|
olx.source.StamenOptions.prototype.opaque;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.StamenOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -4683,6 +4733,7 @@ olx.source.ImageStaticOptions.prototype.url;
|
|||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
||||||
* projection: ol.proj.ProjectionLike,
|
* projection: ol.proj.ProjectionLike,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: (string|undefined),
|
* url: (string|undefined),
|
||||||
* urls: (Array.<string>|undefined),
|
* urls: (Array.<string>|undefined),
|
||||||
@@ -4754,6 +4805,15 @@ olx.source.TileArcGISRestOptions.prototype.tileGrid;
|
|||||||
olx.source.TileArcGISRestOptions.prototype.projection;
|
olx.source.TileArcGISRestOptions.prototype.projection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.TileArcGISRestOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -4791,6 +4851,7 @@ olx.source.TileArcGISRestOptions.prototype.urls;
|
|||||||
/**
|
/**
|
||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* crossOrigin: (null|string|undefined),
|
* crossOrigin: (null|string|undefined),
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: string,
|
* url: string,
|
||||||
* wrapX: (boolean|undefined)}}
|
* wrapX: (boolean|undefined)}}
|
||||||
@@ -4821,6 +4882,15 @@ olx.source.TileJSONOptions.prototype.attributions;
|
|||||||
olx.source.TileJSONOptions.prototype.crossOrigin;
|
olx.source.TileJSONOptions.prototype.crossOrigin;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.TileJSONOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional function to load a tile given a URL.
|
* Optional function to load a tile given a URL.
|
||||||
* @type {ol.TileLoadFunctionType|undefined}
|
* @type {ol.TileLoadFunctionType|undefined}
|
||||||
@@ -4855,6 +4925,7 @@ olx.source.TileJSONOptions.prototype.wrapX;
|
|||||||
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
||||||
* maxZoom: (number|undefined),
|
* maxZoom: (number|undefined),
|
||||||
* projection: ol.proj.ProjectionLike,
|
* projection: ol.proj.ProjectionLike,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* serverType: (ol.source.wms.ServerType|string|undefined),
|
* serverType: (ol.source.wms.ServerType|string|undefined),
|
||||||
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
* tileLoadFunction: (ol.TileLoadFunctionType|undefined),
|
||||||
* url: (string|undefined),
|
* url: (string|undefined),
|
||||||
@@ -4955,6 +5026,15 @@ olx.source.TileWMSOptions.prototype.maxZoom;
|
|||||||
olx.source.TileWMSOptions.prototype.projection;
|
olx.source.TileWMSOptions.prototype.projection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.TileWMSOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the remote WMS server. Currently only used when `hidpi` is
|
* The type of the remote WMS server. Currently only used when `hidpi` is
|
||||||
* `true`. Default is `undefined`.
|
* `true`. Default is `undefined`.
|
||||||
@@ -5120,6 +5200,7 @@ olx.source.VectorOptions.prototype.wrapX;
|
|||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
* tileGrid: ol.tilegrid.WMTS,
|
* tileGrid: ol.tilegrid.WMTS,
|
||||||
* projection: ol.proj.ProjectionLike,
|
* projection: ol.proj.ProjectionLike,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* requestEncoding: (ol.source.WMTSRequestEncoding|string|undefined),
|
* requestEncoding: (ol.source.WMTSRequestEncoding|string|undefined),
|
||||||
* layer: string,
|
* layer: string,
|
||||||
* style: string,
|
* style: string,
|
||||||
@@ -5185,6 +5266,15 @@ olx.source.WMTSOptions.prototype.tileGrid;
|
|||||||
olx.source.WMTSOptions.prototype.projection;
|
olx.source.WMTSOptions.prototype.projection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.WMTSOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request encoding. Default is `KVP`.
|
* Request encoding. Default is `KVP`.
|
||||||
* @type {ol.source.WMTSRequestEncoding|string|undefined}
|
* @type {ol.source.WMTSRequestEncoding|string|undefined}
|
||||||
@@ -5311,6 +5401,7 @@ olx.source.WMTSOptions.prototype.wrapX;
|
|||||||
* crossOrigin: (null|string|undefined),
|
* crossOrigin: (null|string|undefined),
|
||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
* projection: ol.proj.ProjectionLike,
|
* projection: ol.proj.ProjectionLike,
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* maxZoom: (number|undefined),
|
* maxZoom: (number|undefined),
|
||||||
* minZoom: (number|undefined),
|
* minZoom: (number|undefined),
|
||||||
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
* tileGrid: (ol.tilegrid.TileGrid|undefined),
|
||||||
@@ -5362,6 +5453,15 @@ olx.source.XYZOptions.prototype.logo;
|
|||||||
olx.source.XYZOptions.prototype.projection;
|
olx.source.XYZOptions.prototype.projection;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.XYZOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optional max zoom level. Default is `18`.
|
* Optional max zoom level. Default is `18`.
|
||||||
* @type {number|undefined}
|
* @type {number|undefined}
|
||||||
@@ -5452,6 +5552,7 @@ olx.source.XYZOptions.prototype.wrapX;
|
|||||||
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
* @typedef {{attributions: (Array.<ol.Attribution>|undefined),
|
||||||
* crossOrigin: (null|string|undefined),
|
* crossOrigin: (null|string|undefined),
|
||||||
* logo: (string|olx.LogoOptions|undefined),
|
* logo: (string|olx.LogoOptions|undefined),
|
||||||
|
* reprojectionErrorThreshold: (number|undefined),
|
||||||
* url: !string,
|
* url: !string,
|
||||||
* tierSizeCalculation: (string|undefined),
|
* tierSizeCalculation: (string|undefined),
|
||||||
* size: ol.Size}}
|
* size: ol.Size}}
|
||||||
@@ -5488,6 +5589,15 @@ olx.source.ZoomifyOptions.prototype.crossOrigin;
|
|||||||
olx.source.ZoomifyOptions.prototype.logo;
|
olx.source.ZoomifyOptions.prototype.logo;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Maximum allowed reprojection error (in pixels). Default is `0.5`.
|
||||||
|
* Higher values can increase reprojection performance, but decrease precision.
|
||||||
|
* @type {number|undefined}
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
olx.source.ZoomifyOptions.prototype.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefix of URL template.
|
* Prefix of URL template.
|
||||||
* @type {!string}
|
* @type {!string}
|
||||||
|
|||||||
@@ -78,6 +78,69 @@ ol.math.squaredDistance = function(x1, y1, x2, y2) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Solves system of linear equations using Gaussian elimination method.
|
||||||
|
*
|
||||||
|
* @param {Array.<Array.<number>>} mat Augmented matrix (n x n + 1 column)
|
||||||
|
* in row-major order.
|
||||||
|
* @return {Array.<number>} The resulting vector.
|
||||||
|
*/
|
||||||
|
ol.math.solveLinearSystem = function(mat) {
|
||||||
|
var n = mat.length;
|
||||||
|
|
||||||
|
if (goog.asserts.ENABLE_ASSERTS) {
|
||||||
|
for (var row = 0; row < n; row++) {
|
||||||
|
goog.asserts.assert(mat[row].length == n + 1,
|
||||||
|
'every row should have correct number of columns');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
// Find max in the i-th column (ignoring i - 1 first rows)
|
||||||
|
var maxRow = i;
|
||||||
|
var maxEl = Math.abs(mat[i][i]);
|
||||||
|
for (var r = i + 1; r < n; r++) {
|
||||||
|
var absValue = Math.abs(mat[r][i]);
|
||||||
|
if (absValue > maxEl) {
|
||||||
|
maxEl = absValue;
|
||||||
|
maxRow = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxEl === 0) {
|
||||||
|
return null; // matrix is singular
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap max row with i-th (current) row
|
||||||
|
var tmp = mat[maxRow];
|
||||||
|
mat[maxRow] = mat[i];
|
||||||
|
mat[i] = tmp;
|
||||||
|
|
||||||
|
// Subtract the i-th row to make all the remaining rows 0 in the i-th column
|
||||||
|
for (var j = i + 1; j < n; j++) {
|
||||||
|
var coef = -mat[j][i] / mat[i][i];
|
||||||
|
for (var k = i; k < n + 1; k++) {
|
||||||
|
if (i == k) {
|
||||||
|
mat[j][k] = 0;
|
||||||
|
} else {
|
||||||
|
mat[j][k] += coef * mat[i][k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Solve Ax=b for upper triangular matrix A (mat)
|
||||||
|
var x = new Array(n);
|
||||||
|
for (var l = n - 1; l >= 0; l--) {
|
||||||
|
x[l] = mat[l][n] / mat[l][l];
|
||||||
|
for (var m = l - 1; m >= 0; m--) {
|
||||||
|
mat[m][n] -= mat[m][l] * x[l];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts radians to to degrees.
|
* Converts radians to to degrees.
|
||||||
*
|
*
|
||||||
|
|||||||
49
src/ol/ol.js
@@ -28,6 +28,13 @@ ol.DEFAULT_MAX_ZOOM = 42;
|
|||||||
ol.DEFAULT_MIN_ZOOM = 0;
|
ol.DEFAULT_MIN_ZOOM = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @define {number} Default maximum allowed threshold (in pixels) for
|
||||||
|
* reprojection triangulation. Default is `0.5`.
|
||||||
|
*/
|
||||||
|
ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD = 0.5;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @define {number} Default high water mark.
|
* @define {number} Default high water mark.
|
||||||
*/
|
*/
|
||||||
@@ -94,6 +101,13 @@ ol.ENABLE_NAMED_COLORS = false;
|
|||||||
ol.ENABLE_PROJ4JS = true;
|
ol.ENABLE_PROJ4JS = true;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @define {boolean} Enable automatic reprojection of raster sources. Default is
|
||||||
|
* `true`.
|
||||||
|
*/
|
||||||
|
ol.ENABLE_RASTER_REPROJECTION = true;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @define {boolean} Enable rendering of ol.layer.Tile based layers. Default is
|
* @define {boolean} Enable rendering of ol.layer.Tile based layers. Default is
|
||||||
* `true`. Setting this to false at compile time in advanced mode removes
|
* `true`. Setting this to false at compile time in advanced mode removes
|
||||||
@@ -159,6 +173,41 @@ ol.OVERVIEWMAP_MAX_RATIO = 0.75;
|
|||||||
ol.OVERVIEWMAP_MIN_RATIO = 0.1;
|
ol.OVERVIEWMAP_MIN_RATIO = 0.1;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @define {number} Maximum number of source tiles for raster reprojection of
|
||||||
|
* a single tile.
|
||||||
|
* If too many source tiles are determined to be loaded to create a single
|
||||||
|
* reprojected tile the browser can become unresponsive or even crash.
|
||||||
|
* This can happen if the developer defines projections improperly and/or
|
||||||
|
* with unlimited extents.
|
||||||
|
* If too many tiles are required, no tiles are loaded and
|
||||||
|
* `ol.TileState.ERROR` state is set. Default is `100`.
|
||||||
|
*/
|
||||||
|
ol.RASTER_REPROJECTION_MAX_SOURCE_TILES = 100;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @define {number} Maximum number of subdivision steps during raster
|
||||||
|
* reprojection triangulation. Prevents high memory usage and large
|
||||||
|
* number of proj4 calls (for certain transformations and areas).
|
||||||
|
* At most `2*(2^this)` triangles are created for each triangulated
|
||||||
|
* extent (tile/image). Default is `10`.
|
||||||
|
*/
|
||||||
|
ol.RASTER_REPROJECTION_MAX_SUBDIVISION = 10;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @define {number} Maximum allowed size of triangle relative to world width.
|
||||||
|
* When transforming corners of world extent between certain projections,
|
||||||
|
* the resulting triangulation seems to have zero error and no subdivision
|
||||||
|
* is performed.
|
||||||
|
* If the triangle width is more than this (relative to world width; 0-1),
|
||||||
|
* subdivison is forced (up to `ol.RASTER_REPROJECTION_MAX_SUBDIVISION`).
|
||||||
|
* Default is `0.25`.
|
||||||
|
*/
|
||||||
|
ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH = 0.25;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @define {number} Tolerance for geometry simplification in device pixels.
|
* @define {number} Tolerance for geometry simplification in device pixels.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -170,11 +170,13 @@ ol.renderer.canvas.ImageLayer.prototype.prepareFrame =
|
|||||||
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
||||||
!ol.extent.isEmpty(renderedExtent)) {
|
!ol.extent.isEmpty(renderedExtent)) {
|
||||||
var projection = viewState.projection;
|
var projection = viewState.projection;
|
||||||
var sourceProjection = imageSource.getProjection();
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
if (sourceProjection) {
|
var sourceProjection = imageSource.getProjection();
|
||||||
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
if (sourceProjection) {
|
||||||
'projection and sourceProjection are equivalent');
|
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
||||||
projection = sourceProjection;
|
'projection and sourceProjection are equivalent');
|
||||||
|
projection = sourceProjection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
image = imageSource.getImage(
|
image = imageSource.getImage(
|
||||||
renderedExtent, viewResolution, pixelRatio, projection);
|
renderedExtent, viewResolution, pixelRatio, projection);
|
||||||
|
|||||||
@@ -309,7 +309,8 @@ ol.renderer.canvas.TileLayer.prototype.prepareFrame =
|
|||||||
/** @type {Array.<ol.Tile>} */
|
/** @type {Array.<ol.Tile>} */
|
||||||
var tilesToClear = [];
|
var tilesToClear = [];
|
||||||
|
|
||||||
var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
|
var findLoadedTiles = this.createLoadedTileFinder(
|
||||||
|
tileSource, projection, tilesToDrawByZ);
|
||||||
|
|
||||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||||
|
|
||||||
|
|||||||
@@ -101,11 +101,13 @@ ol.renderer.dom.ImageLayer.prototype.prepareFrame =
|
|||||||
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
||||||
!ol.extent.isEmpty(renderedExtent)) {
|
!ol.extent.isEmpty(renderedExtent)) {
|
||||||
var projection = viewState.projection;
|
var projection = viewState.projection;
|
||||||
var sourceProjection = imageSource.getProjection();
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
if (sourceProjection) {
|
var sourceProjection = imageSource.getProjection();
|
||||||
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
if (sourceProjection) {
|
||||||
'projection and sourceProjection are equivalent');
|
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
||||||
projection = sourceProjection;
|
'projection and sourceProjection are equivalent');
|
||||||
|
projection = sourceProjection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var image_ = imageSource.getImage(renderedExtent, viewResolution,
|
var image_ = imageSource.getImage(renderedExtent, viewResolution,
|
||||||
frameState.pixelRatio, projection);
|
frameState.pixelRatio, projection);
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ ol.renderer.dom.TileLayer.prototype.prepareFrame =
|
|||||||
var tilesToDrawByZ = {};
|
var tilesToDrawByZ = {};
|
||||||
tilesToDrawByZ[z] = {};
|
tilesToDrawByZ[z] = {};
|
||||||
|
|
||||||
var findLoadedTiles = this.createLoadedTileFinder(tileSource, tilesToDrawByZ);
|
var findLoadedTiles = this.createLoadedTileFinder(
|
||||||
|
tileSource, projection, tilesToDrawByZ);
|
||||||
|
|
||||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||||
|
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ ol.renderer.Layer.prototype.hasFeatureAtCoordinate = goog.functions.FALSE;
|
|||||||
/**
|
/**
|
||||||
* Create a function that adds loaded tiles to the tile lookup.
|
* Create a function that adds loaded tiles to the tile lookup.
|
||||||
* @param {ol.source.Tile} source Tile source.
|
* @param {ol.source.Tile} source Tile source.
|
||||||
|
* @param {ol.proj.Projection} projection Projection of the tiles.
|
||||||
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
||||||
* tiles by zoom level.
|
* tiles by zoom level.
|
||||||
* @return {function(number, ol.TileRange):boolean} A function that can be
|
* @return {function(number, ol.TileRange):boolean} A function that can be
|
||||||
@@ -94,7 +95,8 @@ ol.renderer.Layer.prototype.hasFeatureAtCoordinate = goog.functions.FALSE;
|
|||||||
* lookup.
|
* lookup.
|
||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
|
ol.renderer.Layer.prototype.createLoadedTileFinder =
|
||||||
|
function(source, projection, tiles) {
|
||||||
return (
|
return (
|
||||||
/**
|
/**
|
||||||
* @param {number} zoom Zoom level.
|
* @param {number} zoom Zoom level.
|
||||||
@@ -102,12 +104,13 @@ ol.renderer.Layer.prototype.createLoadedTileFinder = function(source, tiles) {
|
|||||||
* @return {boolean} The tile range is fully loaded.
|
* @return {boolean} The tile range is fully loaded.
|
||||||
*/
|
*/
|
||||||
function(zoom, tileRange) {
|
function(zoom, tileRange) {
|
||||||
return source.forEachLoadedTile(zoom, tileRange, function(tile) {
|
return source.forEachLoadedTile(projection, zoom,
|
||||||
if (!tiles[zoom]) {
|
tileRange, function(tile) {
|
||||||
tiles[zoom] = {};
|
if (!tiles[zoom]) {
|
||||||
}
|
tiles[zoom] = {};
|
||||||
tiles[zoom][tile.tileCoord.toString()] = tile;
|
}
|
||||||
});
|
tiles[zoom][tile.tileCoord.toString()] = tile;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -156,8 +159,9 @@ ol.renderer.Layer.prototype.loadImage = function(image) {
|
|||||||
if (imageState == ol.ImageState.IDLE) {
|
if (imageState == ol.ImageState.IDLE) {
|
||||||
image.load();
|
image.load();
|
||||||
imageState = image.getState();
|
imageState = image.getState();
|
||||||
goog.asserts.assert(imageState == ol.ImageState.LOADING,
|
goog.asserts.assert(imageState == ol.ImageState.LOADING ||
|
||||||
'imageState is "loading"');
|
imageState == ol.ImageState.LOADED,
|
||||||
|
'imageState is "loading" or "loaded"');
|
||||||
}
|
}
|
||||||
return imageState == ol.ImageState.LOADED;
|
return imageState == ol.ImageState.LOADED;
|
||||||
};
|
};
|
||||||
@@ -191,7 +195,8 @@ ol.renderer.Layer.prototype.scheduleExpireCache =
|
|||||||
*/
|
*/
|
||||||
function(tileSource, map, frameState) {
|
function(tileSource, map, frameState) {
|
||||||
var tileSourceKey = goog.getUid(tileSource).toString();
|
var tileSourceKey = goog.getUid(tileSource).toString();
|
||||||
tileSource.expireCache(frameState.usedTiles[tileSourceKey]);
|
tileSource.expireCache(frameState.viewState.projection,
|
||||||
|
frameState.usedTiles[tileSourceKey]);
|
||||||
}, tileSource));
|
}, tileSource));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -324,7 +329,7 @@ ol.renderer.Layer.prototype.manageTilePyramid = function(
|
|||||||
opt_tileCallback.call(opt_this, tile);
|
opt_tileCallback.call(opt_this, tile);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
tileSource.useTile(z, x, y);
|
tileSource.useTile(z, x, y, projection);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -125,11 +125,13 @@ ol.renderer.webgl.ImageLayer.prototype.prepareFrame =
|
|||||||
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] &&
|
||||||
!ol.extent.isEmpty(renderedExtent)) {
|
!ol.extent.isEmpty(renderedExtent)) {
|
||||||
var projection = viewState.projection;
|
var projection = viewState.projection;
|
||||||
var sourceProjection = imageSource.getProjection();
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
if (sourceProjection) {
|
var sourceProjection = imageSource.getProjection();
|
||||||
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
if (sourceProjection) {
|
||||||
'projection and sourceProjection are equivalent');
|
goog.asserts.assert(ol.proj.equivalent(projection, sourceProjection),
|
||||||
projection = sourceProjection;
|
'projection and sourceProjection are equivalent');
|
||||||
|
projection = sourceProjection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
var image_ = imageSource.getImage(renderedExtent, viewResolution,
|
var image_ = imageSource.getImage(renderedExtent, viewResolution,
|
||||||
pixelRatio, projection);
|
pixelRatio, projection);
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
|
|||||||
/**
|
/**
|
||||||
* Create a function that adds loaded tiles to the tile lookup.
|
* Create a function that adds loaded tiles to the tile lookup.
|
||||||
* @param {ol.source.Tile} source Tile source.
|
* @param {ol.source.Tile} source Tile source.
|
||||||
|
* @param {ol.proj.Projection} projection Projection of the tiles.
|
||||||
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
* @param {Object.<number, Object.<string, ol.Tile>>} tiles Lookup of loaded
|
||||||
* tiles by zoom level.
|
* tiles by zoom level.
|
||||||
* @return {function(number, ol.TileRange):boolean} A function that can be
|
* @return {function(number, ol.TileRange):boolean} A function that can be
|
||||||
@@ -113,7 +114,7 @@ ol.renderer.webgl.TileLayer.prototype.disposeInternal = function() {
|
|||||||
* @protected
|
* @protected
|
||||||
*/
|
*/
|
||||||
ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder =
|
ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder =
|
||||||
function(source, tiles) {
|
function(source, projection, tiles) {
|
||||||
var mapRenderer = this.mapRenderer;
|
var mapRenderer = this.mapRenderer;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -123,16 +124,17 @@ ol.renderer.webgl.TileLayer.prototype.createLoadedTileFinder =
|
|||||||
* @return {boolean} The tile range is fully loaded.
|
* @return {boolean} The tile range is fully loaded.
|
||||||
*/
|
*/
|
||||||
function(zoom, tileRange) {
|
function(zoom, tileRange) {
|
||||||
return source.forEachLoadedTile(zoom, tileRange, function(tile) {
|
return source.forEachLoadedTile(projection, zoom,
|
||||||
var loaded = mapRenderer.isTileTextureLoaded(tile);
|
tileRange, function(tile) {
|
||||||
if (loaded) {
|
var loaded = mapRenderer.isTileTextureLoaded(tile);
|
||||||
if (!tiles[zoom]) {
|
if (loaded) {
|
||||||
tiles[zoom] = {};
|
if (!tiles[zoom]) {
|
||||||
}
|
tiles[zoom] = {};
|
||||||
tiles[zoom][tile.tileCoord.toString()] = tile;
|
}
|
||||||
}
|
tiles[zoom][tile.tileCoord.toString()] = tile;
|
||||||
return loaded;
|
}
|
||||||
});
|
return loaded;
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -239,7 +241,7 @@ ol.renderer.webgl.TileLayer.prototype.prepareFrame =
|
|||||||
tilesToDrawByZ[z] = {};
|
tilesToDrawByZ[z] = {};
|
||||||
|
|
||||||
var findLoadedTiles = this.createLoadedTileFinder(
|
var findLoadedTiles = this.createLoadedTileFinder(
|
||||||
tileSource, tilesToDrawByZ);
|
tileSource, projection, tilesToDrawByZ);
|
||||||
|
|
||||||
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
var useInterimTilesOnError = tileLayer.getUseInterimTilesOnError();
|
||||||
var allTilesLoaded = true;
|
var allTilesLoaded = true;
|
||||||
|
|||||||
210
src/ol/reproj/image.js
Normal file
@@ -0,0 +1,210 @@
|
|||||||
|
goog.provide('ol.reproj.Image');
|
||||||
|
goog.provide('ol.reproj.ImageFunctionType');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
|
goog.require('goog.events');
|
||||||
|
goog.require('goog.events.EventType');
|
||||||
|
goog.require('ol.ImageBase');
|
||||||
|
goog.require('ol.ImageState');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj');
|
||||||
|
goog.require('ol.reproj.Triangulation');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {function(ol.Extent, number, number) : ol.ImageBase}
|
||||||
|
*/
|
||||||
|
ol.reproj.ImageFunctionType;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* Class encapsulating single reprojected image.
|
||||||
|
* See {@link ol.source.Image}.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @extends {ol.ImageBase}
|
||||||
|
* @param {ol.proj.Projection} sourceProj Source projection (of the data).
|
||||||
|
* @param {ol.proj.Projection} targetProj Target projection.
|
||||||
|
* @param {ol.Extent} targetExtent Target extent.
|
||||||
|
* @param {number} targetResolution Target resolution.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {ol.reproj.ImageFunctionType} getImageFunction
|
||||||
|
* Function returning source images (extent, resolution, pixelRatio).
|
||||||
|
*/
|
||||||
|
ol.reproj.Image = function(sourceProj, targetProj,
|
||||||
|
targetExtent, targetResolution, pixelRatio, getImageFunction) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.proj.Projection}
|
||||||
|
*/
|
||||||
|
this.targetProj_ = targetProj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.Extent}
|
||||||
|
*/
|
||||||
|
this.maxSourceExtent_ = sourceProj.getExtent();
|
||||||
|
var maxTargetExtent = targetProj.getExtent();
|
||||||
|
|
||||||
|
var limitedTargetExtent = maxTargetExtent ?
|
||||||
|
ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent;
|
||||||
|
|
||||||
|
var targetCenter = ol.extent.getCenter(limitedTargetExtent);
|
||||||
|
var sourceResolution = ol.reproj.calculateSourceResolution(
|
||||||
|
sourceProj, targetProj, targetCenter, targetResolution);
|
||||||
|
|
||||||
|
var errorThresholdInPixels = ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {!ol.reproj.Triangulation}
|
||||||
|
*/
|
||||||
|
this.triangulation_ = new ol.reproj.Triangulation(
|
||||||
|
sourceProj, targetProj, limitedTargetExtent, this.maxSourceExtent_,
|
||||||
|
sourceResolution * errorThresholdInPixels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.targetResolution_ = targetResolution;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.Extent}
|
||||||
|
*/
|
||||||
|
this.targetExtent_ = targetExtent;
|
||||||
|
|
||||||
|
var sourceExtent = this.triangulation_.calculateSourceExtent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.ImageBase}
|
||||||
|
*/
|
||||||
|
this.sourceImage_ =
|
||||||
|
getImageFunction(sourceExtent, sourceResolution, pixelRatio);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.sourcePixelRatio_ =
|
||||||
|
this.sourceImage_ ? this.sourceImage_.getPixelRatio() : 1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLCanvasElement}
|
||||||
|
*/
|
||||||
|
this.canvas_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {goog.events.Key}
|
||||||
|
*/
|
||||||
|
this.sourceListenerKey_ = null;
|
||||||
|
|
||||||
|
|
||||||
|
var state = ol.ImageState.LOADED;
|
||||||
|
var attributions = [];
|
||||||
|
|
||||||
|
if (this.sourceImage_) {
|
||||||
|
state = ol.ImageState.IDLE;
|
||||||
|
attributions = this.sourceImage_.getAttributions();
|
||||||
|
}
|
||||||
|
|
||||||
|
goog.base(this, targetExtent, targetResolution, this.sourcePixelRatio_,
|
||||||
|
state, attributions);
|
||||||
|
};
|
||||||
|
goog.inherits(ol.reproj.Image, ol.ImageBase);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.disposeInternal = function() {
|
||||||
|
if (this.state == ol.ImageState.LOADING) {
|
||||||
|
this.unlistenSource_();
|
||||||
|
}
|
||||||
|
goog.base(this, 'disposeInternal');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.getImage = function(opt_context) {
|
||||||
|
return this.canvas_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {ol.proj.Projection} Projection.
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.getProjection = function() {
|
||||||
|
return this.targetProj_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.reproject_ = function() {
|
||||||
|
var sourceState = this.sourceImage_.getState();
|
||||||
|
if (sourceState == ol.ImageState.LOADED) {
|
||||||
|
var width = ol.extent.getWidth(this.targetExtent_) / this.targetResolution_;
|
||||||
|
var height =
|
||||||
|
ol.extent.getHeight(this.targetExtent_) / this.targetResolution_;
|
||||||
|
|
||||||
|
this.canvas_ = ol.reproj.render(width, height, this.sourcePixelRatio_,
|
||||||
|
this.sourceImage_.getResolution(), this.maxSourceExtent_,
|
||||||
|
this.targetResolution_, this.targetExtent_, this.triangulation_, [{
|
||||||
|
extent: this.sourceImage_.getExtent(),
|
||||||
|
image: this.sourceImage_.getImage()
|
||||||
|
}]);
|
||||||
|
}
|
||||||
|
this.state = sourceState;
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.load = function() {
|
||||||
|
if (this.state == ol.ImageState.IDLE) {
|
||||||
|
this.state = ol.ImageState.LOADING;
|
||||||
|
this.changed();
|
||||||
|
|
||||||
|
var sourceState = this.sourceImage_.getState();
|
||||||
|
if (sourceState == ol.ImageState.LOADED ||
|
||||||
|
sourceState == ol.ImageState.ERROR) {
|
||||||
|
this.reproject_();
|
||||||
|
} else {
|
||||||
|
this.sourceListenerKey_ = this.sourceImage_.listen(
|
||||||
|
goog.events.EventType.CHANGE, function(e) {
|
||||||
|
var sourceState = this.sourceImage_.getState();
|
||||||
|
if (sourceState == ol.ImageState.LOADED ||
|
||||||
|
sourceState == ol.ImageState.ERROR) {
|
||||||
|
this.unlistenSource_();
|
||||||
|
this.reproject_();
|
||||||
|
}
|
||||||
|
}, false, this);
|
||||||
|
this.sourceImage_.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Image.prototype.unlistenSource_ = function() {
|
||||||
|
goog.asserts.assert(this.sourceListenerKey_,
|
||||||
|
'this.sourceListenerKey_ should not be null');
|
||||||
|
goog.events.unlistenByKey(this.sourceListenerKey_);
|
||||||
|
this.sourceListenerKey_ = null;
|
||||||
|
};
|
||||||
258
src/ol/reproj/reproj.js
Normal file
@@ -0,0 +1,258 @@
|
|||||||
|
goog.provide('ol.reproj');
|
||||||
|
|
||||||
|
goog.require('goog.labs.userAgent.browser');
|
||||||
|
goog.require('goog.labs.userAgent.platform');
|
||||||
|
goog.require('goog.math');
|
||||||
|
goog.require('ol.dom');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.math');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We need to employ more sophisticated solution
|
||||||
|
* if the web browser antialiases clipping edges on canvas.
|
||||||
|
*
|
||||||
|
* Currently only Chrome does not antialias the edges, but this is probably
|
||||||
|
* going to be "fixed" in the future: http://crbug.com/424291
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.browserAntialiasesClip_ = !goog.labs.userAgent.browser.isChrome() ||
|
||||||
|
goog.labs.userAgent.platform.isIos();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates ideal resolution to use from the source in order to achieve
|
||||||
|
* pixel mapping as close as possible to 1:1 during reprojection.
|
||||||
|
* The resolution is calculated regardless of what resolutions
|
||||||
|
* are actually available in the dataset (TileGrid, Image, ...).
|
||||||
|
*
|
||||||
|
* @param {ol.proj.Projection} sourceProj Source projection.
|
||||||
|
* @param {ol.proj.Projection} targetProj Target projection.
|
||||||
|
* @param {ol.Coordinate} targetCenter Target center.
|
||||||
|
* @param {number} targetResolution Target resolution.
|
||||||
|
* @return {number} The best resolution to use. Can be +-Infinity, NaN or 0.
|
||||||
|
*/
|
||||||
|
ol.reproj.calculateSourceResolution = function(sourceProj, targetProj,
|
||||||
|
targetCenter, targetResolution) {
|
||||||
|
|
||||||
|
var sourceCenter = ol.proj.transform(targetCenter, targetProj, sourceProj);
|
||||||
|
|
||||||
|
// calculate the ideal resolution of the source data
|
||||||
|
var sourceResolution =
|
||||||
|
targetProj.getPointResolution(targetResolution, targetCenter);
|
||||||
|
|
||||||
|
var targetMetersPerUnit = targetProj.getMetersPerUnit();
|
||||||
|
if (targetMetersPerUnit !== undefined) {
|
||||||
|
sourceResolution *= targetMetersPerUnit;
|
||||||
|
}
|
||||||
|
var sourceMetersPerUnit = sourceProj.getMetersPerUnit();
|
||||||
|
if (sourceMetersPerUnit !== undefined) {
|
||||||
|
sourceResolution /= sourceMetersPerUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on the projection properties, the point resolution at the specified
|
||||||
|
// coordinates may be slightly different. We need to reverse-compensate this
|
||||||
|
// in order to achieve optimal results.
|
||||||
|
|
||||||
|
var compensationFactor =
|
||||||
|
sourceProj.getPointResolution(sourceResolution, sourceCenter) /
|
||||||
|
sourceResolution;
|
||||||
|
|
||||||
|
if (goog.math.isFiniteNumber(compensationFactor) && compensationFactor > 0) {
|
||||||
|
sourceResolution /= compensationFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sourceResolution;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enlarge the clipping triangle point by 1 pixel to ensure the edges overlap
|
||||||
|
* in order to mask gaps caused by antialiasing.
|
||||||
|
*
|
||||||
|
* @param {number} centroidX Centroid of the triangle (x coordinate in pixels).
|
||||||
|
* @param {number} centroidY Centroid of the triangle (y coordinate in pixels).
|
||||||
|
* @param {number} x X coordinate of the point (in pixels).
|
||||||
|
* @param {number} y Y coordinate of the point (in pixels).
|
||||||
|
* @return {ol.Coordinate} New point 1 px farther from the centroid.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.enlargeClipPoint_ = function(centroidX, centroidY, x, y) {
|
||||||
|
var dX = x - centroidX, dY = y - centroidY;
|
||||||
|
var distance = Math.sqrt(dX * dX + dY * dY);
|
||||||
|
return [Math.round(x + dX / distance), Math.round(y + dY / distance)];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the source data into new canvas based on the triangulation.
|
||||||
|
*
|
||||||
|
* @param {number} width Width of the canvas.
|
||||||
|
* @param {number} height Height of the canvas.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {number} sourceResolution Source resolution.
|
||||||
|
* @param {ol.Extent} sourceExtent Extent of the data source.
|
||||||
|
* @param {number} targetResolution Target resolution.
|
||||||
|
* @param {ol.Extent} targetExtent Target extent.
|
||||||
|
* @param {ol.reproj.Triangulation} triangulation Calculated triangulation.
|
||||||
|
* @param {Array.<{extent: ol.Extent,
|
||||||
|
* image: (HTMLCanvasElement|Image|HTMLVideoElement)}>} sources
|
||||||
|
* Array of sources.
|
||||||
|
* @param {boolean=} opt_renderEdges Render reprojection edges.
|
||||||
|
* @return {HTMLCanvasElement} Canvas with reprojected data.
|
||||||
|
*/
|
||||||
|
ol.reproj.render = function(width, height, pixelRatio,
|
||||||
|
sourceResolution, sourceExtent, targetResolution, targetExtent,
|
||||||
|
triangulation, sources, opt_renderEdges) {
|
||||||
|
|
||||||
|
var context = ol.dom.createCanvasContext2D(Math.round(pixelRatio * width),
|
||||||
|
Math.round(pixelRatio * height));
|
||||||
|
|
||||||
|
if (sources.length === 0) {
|
||||||
|
return context.canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.scale(pixelRatio, pixelRatio);
|
||||||
|
|
||||||
|
var sourceDataExtent = ol.extent.createEmpty();
|
||||||
|
sources.forEach(function(src, i, arr) {
|
||||||
|
ol.extent.extend(sourceDataExtent, src.extent);
|
||||||
|
});
|
||||||
|
|
||||||
|
var canvasWidthInUnits = ol.extent.getWidth(sourceDataExtent);
|
||||||
|
var canvasHeightInUnits = ol.extent.getHeight(sourceDataExtent);
|
||||||
|
var stitchContext = ol.dom.createCanvasContext2D(
|
||||||
|
Math.round(pixelRatio * canvasWidthInUnits / sourceResolution),
|
||||||
|
Math.round(pixelRatio * canvasHeightInUnits / sourceResolution));
|
||||||
|
|
||||||
|
stitchContext.scale(pixelRatio / sourceResolution,
|
||||||
|
pixelRatio / sourceResolution);
|
||||||
|
stitchContext.translate(-sourceDataExtent[0], sourceDataExtent[3]);
|
||||||
|
|
||||||
|
sources.forEach(function(src, i, arr) {
|
||||||
|
var xPos = src.extent[0];
|
||||||
|
var yPos = -src.extent[3];
|
||||||
|
var srcWidth = ol.extent.getWidth(src.extent);
|
||||||
|
var srcHeight = ol.extent.getHeight(src.extent);
|
||||||
|
|
||||||
|
stitchContext.drawImage(src.image, xPos, yPos, srcWidth, srcHeight);
|
||||||
|
});
|
||||||
|
|
||||||
|
var targetTopLeft = ol.extent.getTopLeft(targetExtent);
|
||||||
|
|
||||||
|
triangulation.getTriangles().forEach(function(triangle, i, arr) {
|
||||||
|
/* Calculate affine transform (src -> dst)
|
||||||
|
* Resulting matrix can be used to transform coordinate
|
||||||
|
* from `sourceProjection` to destination pixels.
|
||||||
|
*
|
||||||
|
* To optimize number of context calls and increase numerical stability,
|
||||||
|
* we also do the following operations:
|
||||||
|
* trans(-topLeftExtentCorner), scale(1 / targetResolution), scale(1, -1)
|
||||||
|
* here before solving the linear system so [ui, vi] are pixel coordinates.
|
||||||
|
*
|
||||||
|
* Src points: xi, yi
|
||||||
|
* Dst points: ui, vi
|
||||||
|
* Affine coefficients: aij
|
||||||
|
*
|
||||||
|
* | x0 y0 1 0 0 0 | |a00| |u0|
|
||||||
|
* | x1 y1 1 0 0 0 | |a01| |u1|
|
||||||
|
* | x2 y2 1 0 0 0 | x |a02| = |u2|
|
||||||
|
* | 0 0 0 x0 y0 1 | |a10| |v0|
|
||||||
|
* | 0 0 0 x1 y1 1 | |a11| |v1|
|
||||||
|
* | 0 0 0 x2 y2 1 | |a12| |v2|
|
||||||
|
*/
|
||||||
|
var source = triangle.source, target = triangle.target;
|
||||||
|
var x0 = source[0][0], y0 = source[0][1],
|
||||||
|
x1 = source[1][0], y1 = source[1][1],
|
||||||
|
x2 = source[2][0], y2 = source[2][1];
|
||||||
|
var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
|
||||||
|
// Shift all the source points to improve numerical stability
|
||||||
|
// of all the subsequent calculations. The [x0, y0] is used here.
|
||||||
|
// This is also used to simplify the linear system.
|
||||||
|
var sourceNumericalShiftX = x0, sourceNumericalShiftY = y0;
|
||||||
|
x0 = 0;
|
||||||
|
y0 = 0;
|
||||||
|
x1 -= sourceNumericalShiftX;
|
||||||
|
y1 -= sourceNumericalShiftY;
|
||||||
|
x2 -= sourceNumericalShiftX;
|
||||||
|
y2 -= sourceNumericalShiftY;
|
||||||
|
|
||||||
|
var augmentedMatrix = [
|
||||||
|
[x1, y1, 0, 0, u1 - u0],
|
||||||
|
[x2, y2, 0, 0, u2 - u0],
|
||||||
|
[0, 0, x1, y1, v1 - v0],
|
||||||
|
[0, 0, x2, y2, v2 - v0]
|
||||||
|
];
|
||||||
|
var affineCoefs = ol.math.solveLinearSystem(augmentedMatrix);
|
||||||
|
if (!affineCoefs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
context.save();
|
||||||
|
context.beginPath();
|
||||||
|
if (ol.reproj.browserAntialiasesClip_) {
|
||||||
|
var centroidX = (u0 + u1 + u2) / 3, centroidY = (v0 + v1 + v2) / 3;
|
||||||
|
var p0 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u0, v0);
|
||||||
|
var p1 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u1, v1);
|
||||||
|
var p2 = ol.reproj.enlargeClipPoint_(centroidX, centroidY, u2, v2);
|
||||||
|
|
||||||
|
context.moveTo(p0[0], p0[1]);
|
||||||
|
context.lineTo(p1[0], p1[1]);
|
||||||
|
context.lineTo(p2[0], p2[1]);
|
||||||
|
} else {
|
||||||
|
context.moveTo(u0, v0);
|
||||||
|
context.lineTo(u1, v1);
|
||||||
|
context.lineTo(u2, v2);
|
||||||
|
}
|
||||||
|
context.closePath();
|
||||||
|
context.clip();
|
||||||
|
|
||||||
|
context.transform(
|
||||||
|
affineCoefs[0], affineCoefs[2], affineCoefs[1], affineCoefs[3], u0, v0);
|
||||||
|
|
||||||
|
context.translate(sourceDataExtent[0] - sourceNumericalShiftX,
|
||||||
|
sourceDataExtent[3] - sourceNumericalShiftY);
|
||||||
|
|
||||||
|
context.scale(sourceResolution / pixelRatio,
|
||||||
|
-sourceResolution / pixelRatio);
|
||||||
|
|
||||||
|
context.drawImage(stitchContext.canvas, 0, 0);
|
||||||
|
context.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opt_renderEdges) {
|
||||||
|
context.save();
|
||||||
|
|
||||||
|
context.strokeStyle = 'black';
|
||||||
|
context.lineWidth = 1;
|
||||||
|
|
||||||
|
triangulation.getTriangles().forEach(function(triangle, i, arr) {
|
||||||
|
var target = triangle.target;
|
||||||
|
var u0 = (target[0][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v0 = -(target[0][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
var u1 = (target[1][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v1 = -(target[1][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
var u2 = (target[2][0] - targetTopLeft[0]) / targetResolution,
|
||||||
|
v2 = -(target[2][1] - targetTopLeft[1]) / targetResolution;
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.moveTo(u0, v0);
|
||||||
|
context.lineTo(u1, v1);
|
||||||
|
context.lineTo(u2, v2);
|
||||||
|
context.closePath();
|
||||||
|
context.stroke();
|
||||||
|
});
|
||||||
|
|
||||||
|
context.restore();
|
||||||
|
}
|
||||||
|
return context.canvas;
|
||||||
|
};
|
||||||
332
src/ol/reproj/tile.js
Normal file
@@ -0,0 +1,332 @@
|
|||||||
|
goog.provide('ol.reproj.Tile');
|
||||||
|
goog.provide('ol.reproj.TileFunctionType');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
|
goog.require('goog.events');
|
||||||
|
goog.require('goog.events.EventType');
|
||||||
|
goog.require('goog.math');
|
||||||
|
goog.require('goog.object');
|
||||||
|
goog.require('ol.Tile');
|
||||||
|
goog.require('ol.TileState');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.math');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj');
|
||||||
|
goog.require('ol.reproj.Triangulation');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {function(number, number, number, number) : ol.Tile}
|
||||||
|
*/
|
||||||
|
ol.reproj.TileFunctionType;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* Class encapsulating single reprojected tile.
|
||||||
|
* See {@link ol.source.TileImage}.
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @extends {ol.Tile}
|
||||||
|
* @param {ol.proj.Projection} sourceProj Source projection.
|
||||||
|
* @param {ol.tilegrid.TileGrid} sourceTileGrid Source tile grid.
|
||||||
|
* @param {ol.proj.Projection} targetProj Target projection.
|
||||||
|
* @param {ol.tilegrid.TileGrid} targetTileGrid Target tile grid.
|
||||||
|
* @param {number} z Zoom level.
|
||||||
|
* @param {number} x X.
|
||||||
|
* @param {number} y Y.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {ol.reproj.TileFunctionType} getTileFunction
|
||||||
|
* Function returning source tiles (z, x, y, pixelRatio).
|
||||||
|
* @param {number=} opt_errorThreshold Acceptable reprojection error (in px).
|
||||||
|
* @param {boolean=} opt_renderEdges Render reprojection edges.
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile = function(sourceProj, sourceTileGrid,
|
||||||
|
targetProj, targetTileGrid, z, x, y, pixelRatio, getTileFunction,
|
||||||
|
opt_errorThreshold,
|
||||||
|
opt_renderEdges) {
|
||||||
|
goog.base(this, [z, x, y], ol.TileState.IDLE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.renderEdges_ = opt_renderEdges !== undefined ? opt_renderEdges : false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.pixelRatio_ = pixelRatio;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {HTMLCanvasElement}
|
||||||
|
*/
|
||||||
|
this.canvas_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Object.<number, HTMLCanvasElement>}
|
||||||
|
*/
|
||||||
|
this.canvasByContext_ = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.tilegrid.TileGrid}
|
||||||
|
*/
|
||||||
|
this.sourceTileGrid_ = sourceTileGrid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.tilegrid.TileGrid}
|
||||||
|
*/
|
||||||
|
this.targetTileGrid_ = targetTileGrid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {!Array.<ol.Tile>}
|
||||||
|
*/
|
||||||
|
this.sourceTiles_ = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {Array.<goog.events.Key>}
|
||||||
|
*/
|
||||||
|
this.sourcesListenerKeys_ = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.sourceZ_ = 0;
|
||||||
|
|
||||||
|
var targetExtent = targetTileGrid.getTileCoordExtent(this.getTileCoord());
|
||||||
|
var maxTargetExtent = this.targetTileGrid_.getExtent();
|
||||||
|
var maxSourceExtent = this.sourceTileGrid_.getExtent();
|
||||||
|
|
||||||
|
var limitedTargetExtent = maxTargetExtent ?
|
||||||
|
ol.extent.getIntersection(targetExtent, maxTargetExtent) : targetExtent;
|
||||||
|
|
||||||
|
if (ol.extent.getArea(limitedTargetExtent) === 0) {
|
||||||
|
// Tile is completely outside range -> EMPTY
|
||||||
|
// TODO: is it actually correct that the source even creates the tile ?
|
||||||
|
this.state = ol.TileState.EMPTY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sourceProjExtent = sourceProj.getExtent();
|
||||||
|
if (sourceProjExtent) {
|
||||||
|
if (!maxSourceExtent) {
|
||||||
|
maxSourceExtent = sourceProjExtent;
|
||||||
|
} else {
|
||||||
|
maxSourceExtent = ol.extent.getIntersection(
|
||||||
|
maxSourceExtent, sourceProjExtent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetResolution = targetTileGrid.getResolution(z);
|
||||||
|
|
||||||
|
var targetCenter = ol.extent.getCenter(limitedTargetExtent);
|
||||||
|
var sourceResolution = ol.reproj.calculateSourceResolution(
|
||||||
|
sourceProj, targetProj, targetCenter, targetResolution);
|
||||||
|
|
||||||
|
if (!goog.math.isFiniteNumber(sourceResolution) || sourceResolution <= 0) {
|
||||||
|
// invalid sourceResolution -> EMPTY
|
||||||
|
// probably edges of the projections when no extent is defined
|
||||||
|
this.state = ol.TileState.EMPTY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var errorThresholdInPixels = opt_errorThreshold !== undefined ?
|
||||||
|
opt_errorThreshold : ol.DEFAULT_RASTER_REPROJECTION_ERROR_THRESHOLD;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {!ol.reproj.Triangulation}
|
||||||
|
*/
|
||||||
|
this.triangulation_ = new ol.reproj.Triangulation(
|
||||||
|
sourceProj, targetProj, limitedTargetExtent, maxSourceExtent,
|
||||||
|
sourceResolution * errorThresholdInPixels);
|
||||||
|
|
||||||
|
if (this.triangulation_.getTriangles().length === 0) {
|
||||||
|
// no valid triangles -> EMPTY
|
||||||
|
this.state = ol.TileState.EMPTY;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sourceZ_ = sourceTileGrid.getZForResolution(sourceResolution);
|
||||||
|
var sourceExtent = this.triangulation_.calculateSourceExtent();
|
||||||
|
|
||||||
|
if (maxSourceExtent) {
|
||||||
|
if (sourceProj.canWrapX()) {
|
||||||
|
sourceExtent[1] = ol.math.clamp(
|
||||||
|
sourceExtent[1], maxSourceExtent[1], maxSourceExtent[3]);
|
||||||
|
sourceExtent[3] = ol.math.clamp(
|
||||||
|
sourceExtent[3], maxSourceExtent[1], maxSourceExtent[3]);
|
||||||
|
} else {
|
||||||
|
sourceExtent = ol.extent.getIntersection(sourceExtent, maxSourceExtent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ol.extent.getArea(sourceExtent)) {
|
||||||
|
this.state = ol.TileState.EMPTY;
|
||||||
|
} else {
|
||||||
|
var sourceRange = sourceTileGrid.getTileRangeForExtentAndZ(
|
||||||
|
sourceExtent, this.sourceZ_);
|
||||||
|
|
||||||
|
var tilesRequired = sourceRange.getWidth() * sourceRange.getHeight();
|
||||||
|
if (!goog.asserts.assert(
|
||||||
|
tilesRequired < ol.RASTER_REPROJECTION_MAX_SOURCE_TILES,
|
||||||
|
'reasonable number of tiles is required')) {
|
||||||
|
this.state = ol.TileState.ERROR;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (var srcX = sourceRange.minX; srcX <= sourceRange.maxX; srcX++) {
|
||||||
|
for (var srcY = sourceRange.minY; srcY <= sourceRange.maxY; srcY++) {
|
||||||
|
var tile = getTileFunction(this.sourceZ_, srcX, srcY, pixelRatio);
|
||||||
|
if (tile) {
|
||||||
|
this.sourceTiles_.push(tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.sourceTiles_.length === 0) {
|
||||||
|
this.state = ol.TileState.EMPTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
goog.inherits(ol.reproj.Tile, ol.Tile);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile.prototype.disposeInternal = function() {
|
||||||
|
if (this.state == ol.TileState.LOADING) {
|
||||||
|
this.unlistenSources_();
|
||||||
|
}
|
||||||
|
goog.base(this, 'disposeInternal');
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile.prototype.getImage = function(opt_context) {
|
||||||
|
if (opt_context !== undefined) {
|
||||||
|
var image;
|
||||||
|
var key = goog.getUid(opt_context);
|
||||||
|
if (key in this.canvasByContext_) {
|
||||||
|
return this.canvasByContext_[key];
|
||||||
|
} else if (goog.object.isEmpty(this.canvasByContext_)) {
|
||||||
|
image = this.canvas_;
|
||||||
|
} else {
|
||||||
|
image = /** @type {HTMLCanvasElement} */ (this.canvas_.cloneNode(false));
|
||||||
|
}
|
||||||
|
this.canvasByContext_[key] = image;
|
||||||
|
return image;
|
||||||
|
} else {
|
||||||
|
return this.canvas_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile.prototype.reproject_ = function() {
|
||||||
|
var sources = [];
|
||||||
|
this.sourceTiles_.forEach(function(tile, i, arr) {
|
||||||
|
if (tile && tile.getState() == ol.TileState.LOADED) {
|
||||||
|
sources.push({
|
||||||
|
extent: this.sourceTileGrid_.getTileCoordExtent(tile.tileCoord),
|
||||||
|
image: tile.getImage()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
this.sourceTiles_.length = 0;
|
||||||
|
|
||||||
|
var tileCoord = this.getTileCoord();
|
||||||
|
var z = tileCoord[0];
|
||||||
|
var size = this.targetTileGrid_.getTileSize(z);
|
||||||
|
var width = goog.isNumber(size) ? size : size[0];
|
||||||
|
var height = goog.isNumber(size) ? size : size[1];
|
||||||
|
var targetResolution = this.targetTileGrid_.getResolution(z);
|
||||||
|
var sourceResolution = this.sourceTileGrid_.getResolution(this.sourceZ_);
|
||||||
|
|
||||||
|
var targetExtent = this.targetTileGrid_.getTileCoordExtent(tileCoord);
|
||||||
|
this.canvas_ = ol.reproj.render(width, height, this.pixelRatio_,
|
||||||
|
sourceResolution, this.sourceTileGrid_.getExtent(),
|
||||||
|
targetResolution, targetExtent, this.triangulation_, sources,
|
||||||
|
this.renderEdges_);
|
||||||
|
|
||||||
|
this.state = ol.TileState.LOADED;
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile.prototype.load = function() {
|
||||||
|
if (this.state == ol.TileState.IDLE) {
|
||||||
|
this.state = ol.TileState.LOADING;
|
||||||
|
this.changed();
|
||||||
|
|
||||||
|
var leftToLoad = 0;
|
||||||
|
|
||||||
|
goog.asserts.assert(!this.sourcesListenerKeys_,
|
||||||
|
'this.sourcesListenerKeys_ should be null');
|
||||||
|
|
||||||
|
this.sourcesListenerKeys_ = [];
|
||||||
|
this.sourceTiles_.forEach(function(tile, i, arr) {
|
||||||
|
var state = tile.getState();
|
||||||
|
if (state == ol.TileState.IDLE || state == ol.TileState.LOADING) {
|
||||||
|
leftToLoad++;
|
||||||
|
|
||||||
|
var sourceListenKey;
|
||||||
|
sourceListenKey = tile.listen(goog.events.EventType.CHANGE,
|
||||||
|
function(e) {
|
||||||
|
var state = tile.getState();
|
||||||
|
if (state == ol.TileState.LOADED ||
|
||||||
|
state == ol.TileState.ERROR ||
|
||||||
|
state == ol.TileState.EMPTY) {
|
||||||
|
goog.events.unlistenByKey(sourceListenKey);
|
||||||
|
leftToLoad--;
|
||||||
|
goog.asserts.assert(leftToLoad >= 0,
|
||||||
|
'leftToLoad should not be negative');
|
||||||
|
if (leftToLoad === 0) {
|
||||||
|
this.unlistenSources_();
|
||||||
|
this.reproject_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, false, this);
|
||||||
|
this.sourcesListenerKeys_.push(sourceListenKey);
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
|
||||||
|
this.sourceTiles_.forEach(function(tile, i, arr) {
|
||||||
|
var state = tile.getState();
|
||||||
|
if (state == ol.TileState.IDLE) {
|
||||||
|
tile.load();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (leftToLoad === 0) {
|
||||||
|
this.reproject_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Tile.prototype.unlistenSources_ = function() {
|
||||||
|
goog.asserts.assert(this.sourcesListenerKeys_,
|
||||||
|
'this.sourcesListenerKeys_ should not be null');
|
||||||
|
this.sourcesListenerKeys_.forEach(goog.events.unlistenByKey);
|
||||||
|
this.sourcesListenerKeys_ = null;
|
||||||
|
};
|
||||||
341
src/ol/reproj/triangulation.js
Normal file
@@ -0,0 +1,341 @@
|
|||||||
|
goog.provide('ol.reproj.Triangulation');
|
||||||
|
|
||||||
|
goog.require('goog.asserts');
|
||||||
|
goog.require('goog.math');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Single triangle; consists of 3 source points and 3 target points.
|
||||||
|
*
|
||||||
|
* @typedef {{source: Array.<ol.Coordinate>,
|
||||||
|
* target: Array.<ol.Coordinate>}}
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @classdesc
|
||||||
|
* Class containing triangulation of the given target extent.
|
||||||
|
* Used for determining source data and the reprojection itself.
|
||||||
|
*
|
||||||
|
* @param {ol.proj.Projection} sourceProj Source projection.
|
||||||
|
* @param {ol.proj.Projection} targetProj Target projection.
|
||||||
|
* @param {ol.Extent} targetExtent Target extent to triangulate.
|
||||||
|
* @param {ol.Extent} maxSourceExtent Maximal source extent that can be used.
|
||||||
|
* @param {number} errorThreshold Acceptable error (in source units).
|
||||||
|
* @constructor
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangulation = function(sourceProj, targetProj, targetExtent,
|
||||||
|
maxSourceExtent, errorThreshold) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {ol.proj.Projection}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.sourceProj_ = sourceProj;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {ol.proj.Projection}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.targetProj_ = targetProj;
|
||||||
|
|
||||||
|
/** @type {!Object.<string, ol.Coordinate>} */
|
||||||
|
var transformInvCache = {};
|
||||||
|
var transformInv = ol.proj.getTransform(this.targetProj_, this.sourceProj_);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Coordinate} c
|
||||||
|
* @return {ol.Coordinate}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.transformInv_ = function(c) {
|
||||||
|
var key = c[0] + '/' + c[1];
|
||||||
|
if (!transformInvCache[key]) {
|
||||||
|
transformInvCache[key] = transformInv(c);
|
||||||
|
}
|
||||||
|
return transformInvCache[key];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {ol.Extent}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.maxSourceExtent_ = maxSourceExtent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.errorThresholdSquared_ = errorThreshold * errorThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {Array.<ol.reproj.Triangle>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.triangles_ = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates that the triangulation crosses edge of the source projection.
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.wrapsXInSource_ = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {boolean}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.canWrapXInSource_ = this.sourceProj_.canWrapX() &&
|
||||||
|
!!maxSourceExtent &&
|
||||||
|
!!this.sourceProj_.getExtent() &&
|
||||||
|
(ol.extent.getWidth(maxSourceExtent) ==
|
||||||
|
ol.extent.getWidth(this.sourceProj_.getExtent()));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {?number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.sourceWorldWidth_ = this.sourceProj_.getExtent() ?
|
||||||
|
ol.extent.getWidth(this.sourceProj_.getExtent()) : null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type {?number}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
this.targetWorldWidth_ = this.targetProj_.getExtent() ?
|
||||||
|
ol.extent.getWidth(this.targetProj_.getExtent()) : null;
|
||||||
|
|
||||||
|
var destinationTopLeft = ol.extent.getTopLeft(targetExtent);
|
||||||
|
var destinationTopRight = ol.extent.getTopRight(targetExtent);
|
||||||
|
var destinationBottomRight = ol.extent.getBottomRight(targetExtent);
|
||||||
|
var destinationBottomLeft = ol.extent.getBottomLeft(targetExtent);
|
||||||
|
var sourceTopLeft = this.transformInv_(destinationTopLeft);
|
||||||
|
var sourceTopRight = this.transformInv_(destinationTopRight);
|
||||||
|
var sourceBottomRight = this.transformInv_(destinationBottomRight);
|
||||||
|
var sourceBottomLeft = this.transformInv_(destinationBottomLeft);
|
||||||
|
|
||||||
|
this.addQuad_(
|
||||||
|
destinationTopLeft, destinationTopRight,
|
||||||
|
destinationBottomRight, destinationBottomLeft,
|
||||||
|
sourceTopLeft, sourceTopRight, sourceBottomRight, sourceBottomLeft,
|
||||||
|
ol.RASTER_REPROJECTION_MAX_SUBDIVISION);
|
||||||
|
|
||||||
|
if (this.wrapsXInSource_) {
|
||||||
|
// Fix coordinates (ol.proj returns wrapped coordinates, "unwrap" here).
|
||||||
|
// This significantly simplifies the rest of the reprojection process.
|
||||||
|
|
||||||
|
goog.asserts.assert(this.sourceWorldWidth_ !== null);
|
||||||
|
var leftBound = Infinity;
|
||||||
|
this.triangles_.forEach(function(triangle, i, arr) {
|
||||||
|
leftBound = Math.min(leftBound,
|
||||||
|
triangle.source[0][0], triangle.source[1][0], triangle.source[2][0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Shift triangles to be as close to `leftBound` as possible
|
||||||
|
// (if the distance is more than `worldWidth / 2` it can be closer.
|
||||||
|
this.triangles_.forEach(function(triangle) {
|
||||||
|
if (Math.max(triangle.source[0][0], triangle.source[1][0],
|
||||||
|
triangle.source[2][0]) - leftBound > this.sourceWorldWidth_ / 2) {
|
||||||
|
var newTriangle = [[triangle.source[0][0], triangle.source[0][1]],
|
||||||
|
[triangle.source[1][0], triangle.source[1][1]],
|
||||||
|
[triangle.source[2][0], triangle.source[2][1]]];
|
||||||
|
if ((newTriangle[0][0] - leftBound) > this.sourceWorldWidth_ / 2) {
|
||||||
|
newTriangle[0][0] -= this.sourceWorldWidth_;
|
||||||
|
}
|
||||||
|
if ((newTriangle[1][0] - leftBound) > this.sourceWorldWidth_ / 2) {
|
||||||
|
newTriangle[1][0] -= this.sourceWorldWidth_;
|
||||||
|
}
|
||||||
|
if ((newTriangle[2][0] - leftBound) > this.sourceWorldWidth_ / 2) {
|
||||||
|
newTriangle[2][0] -= this.sourceWorldWidth_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rarely (if the extent contains both the dateline and prime meridian)
|
||||||
|
// the shift can in turn break some triangles.
|
||||||
|
// Detect this here and don't shift in such cases.
|
||||||
|
var minX = Math.min(
|
||||||
|
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
|
||||||
|
var maxX = Math.max(
|
||||||
|
newTriangle[0][0], newTriangle[1][0], newTriangle[2][0]);
|
||||||
|
if ((maxX - minX) < this.sourceWorldWidth_ / 2) {
|
||||||
|
triangle.source = newTriangle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
transformInvCache = {};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds triangle to the triangulation.
|
||||||
|
* @param {ol.Coordinate} a
|
||||||
|
* @param {ol.Coordinate} b
|
||||||
|
* @param {ol.Coordinate} c
|
||||||
|
* @param {ol.Coordinate} aSrc
|
||||||
|
* @param {ol.Coordinate} bSrc
|
||||||
|
* @param {ol.Coordinate} cSrc
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangulation.prototype.addTriangle_ = function(a, b, c,
|
||||||
|
aSrc, bSrc, cSrc) {
|
||||||
|
this.triangles_.push({
|
||||||
|
source: [aSrc, bSrc, cSrc],
|
||||||
|
target: [a, b, c]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds quad (points in clock-wise order) to the triangulation
|
||||||
|
* (and reprojects the vertices) if valid.
|
||||||
|
* Performs quad subdivision if needed to increase precision.
|
||||||
|
*
|
||||||
|
* @param {ol.Coordinate} a
|
||||||
|
* @param {ol.Coordinate} b
|
||||||
|
* @param {ol.Coordinate} c
|
||||||
|
* @param {ol.Coordinate} d
|
||||||
|
* @param {ol.Coordinate} aSrc
|
||||||
|
* @param {ol.Coordinate} bSrc
|
||||||
|
* @param {ol.Coordinate} cSrc
|
||||||
|
* @param {ol.Coordinate} dSrc
|
||||||
|
* @param {number} maxSubdivision Maximal allowed subdivision of the quad.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangulation.prototype.addQuad_ = function(a, b, c, d,
|
||||||
|
aSrc, bSrc, cSrc, dSrc, maxSubdivision) {
|
||||||
|
|
||||||
|
var sourceQuadExtent = ol.extent.boundingExtent([aSrc, bSrc, cSrc, dSrc]);
|
||||||
|
var sourceCoverageX = this.sourceWorldWidth_ ?
|
||||||
|
ol.extent.getWidth(sourceQuadExtent) / this.sourceWorldWidth_ : null;
|
||||||
|
|
||||||
|
// when the quad is wrapped in the source projection
|
||||||
|
// it covers most of the projection extent, but not fully
|
||||||
|
var wrapsX = this.sourceProj_.canWrapX() &&
|
||||||
|
sourceCoverageX > 0.5 && sourceCoverageX < 1;
|
||||||
|
|
||||||
|
var needsSubdivision = false;
|
||||||
|
|
||||||
|
if (maxSubdivision > 0) {
|
||||||
|
if (this.targetProj_.isGlobal() && this.targetWorldWidth_) {
|
||||||
|
var targetQuadExtent = ol.extent.boundingExtent([a, b, c, d]);
|
||||||
|
var targetCoverageX =
|
||||||
|
ol.extent.getWidth(targetQuadExtent) / this.targetWorldWidth_;
|
||||||
|
needsSubdivision |=
|
||||||
|
targetCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
|
||||||
|
}
|
||||||
|
if (!wrapsX && this.sourceProj_.isGlobal() && sourceCoverageX) {
|
||||||
|
needsSubdivision |=
|
||||||
|
sourceCoverageX > ol.RASTER_REPROJECTION_MAX_TRIANGLE_WIDTH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsSubdivision && this.maxSourceExtent_) {
|
||||||
|
if (!ol.extent.intersects(sourceQuadExtent, this.maxSourceExtent_)) {
|
||||||
|
// whole quad outside source projection extent -> ignore
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needsSubdivision) {
|
||||||
|
if (!isFinite(aSrc[0]) || !isFinite(aSrc[1]) ||
|
||||||
|
!isFinite(bSrc[0]) || !isFinite(bSrc[1]) ||
|
||||||
|
!isFinite(cSrc[0]) || !isFinite(cSrc[1]) ||
|
||||||
|
!isFinite(dSrc[0]) || !isFinite(dSrc[1])) {
|
||||||
|
if (maxSubdivision > 0) {
|
||||||
|
needsSubdivision = true;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxSubdivision > 0) {
|
||||||
|
if (!needsSubdivision) {
|
||||||
|
var center = [(a[0] + c[0]) / 2, (a[1] + c[1]) / 2];
|
||||||
|
var centerSrc = this.transformInv_(center);
|
||||||
|
|
||||||
|
var dx;
|
||||||
|
if (wrapsX) {
|
||||||
|
goog.asserts.assert(this.sourceWorldWidth_);
|
||||||
|
var centerSrcEstimX =
|
||||||
|
(goog.math.modulo(aSrc[0], this.sourceWorldWidth_) +
|
||||||
|
goog.math.modulo(cSrc[0], this.sourceWorldWidth_)) / 2;
|
||||||
|
dx = centerSrcEstimX -
|
||||||
|
goog.math.modulo(centerSrc[0], this.sourceWorldWidth_);
|
||||||
|
} else {
|
||||||
|
dx = (aSrc[0] + cSrc[0]) / 2 - centerSrc[0];
|
||||||
|
}
|
||||||
|
var dy = (aSrc[1] + cSrc[1]) / 2 - centerSrc[1];
|
||||||
|
var centerSrcErrorSquared = dx * dx + dy * dy;
|
||||||
|
needsSubdivision = centerSrcErrorSquared > this.errorThresholdSquared_;
|
||||||
|
}
|
||||||
|
if (needsSubdivision) {
|
||||||
|
if (Math.abs(a[0] - c[0]) <= Math.abs(a[1] - c[1])) {
|
||||||
|
// split horizontally (top & bottom)
|
||||||
|
var bc = [(b[0] + c[0]) / 2, (b[1] + c[1]) / 2];
|
||||||
|
var bcSrc = this.transformInv_(bc);
|
||||||
|
var da = [(d[0] + a[0]) / 2, (d[1] + a[1]) / 2];
|
||||||
|
var daSrc = this.transformInv_(da);
|
||||||
|
|
||||||
|
this.addQuad_(
|
||||||
|
a, b, bc, da, aSrc, bSrc, bcSrc, daSrc, maxSubdivision - 1);
|
||||||
|
this.addQuad_(
|
||||||
|
da, bc, c, d, daSrc, bcSrc, cSrc, dSrc, maxSubdivision - 1);
|
||||||
|
} else {
|
||||||
|
// split vertically (left & right)
|
||||||
|
var ab = [(a[0] + b[0]) / 2, (a[1] + b[1]) / 2];
|
||||||
|
var abSrc = this.transformInv_(ab);
|
||||||
|
var cd = [(c[0] + d[0]) / 2, (c[1] + d[1]) / 2];
|
||||||
|
var cdSrc = this.transformInv_(cd);
|
||||||
|
|
||||||
|
this.addQuad_(
|
||||||
|
a, ab, cd, d, aSrc, abSrc, cdSrc, dSrc, maxSubdivision - 1);
|
||||||
|
this.addQuad_(
|
||||||
|
ab, b, c, cd, abSrc, bSrc, cSrc, cdSrc, maxSubdivision - 1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wrapsX) {
|
||||||
|
if (!this.canWrapXInSource_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.wrapsXInSource_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addTriangle_(a, c, d, aSrc, cSrc, dSrc);
|
||||||
|
this.addTriangle_(a, b, c, aSrc, bSrc, cSrc);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates extent of the 'source' coordinates from all the triangles.
|
||||||
|
*
|
||||||
|
* @return {ol.Extent} Calculated extent.
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangulation.prototype.calculateSourceExtent = function() {
|
||||||
|
var extent = ol.extent.createEmpty();
|
||||||
|
|
||||||
|
this.triangles_.forEach(function(triangle, i, arr) {
|
||||||
|
var src = triangle.source;
|
||||||
|
ol.extent.extendCoordinate(extent, src[0]);
|
||||||
|
ol.extent.extendCoordinate(extent, src[1]);
|
||||||
|
ol.extent.extendCoordinate(extent, src[2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return extent;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Array.<ol.reproj.Triangle>} Array of the calculated triangles.
|
||||||
|
*/
|
||||||
|
ol.reproj.Triangulation.prototype.getTriangles = function() {
|
||||||
|
return this.triangles_;
|
||||||
|
};
|
||||||
@@ -29,6 +29,7 @@ ol.source.BingMaps = function(options) {
|
|||||||
crossOrigin: 'anonymous',
|
crossOrigin: 'anonymous',
|
||||||
opaque: true,
|
opaque: true,
|
||||||
projection: ol.proj.get('EPSG:3857'),
|
projection: ol.proj.get('EPSG:3857'),
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
state: ol.source.State.LOADING,
|
state: ol.source.State.LOADING,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
wrapX: options.wrapX !== undefined ? options.wrapX : true
|
wrapX: options.wrapX !== undefined ? options.wrapX : true
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ goog.inherits(ol.source.ImageCanvas, ol.source.Image);
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.ImageCanvas.prototype.getImage =
|
ol.source.ImageCanvas.prototype.getImageInternal =
|
||||||
function(extent, resolution, pixelRatio, projection) {
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
resolution = this.findNearestResolution(resolution);
|
resolution = this.findNearestResolution(resolution);
|
||||||
|
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ ol.source.ImageMapGuide.prototype.getParams = function() {
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.ImageMapGuide.prototype.getImage =
|
ol.source.ImageMapGuide.prototype.getImageInternal =
|
||||||
function(extent, resolution, pixelRatio, projection) {
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
resolution = this.findNearestResolution(resolution);
|
resolution = this.findNearestResolution(resolution);
|
||||||
pixelRatio = this.hidpi_ ? pixelRatio : 1;
|
pixelRatio = this.hidpi_ ? pixelRatio : 1;
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ goog.require('goog.array');
|
|||||||
goog.require('goog.asserts');
|
goog.require('goog.asserts');
|
||||||
goog.require('goog.events.Event');
|
goog.require('goog.events.Event');
|
||||||
goog.require('ol.Attribution');
|
goog.require('ol.Attribution');
|
||||||
goog.require('ol.Extent');
|
|
||||||
goog.require('ol.ImageState');
|
goog.require('ol.ImageState');
|
||||||
goog.require('ol.array');
|
goog.require('ol.array');
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Image');
|
||||||
goog.require('ol.source.Source');
|
goog.require('ol.source.Source');
|
||||||
|
|
||||||
|
|
||||||
@@ -56,6 +58,20 @@ ol.source.Image = function(options) {
|
|||||||
return b - a;
|
return b - a;
|
||||||
}, true), 'resolutions must be null or sorted in descending order');
|
}, true), 'resolutions must be null or sorted in descending order');
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {ol.reproj.Image}
|
||||||
|
*/
|
||||||
|
this.reprojectedImage_ = null;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
this.reprojectedRevision_ = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
goog.inherits(ol.source.Image, ol.source.Source);
|
goog.inherits(ol.source.Image, ol.source.Source);
|
||||||
|
|
||||||
@@ -90,7 +106,53 @@ ol.source.Image.prototype.findNearestResolution =
|
|||||||
* @param {ol.proj.Projection} projection Projection.
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
* @return {ol.ImageBase} Single image.
|
* @return {ol.ImageBase} Single image.
|
||||||
*/
|
*/
|
||||||
ol.source.Image.prototype.getImage = goog.abstractMethod;
|
ol.source.Image.prototype.getImage =
|
||||||
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
|
var sourceProjection = this.getProjection();
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION ||
|
||||||
|
!sourceProjection ||
|
||||||
|
!projection ||
|
||||||
|
ol.proj.equivalent(sourceProjection, projection)) {
|
||||||
|
if (sourceProjection) {
|
||||||
|
projection = sourceProjection;
|
||||||
|
}
|
||||||
|
return this.getImageInternal(extent, resolution, pixelRatio, projection);
|
||||||
|
} else {
|
||||||
|
if (this.reprojectedImage_) {
|
||||||
|
if (this.reprojectedRevision_ == this.getRevision() &&
|
||||||
|
ol.proj.equivalent(
|
||||||
|
this.reprojectedImage_.getProjection(), projection) &&
|
||||||
|
this.reprojectedImage_.getResolution() == resolution &&
|
||||||
|
this.reprojectedImage_.getPixelRatio() == pixelRatio &&
|
||||||
|
ol.extent.equals(this.reprojectedImage_.getExtent(), extent)) {
|
||||||
|
return this.reprojectedImage_;
|
||||||
|
}
|
||||||
|
this.reprojectedImage_.dispose();
|
||||||
|
this.reprojectedImage_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.reprojectedImage_ = new ol.reproj.Image(
|
||||||
|
sourceProjection, projection, extent, resolution, pixelRatio,
|
||||||
|
goog.bind(function(extent, resolution, pixelRatio) {
|
||||||
|
return this.getImageInternal(extent, resolution,
|
||||||
|
pixelRatio, sourceProjection);
|
||||||
|
}, this));
|
||||||
|
this.reprojectedRevision_ = this.getRevision();
|
||||||
|
|
||||||
|
return this.reprojectedImage_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.Extent} extent Extent.
|
||||||
|
* @param {number} resolution Resolution.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
|
* @return {ol.ImageBase} Single image.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
ol.source.Image.prototype.getImageInternal = goog.abstractMethod;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ goog.inherits(ol.source.ImageStatic, ol.source.Image);
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.ImageStatic.prototype.getImage =
|
ol.source.ImageStatic.prototype.getImageInternal =
|
||||||
function(extent, resolution, pixelRatio, projection) {
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
if (ol.extent.intersects(extent, this.image_.getExtent())) {
|
if (ol.extent.intersects(extent, this.image_.getExtent())) {
|
||||||
return this.image_;
|
return this.image_;
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ ol.source.ImageWMS.prototype.getParams = function() {
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.ImageWMS.prototype.getImage =
|
ol.source.ImageWMS.prototype.getImageInternal =
|
||||||
function(extent, resolution, pixelRatio, projection) {
|
function(extent, resolution, pixelRatio, projection) {
|
||||||
|
|
||||||
if (this.url_ === undefined) {
|
if (this.url_ === undefined) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ ol.source.MapQuest = function(opt_options) {
|
|||||||
crossOrigin: 'anonymous',
|
crossOrigin: 'anonymous',
|
||||||
logo: 'https://developer.mapquest.com/content/osm/mq_logo.png',
|
logo: 'https://developer.mapquest.com/content/osm/mq_logo.png',
|
||||||
maxZoom: layerConfig.maxZoom,
|
maxZoom: layerConfig.maxZoom,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
url: url
|
url: url
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ ol.source.OSM = function(opt_options) {
|
|||||||
crossOrigin: crossOrigin,
|
crossOrigin: crossOrigin,
|
||||||
opaque: true,
|
opaque: true,
|
||||||
maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19,
|
maxZoom: options.maxZoom !== undefined ? options.maxZoom : 19,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
url: url,
|
url: url,
|
||||||
wrapX: options.wrapX
|
wrapX: options.wrapX
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ ol.source.Stamen = function(options) {
|
|||||||
// FIXME uncomment the following when tilegrid supports minZoom
|
// FIXME uncomment the following when tilegrid supports minZoom
|
||||||
//minZoom: providerConfig.minZoom,
|
//minZoom: providerConfig.minZoom,
|
||||||
opaque: layerConfig.opaque,
|
opaque: layerConfig.opaque,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
url: url
|
url: url
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ ol.source.TileArcGISRest = function(opt_options) {
|
|||||||
crossOrigin: options.crossOrigin,
|
crossOrigin: options.crossOrigin,
|
||||||
logo: options.logo,
|
logo: options.logo,
|
||||||
projection: options.projection,
|
projection: options.projection,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileGrid: options.tileGrid,
|
tileGrid: options.tileGrid,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
tileUrlFunction: goog.bind(this.tileUrlFunction_, this),
|
tileUrlFunction: goog.bind(this.tileUrlFunction_, this),
|
||||||
|
|||||||
@@ -3,12 +3,16 @@ goog.provide('ol.source.TileImage');
|
|||||||
goog.require('goog.asserts');
|
goog.require('goog.asserts');
|
||||||
goog.require('goog.events');
|
goog.require('goog.events');
|
||||||
goog.require('goog.events.EventType');
|
goog.require('goog.events.EventType');
|
||||||
|
goog.require('goog.object');
|
||||||
goog.require('ol.ImageTile');
|
goog.require('ol.ImageTile');
|
||||||
|
goog.require('ol.TileCache');
|
||||||
goog.require('ol.TileCoord');
|
goog.require('ol.TileCoord');
|
||||||
goog.require('ol.TileLoadFunctionType');
|
goog.require('ol.TileLoadFunctionType');
|
||||||
goog.require('ol.TileState');
|
goog.require('ol.TileState');
|
||||||
goog.require('ol.TileUrlFunction');
|
goog.require('ol.TileUrlFunction');
|
||||||
goog.require('ol.TileUrlFunctionType');
|
goog.require('ol.TileUrlFunctionType');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Tile');
|
||||||
goog.require('ol.source.Tile');
|
goog.require('ol.source.Tile');
|
||||||
goog.require('ol.source.TileEvent');
|
goog.require('ol.source.TileEvent');
|
||||||
|
|
||||||
@@ -69,6 +73,29 @@ ol.source.TileImage = function(options) {
|
|||||||
this.tileClass = options.tileClass !== undefined ?
|
this.tileClass = options.tileClass !== undefined ?
|
||||||
options.tileClass : ol.ImageTile;
|
options.tileClass : ol.ImageTile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* @type {Object.<string, ol.TileCache>}
|
||||||
|
*/
|
||||||
|
this.tileCacheForProjection = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @protected
|
||||||
|
* @type {Object.<string, ol.tilegrid.TileGrid>}
|
||||||
|
*/
|
||||||
|
this.tileGridForProjection = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {number|undefined}
|
||||||
|
*/
|
||||||
|
this.reprojectionErrorThreshold_ = options.reprojectionErrorThreshold;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @private
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
this.renderReprojectionEdges_ = false;
|
||||||
};
|
};
|
||||||
goog.inherits(ol.source.TileImage, ol.source.Tile);
|
goog.inherits(ol.source.TileImage, ol.source.Tile);
|
||||||
|
|
||||||
@@ -82,11 +109,129 @@ ol.source.TileImage.defaultTileLoadFunction = function(imageTile, src) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.canExpireCache = function() {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
|
return goog.base(this, 'canExpireCache');
|
||||||
|
}
|
||||||
|
var canExpire = this.tileCache.canExpireCache();
|
||||||
|
if (canExpire) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return goog.object.some(this.tileCacheForProjection, function(tileCache) {
|
||||||
|
return tileCache.canExpireCache();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.expireCache = function(projection, usedTiles) {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
|
goog.base(this, 'expireCache', projection, usedTiles);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var usedTileCache = this.getTileCacheForProjection(projection);
|
||||||
|
|
||||||
|
this.tileCache.expireCache(this.tileCache == usedTileCache ? usedTiles : {});
|
||||||
|
goog.object.forEach(this.tileCacheForProjection, function(tileCache) {
|
||||||
|
tileCache.expireCache(tileCache == usedTileCache ? usedTiles : {});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.getTileGridForProjection = function(projection) {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
|
return goog.base(this, 'getTileGridForProjection', projection);
|
||||||
|
}
|
||||||
|
var thisProj = this.getProjection();
|
||||||
|
if (this.tileGrid &&
|
||||||
|
(!thisProj || ol.proj.equivalent(thisProj, projection))) {
|
||||||
|
return this.tileGrid;
|
||||||
|
} else {
|
||||||
|
var projKey = goog.getUid(projection).toString();
|
||||||
|
if (!(projKey in this.tileGridForProjection)) {
|
||||||
|
this.tileGridForProjection[projKey] =
|
||||||
|
ol.tilegrid.getForProjection(projection);
|
||||||
|
}
|
||||||
|
return this.tileGridForProjection[projKey];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.getTileCacheForProjection = function(projection) {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
|
return goog.base(this, 'getTileCacheForProjection', projection);
|
||||||
|
}
|
||||||
|
var thisProj = this.getProjection();
|
||||||
|
if (!thisProj || ol.proj.equivalent(thisProj, projection)) {
|
||||||
|
return this.tileCache;
|
||||||
|
} else {
|
||||||
|
var projKey = goog.getUid(projection).toString();
|
||||||
|
if (!(projKey in this.tileCacheForProjection)) {
|
||||||
|
this.tileCacheForProjection[projKey] = new ol.TileCache();
|
||||||
|
}
|
||||||
|
return this.tileCacheForProjection[projKey];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.TileImage.prototype.getTile =
|
ol.source.TileImage.prototype.getTile =
|
||||||
function(z, x, y, pixelRatio, projection) {
|
function(z, x, y, pixelRatio, projection) {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION ||
|
||||||
|
!this.getProjection() ||
|
||||||
|
!projection ||
|
||||||
|
ol.proj.equivalent(this.getProjection(), projection)) {
|
||||||
|
return this.getTileInternal(z, x, y, pixelRatio, projection);
|
||||||
|
} else {
|
||||||
|
var cache = this.getTileCacheForProjection(projection);
|
||||||
|
var tileCoordKey = this.getKeyZXY(z, x, y);
|
||||||
|
if (cache.containsKey(tileCoordKey)) {
|
||||||
|
return /** @type {!ol.Tile} */(cache.get(tileCoordKey));
|
||||||
|
} else {
|
||||||
|
var sourceProjection = this.getProjection();
|
||||||
|
var sourceTileGrid = this.getTileGridForProjection(sourceProjection);
|
||||||
|
var targetTileGrid = this.getTileGridForProjection(projection);
|
||||||
|
var tile = new ol.reproj.Tile(
|
||||||
|
sourceProjection, sourceTileGrid,
|
||||||
|
projection, targetTileGrid,
|
||||||
|
z, x, y, this.getTilePixelRatio(),
|
||||||
|
goog.bind(function(z, x, y, pixelRatio) {
|
||||||
|
return this.getTileInternal(z, x, y, pixelRatio, sourceProjection);
|
||||||
|
}, this), this.reprojectionErrorThreshold_,
|
||||||
|
this.renderReprojectionEdges_);
|
||||||
|
|
||||||
|
cache.set(tileCoordKey, tile);
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {number} z Tile coordinate z.
|
||||||
|
* @param {number} x Tile coordinate x.
|
||||||
|
* @param {number} y Tile coordinate y.
|
||||||
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
|
* @return {!ol.Tile} Tile.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.getTileInternal =
|
||||||
|
function(z, x, y, pixelRatio, projection) {
|
||||||
var tileCoordKey = this.getKeyZXY(z, x, y);
|
var tileCoordKey = this.getKeyZXY(z, x, y);
|
||||||
if (this.tileCache.containsKey(tileCoordKey)) {
|
if (this.tileCache.containsKey(tileCoordKey)) {
|
||||||
return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
|
return /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
|
||||||
@@ -156,6 +301,50 @@ ol.source.TileImage.prototype.handleTileChange_ = function(event) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets whether to render reprojection edges or not (usually for debugging).
|
||||||
|
* @param {boolean} render Render the edges.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.setRenderReprojectionEdges = function(render) {
|
||||||
|
if (!ol.ENABLE_RASTER_REPROJECTION ||
|
||||||
|
this.renderReprojectionEdges_ == render) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.renderReprojectionEdges_ = render;
|
||||||
|
goog.object.forEach(this.tileCacheForProjection, function(tileCache) {
|
||||||
|
tileCache.clear();
|
||||||
|
});
|
||||||
|
this.changed();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tile grid to use when reprojecting the tiles to the given
|
||||||
|
* projection instead of the default tile grid for the projection.
|
||||||
|
*
|
||||||
|
* This can be useful when the default tile grid cannot be created
|
||||||
|
* (e.g. projection has no extent defined) or
|
||||||
|
* for optimization reasons (custom tile size, resolutions, ...).
|
||||||
|
*
|
||||||
|
* @param {ol.proj.ProjectionLike} projection Projection.
|
||||||
|
* @param {ol.tilegrid.TileGrid} tilegrid Tile grid to use for the projection.
|
||||||
|
* @api
|
||||||
|
*/
|
||||||
|
ol.source.TileImage.prototype.setTileGridForProjection =
|
||||||
|
function(projection, tilegrid) {
|
||||||
|
if (ol.ENABLE_RASTER_REPROJECTION) {
|
||||||
|
var proj = ol.proj.get(projection);
|
||||||
|
if (proj) {
|
||||||
|
var projKey = goog.getUid(proj).toString();
|
||||||
|
if (!(projKey in this.tileGridForProjection)) {
|
||||||
|
this.tileGridForProjection[projKey] = tilegrid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the tile load function of the source.
|
* Set the tile load function of the source.
|
||||||
* @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
|
* @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
|
||||||
@@ -163,6 +352,7 @@ ol.source.TileImage.prototype.handleTileChange_ = function(event) {
|
|||||||
*/
|
*/
|
||||||
ol.source.TileImage.prototype.setTileLoadFunction = function(tileLoadFunction) {
|
ol.source.TileImage.prototype.setTileLoadFunction = function(tileLoadFunction) {
|
||||||
this.tileCache.clear();
|
this.tileCache.clear();
|
||||||
|
this.tileCacheForProjection = {};
|
||||||
this.tileLoadFunction = tileLoadFunction;
|
this.tileLoadFunction = tileLoadFunction;
|
||||||
this.changed();
|
this.changed();
|
||||||
};
|
};
|
||||||
@@ -178,6 +368,7 @@ ol.source.TileImage.prototype.setTileUrlFunction = function(tileUrlFunction) {
|
|||||||
// FIXME cache. The tile URL function would need to be incorporated into the
|
// FIXME cache. The tile URL function would need to be incorporated into the
|
||||||
// FIXME cache key somehow.
|
// FIXME cache key somehow.
|
||||||
this.tileCache.clear();
|
this.tileCache.clear();
|
||||||
|
this.tileCacheForProjection = {};
|
||||||
this.tileUrlFunction = tileUrlFunction;
|
this.tileUrlFunction = tileUrlFunction;
|
||||||
this.changed();
|
this.changed();
|
||||||
};
|
};
|
||||||
@@ -186,9 +377,10 @@ ol.source.TileImage.prototype.setTileUrlFunction = function(tileUrlFunction) {
|
|||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
ol.source.TileImage.prototype.useTile = function(z, x, y) {
|
ol.source.TileImage.prototype.useTile = function(z, x, y, projection) {
|
||||||
|
var tileCache = this.getTileCacheForProjection(projection);
|
||||||
var tileCoordKey = this.getKeyZXY(z, x, y);
|
var tileCoordKey = this.getKeyZXY(z, x, y);
|
||||||
if (this.tileCache.containsKey(tileCoordKey)) {
|
if (tileCache && tileCache.containsKey(tileCoordKey)) {
|
||||||
this.tileCache.get(tileCoordKey);
|
tileCache.get(tileCoordKey);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ ol.source.TileJSON = function(options) {
|
|||||||
attributions: options.attributions,
|
attributions: options.attributions,
|
||||||
crossOrigin: options.crossOrigin,
|
crossOrigin: options.crossOrigin,
|
||||||
projection: ol.proj.get('EPSG:3857'),
|
projection: ol.proj.get('EPSG:3857'),
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
state: ol.source.State.LOADING,
|
state: ol.source.State.LOADING,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
wrapX: options.wrapX !== undefined ? options.wrapX : true
|
wrapX: options.wrapX !== undefined ? options.wrapX : true
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ goog.require('ol.Extent');
|
|||||||
goog.require('ol.TileCache');
|
goog.require('ol.TileCache');
|
||||||
goog.require('ol.TileRange');
|
goog.require('ol.TileRange');
|
||||||
goog.require('ol.TileState');
|
goog.require('ol.TileState');
|
||||||
|
goog.require('ol.proj');
|
||||||
goog.require('ol.size');
|
goog.require('ol.size');
|
||||||
goog.require('ol.source.Source');
|
goog.require('ol.source.Source');
|
||||||
goog.require('ol.tilecoord');
|
goog.require('ol.tilecoord');
|
||||||
@@ -97,14 +98,19 @@ ol.source.Tile.prototype.canExpireCache = function() {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
* @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
|
* @param {Object.<string, ol.TileRange>} usedTiles Used tiles.
|
||||||
*/
|
*/
|
||||||
ol.source.Tile.prototype.expireCache = function(usedTiles) {
|
ol.source.Tile.prototype.expireCache = function(projection, usedTiles) {
|
||||||
this.tileCache.expireCache(usedTiles);
|
var tileCache = this.getTileCacheForProjection(projection);
|
||||||
|
if (tileCache) {
|
||||||
|
tileCache.expireCache(usedTiles);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
* @param {number} z Zoom level.
|
* @param {number} z Zoom level.
|
||||||
* @param {ol.TileRange} tileRange Tile range.
|
* @param {ol.TileRange} tileRange Tile range.
|
||||||
* @param {function(ol.Tile):(boolean|undefined)} callback Called with each
|
* @param {function(ol.Tile):(boolean|undefined)} callback Called with each
|
||||||
@@ -112,15 +118,21 @@ ol.source.Tile.prototype.expireCache = function(usedTiles) {
|
|||||||
* considered loaded.
|
* considered loaded.
|
||||||
* @return {boolean} The tile range is fully covered with loaded tiles.
|
* @return {boolean} The tile range is fully covered with loaded tiles.
|
||||||
*/
|
*/
|
||||||
ol.source.Tile.prototype.forEachLoadedTile = function(z, tileRange, callback) {
|
ol.source.Tile.prototype.forEachLoadedTile =
|
||||||
|
function(projection, z, tileRange, callback) {
|
||||||
|
var tileCache = this.getTileCacheForProjection(projection);
|
||||||
|
if (!tileCache) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var covered = true;
|
var covered = true;
|
||||||
var tile, tileCoordKey, loaded;
|
var tile, tileCoordKey, loaded;
|
||||||
for (var x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
for (var x = tileRange.minX; x <= tileRange.maxX; ++x) {
|
||||||
for (var y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
for (var y = tileRange.minY; y <= tileRange.maxY; ++y) {
|
||||||
tileCoordKey = this.getKeyZXY(z, x, y);
|
tileCoordKey = this.getKeyZXY(z, x, y);
|
||||||
loaded = false;
|
loaded = false;
|
||||||
if (this.tileCache.containsKey(tileCoordKey)) {
|
if (tileCache.containsKey(tileCoordKey)) {
|
||||||
tile = /** @type {!ol.Tile} */ (this.tileCache.get(tileCoordKey));
|
tile = /** @type {!ol.Tile} */ (tileCache.get(tileCoordKey));
|
||||||
loaded = tile.getState() === ol.TileState.LOADED;
|
loaded = tile.getState() === ol.TileState.LOADED;
|
||||||
if (loaded) {
|
if (loaded) {
|
||||||
loaded = (callback(tile) !== false);
|
loaded = (callback(tile) !== false);
|
||||||
@@ -174,7 +186,7 @@ ol.source.Tile.prototype.getResolutions = function() {
|
|||||||
* @param {number} x Tile coordinate x.
|
* @param {number} x Tile coordinate x.
|
||||||
* @param {number} y Tile coordinate y.
|
* @param {number} y Tile coordinate y.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
* @param {ol.proj.Projection=} opt_projection Projection.
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
* @return {!ol.Tile} Tile.
|
* @return {!ol.Tile} Tile.
|
||||||
*/
|
*/
|
||||||
ol.source.Tile.prototype.getTile = goog.abstractMethod;
|
ol.source.Tile.prototype.getTile = goog.abstractMethod;
|
||||||
@@ -203,6 +215,29 @@ ol.source.Tile.prototype.getTileGridForProjection = function(projection) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
|
* @return {ol.TileCache} Tile cache.
|
||||||
|
* @protected
|
||||||
|
*/
|
||||||
|
ol.source.Tile.prototype.getTileCacheForProjection = function(projection) {
|
||||||
|
var thisProj = this.getProjection();
|
||||||
|
if (thisProj && !ol.proj.equivalent(thisProj, projection)) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return this.tileCache;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {number} Tile pixel ratio.
|
||||||
|
*/
|
||||||
|
ol.source.Tile.prototype.getTilePixelRatio = function() {
|
||||||
|
return this.tilePixelRatio_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} z Z.
|
* @param {number} z Z.
|
||||||
* @param {number} pixelRatio Pixel ratio.
|
* @param {number} pixelRatio Pixel ratio.
|
||||||
@@ -244,6 +279,7 @@ ol.source.Tile.prototype.getTileCoordForTileUrlFunction =
|
|||||||
* @param {number} z Tile coordinate z.
|
* @param {number} z Tile coordinate z.
|
||||||
* @param {number} x Tile coordinate x.
|
* @param {number} x Tile coordinate x.
|
||||||
* @param {number} y Tile coordinate y.
|
* @param {number} y Tile coordinate y.
|
||||||
|
* @param {ol.proj.Projection} projection Projection.
|
||||||
*/
|
*/
|
||||||
ol.source.Tile.prototype.useTile = ol.nullFunction;
|
ol.source.Tile.prototype.useTile = ol.nullFunction;
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ ol.source.TileWMS = function(opt_options) {
|
|||||||
logo: options.logo,
|
logo: options.logo,
|
||||||
opaque: !transparent,
|
opaque: !transparent,
|
||||||
projection: options.projection,
|
projection: options.projection,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileGrid: options.tileGrid,
|
tileGrid: options.tileGrid,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
tileUrlFunction: goog.bind(this.tileUrlFunction_, this),
|
tileUrlFunction: goog.bind(this.tileUrlFunction_, this),
|
||||||
|
|||||||
@@ -185,6 +185,7 @@ ol.source.WMTS = function(options) {
|
|||||||
crossOrigin: options.crossOrigin,
|
crossOrigin: options.crossOrigin,
|
||||||
logo: options.logo,
|
logo: options.logo,
|
||||||
projection: options.projection,
|
projection: options.projection,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileClass: options.tileClass,
|
tileClass: options.tileClass,
|
||||||
tileGrid: tileGrid,
|
tileGrid: tileGrid,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ ol.source.XYZ = function(options) {
|
|||||||
crossOrigin: options.crossOrigin,
|
crossOrigin: options.crossOrigin,
|
||||||
logo: options.logo,
|
logo: options.logo,
|
||||||
projection: projection,
|
projection: projection,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileGrid: tileGrid,
|
tileGrid: tileGrid,
|
||||||
tileLoadFunction: options.tileLoadFunction,
|
tileLoadFunction: options.tileLoadFunction,
|
||||||
tilePixelRatio: options.tilePixelRatio,
|
tilePixelRatio: options.tilePixelRatio,
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ ol.source.Zoomify = function(opt_options) {
|
|||||||
attributions: options.attributions,
|
attributions: options.attributions,
|
||||||
crossOrigin: options.crossOrigin,
|
crossOrigin: options.crossOrigin,
|
||||||
logo: options.logo,
|
logo: options.logo,
|
||||||
|
reprojectionErrorThreshold: options.reprojectionErrorThreshold,
|
||||||
tileClass: ol.source.ZoomifyTile_,
|
tileClass: ol.source.ZoomifyTile_,
|
||||||
tileGrid: tileGrid,
|
tileGrid: tileGrid,
|
||||||
tileUrlFunction: tileUrlFunction
|
tileUrlFunction: tileUrlFunction
|
||||||
|
|||||||
@@ -91,6 +91,45 @@ describe('ol.math.roundUpToPowerOfTwo', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('ol.math.solveLinearSystem', function() {
|
||||||
|
it('calculates correctly', function() {
|
||||||
|
var result = ol.math.solveLinearSystem([
|
||||||
|
[2, 1, 3, 1],
|
||||||
|
[2, 6, 8, 3],
|
||||||
|
[6, 8, 18, 5]
|
||||||
|
]);
|
||||||
|
expect(result[0]).to.roughlyEqual(0.3, 1e-9);
|
||||||
|
expect(result[1]).to.roughlyEqual(0.4, 1e-9);
|
||||||
|
expect(result[2]).to.roughlyEqual(0, 1e-9);
|
||||||
|
});
|
||||||
|
it('can handle singular matrix', function() {
|
||||||
|
var result = ol.math.solveLinearSystem([
|
||||||
|
[2, 1, 3, 1],
|
||||||
|
[2, 6, 8, 3],
|
||||||
|
[2, 1, 3, 1]
|
||||||
|
]);
|
||||||
|
expect(result).to.be(null);
|
||||||
|
});
|
||||||
|
it('raises an exception when the matrix is malformed', function() {
|
||||||
|
expect(function() {
|
||||||
|
ol.math.solveLinearSystem([
|
||||||
|
[2, 1, 3, 1],
|
||||||
|
[2, 6, 8, 3],
|
||||||
|
[6, 8, 18]
|
||||||
|
]);
|
||||||
|
}).to.throwException();
|
||||||
|
|
||||||
|
expect(function() {
|
||||||
|
ol.math.solveLinearSystem([
|
||||||
|
[2, 1, 3, 1],
|
||||||
|
[2, 6, 8, 3],
|
||||||
|
[6, 8, 18, 5, 0]
|
||||||
|
]);
|
||||||
|
}).to.throwException();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('ol.math.toDegrees', function() {
|
describe('ol.math.toDegrees', function() {
|
||||||
it('returns the correct value at -π', function() {
|
it('returns the correct value at -π', function() {
|
||||||
expect(ol.math.toDegrees(-Math.PI)).to.be(-180);
|
expect(ol.math.toDegrees(-Math.PI)).to.be(-180);
|
||||||
|
|||||||
60
test/spec/ol/reproj/image.test.js
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
goog.provide('ol.test.reproj.Image');
|
||||||
|
|
||||||
|
describe('ol.reproj.Image', function() {
|
||||||
|
function createImage(pixelRatio) {
|
||||||
|
return new ol.reproj.Image(
|
||||||
|
ol.proj.get('EPSG:3857'), ol.proj.get('EPSG:4326'),
|
||||||
|
[-180, -85, 180, 85], 10, pixelRatio,
|
||||||
|
function(extent, resolution, pixelRatio) {
|
||||||
|
return new ol.Image(extent, resolution, pixelRatio, [],
|
||||||
|
'data:image/gif;base64,' +
|
||||||
|
'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', null,
|
||||||
|
function(image, src) {
|
||||||
|
image.getImage().src = src;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('changes state as expected', function(done) {
|
||||||
|
var image = createImage(1);
|
||||||
|
expect(image.getState()).to.be(ol.ImageState.IDLE);
|
||||||
|
image.listen('change', function() {
|
||||||
|
if (image.getState() == ol.ImageState.LOADED) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
image.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns correct canvas size', function(done) {
|
||||||
|
var image = createImage(1);
|
||||||
|
image.listen('change', function() {
|
||||||
|
if (image.getState() == ol.ImageState.LOADED) {
|
||||||
|
var canvas = image.getImage();
|
||||||
|
expect(canvas.width).to.be(36);
|
||||||
|
expect(canvas.height).to.be(17);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
image.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('respects pixelRatio', function(done) {
|
||||||
|
var image = createImage(2);
|
||||||
|
image.listen('change', function() {
|
||||||
|
if (image.getState() == ol.ImageState.LOADED) {
|
||||||
|
var canvas = image.getImage();
|
||||||
|
expect(canvas.width).to.be(72);
|
||||||
|
expect(canvas.height).to.be(34);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
image.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
goog.require('ol.Image');
|
||||||
|
goog.require('ol.ImageState');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Image');
|
||||||
45
test/spec/ol/reproj/reproj.test.js
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
goog.provide('ol.test.reproj');
|
||||||
|
|
||||||
|
describe('ol.reproj', function() {
|
||||||
|
|
||||||
|
describe('#calculateSourceResolution', function() {
|
||||||
|
var proj3857 = ol.proj.get('EPSG:3857');
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var origin = [0, 0];
|
||||||
|
var point3857 = [50, 40];
|
||||||
|
var point4326 = ol.proj.transform(point3857, proj3857, proj4326);
|
||||||
|
|
||||||
|
it('is identity for identical projection', function() {
|
||||||
|
var result;
|
||||||
|
var resolution = 500;
|
||||||
|
result = ol.reproj.calculateSourceResolution(
|
||||||
|
proj3857, proj3857, origin, resolution);
|
||||||
|
expect(result).to.be(resolution);
|
||||||
|
|
||||||
|
result = ol.reproj.calculateSourceResolution(
|
||||||
|
proj3857, proj3857, point3857, resolution);
|
||||||
|
expect(result).to.be(resolution);
|
||||||
|
|
||||||
|
result = ol.reproj.calculateSourceResolution(
|
||||||
|
proj4326, proj4326, point4326, resolution);
|
||||||
|
expect(result).to.be(resolution);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calculates correctly', function() {
|
||||||
|
var resolution4326 = 5;
|
||||||
|
|
||||||
|
var resolution3857 = ol.reproj.calculateSourceResolution(
|
||||||
|
proj3857, proj4326, point4326, resolution4326);
|
||||||
|
expect(resolution3857).not.to.be(resolution4326);
|
||||||
|
expect(resolution3857).to.roughlyEqual(555974.3714343394, 1e-6);
|
||||||
|
|
||||||
|
var result = ol.reproj.calculateSourceResolution(
|
||||||
|
proj4326, proj3857, point3857, resolution3857);
|
||||||
|
expect(result).to.be(resolution4326);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
goog.require('ol.reproj');
|
||||||
|
goog.require('ol.proj');
|
||||||
100
test/spec/ol/reproj/tile.test.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
goog.provide('ol.test.reproj.Tile');
|
||||||
|
|
||||||
|
describe('ol.reproj.Tile', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
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');
|
||||||
|
var proj27700 = ol.proj.get('EPSG:27700');
|
||||||
|
proj27700.setExtent([0, 0, 700000, 1300000]);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
delete proj4.defs['EPSG:27700'];
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function createTile(pixelRatio, opt_tileSize) {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var proj3857 = ol.proj.get('EPSG:3857');
|
||||||
|
return new ol.reproj.Tile(
|
||||||
|
proj3857, ol.tilegrid.createForProjection(proj3857), proj4326,
|
||||||
|
ol.tilegrid.createForProjection(proj4326, 3, opt_tileSize),
|
||||||
|
3, 2, -2, pixelRatio, function(z, x, y, pixelRatio) {
|
||||||
|
return new ol.ImageTile([z, x, y], ol.TileState.IDLE,
|
||||||
|
'data:image/gif;base64,' +
|
||||||
|
'R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs=', null,
|
||||||
|
function(tile, src) {
|
||||||
|
tile.getImage().src = src;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
it('changes state as expected', function(done) {
|
||||||
|
var tile = createTile(1);
|
||||||
|
expect(tile.getState()).to.be(ol.TileState.IDLE);
|
||||||
|
tile.listen('change', function() {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is empty when outside target tile grid', function() {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var proj3857 = ol.proj.get('EPSG:3857');
|
||||||
|
var tile = new ol.reproj.Tile(
|
||||||
|
proj3857, ol.tilegrid.createForProjection(proj3857),
|
||||||
|
proj4326, ol.tilegrid.createForProjection(proj4326),
|
||||||
|
0, -1, 0, 1, function() {
|
||||||
|
expect().fail('No tiles should be required');
|
||||||
|
});
|
||||||
|
expect(tile.getState()).to.be(ol.TileState.EMPTY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is empty when outside source tile grid', function() {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var proj27700 = ol.proj.get('EPSG:27700');
|
||||||
|
var tile = new ol.reproj.Tile(
|
||||||
|
proj27700, ol.tilegrid.createForProjection(proj27700),
|
||||||
|
proj4326, ol.tilegrid.createForProjection(proj4326),
|
||||||
|
3, 2, -2, 1, function() {
|
||||||
|
expect().fail('No tiles should be required');
|
||||||
|
});
|
||||||
|
expect(tile.getState()).to.be(ol.TileState.EMPTY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('respects tile size of target tile grid', function(done) {
|
||||||
|
var tile = createTile(1, [100, 40]);
|
||||||
|
tile.listen('change', function() {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
var canvas = tile.getImage();
|
||||||
|
expect(canvas.width).to.be(100);
|
||||||
|
expect(canvas.height).to.be(40);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('respects pixelRatio', function(done) {
|
||||||
|
var tile = createTile(3, [60, 20]);
|
||||||
|
tile.listen('change', function() {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
var canvas = tile.getImage();
|
||||||
|
expect(canvas.width).to.be(180);
|
||||||
|
expect(canvas.height).to.be(60);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
goog.require('ol.ImageTile');
|
||||||
|
goog.require('ol.TileState');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Tile');
|
||||||
44
test/spec/ol/reproj/triangulation.test.js
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
goog.provide('ol.test.reproj.Triangulation');
|
||||||
|
|
||||||
|
describe('ol.reproj.Triangulation', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
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');
|
||||||
|
var proj27700 = ol.proj.get('EPSG:27700');
|
||||||
|
proj27700.setExtent([0, 0, 700000, 1300000]);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
delete proj4.defs['EPSG:27700'];
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('constructor', function() {
|
||||||
|
it('is trivial for identity', function() {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var triangulation = new ol.reproj.Triangulation(proj4326, proj4326,
|
||||||
|
[20, 20, 30, 30], [-180, -90, 180, 90], 0);
|
||||||
|
expect(triangulation.getTriangles().length).to.be(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('is empty when outside source extent', function() {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var proj27700 = ol.proj.get('EPSG:27700');
|
||||||
|
var triangulation = new ol.reproj.Triangulation(proj27700, proj4326,
|
||||||
|
[0, 0, 10, 10], proj27700.getExtent(), 0);
|
||||||
|
expect(triangulation.getTriangles().length).to.be(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle null source extent', function() {
|
||||||
|
var proj4326 = ol.proj.get('EPSG:4326');
|
||||||
|
var triangulation = new ol.reproj.Triangulation(proj4326, proj4326,
|
||||||
|
[20, 20, 30, 30], null, 0);
|
||||||
|
expect(triangulation.getTriangles().length).to.be(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Triangulation');
|
||||||
94
test/spec/ol/source/tileimagesource.test.js
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
goog.provide('ol.test.source.TileImageSource');
|
||||||
|
|
||||||
|
describe('ol.source.TileImage', function() {
|
||||||
|
function createSource(opt_proj, opt_tileGrid) {
|
||||||
|
var proj = opt_proj || 'EPSG:3857';
|
||||||
|
return new ol.source.TileImage({
|
||||||
|
projection: proj,
|
||||||
|
tileGrid: opt_tileGrid ||
|
||||||
|
ol.tilegrid.createForProjection(proj, undefined, [2, 2]),
|
||||||
|
tileUrlFunction: ol.TileUrlFunction.createFromTemplate(
|
||||||
|
'data:image/gif;base64,R0lGODlhAQABAAAAACwAAAAAAQABAAA=')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('#setTileGridForProjection', function() {
|
||||||
|
it('uses the tilegrid for given projection', function() {
|
||||||
|
var source = createSource();
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:4326', 3, [10, 20]);
|
||||||
|
source.setTileGridForProjection('EPSG:4326', tileGrid);
|
||||||
|
var retrieved = source.getTileGridForProjection(ol.proj.get('EPSG:4326'));
|
||||||
|
expect(retrieved).to.be(tileGrid);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#getTile', function() {
|
||||||
|
it('does not do reprojection for identity', function() {
|
||||||
|
var source3857 = createSource('EPSG:3857');
|
||||||
|
var tile3857 = source3857.getTile(0, 0, -1, 1, ol.proj.get('EPSG:3857'));
|
||||||
|
expect(tile3857).to.be.a(ol.ImageTile);
|
||||||
|
expect(tile3857).not.to.be.a(ol.reproj.Tile);
|
||||||
|
|
||||||
|
var projXXX = new ol.proj.Projection({
|
||||||
|
code: 'XXX',
|
||||||
|
units: 'degrees'
|
||||||
|
});
|
||||||
|
var sourceXXX = createSource(projXXX);
|
||||||
|
var tileXXX = sourceXXX.getTile(0, 0, -1, 1, projXXX);
|
||||||
|
expect(tileXXX).to.be.a(ol.ImageTile);
|
||||||
|
expect(tileXXX).not.to.be.a(ol.reproj.Tile);
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
proj4.defs('4326_noextentnounits', '+proj=longlat +datum=WGS84 +no_defs');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
delete proj4.defs['4326_noextentnounits'];
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle source projection without extent and units', function(done) {
|
||||||
|
var source = createSource('4326_noextentnounits', ol.tilegrid.createXYZ({
|
||||||
|
extent: [-180, -90, 180, 90],
|
||||||
|
tileSize: [2, 2]
|
||||||
|
}));
|
||||||
|
var tile = source.getTile(0, 0, -1, 1, ol.proj.get('EPSG:3857'));
|
||||||
|
expect(tile).to.be.a(ol.reproj.Tile);
|
||||||
|
|
||||||
|
tile.listen('change', function() {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can handle target projection without extent and units', function(done) {
|
||||||
|
var proj = ol.proj.get('4326_noextentnounits');
|
||||||
|
var source = createSource();
|
||||||
|
source.setTileGridForProjection(proj,
|
||||||
|
ol.tilegrid.createXYZ({
|
||||||
|
extent: [-180, -90, 180, 90],
|
||||||
|
tileSize: [2, 2]
|
||||||
|
}));
|
||||||
|
var tile = source.getTile(0, 0, -1, 1, proj);
|
||||||
|
expect(tile).to.be.a(ol.reproj.Tile);
|
||||||
|
|
||||||
|
tile.listen('change', function() {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
goog.require('ol.ImageTile');
|
||||||
|
goog.require('ol.Tile');
|
||||||
|
goog.require('ol.TileState');
|
||||||
|
goog.require('ol.TileUrlFunction');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.proj.Projection');
|
||||||
|
goog.require('ol.reproj.Tile');
|
||||||
|
goog.require('ol.source.TileImage');
|
||||||
@@ -26,7 +26,7 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 3;
|
var zoom = 3;
|
||||||
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
||||||
|
|
||||||
source.forEachLoadedTile(zoom, range, callback);
|
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
|
||||||
expect(callback.callCount).to.be(0);
|
expect(callback.callCount).to.be(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -38,7 +38,7 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 3;
|
var zoom = 3;
|
||||||
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
var range = grid.getTileRangeForExtentAndZ(extent, zoom);
|
||||||
|
|
||||||
source.forEachLoadedTile(zoom, range, callback);
|
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
|
||||||
expect(source.getTile.callCount).to.be(0);
|
expect(source.getTile.callCount).to.be(0);
|
||||||
source.getTile.restore();
|
source.getTile.restore();
|
||||||
});
|
});
|
||||||
@@ -55,7 +55,7 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 1;
|
var zoom = 1;
|
||||||
var range = new ol.TileRange(0, 1, 0, 1);
|
var range = new ol.TileRange(0, 1, 0, 1);
|
||||||
|
|
||||||
source.forEachLoadedTile(zoom, range, callback);
|
source.forEachLoadedTile(source.getProjection(), zoom, range, callback);
|
||||||
expect(callback.callCount).to.be(3);
|
expect(callback.callCount).to.be(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -71,9 +71,10 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 1;
|
var zoom = 1;
|
||||||
var range = new ol.TileRange(0, 1, 0, 1);
|
var range = new ol.TileRange(0, 1, 0, 1);
|
||||||
|
|
||||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
var covered = source.forEachLoadedTile(source.getProjection(), zoom,
|
||||||
return true;
|
range, function() {
|
||||||
});
|
return true;
|
||||||
|
});
|
||||||
expect(covered).to.be(true);
|
expect(covered).to.be(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -89,9 +90,10 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 1;
|
var zoom = 1;
|
||||||
var range = new ol.TileRange(0, 1, 0, 1);
|
var range = new ol.TileRange(0, 1, 0, 1);
|
||||||
|
|
||||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
var covered = source.forEachLoadedTile(source.getProjection(), zoom,
|
||||||
return true;
|
range, function() {
|
||||||
});
|
return true;
|
||||||
|
});
|
||||||
expect(covered).to.be(false);
|
expect(covered).to.be(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -107,9 +109,10 @@ describe('ol.source.Tile', function() {
|
|||||||
var zoom = 1;
|
var zoom = 1;
|
||||||
var range = new ol.TileRange(0, 1, 0, 1);
|
var range = new ol.TileRange(0, 1, 0, 1);
|
||||||
|
|
||||||
var covered = source.forEachLoadedTile(zoom, range, function() {
|
var covered = source.forEachLoadedTile(source.getProjection(), zoom,
|
||||||
return false;
|
range, function() {
|
||||||
});
|
return false;
|
||||||
|
});
|
||||||
expect(covered).to.be(false);
|
expect(covered).to.be(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -390,6 +390,7 @@
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
global.resembleCanvas = resembleCanvas;
|
||||||
|
|
||||||
function expectResembleCanvas(map, referenceImage, tolerance, done) {
|
function expectResembleCanvas(map, referenceImage, tolerance, done) {
|
||||||
map.render();
|
map.render();
|
||||||
|
|||||||
BIN
test_rendering/spec/ol/data/tiles/4326/0/0/0.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
test_rendering/spec/ol/data/tiles/osm-512x256/5/3/12.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
test_rendering/spec/ol/data/tiles/osm/5/5/13.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
test_rendering/spec/ol/data/tiles/osm/5/6/12.png
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
test_rendering/spec/ol/data/tiles/osm/5/6/13.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
test_rendering/spec/ol/reproj/expected/4326-to-3857.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
test_rendering/spec/ol/reproj/expected/512x256-to-64x128.png
Normal file
|
After Width: | Height: | Size: 3.8 KiB |
BIN
test_rendering/spec/ol/reproj/expected/dateline-merc-180.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
test_rendering/spec/ol/reproj/expected/dateline-pole.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
test_rendering/spec/ol/reproj/expected/image-3857-to-4326.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
BIN
test_rendering/spec/ol/reproj/expected/image-dateline-pole.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
test_rendering/spec/ol/reproj/expected/osm4326.png
Normal file
|
After Width: | Height: | Size: 4.1 KiB |
BIN
test_rendering/spec/ol/reproj/expected/osm5070.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
test_rendering/spec/ol/reproj/expected/osm54009.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
test_rendering/spec/ol/reproj/expected/stitch-osm3740.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
test_rendering/spec/ol/reproj/expected/stitch-osm4326.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
61
test_rendering/spec/ol/reproj/image.test.js
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
goog.provide('ol.test.rendering.reproj.Image');
|
||||||
|
|
||||||
|
describe('ol.rendering.reproj.Image', function() {
|
||||||
|
|
||||||
|
function testSingleImage(source, targetProj,
|
||||||
|
targetExtent, targetResolution, pixelRatio, expectedUrl, done) {
|
||||||
|
var sourceProj = source.getProjection();
|
||||||
|
|
||||||
|
var imagesRequested = 0;
|
||||||
|
|
||||||
|
var image = new ol.reproj.Image(sourceProj, ol.proj.get(targetProj),
|
||||||
|
targetExtent, targetResolution, pixelRatio,
|
||||||
|
function(extent, resolution, pixelRatio) {
|
||||||
|
imagesRequested++;
|
||||||
|
return source.getImage(extent, resolution, pixelRatio, sourceProj);
|
||||||
|
});
|
||||||
|
if (image.getState() == ol.ImageState.IDLE) {
|
||||||
|
image.listen('change', function(e) {
|
||||||
|
if (image.getState() == ol.ImageState.LOADED) {
|
||||||
|
expect(imagesRequested).to.be(1);
|
||||||
|
resembleCanvas(image.getImage(), expectedUrl, IMAGE_TOLERANCE, done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
image.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var source;
|
||||||
|
|
||||||
|
describe('image reprojections from EPSG:3857', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.ImageStatic({
|
||||||
|
url: 'spec/ol/data/tiles/osm/5/5/12.png',
|
||||||
|
imageExtent: ol.tilegrid.createXYZ().getTileCoordExtent([5, 5, -13]),
|
||||||
|
projection: ol.proj.get('EPSG:3857')
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for identity reprojection', function(done) {
|
||||||
|
testSingleImage(source, 'EPSG:3857',
|
||||||
|
ol.tilegrid.createXYZ().getTileCoordExtent([5, 5, -13]),
|
||||||
|
2 * ol.proj.EPSG3857.HALF_SIZE / (256 * (1 << 5)), 1,
|
||||||
|
'spec/ol/data/tiles/osm/5/5/12.png', done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:4326', function(done) {
|
||||||
|
testSingleImage(source, 'EPSG:4326',
|
||||||
|
ol.tilegrid.createForProjection('EPSG:4326').
|
||||||
|
getTileCoordExtent([6, 10, -10]),
|
||||||
|
360 / (256 * (1 << 4)), 1,
|
||||||
|
'spec/ol/reproj/expected/image-3857-to-4326.png', done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
goog.require('ol.extent');
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.proj.EPSG3857');
|
||||||
|
goog.require('ol.reproj.Image');
|
||||||
|
goog.require('ol.source.ImageStatic');
|
||||||
|
goog.require('ol.ImageState');
|
||||||
178
test_rendering/spec/ol/reproj/tile.test.js
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
goog.provide('ol.test.rendering.reproj.Tile');
|
||||||
|
|
||||||
|
describe('ol.rendering.reproj.Tile', function() {
|
||||||
|
|
||||||
|
function testSingleTile(source, targetProjection, targetTileGrid, z, x, y,
|
||||||
|
pixelRatio, expectedUrl, expectedRequests, done) {
|
||||||
|
var sourceProjection = source.getProjection();
|
||||||
|
|
||||||
|
var tilesRequested = 0;
|
||||||
|
|
||||||
|
var tile = new ol.reproj.Tile(sourceProjection, source.getTileGrid(),
|
||||||
|
ol.proj.get(targetProjection), targetTileGrid, z, x, y, pixelRatio,
|
||||||
|
function(z, x, y, pixelRatio) {
|
||||||
|
tilesRequested++;
|
||||||
|
return source.getTile(z, x, y, pixelRatio, sourceProjection);
|
||||||
|
});
|
||||||
|
if (tile.getState() == ol.TileState.IDLE) {
|
||||||
|
tile.listen('change', function(e) {
|
||||||
|
if (tile.getState() == ol.TileState.LOADED) {
|
||||||
|
expect(tilesRequested).to.be(expectedRequests);
|
||||||
|
resembleCanvas(tile.getImage(), expectedUrl,
|
||||||
|
IMAGE_TOLERANCE, done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
tile.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var source;
|
||||||
|
|
||||||
|
describe('single tile reprojections from EPSG:3857', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for identity reprojection', function(done) {
|
||||||
|
testSingleTile(source, 'EPSG:3857', source.getTileGrid(), 5, 5, -13, 1,
|
||||||
|
'spec/ol/data/tiles/osm/5/5/12.png', 1, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:4326', function(done) {
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:4326', 7, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:4326', tileGrid, 7, 21, -20, 1,
|
||||||
|
'spec/ol/reproj/expected/osm4326.png', 1, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:5070', function(done) {
|
||||||
|
proj4.defs('EPSG:5070',
|
||||||
|
'+proj=aea +lat_1=29.5 +lat_2=45.5 +lat_0=23 +lon_0=-96 +x_0=0 ' +
|
||||||
|
'+y_0=0 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs');
|
||||||
|
var proj5070 = ol.proj.get('EPSG:5070');
|
||||||
|
proj5070.setExtent([-6e6, 0, 4e6, 6e6]);
|
||||||
|
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:5070', 5, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:5070', tileGrid, 5, 13, -15, 1,
|
||||||
|
'spec/ol/reproj/expected/osm5070.png', 1, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to ESRI:54009', function(done) {
|
||||||
|
proj4.defs('ESRI:54009',
|
||||||
|
'+proj=moll +lon_0=0 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
|
||||||
|
var proj54009 = ol.proj.get('ESRI:54009');
|
||||||
|
proj54009.setExtent([-18e6, -9e6, 18e6, 9e6]);
|
||||||
|
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('ESRI:54009', 7, [64, 64]);
|
||||||
|
testSingleTile(source, 'ESRI:54009', tileGrid, 7, 27, -16, 1,
|
||||||
|
'spec/ol/reproj/expected/osm54009.png', 1, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('stitching several tiles from EPSG:3857', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
url: 'spec/ol/data/tiles/osm/{z}/{x}/{y}.png'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:4326', function(done) {
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:4326', 7, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:4326', tileGrid, 7, 23, -21, 1,
|
||||||
|
'spec/ol/reproj/expected/stitch-osm4326.png', 2, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:3740', function(done) {
|
||||||
|
proj4.defs('EPSG:3740',
|
||||||
|
'+proj=utm +zone=10 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 ' +
|
||||||
|
'+units=m +no_defs');
|
||||||
|
var proj3740 = ol.proj.get('EPSG:3740');
|
||||||
|
proj3740.setExtent([318499.05, 2700792.39, 4359164.89, 7149336.98]);
|
||||||
|
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:3740', 4, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:3740', tileGrid, 4, 4, -13, 1,
|
||||||
|
'spec/ol/reproj/expected/stitch-osm3740.png', 4, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('tile projection from EPSG:4326', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:4326',
|
||||||
|
maxZoom: 0,
|
||||||
|
url: 'spec/ol/data/tiles/4326/{z}/{x}/{y}.png'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for identity reprojection', function(done) {
|
||||||
|
testSingleTile(source, 'EPSG:4326', source.getTileGrid(), 0, 0, -1, 1,
|
||||||
|
'spec/ol/data/tiles/4326/0/0/0.png', 1, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to EPSG:3857', function(done) {
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:3857', 0, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:3857', tileGrid, 0, 0, -1, 1,
|
||||||
|
'spec/ol/reproj/expected/4326-to-3857.png', 1, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('non-square source tiles', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:3857',
|
||||||
|
url: 'spec/ol/data/tiles/osm-512x256/{z}/{x}/{y}.png',
|
||||||
|
tileSize: [512, 256]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for identity reprojection', function(done) {
|
||||||
|
testSingleTile(source, 'EPSG:3857', source.getTileGrid(), 5, 3, -13, 1,
|
||||||
|
'spec/ol/data/tiles/osm-512x256/5/3/12.png', 1, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('to 64x128 EPSG:4326', function(done) {
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:4326', 7, [64, 128]);
|
||||||
|
testSingleTile(source, 'EPSG:4326', tileGrid, 7, 27, -10, 1,
|
||||||
|
'spec/ol/reproj/expected/512x256-to-64x128.png', 1, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dateline wrapping', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
source = new ol.source.XYZ({
|
||||||
|
projection: 'EPSG:4326',
|
||||||
|
maxZoom: 0,
|
||||||
|
url: 'spec/ol/data/tiles/4326/{z}/{x}/{y}.png'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wraps X when prime meridian is shifted', function(done) {
|
||||||
|
proj4.defs('merc_180', '+proj=merc +lon_0=180 +units=m +no_defs');
|
||||||
|
var proj_ = ol.proj.get('merc_180');
|
||||||
|
proj_.setExtent([-20026376.39, -20048966.10, 20026376.39, 20048966.10]);
|
||||||
|
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('merc_180', 0, [64, 64]);
|
||||||
|
testSingleTile(source, 'merc_180', tileGrid, 0, 0, -1, 1,
|
||||||
|
'spec/ol/reproj/expected/dateline-merc-180.png', 2, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays north pole correctly (EPSG:3413)', function(done) {
|
||||||
|
proj4.defs('EPSG:3413', '+proj=stere +lat_0=90 +lat_ts=70 +lon_0=-45 ' +
|
||||||
|
'+k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs');
|
||||||
|
var proj3413 = ol.proj.get('EPSG:3413');
|
||||||
|
proj3413.setExtent([-4194304, -4194304, 4194304, 4194304]);
|
||||||
|
|
||||||
|
var tileGrid = ol.tilegrid.createForProjection('EPSG:3413', 0, [64, 64]);
|
||||||
|
testSingleTile(source, 'EPSG:3413', tileGrid, 0, 0, -1, 1,
|
||||||
|
'spec/ol/reproj/expected/dateline-pole.png', 2, done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
goog.require('ol.proj');
|
||||||
|
goog.require('ol.reproj.Tile');
|
||||||
|
goog.require('ol.source.XYZ');
|
||||||
|
goog.require('ol.TileState');
|
||||||