Skip to content

Add support for OSM vector tiles #4518

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

Merged
merged 1 commit into from
Dec 8, 2015
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
3 changes: 3 additions & 0 deletions examples/osm-vector-tiles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.map {
background: #f8f4f0;
}
9 changes: 9 additions & 0 deletions examples/osm-vector-tiles.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
layout: example.html
title: OSM Vector Tiles
shortdesc: Using OpenStreetMap vector tiles.
docs: >
A simple vector tiles map with OpenStreetMap vector tiles. **Note**: The tiles used in this example are not optimized for rendering - they clip tiles exactly at the tile boundary instead of adding a buffer, and use geographic coordinates instead of tile relative pixel coordinates in view projection.
tags: "vector, tiles, osm"
---
<div id="map" class="map"></div>
142 changes: 142 additions & 0 deletions examples/osm-vector-tiles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
goog.require('ol.Map');
goog.require('ol.View');
goog.require('ol.format.TopoJSON');
goog.require('ol.layer.VectorTile');
goog.require('ol.proj');
goog.require('ol.source.VectorTile');
goog.require('ol.style.Fill');
goog.require('ol.style.Stroke');
goog.require('ol.style.Style');


var format = new ol.format.TopoJSON();
var tileGrid = ol.tilegrid.createXYZ({ maxZoom: 19 });
var roadStyleCache = {};
var roadColor = {
'major_road': '#776',
'minor_road': '#ccb',
'highway': '#f39'
};
var landuseStyleCache = {};
var buildingStyle = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For future reference, this could also just be a ol.style.Style instance instead of an array (and you get a very slight performance gain for never using arrays).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, one I forgot to change. Thanks, I created #4535.

new ol.style.Style({
fill: new ol.style.Fill({
color: '#666',
opacity: 0.4
}),
stroke: new ol.style.Stroke({
color: '#444',
width: 1
})
})
];

var map = new ol.Map({
layers: [
new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: format,
tileGrid: tileGrid,
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-water-areas/{z}/{x}/{y}.topojson'
}),
style: new ol.style.Style({
fill: new ol.style.Fill({
color: '#9db9e8'
})
})
}),
new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: format,
tileGrid: tileGrid,
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-highroad/{z}/{x}/{y}.topojson'
}),
style: function(feature, resolution) {
var kind = feature.get('kind');
var railway = feature.get('railway');
var sort_key = feature.get('sort_key');
var styleKey = kind + '/' + railway + '/' + sort_key;
var style = roadStyleCache[styleKey];
if (!style) {
var color, width;
if (railway) {
color = '#7de';
width = 1;
} else {
color = roadColor[kind];
width = kind == 'highway' ? 1.5 : 1;
}
style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: color,
width: width
}),
zIndex: sort_key
});
roadStyleCache[styleKey] = style;
}
return style;
}
}),
new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: format,
tileGrid: tileGrid,
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-buildings/{z}/{x}/{y}.topojson'
}),
style: function(f, resolution) {
return (resolution < 10) ? buildingStyle : null;
}
}),
new ol.layer.VectorTile({
source: new ol.source.VectorTile({
format: format,
tileGrid: tileGrid,
url: 'http://{a-c}.tile.openstreetmap.us/' +
'vectiles-land-usages/{z}/{x}/{y}.topojson'
}),
visible: false,
style: function(feature, resolution) {
var kind = feature.get('kind');
var styleKey = kind;
var style = landuseStyleCache[styleKey];
if (!style) {
var color, width;
color = {
'parking': '#ddd',
'industrial': '#aaa',
'urban area': '#aaa',
'park': '#76C759',
'school': '#DA10E7',
'garden': '#76C759',
'pitch': '#D58F8D',
'scrub': '#3E7D28',
'residential': '#4C9ED9'
}[kind];
width = kind == 'highway' ? 1.5 : 1;
style = new ol.style.Style({
stroke: new ol.style.Stroke({
color: color,
width: width
}),
fill: new ol.style.Fill({
color: color,
opacity: 0.5
})
});
landuseStyleCache[styleKey] = style;
}
return style;
}
})
],
target: 'map',
view: new ol.View({
center: ol.proj.fromLonLat([-74.0064, 40.7142]),
maxZoom: 19,
zoom: 15
})
});
24 changes: 19 additions & 5 deletions src/ol/featureloader.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ goog.require('goog.net.XhrIo');
goog.require('goog.net.XhrIo.ResponseType');
goog.require('ol.TileState');
goog.require('ol.format.FormatType');
goog.require('ol.proj');
goog.require('ol.proj.Projection');
goog.require('ol.proj.Units');
goog.require('ol.xml');


