import { CSISHelpers, CSISRemoteHelpers } from 'csis-helpers-js'; import log from 'loglevel'; import PropTypes from 'prop-types'; import queryString from 'query-string'; import React from 'react'; import turf from 'turf'; import Wkt from 'wicket'; import './../../App.css'; import logo from './../../logo.svg'; log.enableAll(); /** * This is the basic class of all map classes. * It implements the common way to extract the overlay layers from the study. * Why is it called map? I fact it's a wrapper component for a wrapper component that wraps a leaflet map. :o * * * TODO: transform to functional component. See https://github.com/clarity-h2020/simple-table-component * Complete redesign is necessary. */ export default class BasicMap extends React.Component { constructor(props) { super(props); this.initialBounds = this.props.initialBounds ? this.props.initialBounds : [[72, 55], [30, -30]]; // We still use 'class' properties instead of *immutable* React 'props' -> pure functional component this.backgroundLayersTagType = 'taxonomy_term--dp_resourcetype'; this.backgroundLayersTagName = 'background-layer'; this.backgroundLayersGroupName = 'Backgrounds'; this.clarityBackgroundLayersTagType = 'taxonomy_term--dp_resourcetype'; this.clarityBackgroundLayersTagName = 'clarity-background-layer'; this.clarityBackgroundLayersGroupName = 'CLARITY Backgrounds'; // default **filter** criteria for overlay layers this.overlayLayersTagType = 'taxonomy_term--eu_gl'; /** * This correponds to props.mapSelectionId * Example: 'eu-gl:hazard-characterization' */ this.overlayLayersTagName = undefined; // this means all resources // default **grouping** criteria for overlay layers /** * this corresponds to queryParams.grouping_tag and props.groupingCriteria * e.g. 'taxonomy_term--hazards' */ this.overlayLayersGroupingTagType = undefined; this.overlayLayersGroupName = 'Default'; //default group name this.hasAdaptedScenario = false; /** * Base Layers * * FIXME: Attribution not shown! * * @type {Object[]} */ this.baseLayers = this.props.baseLayers ? this.props.baseLayers : [ { name: 'WorldTopoMap', title: 'World Topo Map', url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', attribution: 'Tiles © <a href="https://services.arcgisonline.com/ArcGIS/' + 'rest/services/World_Topo_Map/MapServer">ArcGIS</a>' }, { name: 'OpenStreetMap', title: 'OpenStreetMap', url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: 'Tiles © <a href="https://services.arcgisonline.com/ArcGIS/' + 'rest/services/World_Topo_Map/MapServer">ArcGIS</a>' }, { name: 'OpenTopoMap', title: 'Open Topo Map', url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png' } ]; // FIXME: write_permissions as query param?! OMFG! /** * Query params extracted from CSIS Helpers. See /examples and /fixtures/csisHelpers.json */ this.queryParams = { ...CSISHelpers.defaultQueryParams }; // we need also the setters and getters introduced for backwards capability. // this.queryParams = Object.create(Object.getPrototypeOf(CSISHelpers.defaultQueryParams), Object.getOwnPropertyDescriptors(CSISHelpers.defaultQueryParams)) ; <- does not work if (this.props.location && this.props.location.search) { // copy and extend queryParams using spread operator :o this.queryParams = { ...this.queryParams, ...queryString.parse(this.props.location.search) }; } else { log.warn('no query parameters found, showing empty map!'); } /** * The protocol that is used by the server. The protocol of the server is https://, but for local testing it can be changed to http:// * @deprecated */ this.protocol = 'https://'; // TODO: Support for different reference types?! this.referenceType = '@mapview:ogc:wms'; this.clarityBackgroundLayersTagType = this.queryParams.clarityBackgroundLayersTagType ? this.queryParams.clarityBackgroundLayersTagType : this.clarityBackgroundLayersTagType; this.clarityBackgroundLayersTagName = this.queryParams.clarityBackgroundLayersTagName ? this.queryParams.clarityBackgroundLayersTagName : this.clarityBackgroundLayersTagName; this.clarityBackgroundLayersGroupName = this.queryParams.clarityBackgroundLayersGroupName ? this.queryParams.clarityBackgroundLayersGroupName : this.clarityBackgroundLayersGroupName; this.overlayLayersGroupName = this.queryParams.overlayLayersGroupName ? this.queryParams.overlayLayersGroupName : this.overlayLayersGroupName; this.overlayLayersTagType = this.queryParams.overlayLayersTagType ? this.queryParams.overlayLayersTagType : this.overlayLayersTagType; // its defined by the sub-type, child classes override mapSelectionId props, e.g. 'eu-gl:hazard-characterization' for HC Map this.overlayLayersTagName = this.queryParams.overlayLayersTagName ? this.queryParams.overlayLayersTagName : props.mapSelectionId; // grouping_tag query params overwrites the grouping criteria set by the child class in this.props! // if we use child.groupingCriteria = x it will override queryParams, therefore we put them in class variable this.overlayLayersGroupingTagType this.overlayLayersGroupingTagType = this.queryParams.overlayLayersGroupingTagType ? this.queryParams.overlayLayersGroupingTagType : (this.queryParams.grouping_tag ? this.queryParams.grouping_tag : props.groupingCriteria); //e.g. taxonomy_term--eu_gl // Actually, this parameters are not used anymore! For data package and resource we use study_area as initial bbox // Yeah, that's inconsistent and not correct, but we reuse this query param since we don't want to re-implement // handling of initial bbox just because the data model contains rubbish. :-/ // See https://github.com/clarity-h2020/map-component/issues/53 this.initialBounds[0][0] = this.queryParams.minx; this.initialBounds[0][1] = this.queryParams.miny; this.initialBounds[1][0] = this.queryParams.maxx; this.initialBounds[1][1] = this.queryParams.maxy; this.hasAdaptedScenario = this.queryParams.has_adapted_scenario ? true : false; log.info( `creating new ${props.mapSelectionId} map with layer group from ${this .overlayLayersGroupingTagType} and initial bbox ${this.queryParams.study_area}` ); } /** * Main work is done here. * * For standalone use, e.g. * http://localhost:3000//?url=https://csis.myclimateservice.eu&study_id=c3609e3e-f80f-482b-9e9f-3a26226a6859 * */ async componentDidMount() { let studyApiResponse = undefined; let resourcesApiResponse = undefined; // apply the study area if (this.queryParams.study_area) { try { const studyArea = new Wkt.Wkt(); studyArea.read(this.queryParams.study_area); const studyAreaJson = studyArea.toJson(); this.applyStudyAreaGeometry(studyAreaJson); } catch (error) { log.error(error); // if faulty coordinates submitted, try to lod them from study area this.queryParams.study_area = undefined; } } else if (!this.queryParams.study_area && this.queryParams.study_uuid) { log.warn( `no study area submitted via query params, trying to load it from API for study $this.queryParams.study_uuid` ); studyApiResponse = await CSISRemoteHelpers.getStudyGroupNodeFromCsis( this.queryParams.host, this.queryParams.study_uuid ); const studyArea = CSISHelpers.extractStudyAreaFromStudyGroupNode(studyApiResponse.data); this.applyStudyAreaGeometry(studyArea); } else { log.warn(`no study_area nor study_uuid submitted via query params, cannot set study area bbox`); } // load and process the resources to generate the overlay layers for the leaflet map if (this.queryParams.resource_uuid) { log.debug(`loading resource ${this.queryParams.resource_uuid}`); resourcesApiResponse = await CSISRemoteHelpers.getDatapackageResourceFromCsis( this.queryParams.host, this.queryParams.resource_uuid ); } else if (this.queryParams.datapackage_uuid) { log.debug(`loading data package ${this.queryParams.datapackage_uuid}`); resourcesApiResponse = await CSISRemoteHelpers.getDatapackageResourcesFromCsis( this.queryParams.host, this.queryParams.datapackage_uuid ); } else if (this.queryParams.study_uuid) { log.warn( `no datapackage_uuid or resource_uuid submitted via query params, trying to load it from API for study ${this .queryParams.study_uuid}` ); if (!studyApiResponse) { studyApiResponse = await CSISRemoteHelpers.getStudyGroupNodeFromCsis( this.queryParams.host, this.queryParams.study_uuid ); } if ( studyApiResponse && studyApiResponse.data && studyApiResponse.data.relationships && studyApiResponse.data.relationships.field_data_package && studyApiResponse.data.relationships.field_data_package.data ) { this.queryParams.datapackage_uuid = studyApiResponse.data.relationships.field_data_package.data.id; resourcesApiResponse = await CSISRemoteHelpers.getDatapackageResourcesFromCsis( this.queryParams.host, this.queryParams.datapackage_uuid ); } else { log.error( `no data package associated with study ${this.queryParams.study_uuid}, cannot load resources!` ); } } else { log.error( `no study_uuid nor datapackage_uuid nor resource_uuid submitted via query params, cannot load addtional resource layers` ); } if (resourcesApiResponse && resourcesApiResponse.data && resourcesApiResponse.included) { const leafletMapModel = this.processResources( resourcesApiResponse, this.overlayLayersTagName, this.overlayLayersGroupingTagType, this.referenceType ); if (leafletMapModel.length > 0) { this.applyLeafletMapModel(leafletMapModel); } } else { log.warn(`cannot load additional resource layers`); } if(this.hasAdaptedScenario === true && this.props.showAdaptationScenario === true) { log.warn(`want to show AdaptedScenario but no AdaptedScenarios are available! EMIKAT Map Layers will be empty.`); } } /** * Add the given study area to the map * * @param {Object} studyAreaGeometry the geometry of the study area */ applyStudyAreaGeometry(studyAreaGeometry) { if (studyAreaGeometry != null) { var studyArea = { type: 'Feature', properties: { popupContent: 'study', style: { weight: 2, color: 'black', opacity: 0.3, fillColor: '#ff0000', fillOpacity: 0.1 } }, geometry: studyAreaGeometry }; this.setState({ studyAreaPolygon: null }); this.setState({ studyAreaPolygon: studyArea, bounds: this.getBoundsFromArea(studyAreaGeometry) }); } } /** * Set 'exclusive' groups and add the given overlay layers to the state * * FIXME: split method, externalize layer generation part * * @param {Array} mapData the overlay layers * @param {Number} resourceLength the resource count of the current study * @deprecated */ applyLeafletMapModel(leafletMapModel) { if (leafletMapModel && leafletMapModel.length > 0) { this.setState({ overlays: leafletMapModel, loading: false, exclusiveGroups: this.extractExclusiveGroups(leafletMapModel) }); } else { log.error('no leafletMapModel set, cannot show addtional layers'); } } /** * Creates an array of Leaflet Layer definitions from one CSIS Resource Meta Data item. * * **ACCIDENTAL-COMPLEXITY-ALERT:** There are now **three** ways for generating multiple layer from one (atomic!) resource. * * @param {Object} resource * @param {Object[]} includedArray * @param {String} referenceType * @param {String} defaultGroupName * @param {String} groupingTagType * @param {Boolean} expandTemplateResources * @return {Object[]} */ createLeafletLayers( resource, includedArray, referenceType, defaultGroupName, groupingTagType, expandTemplateResources = false ) { const resourceReferences = CSISHelpers.extractReferencesFromResource(resource, includedArray, referenceType); const leafletLayers = []; const prepareLayer = function (url, title) { // process URL will add the query parameters for Variables. // Unfortunately, ATM it uses this.queryParams. So resource expansion (e.g. submitting **two** time_periods via query params) is not easily possible). // See also https://github.com/clarity-h2020/map-component/issues/74 const layerUrl = this.processUrl(resource, includedArray, url); const groupTitle = this.extractGroupName(groupingTagType, defaultGroupName, resource, includedArray); const leafletLayer = this.createLeafletLayer(groupTitle, title, layerUrl); return leafletLayer; }.bind(this); // yes, need to bind to this. // Create separate Layers for each Reference? if (resourceReferences.length > 1) { log.info( `processing ${resourceReferences.length} ${referenceType} references in resource ${resource.attributes .title}` ); } else if (resourceReferences.length === 0) { log.error(`expected ${referenceType} reference in resource ${resource.attributes.title}`); return leafletLayers; } resourceReferences.forEach((resourceReference, referenceIndex) => { // INCOHERENCE-ALERT: use the reference title instead of the resource title, if there are more than one references. /** * @type {String} */ let title = resourceReferences.length > 1 ? resourceReference.attributes.title : resource.attributes.title; // ACCIDENTAL-COMPLEXITY-ALERT: another workaround caused by incoherent usage of predefined entity properties like title, name, etc. // the title is automatically is set the value of referenceType by Drupal, if not it is not explicitly set. Makes sense. NOT. if (title.indexOf(referenceType) === 0) { log.warn( `title of reference #${referenceIndex} not explicitely set, using attribute title instead. Check resource ${resource .attributes.title}.` ); title = resource.attributes.title; } let uri = resourceReference.attributes.field_reference_path; // @deprecated: This "strongly demanded feature" has become unnecessary now. It didn't make any sense in the first place, tough. // now we have to decide whether this resource is a template resource and whether it should be expanded. // WARNING: This expands also all references :o if (expandTemplateResources === true) { const parametersMaps = CSISHelpers.parametersMapsFromTemplateResource(resource, includedArray); if (parametersMaps.length > 0) { parametersMaps.forEach((parametersMap, parametersMapIndex) => { // PITFALL ALERT: map.size vs. array.length if (parametersMap.size > 0) { const expandedUrl = CSISHelpers.addUrlParameters(uri, parametersMap); title += ' ['; // FIXME: just value or value + key? parametersMap.forEach((value) => { title += value + ', '; }); title = title.slice(0, -2) + ']'; const leafletLayer = prepareLayer(expandedUrl, title); if (leafletLayer && leafletLayer !== null) { leafletLayers.push(leafletLayer); } log.debug(`resource ${resource.attributes.title} expanded to ${title} = ${expandedUrl}`); } else { log.warn( `resource ${resource.attributes .title} NOT expanded due to empty parameters map at index ${parametersMapIndex}` ); // WARNING: We add the layer anyway. const leafletLayer = prepareLayer(uri, title); if (leafletLayer && leafletLayer !== null) { leafletLayers.push(leafletLayer); } } }); } else { log.warn(`resource ${resource.attributes.title} NOT expanded due to empty parameters maps`); // does it make sense to push the unexpanded layer? const leafletLayer = prepareLayer(uri, title); if (leafletLayer && leafletLayer !== null) { leafletLayers.push(leafletLayer); } } } else { const leafletLayer = prepareLayer(uri, title); if (leafletLayer && leafletLayer !== null) { leafletLayers.push(leafletLayer); } } }); return leafletLayers; } /** * Creat a single leaflet layer from a resource. * * @param {String} groupTitle * @param {String} layerTitle * @param {String} layerUrl * @return {Object} */ createLeafletLayer(groupTitle, layerTitle, layerUrl) { var leafletLayer = {}; leafletLayer.checked = false; leafletLayer.groupTitle = groupTitle; leafletLayer.name = this.titleToName(layerTitle); leafletLayer.title = layerTitle; leafletLayer.layers = this.extractLayers(layerUrl.toString()); leafletLayer.url = this.extractUrl(layerUrl.toString()); leafletLayer.style = this.extractStyle(layerUrl.toString()); // TODO: #54 // If no variables can be set, we currently remove the layer until #54 is implemented if (leafletLayer.url.indexOf('$') === -1) { return leafletLayer; } else { log.warn( `layer ${leafletLayer.name} not added! URL contains unprocessed $EMIKAT variables: \n${leafletLayer.url}` ); } return null; } /** * Extract the group name for the layer selection control * * @param {*} groupingTagType * @param {*} defaultGroupName * @param {*} resource * @param {*} includedArray */ extractGroupName(groupingTagType, defaultGroupName, resource, includedArray) { // FIXME: Dangerous, if multiple values for tagType let groupTags = null; let groupTitle = defaultGroupName; if (groupingTagType && groupingTagType != null) { groupTags = CSISHelpers.extractTagsfromResource(resource, includedArray, groupingTagType); } if (groupTags != null && groupTags.length > 0) { groupTitle = groupTags[0].attributes.name; if (groupTags.length > 1) { log.warn( `${groupTags.length} ${groupingTagType} tags in resource ${resource.attributes .title}, using only 1st tag ${groupTags[0].attributes.name} as group title for layer ${resource .attributes.title}` ); } } else { log.warn(`no ${groupingTagType} tag found in resource ${resource.attributes.title}, using default group name`); } return groupTitle; } /** * Main difference between background layers and overlay layers is the possbility * for single / multiple selection. * * See https://github.com/clarity-h2020/map-component/issues/84 * * @param {*} resourceArray * @param {*} includedArray * @param {*} referenceType * @param {*} backgroundLayersTagName * @param {*} backgroundLayersGroupName * @deprecated */ createLeafletMapModel( resourceArray, includedArray, referenceType, layersTagType, layersTagName, layersGroupName, groupingTagType, expandTemplateResources ) { // FIXME: Define separate tag type for background layers let leafletMapModel = []; let filteredResources; if (layersTagName) { filteredResources = CSISHelpers.filterResourcesByReferenceType( CSISHelpers.filterResourcesbyTagName(resourceArray, includedArray, layersTagType, layersTagName), includedArray, referenceType ); } else { // layersTagName is undefined, so we process all resources. filteredResources = CSISHelpers.filterResourcesByReferenceType( resourceArray, includedArray, referenceType ); } log.info( `${filteredResources.length} valid layers for ${layersTagType} = ${layersTagName} with ${referenceType} references found in ${resourceArray.length} available resources.` ); // 1st process the resources // FIXME: ~~expandTemplateResources currently ony supported for Backgrounds Layers~~ DISABLED! // See https://github.com/clarity-h2020/map-component/issues/69#issuecomment-558206120 // WARNING: Even for Backgrounds Layers, expandTemplateResources should not be used! :-( // See https://github.com/clarity-h2020/map-component/issues/72 for (let i = 0; i < filteredResources.length; ++i) { let leafletLayers = this.createLeafletLayers( filteredResources[i], includedArray, referenceType, layersGroupName, groupingTagType, expandTemplateResources ); if (leafletLayers.length > 0) { leafletMapModel.push(...leafletLayers); } } return leafletMapModel; } /** * Extracts the layers from the given RESOURCES array and add it to the map. * Sorts it into categories according to flexible categorisation tags * See * * FIXME: externalize, eventually merge with finishMapExtraction() * * @param {Object} resourcesApiResponse the resources API response * @param {String} overlayLayersTagType usually the eu-gl taxonomy * @param {String} overlayLayersTagName the eu-gl step of the layers, which should be added. E.g. eu-gl:risk-and-impact-assessment * @param {String} groupingTagType the grouping criteria of the layers. E.g. taxonomy_term--hazards * @return {Object[]} leafletMapModel internal map model */ processResources(resourcesApiResponse) { // if we requested a single resource with getDatapackageResourceFromCsis(), put it into an array. const resourceArray = Array.isArray(resourcesApiResponse.data) ? resourcesApiResponse.data : [resourcesApiResponse.data]; const includedArray = resourcesApiResponse.included; log.debug( `process ${resourceArray.length} resources and ${includedArray.length} included objects for ${this.referenceType} reference type` ); const leafletMapModel = []; /** * The Background layers, e.g. Vector Layers Urban Atlas Roads from Local Effects Input Layers */ let backgroundLayers = this.createLeafletMapModel( resourceArray, includedArray, this.referenceType, this.backgroundLayersTagType, this.backgroundLayersTagName, this.backgroundLayersGroupName, undefined, false); if (backgroundLayers && backgroundLayers.length > 0) { log.info( `${backgroundLayers.length} valid background Layers for ${this.backgroundLayersTagType} = ${this.backgroundLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources` ); leafletMapModel.push(...backgroundLayers); } else { log.warn(`NO valid background Layers for ${this.backgroundLayersTagType} = ${this.backgroundLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources`); } /** * The CLARITY Background layers, e.g. Vector Layers Urban Atlas Roads from Local Effects Input Layers */ let clarityBackgroundLayers = this.createLeafletMapModel( resourceArray, includedArray, this.referenceType, this.clarityBackgroundLayersTagType, this.clarityBackgroundLayersTagName, this.clarityBackgroundLayersGroupName, undefined, false); if (clarityBackgroundLayers && clarityBackgroundLayers.length > 0) { log.info( `${clarityBackgroundLayers.length} valid CLARITY background Layers for ${this.clarityBackgroundLayersTagType} = ${this.clarityBackgroundLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources` ); leafletMapModel.push(...clarityBackgroundLayers); } else { log.warn(`NO valid CLARITY background Layers for ${this.clarityBackgroundLayersTagType} = ${this.clarityBackgroundLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources`); } /** * The Overlay layers, e.g. Hazard Raster Layers */ let overlayLayers = this.createLeafletMapModel( resourceArray, includedArray, this.referenceType, this.overlayLayersTagType, this.overlayLayersTagName, this.overlayLayersGroupName, this.overlayLayersGroupingTagType, false); if (overlayLayers && overlayLayers.length > 0) { log.info( `${overlayLayers.length} valid overlay Layers for ${this.overlayLayersTagType} = ${this.overlayLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources` ); leafletMapModel.push(...overlayLayers); } else { log.warn(`NO valid CLARITY overlay Layers for ${this.overlayLayersTagType} = ${this.overlayLayersTagName} with ${this.referenceType} references found in ${resourceArray.length} available resources`); } // Sort by name ... leafletMapModel.sort(function (a, b) { if (a.name < b.name) { return -1; } else if (a.name > b.name) { return 1; } else { return 0; } }); // ... and then by group. leafletMapModel.sort(function (a, b) { if (a.groupTitle < b.groupTitle) { return -1; } else if (a.groupTitle > b.groupTitle) { return 1; } else { return 0; } }); // Disabled by request. See #81 /* if (leafletMapModel.length > 0) { leafletMapModel[0].checked = true; }*/ return leafletMapModel; } /** * Method can be overridden in subclasses to support custom behaviour. * * FIXME: externalize, use callback method instead! * * @param {Object} resource * @param {Object} includedArray * @param {String} url * @return String */ processUrl(resource, includedArray, url) { // the whole "variable meaning mess" (https://github.com/clarity-h2020/csis/issues/101#issuecomment-565025875) is hidden in this method: const parametersMap = CSISHelpers.generateParametersMap( CSISHelpers.QUERY_PARAMS, this.queryParams, resource, includedArray ); return CSISHelpers.addUrlParameters(url, parametersMap); } /** * Drupal JSON API 'deeply' includes objects, e.g. &include=field_references are provided only once in a separate array name 'included'. * This method resolves the references and extracts the included object. * * FIXME: Remove! Replace all occurrences with CSISHelpers.getIncludedObject! * * @deprecated */ getIncludedObject(type, id, includedArray) { return CSISHelpers.getIncludedObject(type, id, includedArray); } /** * Extract the groups from the given overlay layers * * FIXME: externalize method! Use a Set! * ExclusiveGroups?! -> https://github.com/ismyrnow/leaflet-groupedlayercontrol#leaflet-groupedlayercontrol * * @param {Array} mapData * @returns an array with all group names * @deprecated */ extractExclusiveGroups(mapData) { var groups = []; for (var i = 0; i < mapData.length; ++i) { let groupTitle = mapData[i].groupTitle; // FIXME: use constant for group names if ( !groups.includes(groupTitle) && groupTitle !== this.backgroundLayersGroupName && groupTitle !== this.clarityBackgroundLayersGroupName ) { groups.push(groupTitle); } } return groups; } /** * Replace spaces, because spaces are not allowed in leaflet layer names * :-( * * @param {String} title */ titleToName(title) { return title.replace(' ', '_'); } /** * Extracts the layer name from the given wms GetMap request * * @param {String} url a get MapRequest */ extractLayers(url) { var layerParamName = 'layers='; var layerParam = url.substring(url.toLowerCase().indexOf(layerParamName) + layerParamName.length); return layerParam.indexOf('&') !== -1 ? layerParam.substring(0, layerParam.indexOf('&')) : layerParam; } /** * Extracts the style name from the given wms GetMap request * * @param {String} url a get MapRequest */ extractStyle(url) { var styleParamName = 'styles='; if (url !== null && url.toLowerCase().indexOf(styleParamName) !== -1) { var styleParam = url.substring(url.toLowerCase().indexOf(styleParamName) + styleParamName.length); return styleParam.indexOf('&') !== -1 ? styleParam.substring(0, styleParam.indexOf('&')) : styleParam; } else { return null; } } /** * Returns the given url without parameters * FIXME: use queryString.parse() ! * * @param {String} url */ extractUrl(url) { //remove the parameters, which will be set by leaflet var parameterList = [ 'request', 'version', 'service', 'layers', 'bbox', 'width', 'height', 'srs', 'crs', 'format', 'styles', 'transparent', 'bgcolor', 'exceptions' ]; var baseUrl = url.indexOf('?') !== -1 ? url.substring(0, url.indexOf('?')) : url; if (url.indexOf('?') !== -1) { /** * The parameters part of the URL * @deprecated use queryString.parse() instead! * @type String */ var urlParameters = url.substring(url.indexOf('?')); for (var index = 0; index < parameterList.length; ++index) { /** * e.g. 'request' */ var parameter = parameterList[index]; // automatically generated parameter from parameterList found in URL .. if (urlParameters.toLowerCase().indexOf(parameter) !== -1) { // ??? var lastUrlPart = urlParameters.substring(urlParameters.toLowerCase().indexOf(parameter)); urlParameters = urlParameters.substring(0, urlParameters.toLowerCase().indexOf(parameter)); if (lastUrlPart.indexOf('&') !== -1) { urlParameters = urlParameters + lastUrlPart.substring(lastUrlPart.indexOf('&') + 1); } } } return baseUrl + urlParameters; } else { return baseUrl; } } /** * Returns the bounding box of the given polygon geometry * * @param {Object} area */ getBoundsFromArea(area) { const bboxArray = turf.bbox(area); const corner1 = [bboxArray[1], bboxArray[0]]; const corner2 = [bboxArray[3], bboxArray[2]]; var bounds = [corner1, corner2]; return bounds; } render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h2>Map Component not initilaised correctly</h2> <p> Query Parametes, e.g. <i>study_id</i> missing! </p> </header> </div> ); } } /** * Prop Types. * FIXME: replace by query params */ BasicMap.propTypes = { /** * deprecated */ match: PropTypes.object, location: PropTypes.any, /** * ~ mapType, e.g. 'eu-gl:risk-and-impact-assessment' = Taxonomy Term */ mapSelectionId: PropTypes.string, /** * Taxonomy for Layer Groups, e.g. 'taxonomy_term--hazards' */ groupingCriteria: PropTypes.string, /** * show two synchronised maps (GenericMap will take care about this) */ isSynchronised: PropTypes.bool, /** * tell LeafletMap to load STUDY_VARIANT='ADAPTATION-01' */ showAdaptationScenario: PropTypes.bool }; /** * Default props. ATM pretty useless. * Redesign to functional component pending. */ BasicMap.defaultProps = { mapSelectionId: undefined, groupingCriteria: undefined, isSynchronised: false, showAdaptationScenario:false };