@geogirafe/lib-geoportal
Version:
GeoGirafe is a flexible application to build online geoportals.
193 lines (192 loc) • 7.09 kB
JavaScript
import { fromCircle } from 'ol/geom/Polygon.js';
import { get as getProjection } from 'ol/proj.js';
import { getArea as getSphericalArea, getDistance as getSphericalDistance } from 'ol/sphere.js';
import { unByKey } from 'ol/Observable.js';
import { LineString, Point } from 'ol/geom.js';
import GeoConsts from '../geoconsts.js';
import { buffer } from 'ol/extent.js';
/**
* Unsubscribe to all OpenLayer listeners.
*/
export const unByKeyAll = (eventKeys) => {
eventKeys.forEach((eventKey) => unByKey(eventKey));
};
/**
* @returns a layer in the map that match the given name (property name).
*/
export const getOlayerByName = (map, layerName) => {
return map.getAllLayers().find((layer) => layer.get('name') === layerName);
};
/**
* Clone the properties of the given feature and delete ol properties to keep only the feature "app" properties.
* Handle map server values served as object and serve them as "simple" values.
*/
export const removeUnwantedOlParams = (feature, keepGeom = false) => {
const properties = { ...feature.getProperties() };
delete properties.boundedBy;
if (!keepGeom) {
delete properties[feature.getGeometryName()];
}
// Handle map server values served as object.
Object.keys(properties).forEach((key) => {
const value = properties[key];
if (typeof value === 'object') {
if (value['xsi:nil'] === 'true') {
properties[key] = undefined;
}
else if (value['_content_']) {
properties[key] = value['_content_'];
}
}
});
return properties;
};
/**
* @returns A polygon generated from the circle.
*/
export const polygonFromCircle = (geometry) => {
return fromCircle(geometry, GeoConsts.CIRCLE_TO_POLYGON_SIDES);
};
/**
* @param coordinates ol Coordinate list
* @param projection Projection identifier
* @returns the length between coordinates, considering the current map projection (projected or geographic)
*/
export const getDistance = (coordinates, projection) => {
if (isProjectionInDegrees(projection)) {
let totalLength = 0;
coordinates.forEach((coordinate, idx) => {
if (coordinates[idx + 1]) {
totalLength += getSphericalDistance(coordinate, coordinates[idx + 1]);
}
});
return totalLength;
}
return new LineString(coordinates).getLength();
};
/**
* @param polygon ol Polygon
* @param projection Projection identifier
* @returns the area of a polygon, considering the current map projection (projected or geographic)
*/
export const getAreaOfPolygon = (polygon, projection) => {
if (isProjectionInDegrees(projection)) {
return getSphericalArea(polygon, {
projection: getProjection(projection)
});
}
return polygon.getArea();
};
export const getAreaOfCircle = (circle, projection) => {
if (isProjectionInDegrees(projection)) {
return getSphericalArea(circle, {
projection: getProjection(projection)
});
}
return Math.PI * Math.pow(circle.getRadius(), 2);
};
const isProjectionInDegrees = (proj) => {
const projection = getProjection(proj);
return projection?.getUnits() === 'degrees';
};
export const isCoordinateInDegrees = (coordinate) => {
return coordinate[0] > -90 && coordinate[0] < 90 && coordinate[1] > -180 && coordinate[1] < 180;
};
export const getSelectionBoxFromMapClick = (eventCoordinate, olMap, pixelTolerance) => {
const pointExtent = [...eventCoordinate, ...eventCoordinate];
const pixelExtent = buffer(pointExtent, pixelTolerance);
const mapExtent = [
...olMap.getCoordinateFromPixel([pixelExtent[0], pixelExtent[1]]),
...olMap.getCoordinateFromPixel([pixelExtent[2], pixelExtent[3]])
];
// Because the pixel origin is in the top left corner and the coordinate origin (in projected crs) is in the lower
// left corner, the extent is switched to [xmin, ymin, xmax, ymax]
return [
Math.min(mapExtent[0], mapExtent[2]),
Math.min(mapExtent[1], mapExtent[3]),
Math.max(mapExtent[0], mapExtent[2]),
Math.max(mapExtent[1], mapExtent[3])
];
};
export const reprojectGeometry = (geometry, sourceProjection, destinationProjection) => {
try {
if (typeof sourceProjection === 'string') {
sourceProjection = getProjection(sourceProjection) ?? undefined;
}
if (typeof destinationProjection === 'string') {
destinationProjection = getProjection(destinationProjection) ?? undefined;
}
if (!sourceProjection || !destinationProjection) {
throw new Error(`Not able to reproject geometry, invalid or unknown projection used.`);
}
const reprojectedGeometry = geometry.clone();
return reprojectedGeometry.transform(sourceProjection, destinationProjection);
}
catch (e) {
throw new Error(`Not able to reproject geometry: ${e}`, { cause: e });
}
};
export const ensurePolygonIsProperlyClosed = (polygon) => {
const coordinates = polygon.getCoordinates()[0];
let segments = [...coordinates];
if (coordinates.length > 2 && coordinates[0][0] != coordinates[coordinates.length - 1][0]) {
segments = [...coordinates, coordinates[0]];
polygon.setCoordinates([segments]);
}
return segments;
};
export const getHalfPoint = (coordinates) => {
return new Point(new LineString(coordinates).getCoordinateAt(0.5));
};
export const getLabelStyle = (position, text, labelStyle) => {
if (text.trim() != '') {
const style = labelStyle.clone();
style.setGeometry(position);
style.getText().setText(text);
return style;
}
return undefined;
};
export const getRadiusDataForCircle = (circle, defaultStyle, stroke) => {
const radius = circle.getRadius();
const center = circle.getCenter();
const pointer = circle.getProperties()['pointer'] ?? [center[0] + radius, center[1]];
const radiusLine = [center, pointer];
const radiusLineStyle = defaultStyle.clone();
radiusLineStyle.setStroke(stroke);
radiusLineStyle.getText().setText('');
radiusLineStyle.setGeometry(new LineString(radiusLine));
return {
style: radiusLineStyle,
radius: radius,
radiusLine: radiusLine
};
};
export const getLengthAsMetricText = (length) => {
if (length === undefined) {
return '';
}
if (length <= 500) {
return `${length.toFixed(2)} m`;
}
const lengthInKilometers = length / 1000;
if (length <= 2000) {
return `${lengthInKilometers.toFixed(3)} km`;
}
return `${lengthInKilometers.toFixed(2)} km`;
};
export const getAreaAsMetricText = (area) => {
if (area === undefined) {
return '';
}
if (area <= 10000) {
return `${area.toFixed(2)} m²`;
}
return `${(area / 1000000).toFixed(2)} km²`;
};
export const getAzimuthAsText = (azimuth) => {
if (azimuth) {
return `${azimuth.toFixed(0)}°`;
}
return '';
};