Skip to content

Commit

Permalink
Merge pull request #531 from OpenGeoscience/reprojection-example
Browse files Browse the repository at this point in the history
Add reprojection example
  • Loading branch information
manthey committed Feb 16, 2016
2 parents 6e45ee3 + 787af66 commit e3a9180
Show file tree
Hide file tree
Showing 18 changed files with 503 additions and 53 deletions.
1 change: 1 addition & 0 deletions examples/reprojection/capitals.json

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions examples/reprojection/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"path": "reprojection",
"title": "Reproject tiles",
"exampleCss": ["main.css"],
"exampleJs": ["main.js"],
"about": {
"text": "This example shows how you can reproject tiles from one projection to another. The source tiles are in Web Mercator (EPSG:3857), but can be drawn in many different projections."
}
}
19 changes: 19 additions & 0 deletions examples/reprojection/index.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
extends ../common/templates/index.jade

block append mainContent
div#controls
.form-group(title="The url used to fetch tiles. Use {x}, {y}, {z}, and {s} for templating.")
label(for="layer-url") Tile URL
select#layer-url.layerparam(param-name="url", list="url-list", placeholder="http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png")
option(value="http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png", credit='Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a>') MapQuest
option(value="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", credit="© OpenStreetMap contributors") OpenStreetMap
option(value="/data/tilefancy.png", credit="") Fancy Test Tile
option(value="http://otile1.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png", credit='Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a>') MapQuest Satellite
option(value="http://tile.stamen.com/toner-lite/{z}/{x}/{y}.png", credit='Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.') Toner Lite
.form-group(title="Web maps are most often rendered using a Mercator geographic coordinate system, but other projections can be used. See spatialreference.org for more information on projections.")
label(for="map-gcs") Map GCS
select#map-gcs.mapparam(param-name="gcs", placeholder="EPSG:3857 - Web Mercator", reload="true")
option(value="EPSG:3857") EPSG:3857 - Web Mercator
.form-group(title="Show capital cities of the world. Click on a city to center it.")
label(for="capitals") Show Capitals
input#capitals.mapparam(param-name="capitals", type="checkbox", placeholder="true", checked="checked")
43 changes: 43 additions & 0 deletions examples/reprojection/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#controls {
overflow-x: hidden;
overflow-y: auto;
position: absolute;
left: 10px;
top: 80px;
z-index: 10;
border-radius: 5px;
border: 1px solid grey;
box-shadow: 1px 1px 3px black;
opacity: 0.5;
transition: opacity 250ms ease;
background: #CCC;
color: black;
padding: 4px;
font-size: 14px;
}
#controls:hover {
opacity: 1;
}
#controls.no-controls {
display: none;
}
#controls .form-group {
margin-bottom: 0;
}
#controls label {
min-width: 90px;
}
#map-gcs {
width: 200px;
}
#tooltip {
margin-left: 10px;
margin-top: -10px;
height: 20px;
line-height: 16px;
padding: 2px 5px;
background: rgba(255, 255, 255, 0.5);
border-radius: 10px;
font-size: 12px;
color: black;
}
286 changes: 286 additions & 0 deletions examples/reprojection/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
// This example should be tried with different query strings.

var exampleDebug = {};

