UNPKG

@terrestris/ol-util

Version:

A set of helper classes for working with openLayers

155 lines (136 loc) 6.02 kB
import _isArray from 'lodash/isArray'; import _isNil from 'lodash/isNil'; import _isString from 'lodash/isString'; import OlFeature from 'ol/Feature'; import OlGeometry from 'ol/geom/Geometry'; import StringUtil from '@terrestris/base-util/dist/StringUtil/StringUtil'; /** * Helper class for working with OpenLayers features. * * @class FeatureUtil */ class FeatureUtil { /** * Returns the featureType name out of a given feature. It assumes that * the feature has an ID in the following structure FEATURETYPE.FEATUREID. * * @param {import("ol/Feature").default} feature The feature to obtain the featureType * name from. * @return {string|undefined} The (unqualified) name of the featureType or undefined if * the name could not be picked. */ static getFeatureTypeName(feature: OlFeature<OlGeometry>): string | undefined { const featureId = feature.getId(); const featureIdParts = _isString(featureId) ? featureId.split('.') : featureId; return _isArray(featureIdParts) ? featureIdParts[0] : undefined; } /** * Extracts the featureType name from given GetFeatureInfo URL. * This method is mostly useful for raster layers which features could have * no ID set. * * @param {string} url GetFeatureInfo URL possibly containing featureType name. * @param {boolean} qualified Whether the qualified featureType name should be * returned or not. Default is true. * * @return {string|undefined} Obtained featureType name as string. */ static getFeatureTypeNameFromGetFeatureInfoUrl(url: string, qualified: boolean = true): string | undefined { const regex = /query_layers=(.*?)(&|$)/i; const match = url.match(regex); let featureTypeName; if (match && match[1]) { featureTypeName = decodeURIComponent(match[1]); if (!qualified && featureTypeName.indexOf(':') > 0) { featureTypeName = featureTypeName.split(':')[1]; } } return featureTypeName; } /** * Resolves the given template string with the given feature attributes, e.g. * the template "Size of area is {{AREA_SIZE}} km²" would be to resolved * to "Size of area is 1909 km²" (assuming the feature's attribute AREA_SIZE * really exists). * * @param {import("ol/Feature").default} feature The feature to get the attributes from. * @param {string} template The template string to resolve. * @param {string} [noValueFoundText] The text to apply, if the templated value * could not be found, default is to 'n.v.'. * @param {(key: string, val: string) => string} [valueAdjust] A method that will be called with each * key/value match, we'll use what this function returns for the actual * replacement. Optional, defaults to a function which will return the raw * value it received. This can be used for last minute adjustments before * replacing happens, e.g. to filter out falsy values or to do number * formatting and such. * @param {boolean} leaveAsUrl If set to true, template won't be wrapped into * <a>-tag and will be returned as URL. Default is false. * @return {string} The resolved template string. */ static resolveAttributeTemplate( feature: OlFeature<OlGeometry>, template: string, noValueFoundText: string = 'n.v.', valueAdjust = (key: string, val: any) => val, leaveAsUrl = false ) { const attributeTemplatePrefix = '\\{\\{'; const attributeTemplateSuffix = '\\}\\}'; let resolved; // Find any character between two braces (including the braces in the result) const regExp = new RegExp(attributeTemplatePrefix + '(.*?)' + attributeTemplateSuffix, 'g'); const regExpRes = _isString(template) ? template.match(regExp) : null; // If we have a regex result, it means we found a placeholder in the // template and have to replace the placeholder with its appropriate value. if (regExpRes) { // Iterate over all regex match results and find the proper attribute // for the given placeholder, finally set the desired value to the hover. // field text regExpRes.forEach((res) => { // We count every candidate that is not matching. If this count is equal to // the object array length, we assume that there is no match at all and // set the output value to the value of "noValueFoundText". let noMatchCnt = 0; for (const [key, value] of Object.entries(feature.getProperties())) { // Remove the suffixes and find the matching attribute column. const attributeName = res.slice(2, res.length - 2); if (attributeName.toLowerCase() === key.toLowerCase()) { template = template.replace(res, valueAdjust(key, value)); break; } else { noMatchCnt++; } } // No key match found for this feature (e.g. if key not // present or value is null). if (noMatchCnt === Object.keys(feature.getProperties()).length) { template = template.replace(res, noValueFoundText); } }); } resolved = template; // Fallback if no feature attribute is found. if (!resolved) { resolved = `${feature.getId() ?? feature.get('id')}`; } if (!leaveAsUrl) { // Replace any HTTP url with an <a> element. resolved = StringUtil.urlify(resolved); // Replace all newline breaks with a html <br> tag. resolved = resolved.replace(/\n/g, '<br>'); } return resolved; } /** * Maps an array of features to an array of geometries. * * @param {import("ol/Feature").default[]} features * @return {import("ol/Geometry").default[]} The geometries of the features */ static mapFeaturesToGeometries(features: OlFeature<OlGeometry>[]): OlGeometry[] { return features .filter(feature => !_isNil(feature.getGeometry())) .map(f => f.getGeometry() as OlGeometry); } } export default FeatureUtil;