Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add reprojection example #531

Merged
merged 2 commits into from
Feb 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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