Skip to content

Commit

Permalink
Feature-specific zoom calculations (#163)
Browse files Browse the repository at this point in the history
* start calculating latitude and longitude deltas between source and vector tile

* start calculating more data-specific zoom levels

* set smallest max zoom for point files

* constants

* stick with z10 for now

* update expected zooms

* update sm version

* update appveyor node versions

* pin node versions
  • Loading branch information
mapsam authored Mar 13, 2017
1 parent a434068 commit 3b8e39a
Show file tree
Hide file tree
Showing 19 changed files with 1,842 additions and 11 deletions.
3 changes: 2 additions & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ os: Visual Studio 2015

environment:
matrix:
- nodejs_version: 0.10.40
- nodejs_version: 4.4.1
- nodejs_version: 6.5.0

platform:
- x64
Expand Down
2 changes: 1 addition & 1 deletion lib/csv.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,5 @@ Csv.prototype.getLayers = function(callback) {
};

Csv.prototype.getZooms = function(callback) {
utils.zoomsBySize(this.filepath, this.extent, callback);
utils.zoomsBySize(this.filepath, this.extent, this.datasource, callback);
};
2 changes: 1 addition & 1 deletion lib/geojson.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ GeoJSON.prototype.getLayers = function(callback) {
};

GeoJSON.prototype.getZooms = function(callback) {
utils.zoomsBySize(this.filepath, this.extent, callback);
utils.zoomsBySize(this.filepath, this.extent, this.datasource, callback);
};
2 changes: 2 additions & 0 deletions lib/ogr.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ Ogr.prototype.getZooms = function(callback) {

this.getExtent(function(err, extent) {
if (err) return callback(err);

// no datasource object like mapnik, so don't pass it in
utils.zoomsBySize(filepath, extent, callback);
});
};
Expand Down
3 changes: 2 additions & 1 deletion lib/shape.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,10 @@ Shape.prototype.getLayers = function(callback) {

Shape.prototype.getZooms = function(callback) {
var filepath = this.filepath;
var ds = this.datasource;

this.getExtent(function(err, extent) {
if (err) return callback(err);
utils.zoomsBySize(filepath, extent, callback);
utils.zoomsBySize(filepath, extent, ds, callback);
});
};
2 changes: 1 addition & 1 deletion lib/topojson.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,5 @@ TopoJSON.prototype.getLayers = function(callback) {
};

TopoJSON.prototype.getZooms = function(callback) {
utils.zoomsBySize(this.filepath, this.extent, callback);
utils.zoomsBySize(this.filepath, this.extent, this.datasource, callback);
};
46 changes: 42 additions & 4 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ var invalid = require('./invalid');
var SphericalMercator = require('@mapbox/sphericalmercator');
var sm = new SphericalMercator();

module.exports.zoomsBySize = function(filepath, extent, callback) {
var SMALLEST_MAX_ZOOM_FILE_SIZE = 1000000; // 1mb
var DEFAULT_SMALLEST_MAX_ZOOM = 6;

module.exports.zoomsBySize = function(filepath, extent, datasource, callback) {

// currently the ogr plugin doesn't pass in a datasource,
// so we need to redefine the callback
if (typeof datasource === 'function') callback = datasource;

var maxSize = 500 * 1024;
var max = 22;
var min;
Expand All @@ -18,6 +26,9 @@ module.exports.zoomsBySize = function(filepath, extent, callback) {
var tiles;
var avg;

// set a "smallest max zoom" for different data types
var smallestMaxZoom = stats.size < SMALLEST_MAX_ZOOM_FILE_SIZE ? dataTypeMaxZoom(datasource) : DEFAULT_SMALLEST_MAX_ZOOM;

for (z = max; z >= 0; z--) {
bounds = sm.xyz(extent, z, false, 4326);
x = (bounds.maxX - bounds.minX) + 1;
Expand All @@ -35,18 +46,45 @@ module.exports.zoomsBySize = function(filepath, extent, callback) {
avg = stats.size / tiles;

if (avg < 1000) max = z;

if (avg > maxSize) {
min = z;
return callback(null, min, Math.max(max, 6));
return callback(null, min, Math.max(max, smallestMaxZoom));
} else if (tiles === 1 || z === 0) {
min = 0;
return callback(null, min, Math.max(max, 6));
return callback(null, min, Math.max(max, smallestMaxZoom));
}
}
});
};

/**
* Calculates the smallest max zoom a datasource should be tiled to. This is used for small datasources
* where precision can be lost due to large tile extents. https://github.com/mapbox/mapnik-omnivore/issues/151
*
* @param {object} datasource A Node Mapnik datasource object
* @returns {number} zoom The smallest max zoom to use for the datasource
*/
module.exports.dataTypeMaxZoom = dataTypeMaxZoom;
var dataTypeMaxZoom = function(ds) {

// try describing the datasource (this catches the ogr hole)
var info;
try {
info = ds.describe();
} catch (err) {
return DEFAULT_SMALLEST_MAX_ZOOM;
}

// for point features, we are able to keep within ~50cm (1.5ft) precision at z11
// https://github.com/mapbox/unpacker/issues/1248#issuecomment-256963081
if (info.geometry_type === 'point') {
return 10;
}

// return 6 for all other data types for now
return DEFAULT_SMALLEST_MAX_ZOOM;
};

module.exports.convertToMeters = function(pixelSize, unit) {
var circumference = 40075000;
var conversions = {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"geodata"
],
"dependencies": {
"@mapbox/sphericalmercator": "~1.0.2",
"@mapbox/sphericalmercator": "~1.0.5",
"@mapbox/mapbox-file-sniff": "~1.0.0",
"srs": "~1.2.0",
"mapnik": "~3.5.14",
Expand Down
Loading

0 comments on commit 3b8e39a

Please sign in to comment.