Skip to content

Commit

Permalink
Add MapUtils.getResolutionsForScales() for arbitrary projections
Browse files Browse the repository at this point in the history
  • Loading branch information
mwa authored and manisandro committed Dec 7, 2016
1 parent 37d6142 commit 072c32b
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 10 deletions.
45 changes: 35 additions & 10 deletions web/client/utils/MapUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@

const DEFAULT_SCREEN_DPI = 96;

const METERS_PER_UNIT = {
'm': 1,
'degrees': 111194.87428468118,
'ft': 0.3048,
'us-ft': 1200 / 3937
};

const GOOGLE_MERCATOR = {
RADIUS: 6378137,
TILE_WIDTH: 256,
Expand Down Expand Up @@ -51,6 +58,16 @@ function dpi2dpm(dpi) {
return dpi * (100 / 2.54);
}

/**
* @param dpi {number} screen resolution in dots per inch.
* @param projection {string} map projection.
* @return {number} dots per map unit.
*/
function dpi2dpu(dpi, projection) {
const units = CoordinatesUtils.getUnits(projection || "EPSG:3857");
return METERS_PER_UNIT[units] * dpi2dpm(dpi || DEFAULT_SCREEN_DPI);
}

/**
* @param radius {number} Earth's radius of the model in meters.
* @param tileWidth {number} width of the tiles used to draw the map.
Expand Down Expand Up @@ -114,14 +131,22 @@ function getGoogleMercatorScales(minZoom, maxZoom, dpi) {
);
}

function getResolutionsFromScales(scales, dpi) {
const dpm = dpi2dpm((dpi || DEFAULT_SCREEN_DPI));

return scales.map((scale) => scale / dpm);
/**
* @param scales {array} list of scales.
* @param projection {string} map projection.
* @param dpi {number} screen resolution in dots per inch.
* @return {array} a list of resolutions corresponding to the given scales, projection and dpi.
*/
function getResolutionsForScales(scales, projection, dpi) {
const dpu = dpi2dpu(dpi, projection);
const resolutions = scales.map((scale) => {
return scale / dpu;
});
return resolutions;
}

function getGoogleMercatorResolutions(minZoom, maxZoom, dpi) {
return getResolutionsFromScales(getGoogleMercatorScales(minZoom, maxZoom, dpi), dpi);
return getResolutionsForScales(getGoogleMercatorScales(minZoom, maxZoom, dpi), "EPSG:3857", dpi);
}

function getResolutions() {
Expand All @@ -132,9 +157,8 @@ function getResolutions() {
}

function getScales(projection, dpi) {
const units = CoordinatesUtils.getUnits(projection);
const dpm = dpi2dpm((dpi || DEFAULT_SCREEN_DPI));
return getResolutions().map((resolution) => resolution * dpm * (units === 'degrees' ? 111194.87428468118 : 1));
const dpu = dpi2dpu(dpi, projection);
return getResolutions().map((resolution) => resolution * dpu);
}

function defaultGetZoomForExtent(extent, mapSize, minZoom, maxZoom, dpi, mapResolutions) {
Expand All @@ -145,8 +169,8 @@ function defaultGetZoomForExtent(extent, mapSize, minZoom, maxZoom, dpi, mapReso
const yResolution = Math.abs(hExtent / mapSize.height);
const extentResolution = Math.max(xResolution, yResolution);

const resolutions = mapResolutions || getResolutionsFromScales(getGoogleMercatorScales(
minZoom, maxZoom, (dpi || DEFAULT_SCREEN_DPI)));
const resolutions = mapResolutions || getResolutionsForScales(getGoogleMercatorScales(
minZoom, maxZoom, (dpi || DEFAULT_SCREEN_DPI)), "EPSG:3857", dpi);

const {zoom, ...other} = resolutions.reduce((previous, resolution, index) => {
const diff = Math.abs(resolution - extentResolution);
Expand Down Expand Up @@ -258,6 +282,7 @@ module.exports = {
getGoogleMercatorScales,
getGoogleMercatorResolutions,
getGoogleMercatorScale,
getResolutionsForScales,
getZoomForExtent,
defaultGetZoomForExtent,
getCenterForExtent,
Expand Down
36 changes: 36 additions & 0 deletions web/client/utils/__tests__/MapUtils-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ var {
getGoogleMercatorScales,
getGoogleMercatorResolutions,
getGoogleMercatorScale,
getResolutionsForScales,
getZoomForExtent,
getCenterForExtent,
getBbox,
Expand Down Expand Up @@ -46,6 +47,41 @@ describe('Test the MapUtils', () => {
it('getGoogleMercatorScales', () => {
expect(getGoogleMercatorScales(1, 1, 1).length).toBe(1);
});
it('getResolutionsForScales', () => {
// generate test scales for resolutions
function testScales(resolutions, dpu) {
return resolutions.map((res) => {
return res * dpu;
});
}

function dotsPerUnit(dpi, metersPerUnit) {
return metersPerUnit * dpi * 100 / 2.54;
}

function resolutionsEqual(arrayA, arrayB) {
if (arrayA.length === arrayB.length) {
for (let i in arrayA) {
// check if absolute difference is within epsilon
if (Math.abs(arrayA[i] - arrayB[i]) > 1E-6) {
return false;
}
}
return true;
}
return false;
}

const mPerDegree = 111194.87428468118;
let resolutions = [10000, 1000, 100, 10, 1];
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(96, 1)), "EPSG:3857", 96), resolutions)).toBe(true);
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(96, mPerDegree)), "EPSG:4326", 96), resolutions)).toBe(true);
resolutions = [32000, 16000, 8000, 4000, 2000, 1000, 500, 250];
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(96, 1)), "EPSG:3857", 96), resolutions)).toBe(true);
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(96, mPerDegree)), "EPSG:4326", 96), resolutions)).toBe(true);
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(120, 1)), "EPSG:3857", 120), resolutions)).toBe(true);
expect(resolutionsEqual(getResolutionsForScales(testScales(resolutions, dotsPerUnit(120, mPerDegree)), "EPSG:4326", 120), resolutions)).toBe(true);
});
it('getZoomForExtent without hook', () => {
var extent = [1880758.3574092742, 6084533.340409827, 1291887.4915002766, 5606954.787684047];
var mapSize = {height: 781, width: 963};
Expand Down

0 comments on commit 072c32b

Please sign in to comment.