// Run after the DOM loads
$(function () {
'use strict';

// Most map tile servers use EPSG:3857 (Web Mercator). Using a tile server
// with a different projection works correctly in all renderers. Using a
// different projection for the tiles and the map can work in the vgl
// renderer, but may have problems as the tile density is not uniform or
// regular.
var gcsTable = {
'EPSG:3857': 'EPSG:3857',
};
var gcsBounds = {};
var gcsList = [
'EPSG:3857', 'EPSG:3031', 'EPSG:3032', 'EPSG:3033', 'EPSG:3294',
'EPSG:3408', 'EPSG:3409', 'EPSG:3410', 'EPSG:3411', 'EPSG:3412',
'EPSG:3413', 'EPSG:3571', 'EPSG:3572', 'EPSG:3573', 'EPSG:3574',
'EPSG:3575', 'EPSG:3576', 'EPSG:3786', 'EPSG:32661', 'EPSG:32662',
'ESRI:53002', 'ESRI:53003', 'ESRI:53008', 'ESRI:53009', 'ESRI:53021',
'ESRI:53027', 'ESRI:54002', 'ESRI:54003', 'ESRI:54009', 'ESRI:54021',
'ESRI:54026', 'ESRI:54027', 'ESRI:102005', 'ESRI:102010', 'ESRI:102011',
'ESRI:102016', 'ESRI:102017', 'ESRI:102018', 'ESRI:102019',
'ESRI:102020', 'ESRI:102021', 'ESRI:102023', 'ESRI:102026',
'ESRI:102029', 'ESRI:102031', 'ESRI:102032', 'IAU2000:39914',
'IAU2000:39918', 'IAU2000:39920', 'IAU2000:39962', 'IAU2000:39972',
'SR-ORG:7', 'SR-ORG:22', 'SR-ORG:4695', 'SR-ORG:6661', 'SR-ORG:6842',
'SR-ORG:6882', 'SR-ORG:6888', 'SR-ORG:6890', 'SR-ORG:6891',
'SR-ORG:6892', 'SR-ORG:6893', 'SR-ORG:6894', 'SR-ORG:6895',
'SR-ORG:6896', 'SR-ORG:6897', 'SR-ORG:6898', 'SR-ORG:7250',
'SR-ORG:8209', 'SR-ORG:8287'
];
var capitals;

$.when(
/* Fetch projections */
$.ajax({url: 'proj.json'}).done(function (resp) {
$('#map-gcs option').slice(1).remove();
resp.sort(function (a, b) {
var sa = a.name.split(':'), sb = b.name.split(':');
if (sa[0] !== sb[0]) {
return sa[0] < sb[0] ? -1 : 1;
}
if (parseInt(sb[1], 10) > 0 && parseInt(sa[1], 10) > 0) {
return parseInt(sa[1], 10) - parseInt(sb[1], 10);
}
return a.name < b.name ? -1 : 1;
});
$.each(resp, function (idx, proj) {
gcsTable[proj.name] = proj.proj4;
if (proj.bounds) {
gcsBounds[proj.name] = proj.bounds;
}
var opt = $('<option/>').attr({value: proj.name}).text(
proj.name + ' - ' + proj.desc);
$('#map-gcs').append(opt);
});
var pos = 0;
$.each(gcsList, function (idx, proj) {
var opt = $('#map-gcs option[value="' + proj + '"]');
if (opt.length) {
opt.remove();
$('#map-gcs option').eq(pos).before(opt);
pos += 1;
}
});
$('#map-gcs option').eq(pos).before($('<option/>').attr(
{value: 'EPSG:3857'}).text('--------'));
// select boxes with thousands of options cause performance issues. To
// see all of the projection options, disable the following line:
$('#map-gcs option').slice(pos).remove();
}),

/* Fetch cities */
$.ajax({url: 'capitals.json'}).done(function (resp) {
capitals = resp;
})

).then(function () {
// Parse query parameters into an object for ease of access
var query = document.location.search.replace(/(^\?)/, '').split(
'&').map(function (n) {
n = n.split('=');
if (n[0]) {
this[decodeURIComponent(n[0])] = decodeURIComponent(n[1]);
}
return this;
}.bind({}))[0];

// hide the controls if requested
$('#controls').toggleClass('no-controls', query.controls === 'false');
// populate the controls with the current settings
$.each(query, function (key, value) {
if (key.indexOf('"') < 0) {
var ctl = $('#controls [param-name="' + key + '"]');
if (ctl.is('[type="checkbox"]')) {
ctl.prop('checked', value === 'true');
} else {
ctl.val(value);
}
}
});
$('#controls').on('change', change_controls);

var range = geo.transform.transformCoordinates(
'EPSG:4326', 'EPSG:3857', [{x: -180, y: 0}, {x: 180, y: 0}]);
// Set map defaults to use our named node and have a reasonable center and
// zoom level
var gcs = query.gcs || 'EPSG:3857';
var mapParams = {
node: '#map',
center: {x: 0, y: 0},
zoom: 2,
gcs: gcsTable[gcs],
unitsPerPixel: (range[1].x - range[0].x) / 256,
clampBoundsX: false,
clampBoundsY: false,
clampZoom: false,
discreteZoom: false
};
if (gcsBounds[gcs]) {
mapParams.maxBounds = gcsBounds[gcs];
}
// Set the tile layer defaults to use the specified renderer and opacity
var layerParams = {
renderer: 'vgl',
zIndex: 0,
gcs: 'EPSG:3857',
attribution: $('#url-list [value="' + $('#layer-url').val() + '"]').attr(
'credit'),
minLevel: 4,
keepLower: true,
wrapX: false,
wrapY: false
};
// Allow a custom tile url, including subdomains.
if (query.url) {
layerParams.url = query.url;
} else {
layerParams.baseUrl = 'http://otile1.mqcdn.com/tiles/1.0.0/map/';
}
// Create a map object
var map = geo.map(mapParams);
// Add the tile layer with the specified parameters
var osmLayer = map.createLayer('osm', layerParams);
// create a tool-tip layer
var uiLayer = map.createLayer('ui', {zIndex: 2});
var tooltip = uiLayer.createWidget('dom', {position: {x: 0, y: 0}});
var tooltipElem = $(tooltip.canvas()).attr('id', 'tooltip').addClass(
'hidden');
// Create a layer with cities
var pointLayer = map.createLayer('feature', {renderer: 'vgl', zIndex: 1});
var pointFeature = pointLayer
.createFeature('point', {
selectionAPI: true,
style: {
fillColor: '#8080FF',
fillOpacity: function (d) { return d.opacity ? d.opacity : 0.25; },
strokeColor: 'black',
strokeOpacity: function (d) {
return d.strokeOpacity ? d.strokeOpacity : 0.25;
}
},
visible: query.capitals !== 'false'
})
.data(capitals)
.position(function (d) {
return {x: d.longitude, y: d.latitude};
})
.geoOn(geo.event.feature.mouseclick, function (evt) {
pointLayer.map().center({x: evt.data.longitude, y: evt.data.latitude});
})
.geoOn(geo.event.feature.mouseover, function (evt) {
evt.data.opacity = 0.5;
evt.data.strokeOpacity = 1;
this.modified();
pointLayer.map().draw();
tooltip.position({x: evt.data.longitude, y: evt.data.latitude});
tooltipElem.text(evt.data.city);
tooltipElem.removeClass('hidden');
})
.geoOn(geo.event.feature.mouseout, function (evt) {
evt.data.opacity = undefined;
evt.data.strokeOpacity = undefined;
this.modified();
pointLayer.map().draw();
tooltipElem.addClass('hidden');
});
pointLayer.map().draw();

// Make variables available as a global for easier debug
exampleDebug.map = map;
exampleDebug.mapParams = mapParams;
exampleDebug.layerParams = layerParams;
exampleDebug.osmLayer = osmLayer;
exampleDebug.pointLayer = pointLayer;
exampleDebug.pointFeature = pointFeature;
exampleDebug.uiLayer = uiLayer;
exampleDebug.tooltip = tooltip;
exampleDebug.tooltipElem = tooltipElem;
exampleDebug.gcsTable = gcsTable;
exampleDebug.gcsBounds = gcsBounds;
exampleDebug.gcsName = gcs;

/**
* Handle changes to our controls.
*
* @param evt jquery evt that triggered this call.
*/
function change_controls(evt) {
var ctl = $(evt.target),
param = ctl.attr('param-name'),
value = ctl.val();
if (ctl.is('[type="checkbox"]')) {
value = ctl.is(':checked') ? 'true' : 'false';
}
if (value === '' && ctl.attr('placeholder')) {
value = ctl.attr('placeholder');
}
if (!param || value === query[param]) {
return;
}
var processedValue = (ctl.is('[type="checkbox"]') ?
(value === 'true') : value);
switch (param) {
case 'capitals':
pointFeature.visible(processedValue);
map.draw();
break;
case 'gcs':
mapParams.gcs = gcsTable[processedValue] || 'EPSG:3857';
map.gcs(mapParams.gcs);
exampleDebug.gcsName = processedValue;
osmLayer.clear();
pointFeature.modified();
map.draw();
break;
case 'url':
var url = processedValue;
if (layerParams.baseUrl) {
delete layerParams.baseUrl;
}
layerParams[param] = processedValue;
osmLayer.url(url);
osmLayer.attribution($('#url-list [value="' + value + '"]').attr(
'credit'));
break;
default:
if (ctl.is('.layerparam')) {
layerParams[param] = processedValue;
if (param === 'url' && layerParams.baseUrl) {
delete layerParams.baseUrl;
}
if (osmLayer[param]) {
osmLayer[param](processedValue);
}
} else if (ctl.is('.mapparam')) {
mapParams[param] = processedValue;
if (map[param]) {
map[param](processedValue);
}
} else {
return;
}
break;
}
if (ctl.is('.layerparam') && ctl.attr('reload') === 'true') {
map.deleteLayer(osmLayer);
osmLayer = map.createLayer('osm', layerParams);
exampleDebug.osmLayer = osmLayer;
}
// update the url to reflect the changes
query[param] = value;
if (value === '' || (ctl.attr('placeholder') &&
value === ctl.attr('placeholder'))) {
delete query[param];
}
var newurl = window.location.protocol + '//' + window.location.host +
window.location.pathname + '?' + $.param(query);
window.history.replaceState(query, '', newurl);
}
});
});
1 change: 1 addition & 0 deletions examples/reprojection/proj.json

