import * as Constants from '../constants.js'; const { LAT_MIN, LAT_MAX, LAT_RENDERED_MIN, LAT_RENDERED_MAX, LNG_MIN, LNG_MAX, } = Constants; function extent(feature) { const depth = { Point: 0, LineString: 1, Polygon: 2, MultiPoint: 1, MultiLineString: 2, MultiPolygon: 3, }[feature.geometry.type]; const coords = [feature.geometry.coordinates].flat(depth); const lngs = coords.map(coord => coord[0]); const lats = coords.map(coord => coord[1]); const min = vals => Math.min.apply(null, vals); const max = vals => Math.max.apply(null, vals); return [min(lngs), min(lats), max(lngs), max(lats)]; } // Ensure that we do not drag north-south far enough for // - any part of any feature to exceed the poles // - any feature to be completely lost in the space between the projection's // edge and the poles, such that it couldn't be re-selected and moved back export default function(geojsonFeatures, delta) { // "inner edge" = a feature's latitude closest to the equator let northInnerEdge = LAT_MIN; let southInnerEdge = LAT_MAX; // "outer edge" = a feature's latitude furthest from the equator let northOuterEdge = LAT_MIN; let southOuterEdge = LAT_MAX; let westEdge = LNG_MAX; let eastEdge = LNG_MIN; geojsonFeatures.forEach((feature) => { const bounds = extent(feature); const featureSouthEdge = bounds[1]; const featureNorthEdge = bounds[3]; const featureWestEdge = bounds[0]; const featureEastEdge = bounds[2]; if (featureSouthEdge > northInnerEdge) northInnerEdge = featureSouthEdge; if (featureNorthEdge < southInnerEdge) southInnerEdge = featureNorthEdge; if (featureNorthEdge > northOuterEdge) northOuterEdge = featureNorthEdge; if (featureSouthEdge < southOuterEdge) southOuterEdge = featureSouthEdge; if (featureWestEdge < westEdge) westEdge = featureWestEdge; if (featureEastEdge > eastEdge) eastEdge = featureEastEdge; }); // These changes are not mutually exclusive: we might hit the inner // edge but also have hit the outer edge and therefore need // another readjustment const constrainedDelta = delta; if (northInnerEdge + constrainedDelta.lat > LAT_RENDERED_MAX) { constrainedDelta.lat = LAT_RENDERED_MAX - northInnerEdge; } if (northOuterEdge + constrainedDelta.lat > LAT_MAX) { constrainedDelta.lat = LAT_MAX - northOuterEdge; } if (southInnerEdge + constrainedDelta.lat < LAT_RENDERED_MIN) { constrainedDelta.lat = LAT_RENDERED_MIN - southInnerEdge; } if (southOuterEdge + constrainedDelta.lat < LAT_MIN) { constrainedDelta.lat = LAT_MIN - southOuterEdge; } if (westEdge + constrainedDelta.lng <= LNG_MIN) { constrainedDelta.lng += Math.ceil(Math.abs(constrainedDelta.lng) / 360) * 360; } if (eastEdge + constrainedDelta.lng >= LNG_MAX) { constrainedDelta.lng -= Math.ceil(Math.abs(constrainedDelta.lng) / 360) * 360; } return constrainedDelta; }