Skip to content

Commit

Permalink
Better CRS improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
m-mohr committed Aug 5, 2020
1 parent c1df0d1 commit 398509a
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 35 deletions.
8 changes: 5 additions & 3 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,8 @@ module.exports = class Config {
epsgCode: {
type: 'integer',
subtype: 'epsg-code', // The formats are not specification compliant, but are allowed to be added.
description: 'EPSG Code to reproject the images to. Defaults to Web Mercator (EPSG Code 3857).',
default: 3857
description: 'EPSG Code to reproject the images to. Defaults to WGS 84 (EPSG Code 4326). This option is ignored for XYZ web services.',
default: 4326
}
};

Expand Down Expand Up @@ -120,7 +120,9 @@ module.exports = class Config {
};

this.services = {
xyz: {}
xyz: {
description: "XYZ tiles for web mapping libraries such as OpenLayers or LeafLet.\n\nAlways rendered in Web Mercator (EPSG code 3857), other reference systems specified are ignored."
}
};

this.otherVersions = [];
Expand Down
2 changes: 1 addition & 1 deletion src/processgraph/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ module.exports = class ProcessingContext {
}
// Handle CRS + bbox settings
if (!parameters.epsgCode && (format === 'jpeg' || format === 'png')) {
dataCube.setCrs(3857);
dataCube.setCrs(4326);
}
else if (parameters.epsgCode > 0) {
dataCube.setCrs(parameters.epsgCode);
Expand Down
43 changes: 31 additions & 12 deletions src/processgraph/datacube.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,29 +228,48 @@ module.exports = class DataCube {
}
}

limitExtentToCrs(extent, newCrs) {
extent.crs = extent.crs > 0 ? extent.crs : 4326;
if (extent.crs === newCrs) {
// CRS doesn't change, skip
return extent;
}
if (extent.crs !== 4326) {
// Project to 4326 so that we can compare it to the CRS bbox
extent = Utils.projExtent(extent, 4326);
}
// Use only the overlapping part of the bbox specified by the data and the bbox specified by the CRS
let crsBbox = Utils.getCrsBBox(newCrs);
if (Array.isArray(crsBbox)) {
if(this.logger && (crsBbox[1] > extent.west || crsBbox[2] > extent.south || crsBbox[3] < extent.east || crsBbox[0] < extent.north)) {
this.logger.warn("Bounding Box has been reduced to the maximum bounding box supported by the target CRS.");
}
extent.west = Math.max(extent.west, crsBbox[1]);
extent.south = Math.max(extent.south, crsBbox[2]);
extent.east = Math.min(extent.east, crsBbox[3]);
extent.north = Math.min(extent.north, crsBbox[0]);
}
if (newCrs !== 4326) {
// Project to the new CRS
extent = Utils.projExtent(extent, newCrs);
}
return extent;
}