Expand Down Expand Up @@ -100,12 +103,21 @@ ol.featureloader.loadFeaturesXhr = function(url, format, success, failure) {
goog.asserts.fail('unexpected format type');
}
if (source) {
var features = format.readFeatures(source,
{featureProjection: projection});
if (ol.ENABLE_VECTOR_TILE && success.length == 2) {
success.call(this, features, format.readProjection(source));
var dataProjection = format.readProjection(source);
var units = dataProjection.getUnits();
if (units === ol.proj.Units.TILE_PIXELS) {
projection = new ol.proj.Projection({
code: projection.getCode(),
units: units
});
this.setProjection(projection);
}
success.call(this, format.readFeatures(source,
{featureProjection: projection}), dataProjection);
} else {
success.call(this, features);
success.call(this, format.readFeatures(source,
{featureProjection: projection}));
}
} else {
goog.asserts.fail('undefined or null source');
Expand Down Expand Up @@ -142,7 +154,9 @@ ol.featureloader.tile = function(url, format) {
* @this {ol.VectorTile}
*/
function(features, projection) {
this.setProjection(projection);
if (ol.proj.equivalent(projection, this.getProjection())) {
this.setProjection(projection);
}
this.setFeatures(features);
},
/**
Expand Down
8 changes: 5 additions & 3 deletions src/ol/proj/proj.js
Original file line number Diff line number Diff line change
Expand Up @@ -677,12 +677,14 @@ ol.proj.get = function(projectionLike) {
ol.proj.equivalent = function(projection1, projection2) {
if (projection1 === projection2) {
return true;
} else if (projection1.getCode() === projection2.getCode()) {
return projection1.getUnits() === projection2.getUnits();
}
var equalUnits = projection1.getUnits() === projection2.getUnits();
if (projection1.getCode() === projection2.getCode()) {
return equalUnits;
} else {
var transformFn = ol.proj.getTransformFromProjections(
projection1, projection2);
return transformFn === ol.proj.cloneTransform;
return transformFn === ol.proj.cloneTransform && equalUnits;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this line ever return true?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? 1 === 1 && true returns true as well. This code path is covered by the tests BTW.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good then. I was just wondering.

}
};

Expand Down
5 changes: 2 additions & 3 deletions src/ol/source/vectortilesource.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ ol.source.VectorTile = function(options) {
/**
* @protected
* @type {function(new: ol.VectorTile, ol.TileCoord, ol.TileState, string,
* ol.format.Feature, ol.TileLoadFunctionType)}
* ol.format.Feature, ol.TileLoadFunctionType, ol.proj.Projection)}
*/
this.tileClass = options.tileClass ? options.tileClass : ol.VectorTile;

Expand All @@ -83,8 +83,7 @@ ol.source.VectorTile.prototype.getTile =
tileCoord,
tileUrl !== undefined ? ol.TileState.IDLE : ol.TileState.EMPTY,
tileUrl !== undefined ? tileUrl : '',
this.format_,
this.tileLoadFunction);
this.format_, this.tileLoadFunction, projection);
goog.events.listen(tile, goog.events.EventType.CHANGE,
this.handleTileChange, false, this);

Expand Down
13 changes: 8 additions & 5 deletions src/ol/vectortile.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ goog.require('ol.Tile');
goog.require('ol.TileCoord');
goog.require('ol.TileLoadFunctionType');
goog.require('ol.TileState');
goog.require('ol.proj.Projection');