Large diffs are not rendered by default.

Binary file added examples/reprojection/thumb.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 5 additions & 17 deletions examples/tiles/index.jade
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@ block append mainContent
label(for="layer-url") Tile URL
input#layer-url.layerparam(param-name="url", list="url-list", placeholder="http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png")
datalist#url-list
option(value="http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png") MapQuest
option(value="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png") OSM
option(value="/data/tilefancy.png") Fancy Test Tile
option(value="http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png", credit='Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a>') MapQuest
option(value="http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", credit="© OpenStreetMap contributors") OSM
option(value="/data/tilefancy.png", credit="") Fancy Test Tile
option(value="http://otile1.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png", credit='Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a>') MapQuest Satellite
option(value="http://tile.stamen.com/toner-lite/{z}/{x}/{y}.png", credit='Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.') Toner Lite
.form-group(title="The subdomains used to fetch tiles. This can be a comma-separated list or a string of single-letter subdomains.")
label(for="layer-subdomains") URL Subdomains
input#layer-subdomains.layerparam(param-name="subdomains" placeholder="abc")
Expand All @@ -78,20 +80,6 @@ block append mainContent
label(for="layer-opacity") Opacity
input#layer-opacity.layerparam(param-name="opacity", placeholder="1.0")

//- Although the projection can be changed, this currently has odd
repercussions when using a standard EPSG:3857 tile set. If wrapping is
turned on, an infinite number of tiles may be requested, and, in general,
the number of tiles for a particular zoom level can be significantly
wrong. Using a projection that differs from the tile set could be useful
for localized data, but probably won't work as expected for the entire
world map.
//- form-group(title="Web maps are most often rendered using a Mercator geographic coordinate system, but other projections can be used. Many projections will only work with the VGL renderer.")
label(for="map-gcs") Map GCS
select#map-gcs.mapparam(param-name="gcs", placeholder="EPSG:3857 - Web Mercator", reload="true")
option(value="EPSG:3857") Web Mercator
option(value="SR-ORG:6865") MODIS Sinusoidal
option(value="ESRI:54028") Cassini
.form-group(title="The camera can use a parallel or perspective projection. The difference is subtly unless the data has non-zero z-values.")
label(for="camera-projection") Camera Projection
select#camera-projection.cameraparam(param-name="projection", placeholder="parallel")
Expand Down
Loading

0 comments on commit e3a9180

Please sign in to comment.