setSpatialExtent(extent) {
extent.crs = extent.crs > 0 ? extent.crs : 4326;
var toCrs = this.getCrs();
var p1 = Utils.proj(extent.crs, toCrs, [extent.west, extent.south]);
var p2 = Utils.proj(extent.crs, toCrs, [extent.east, extent.north]);
this.dimX().setExtent(p1[0], p2[0]);
this.dimY().setExtent(p1[1], p2[1]);
if (extent.base && extent.height) {
if (Utils.isNumeric(extent.base) && Utils.isNumeric(extent.height)) {
this.dimZ().setExtent(extent.base, extent.height);
}
}

setSpatialExtentFromGeometry(geometry) { // GeoJSON geometry
var bbox = Utils.geoJsonBbox(geometry);
var hasZ = bbox.length > 4;
var toCrs = this.getCrs();
var p1 = Utils.proj(4326, toCrs, [bbox[0], bbox[1]]);
var p2 = Utils.proj(4326, toCrs, [bbox[hasZ ? 3 : 2], bbox[hasZ ? 4 : 3]]);
this.dimX().setExtent(p1[0], p2[0]);
this.dimY().setExtent(p1[1], p2[1]);
if (hasZ) {
this.dimZ().setExtent(bbox[2], bbox[5]);
}
this.setSpatialExtent(Utils.geoJsonBbox(geometry));
}

getSpatialExtent() {
Expand Down Expand Up @@ -314,7 +333,7 @@ module.exports = class DataCube {
var extent = this.getSpatialExtent();
this.dimX().setReferenceSystem(refSys);
this.dimY().setReferenceSystem(refSys);
this.setSpatialExtent(extent); // Update the extent based on the new CRS
this.setSpatialExtent(this.limitExtentToCrs(extent, refSys)); // Update the extent based on the new CRS
}

setReferenceSystem(dimName, refSys) {
Expand Down
83 changes: 64 additions & 19 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const proj4 = require('proj4');

var Utils = {

crsBboxes: {},
serverUrl: null,
apiPath: null,

Expand Down Expand Up @@ -61,6 +62,10 @@ var Utils = {
return ret.join('&');
},

isNumeric(num) {
return CommonUtils.isNumeric(num);
},

isObject(obj) {
return CommonUtils.isObject(obj);
},
Expand Down Expand Up @@ -110,6 +115,16 @@ var Utils = {
return crs;
},

crsToNumber(crs) {
if (typeof crs === 'number') {
return crs;
}
if (typeof crs === 'string' && crs.startsWith('EPSG:')) {
return parseInt(crs.substring(5), 10);
}
return null;
},

bboxToGeoJson(bbox) {
var geom = {
geodesic: false,
Expand All @@ -125,7 +140,7 @@ var Utils = {
geom.crs = {
type: "name",
properties: {
name: Utils.crsToString(bbox.crs)
name: this.crsToString(bbox.crs)
}
};
}
Expand Down Expand Up @@ -166,15 +181,22 @@ var Utils = {
}
};
var coords = getCoordinatesDump(geojson);
var bbox = [Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY];
return coords.reduce(function(prev,coord) {
return [
Math.min(coord[0], prev[0]),
Math.min(coord[1], prev[1]),
Math.max(coord[0], prev[2]),
Math.max(coord[1], prev[3])
];
}, bbox);
var bbox = coords.reduce(function(prev,coord) {
return {
west: Math.min(coord[0], prev[0]),
south: Math.min(coord[1], prev[1]),
east: Math.max(coord[0], prev[2]),
north: Math.max(coord[1], prev[3]),
crs: 4326
};
}, [Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY,Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY]);
return {
west: bbox[0],
south: bbox[1],
east: bbox[2],
north: bbox[3],
crs: 4326
}
},

geoJsonToGeometry(geojson) {
Expand Down Expand Up @@ -273,34 +295,57 @@ var Utils = {
},

proj(from, to, coords) {
var fromCrs = Utils.crsToString(from);
var toCrs = Utils.crsToString(to);
var fromCrs = this.crsToString(from);
var toCrs = this.crsToString(to);
if (fromCrs === toCrs) {
return coords;
}

Utils.loadCrsDef(fromCrs);
Utils.loadCrsDef(toCrs);
this.loadCrsDef(fromCrs);
this.loadCrsDef(toCrs);

let newCoords = proj4(fromCrs, toCrs, coords);
if (newCoords.filter(c => Number.isNaN(c) || !Number.isFinite(c)).length > 0) {
if (newCoords.filter(n => !this.isNumeric(n)).length > 0) {
throw new Error("CRS conversion from " + fromCrs + " to " + toCrs + " failed.");
}
return newCoords;
},

projExtent(extent, targetCrs) {
extent.crs = extent.crs > 0 ? extent.crs : 4326;
var p1 = this.proj(extent.crs, targetCrs, [extent.west, extent.south]);
var p2 = this.proj(extent.crs, targetCrs, [extent.east, extent.north]);
return {
west: p1[0],
south: p1[1],
east: p2[0],
north: p2[1],
crs: this.crsToNumber(targetCrs)
};
},

getCrsBBox(crs) {
crs = this.crsToString(crs);
if (!this.crsBboxes[crs]) {
this.loadCrsDef(crs);
}
return this.crsBboxes[crs];
},

loadCrsDef(crs) {
if (proj4.defs(crs)) {
crs = this.crsToString(crs);
if (proj4.defs(crs) && this.crsBboxes[crs]) {
return; // CRS already available
}
if (!crs.startsWith('EPSG:')) {
if (typeof crs !== 'string' || !crs.startsWith('EPSG:')) {
throw new Error("CRS " + crs + " not supported");
}

try {
let code = crs.substring(5);
const def = require('epsg-index/s/' + code + '.json');
let epsgCode = this.crsToNumber(crs);
const def = require('epsg-index/s/' + epsgCode + '.json');
proj4.defs(crs, def.proj4);
this.crsBboxes[crs] = def.bbox;
} catch (error) {
throw new Error("CRS " + crs + " not available for reprojection");
}
Expand Down

0 comments on commit 398509a

Please sign in to comment.