/**
Expand All @@ -25,8 +26,10 @@ ol.TileReplayState;
* @param {string} src Data source url.
* @param {ol.format.Feature} format Feature format.
* @param {ol.TileLoadFunctionType} tileLoadFunction Tile load function.
* @param {ol.proj.Projection} projection Feature projection.
*/
ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) {
ol.VectorTile =
function(tileCoord, state, src, format, tileLoadFunction, projection) {

goog.base(this, tileCoord, state);

Expand All @@ -52,7 +55,7 @@ ol.VectorTile = function(tileCoord, state, src, format, tileLoadFunction) {
* @private
* @type {ol.proj.Projection}
*/
this.projection_ = null;
this.projection_ = projection;

/**
* @private
Expand Down Expand Up @@ -124,7 +127,7 @@ ol.VectorTile.prototype.getKey = function() {


/**
* @return {ol.proj.Projection} Projection.
* @return {ol.proj.Projection} Feature projection.
*/
ol.VectorTile.prototype.getProjection = function() {
return this.projection_;
Expand All @@ -138,7 +141,7 @@ ol.VectorTile.prototype.load = function() {
if (this.state == ol.TileState.IDLE) {
this.setState(ol.TileState.LOADING);
this.tileLoadFunction_(this, this.url_);
this.loader_(null, NaN, null);
this.loader_(null, NaN, this.projection_);
}
};

Expand All @@ -153,7 +156,7 @@ ol.VectorTile.prototype.setFeatures = function(features) {


/**
* @param {ol.proj.Projection} projection Projection.
* @param {ol.proj.Projection} projection Feature projection.
*/
ol.VectorTile.prototype.setProjection = function(projection) {
this.projection_ = projection;
Expand Down
40 changes: 40 additions & 0 deletions test/spec/ol/featureloader.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
goog.provide('ol.test.featureloader');

describe('ol.featureloader', function() {

describe('ol.featureloader.xhr', function() {
var loader;
var source;
Expand Down Expand Up @@ -53,9 +54,48 @@ describe('ol.featureloader', function() {
});

});

describe('ol.featureloader.tile', function() {
var loader;
var tile;

beforeEach(function() {
tile = new ol.VectorTile([0, 0, 0], undefined, undefined, undefined,
undefined, ol.proj.get('EPSG:3857'));
});

it('sets features on the tile', function(done) {
var url = 'spec/ol/data/point.json';
var format = new ol.format.GeoJSON();
loader = ol.featureloader.tile(url, format);
goog.events.listen(tile, 'change', function(e) {
expect(tile.getFeatures().length).to.be.greaterThan(0);
done();
});
loader.call(tile, [], 1, ol.proj.get('EPSG:3857'));
});

it('sets features on the tile and updates proj units', function(done) {
var url = 'spec/ol/data/14-8938-5680.vector.pbf';
var format = new ol.format.MVT();
loader = ol.featureloader.tile(url, format);
goog.events.listen(tile, 'change', function(e) {
expect(tile.getFeatures().length).to.be.greaterThan(0);
expect(tile.getProjection().getUnits()).to.be('tile-pixels');
done();
});
loader.call(tile, [], 1, ol.proj.get('EPSG:3857'));
});

});

});

goog.require('goog.events');
goog.require('ol.VectorTile');
goog.require('ol.featureloader');
goog.require('ol.format.GeoJSON');
goog.require('ol.format.MVT');
goog.require('ol.proj');
goog.require('ol.source.Vector');
goog.require('ol.source.VectorEventType');
1 change: 1 addition & 0 deletions test/spec/ol/proj/proj.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ describe('ol.proj', function() {
});
expect(ol.proj.equivalent(proj1, proj2)).to.not.be.ok();
});

});

describe('identify transform', function() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ describe('ol.renderer.canvas.VectorTileLayer', function() {
ol.VectorTile.apply(this, arguments);
this.setState('loaded');
this.setFeatures([feature1, feature2]);
this.setProjection(ol.proj.get('EPSG:3857'));
};
ol.inherits(TileClass, ol.VectorTile);
var source = new ol.source.VectorTile({
Expand Down