diff --git a/web/client/actions/draw.js b/web/client/actions/draw.js
index 8f480da46c..7d85b9c69a 100644
--- a/web/client/actions/draw.js
+++ b/web/client/actions/draw.js
@@ -8,6 +8,7 @@
const CHANGE_DRAWING_STATUS = 'CHANGE_DRAWING_STATUS';
const END_DRAWING = 'END_DRAWING';
+const SET_CURRENT_STYLE = 'SET_CURRENT_STYLE';
function changeDrawingStatus(status, method, owner, features, options) {
return {
@@ -28,9 +29,18 @@ function endDrawing(geometry, owner) {
};
}
+function setCurrentStyle(style) {
+ return {
+ type: SET_CURRENT_STYLE,
+ currentStyle: style
+ };
+}
+
module.exports = {
CHANGE_DRAWING_STATUS,
END_DRAWING,
+ SET_CURRENT_STYLE,
changeDrawingStatus,
- endDrawing
+ endDrawing,
+ setCurrentStyle
};
diff --git a/web/client/actions/map.js b/web/client/actions/map.js
index b658b02f41..c68fb147c5 100644
--- a/web/client/actions/map.js
+++ b/web/client/actions/map.js
@@ -12,11 +12,20 @@ const CHANGE_MOUSE_POINTER = 'CHANGE_MOUSE_POINTER';
const CHANGE_ZOOM_LVL = 'CHANGE_ZOOM_LVL';
const PAN_TO = 'PAN_TO';
const ZOOM_TO_EXTENT = 'ZOOM_TO_EXTENT';
+const ZOOM_TO_POINT = 'ZOOM_TO_POINT';
const CHANGE_MAP_CRS = 'CHANGE_MAP_CRS';
const CHANGE_MAP_SCALES = 'CHANGE_MAP_SCALES';
const CHANGE_MAP_STYLE = 'CHANGE_MAP_STYLE';
const CHANGE_ROTATION = 'CHANGE_ROTATION';
+function zoomToPoint(pos, zoom, crs) {
+ return {
+ type: ZOOM_TO_POINT,
+ pos,
+ zoom,
+ crs
+ };
+}
function changeMapView(center, zoom, bbox, size, mapStateSource, projection, viewerOptions) {
return {
@@ -108,6 +117,7 @@ module.exports = {
CHANGE_MAP_SCALES,
CHANGE_MAP_STYLE,
CHANGE_ROTATION,
+ ZOOM_TO_POINT,
changeMapView,
clickOnMap,
changeMousePointer,
@@ -117,5 +127,6 @@ module.exports = {
zoomToExtent,
panTo,
changeMapStyle,
- changeRotation
+ changeRotation,
+ zoomToPoint
};
diff --git a/web/client/actions/mapInfo.js b/web/client/actions/mapInfo.js
index 0182edcfb2..c161051bc1 100644
--- a/web/client/actions/mapInfo.js
+++ b/web/client/actions/mapInfo.js
@@ -215,5 +215,7 @@ module.exports = {
showMapinfoRevGeocode,
getVectorInfo,
noQueryableLayers,
- clearWarning
+ clearWarning,
+ errorFeatureInfo,
+ loadFeatureInfo
};
diff --git a/web/client/actions/measurement.js b/web/client/actions/measurement.js
index a460e30404..7fec02f107 100644
--- a/web/client/actions/measurement.js
+++ b/web/client/actions/measurement.js
@@ -27,6 +27,7 @@ function changeMeasurement(measurement) {
function changeMeasurementState(measureState) {
return {
type: CHANGE_MEASUREMENT_STATE,
+ pointMeasureEnabled: measureState.pointMeasureEnabled,
lineMeasureEnabled: measureState.lineMeasureEnabled,
areaMeasureEnabled: measureState.areaMeasureEnabled,
bearingMeasureEnabled: measureState.bearingMeasureEnabled,
diff --git a/web/client/actions/search.js b/web/client/actions/search.js
index b100a9d5b1..229549590f 100644
--- a/web/client/actions/search.js
+++ b/web/client/actions/search.js
@@ -17,6 +17,7 @@ const TEXT_SEARCH_NESTED_SERVICES_SELECTED = 'TEXT_SEARCH_NESTED_SERVICE_SELECTE
const TEXT_SEARCH_ERROR = 'TEXT_SEARCH_ERROR';
const TEXT_SEARCH_CANCEL_ITEM = 'TEXT_SEARCH_CANCEL_ITEM';
const TEXT_SEARCH_ITEM_SELECTED = 'TEXT_SEARCH_ITEM_SELECTED';
+const TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE = 'TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE';
/**
* updates the results of the search result loaded
* @memberof actions.search
@@ -92,10 +93,11 @@ function resetSearch() {
* @memberof actions.search
* @param {object} itemPosition
*/
-function addMarker(itemPosition) {
+function addMarker(itemPosition, itemText) {
return {
type: TEXT_SEARCH_ADD_MARKER,
- markerPosition: itemPosition
+ markerPosition: itemPosition,
+ markerLabel: itemText
};
}
@@ -157,6 +159,18 @@ function cancelSelectedItem(item) {
};
}
+/**
+ * Highlights the given feature
+ * @memberof actions.search
+ * @param {object} feature the feature to highlight
+ */
+function setHighlightedFeature(feature) {
+ return {
+ type: TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE,
+ highlightedFeature: feature
+ };
+}
+
/**
* Actions for search
* @name actions.search
@@ -174,6 +188,7 @@ module.exports = {
TEXT_SEARCH_ITEM_SELECTED,
TEXT_SEARCH_NESTED_SERVICES_SELECTED,
TEXT_SEARCH_CANCEL_ITEM,
+ TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE,
searchTextLoading,
searchResultError,
searchResultLoaded,
@@ -184,5 +199,6 @@ module.exports = {
searchTextChanged,
selectNestedService,
selectSearchItem,
- cancelSelectedItem
+ cancelSelectedItem,
+ setHighlightedFeature
};
diff --git a/web/client/actions/selection.js b/web/client/actions/selection.js
new file mode 100644
index 0000000000..dc7bc681d6
--- /dev/null
+++ b/web/client/actions/selection.js
@@ -0,0 +1,23 @@
+/**
+ * Copyright 2017, Sourcepole AG.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+const CHANGE_SELECTION_STATE = 'CHANGE_SELECTION_STATE';
+
+function changeSelectionState(selectionState) {
+ return {
+ type: CHANGE_SELECTION_STATE,
+ geomType: selectionState.geomType,
+ point: selectionState.point,
+ line: selectionState.line,
+ polygon: selectionState.polygon
+ };
+}
+
+module.exports = {
+ CHANGE_SELECTION_STATE,
+ changeSelectionState
+};
diff --git a/web/client/components/buttons/ZoomButton.jsx b/web/client/components/buttons/ZoomButton.jsx
index 0df93c6129..afee8d505f 100644
--- a/web/client/components/buttons/ZoomButton.jsx
+++ b/web/client/components/buttons/ZoomButton.jsx
@@ -39,7 +39,8 @@ const ZoomButton = React.createClass({
minZoom: 0,
maxZoom: 28,
onZoom: () => {},
- bsStyle: "default"
+ bsStyle: "default",
+ style: {}
};
},
render() {
diff --git a/web/client/components/data/identify/Identify.jsx b/web/client/components/data/identify/Identify.jsx
index 1d39bbb6bd..3aea10af2e 100644
--- a/web/client/components/data/identify/Identify.jsx
+++ b/web/client/components/data/identify/Identify.jsx
@@ -226,7 +226,7 @@ const Identify = React.createClass({
},
render() {
if (this.props.enabled && this.props.requests.length !== 0) {
- return this.props.draggable ? (
+ return this.props.draggable && this.props.asPanel ? (
{this.renderContent()}
diff --git a/web/client/components/map/openlayers/DrawSupport.jsx b/web/client/components/map/openlayers/DrawSupport.jsx
index ce0bbd93c7..59936e1397 100644
--- a/web/client/components/map/openlayers/DrawSupport.jsx
+++ b/web/client/components/map/openlayers/DrawSupport.jsx
@@ -8,9 +8,10 @@
const React = require('react');
const ol = require('openlayers');
-const {concat} = require('lodash');
+const {concat, head} = require('lodash');
const assign = require('object-assign');
+const uuid = require('uuid');
const DrawSupport = React.createClass({
propTypes: {
@@ -38,12 +39,20 @@ const DrawSupport = React.createClass({
};
},
componentWillReceiveProps(newProps) {
+ if (this.drawLayer) {
+ this.updateFeatureStyles(newProps.features);
+ }
+
+ if (!newProps.drawStatus && this.selectInteraction) {
+ this.selectInteraction.getFeatures().clear();
+ }
+
switch (newProps.drawStatus) {
case ("create"):
this.addLayer(newProps);
break;
case ("start"):
- this.addDrawInteraction(newProps);
+ this.addInteractions(newProps);
break;
case ("stop"):
this.removeDrawInteraction();
@@ -60,40 +69,32 @@ const DrawSupport = React.createClass({
},
render() {
return null;
+ },
+ updateFeatureStyles(features) {
+ if (features && features.length > 0) {
+ features.map(f => {
+ if (f.style) {
+ let olFeature = this.toOlFeature(f);
+ if (olFeature) {
+ olFeature.setStyle(this.toOlStyle(f.style, f.selected));
+ }
+ }
+ });
+ }
},
addLayer: function(newProps, addInteraction) {
- var source;
- var vector;
this.geojson = new ol.format.GeoJSON();
-
- // create a layer to draw on
- source = new ol.source.Vector();
- vector = new ol.layer.Vector({
- source: source,
+ this.drawSource = new ol.source.Vector();
+ this.drawLayer = new ol.layer.Vector({
+ source: this.drawSource,
zIndex: 1000000,
- style: new ol.style.Style({
- fill: new ol.style.Fill({
- color: 'rgba(255, 255, 255, 0.2)'
- }),
- stroke: new ol.style.Stroke({
- color: '#ffcc33',
- width: 2
- }),
- image: new ol.style.Circle({
- radius: 7,
- fill: new ol.style.Fill({
- color: '#ffcc33'
- })
- })
- })
+ style: this.toOlStyle(newProps.style)
});
- this.props.map.addLayer(vector);
+ this.props.map.addLayer(this.drawLayer);
- this.drawSource = source;
- this.drawLayer = vector;
if (addInteraction) {
- this.addDrawInteraction(newProps);
+ this.addInteractions(newProps);
}
this.addFeatures(newProps.features || []);
@@ -132,18 +133,38 @@ const DrawSupport = React.createClass({
this.addFeatures(newProps.features || []);
}
},
- addDrawInteraction: function(newProps) {
- let draw;
- let geometryType = newProps.drawMethod;
-
- if (!this.drawLayer) {
- this.addLayer(newProps);
- }
-
+ addDrawInteraction(drawMethod, startingPoint, maxPoints) {
if (this.drawInteraction) {
this.removeDrawInteraction();
}
- let features = new ol.Collection();
+ this.drawInteraction = new ol.interaction.Draw(this.drawPropertiesForGeometryType(drawMethod, maxPoints));
+
+ this.drawInteraction.on('drawstart', function(evt) {
+ this.sketchFeature = evt.feature;
+ if (this.selectInteraction) {
+ this.selectInteraction.getFeatures().clear();
+ this.selectInteraction.setActive(false);
+ }
+ }, this);
+
+ this.drawInteraction.on('drawend', function(evt) {
+ this.sketchFeature = evt.feature;
+ this.sketchFeature.set('id', uuid.v1());
+ const feature = this.fromOLFeature(this.sketchFeature, this.props.drawMethod, startingPoint);
+
+ this.props.onEndDrawing(feature, this.props.drawOwner);
+ if (this.props.options.stopAfterDrawing) {
+ this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner, this.props.features.concat([feature]));
+ }
+ if (this.selectInteraction) {
+ this.selectInteraction.setActive(true);
+ }
+ }, this);
+
+ this.props.map.addInteraction(this.drawInteraction);
+ this.setDoubleClickZoomEnabled(false);
+ },
+ drawPropertiesForGeometryType(geometryType, maxPoints) {
let drawBaseProps = {
source: this.drawSource,
type: /** @type {ol.geom.GeometryType} */ geometryType,
@@ -166,7 +187,7 @@ const DrawSupport = React.createClass({
})
})
}),
- features: features,
+ features: new ol.Collection(),
condition: ol.events.condition.always
};
// Prepare the properties for the BBOX drawing
@@ -215,7 +236,7 @@ const DrawSupport = React.createClass({
}
case "LineString": {
roiProps.type = "LineString";
- roiProps.maxPoints = newProps.options.maxPoints;
+ roiProps.maxPoints = maxPoints;
roiProps.geometryFunction = function(coordinates, geometry) {
let geom = geometry;
if (!geom) {
@@ -240,77 +261,216 @@ const DrawSupport = React.createClass({
}
default : return {};
}
- let drawProps = assign({}, drawBaseProps, roiProps);
-
- // create an interaction to draw with
- draw = new ol.interaction.Draw(drawProps);
+ return assign({}, drawBaseProps, roiProps);
+ },
+ setDoubleClickZoomEnabled(enabled) {
+ let interactions = this.props.map.getInteractions();
+ for (let i = 0; i < interactions.getLength(); i++) {
+ let interaction = interactions.item(i);
+ if (interaction instanceof ol.interaction.DoubleClickZoom) {
+ interaction.setActive(enabled);
+ break;
+ }
+ }
+ },
+ updateFeatureExtent(event) {
+ const movedFeatures = event.features.getArray();
+ const updatedFeatures = this.props.features.map((f) => {
+ const moved = head(movedFeatures.filter((mf) => this.fromOLFeature(mf, this.props.drawMethod).id === f.id));
+ return moved ? assign({}, f, {
+ geometry: moved.geometry,
+ center: moved.center,
+ extent: moved.extent,
+ coordinate: moved.coordinates,
+ radius: moved.radius
+ }) : f;
+ });
- draw.on('drawstart', function(evt) {
- this.sketchFeature = evt.feature;
- this.drawSource.clear();
- }, this);
+ this.props.onChangeDrawingStatus('replace', this.props.drawMethod, this.props.drawOwner, updatedFeatures);
+ },
+ addInteractions: function(newProps) {
+ if (!this.drawLayer) {
+ this.addLayer(newProps);
+ }
+ this.addDrawInteraction(newProps.drawMethod, newProps.options.startingPoint, newProps.options.maxPoints);
+ if (newProps.options && newProps.options.editEnabled) {
+ this.addSelectInteraction();
- draw.on('drawend', function(evt) {
- this.sketchFeature = evt.feature;
- let startingPoint = newProps.options.startingPoint;
- let drawnGeometry = this.sketchFeature.getGeometry();
- let radius;
- let extent = drawnGeometry.getExtent();
- let type = drawnGeometry.getType();
- let center = ol.extent.getCenter(drawnGeometry.getExtent());
- let coordinates = drawnGeometry.getCoordinates();
- if (startingPoint) {
- coordinates = concat(startingPoint, coordinates);
- drawnGeometry.setCoordinates(coordinates);
- }
- if (type === "Circle") {
- radius = Math.sqrt(Math.pow(center[0] - coordinates[0][0][0], 2) + Math.pow(center[1] - coordinates[0][0][1], 2));
+ if (this.translateInteraction) {
+ this.props.map.removeInteraction(this.translateInteraction);
}
- let geometry = {
- type,
- extent: extent,
- center: center,
- coordinates: type === "Polygon" ? coordinates[0].concat([coordinates[0][0]]) : coordinates,
- radius: radius,
- projection: this.props.map.getView().getProjection().getCode()
- };
- /*let modifyProps = assign({}, drawProps, {
- features: features,
- deleteCondition: () => false,
- condition: ol.events.condition.never // TODO customize this part to edit
+ this.translateInteraction = new ol.interaction.Translate({
+ features: this.selectInteraction.getFeatures()
});
- let modify = new ol.interaction.Modify(modifyProps);
- this.props.map.addInteraction(modify);*/
- this.props.onEndDrawing(geometry, this.props.drawOwner);
- if (this.props.options.stopAfterDrawing) {
- this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner);
+
+ this.translateInteraction.on('translateend', this.updateFeatureExtent);
+ this.props.map.addInteraction(this.translateInteraction);
+
+
+ if (this.modifyInteraction) {
+ this.props.map.removeInteraction(this.modifyInteraction);
}
- }, this);
- this.props.map.addInteraction(draw);
- this.drawInteraction = draw;
+ this.modifyInteraction = new ol.interaction.Modify({
+ features: this.selectInteraction.getFeatures()
+ });
+
+ this.props.map.addInteraction(this.modifyInteraction);
+ }
this.drawSource.clear();
if (newProps.features.length > 0 ) {
this.addFeatures(newProps.features || []);
}
},
+ addSelectInteraction() {
+ if (this.selectInteraction) {
+ this.props.map.removeInteraction(this.selectInteraction);
+ }
+
+ this.selectInteraction = new ol.interaction.Select({ layers: [this.drawLayer] });
+
+ this.selectInteraction.on('select', () => {
+ let features = this.props.features.map(f => {
+ let selectedFeatures = this.selectInteraction.getFeatures().getArray();
+ const selected = selectedFeatures.reduce((previous, current) => {
+ return current.get('id') === f.id ? true : previous;
+ }, false);
+
+ return assign({}, f, { selected: selected });
+ });
+
+ this.props.onChangeDrawingStatus('select', null, this.props.drawOwner, features);
+ });
+
+ this.props.map.addInteraction(this.selectInteraction);
+ },
removeDrawInteraction: function() {
- if (this.drawInteraction !== null) {
+ if (this.drawInteraction) {
this.props.map.removeInteraction(this.drawInteraction);
this.drawInteraction = null;
this.sketchFeature = null;
+ setTimeout(() => this.setDoubleClickZoomEnabled(true), 250);
}
},
- clean: function() {
+ removeInteractions: function() {
this.removeDrawInteraction();
+ if (this.selectInteraction) {
+ this.props.map.removeInteraction(this.drawInteraction);
+ }
+
+ if (this.modifyInteraction) {
+ this.props.map.removeInteraction(this.modifyInteraction);
+ }
+
+ if (this.translateInteraction) {
+ this.props.map.removeInteraction(this.translateInteraction);
+ }
+ },
+ clean: function() {
+ this.removeInteractions();
+
if (this.drawLayer) {
this.props.map.removeLayer(this.drawLayer);
this.geojson = null;
this.drawLayer = null;
this.drawSource = null;
}
+ },
+ fromOLFeature: function(feature, drawMethod, startingPoint) {
+ const geometry = feature.getGeometry();
+ const extent = geometry.getExtent();
+ const center = ol.extent.getCenter(geometry.getExtent());
+ let coordinates = geometry.getCoordinates();
+ let radius;
+
+ const type = geometry.getType();
+ if (startingPoint) {
+ coordinates = concat(startingPoint, coordinates);
+ geometry.setCoordinates(coordinates);
+ }
+ if (drawMethod === "Circle") {
+ radius = Math.sqrt(Math.pow(center[0] - coordinates[0][0][0], 2) + Math.pow(center[1] - coordinates[0][0][1], 2));
+ }
+
+ return {
+ id: feature.get('id'),
+ type: type,
+ extent: extent,
+ center: center,
+ coordinates,
+ radius: radius,
+ style: this.fromOlStyle(feature.getStyle()),
+ projection: this.props.map.getView().getProjection().getCode()
+ };
+ },
+ toOlFeature: function(feature) {
+ return head(this.drawSource.getFeatures().filter((f) => f.get('id') === feature.id));
+ },
+ fromOlStyle(olStyle) {
+ if (!olStyle) {
+ return {};
+ }
+
+ return {
+ fillColor: this.rgbToHex(olStyle.getFill().getColor()),
+ fillTransparency: olStyle.getFill().getColor()[3],
+ strokeColor: olStyle.getStroke().getColor(),
+ strokeWidth: olStyle.getStroke().getWidth(),
+ text: olStyle.getText().getText()
+ };
+ },
+ toOlStyle: function(style, selected) {
+ let color = style && style.fillColor ? style.fillColor : [255, 255, 255, 0.2];
+ if (typeof color === 'string') {
+ color = this.hexToRgb(color);
+ }
+
+ if (style && style.fillTransparency) {
+ color[3] = style.fillTransparency;
+ }
+
+ let strokeColor = style && style.strokeColor ? style.strokeColor : '#ffcc33';
+ if (selected) {
+ strokeColor = '#4a90e2';
+ }
+
+ return new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: color
+ }),
+ stroke: new ol.style.Stroke({
+ color: strokeColor,
+ width: style && style.strokeWidth ? style.strokeWidth : 2
+ }),
+ image: new ol.style.Circle({
+ radius: style && style.strokeWidth ? style.strokeWidth : 5,
+ fill: new ol.style.Fill({ color: style && style.strokeColor ? style.strokeColor : '#ffcc33' })
+ }),
+ text: new ol.style.Text({
+ text: style && style.text ? style.text : '',
+ fill: new ol.style.Fill({ color: style && style.strokeColor ? style.strokeColor : '#000' }),
+ stroke: new ol.style.Stroke({ color: '#fff', width: 2 }),
+ font: style && style.fontSize ? style.fontSize + 'px helvetica' : ''
+ })
+ });
+ },
+ hexToRgb(hex) {
+ // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
+ var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
+
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex.replace(shorthandRegex, function(m, r, g, b) {
+ return r + r + g + g + b + b;
+ }));
+ return result ? [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)] : null;
+ },
+ componentToHex(c) {
+ var hex = c.toString(16);
+ return hex.length === 1 ? "0" + hex : hex;
+ },
+ rgbToHex(rgb) {
+ return "#" + this.componentToHex(rgb[0]) + this.componentToHex(rgb[1]) + this.componentToHex(rgb[2]);
}
});
diff --git a/web/client/components/map/openlayers/Locate.jsx b/web/client/components/map/openlayers/Locate.jsx
index 4c281de585..97d4226a75 100644
--- a/web/client/components/map/openlayers/Locate.jsx
+++ b/web/client/components/map/openlayers/Locate.jsx
@@ -29,20 +29,15 @@ var Locate = React.createClass({
componentDidMount() {
if (this.props.map) {
this.locate = new OlLocate(this.props.map, this.defaultOpt);
+ this.locate.setStrings(this.props.messages);
this.locate.options.onLocationError = this.onLocationError;
this.locate.on("propertychange", (e) => {this.onStateChange(e.target.get(e.key)); });
+ this.configureLocate(this.props.status);
}
},
componentWillReceiveProps(newProps) {
- let state = this.locate.get("state");
if (newProps.status !== this.props.status) {
- if ( newProps.status === "ENABLED" && state === "DISABLED") {
- this.locate.start();
- }else if (newProps.status === "FOLLOWING" && state === "ENABLED") {
- this.locate.startFollow();
- }else if (newProps.status === "DISABLED") {
- this.locate.stop();
- }
+ this.configureLocate(newProps.status);
}
if (newProps.messages !== this.props.messages) {
this.locate.setStrings(newProps.messages);
@@ -60,6 +55,16 @@ var Locate = React.createClass({
render() {
return null;
},
+ configureLocate(newStatus) {
+ let state = this.locate.get("state");
+ if ( newStatus === "ENABLED" && state === "DISABLED") {
+ this.locate.start();
+ } else if (newStatus === "FOLLOWING" && state === "ENABLED") {
+ this.locate.startFollow();
+ } else if (newStatus === "DISABLED") {
+ this.locate.stop();
+ }
+ },
defaultOpt: {
follow: true,// follow with zoom and pan the user's location
remainActive: true,
diff --git a/web/client/components/map/openlayers/MeasurementSupport.jsx b/web/client/components/map/openlayers/MeasurementSupport.jsx
index 082c879e02..599e686e72 100644
--- a/web/client/components/map/openlayers/MeasurementSupport.jsx
+++ b/web/client/components/map/openlayers/MeasurementSupport.jsx
@@ -159,7 +159,9 @@ const MeasurementSupport = React.createClass({
this.calculateGeodesicDistance(sketchCoords) : 0,
area: this.props.measurement.geomType === 'Polygon' ?
this.calculateGeodesicArea(this.sketchFeature.getGeometry().getLinearRing(0).getCoordinates()) : 0,
- bearing: this.props.measurement.geomType === 'Bearing' ? bearing : 0
+ bearing: this.props.measurement.geomType === 'Bearing' ? bearing : 0,
+ lenUnit: this.props.measurement.lenUnit,
+ areaUnit: this.props.measurement.areaUnit
}
);
this.props.changeMeasurementState(newMeasureState);
diff --git a/web/client/components/map/openlayers/SelectionSupport.jsx b/web/client/components/map/openlayers/SelectionSupport.jsx
new file mode 100644
index 0000000000..3f69b1a46a
--- /dev/null
+++ b/web/client/components/map/openlayers/SelectionSupport.jsx
@@ -0,0 +1,145 @@
+/**
+ * Copyright 2017, Sourcepole AG.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+const React = require('react');
+var ol = require('openlayers');
+
+const SelectionSupport = React.createClass({
+ propTypes: {
+ map: React.PropTypes.object,
+ projection: React.PropTypes.string,
+ selection: React.PropTypes.object,
+ changeSelectionState: React.PropTypes.func
+ },
+ getDefaultProps() {
+ return {
+ selection: {}
+ };
+ },
+ componentWillReceiveProps(newProps) {
+ if (newProps.selection.geomType && newProps.selection.geomType !== this.props.selection.geomType ) {
+ this.addDrawInteraction(newProps);
+ }
+
+ if (!newProps.selection.geomType) {
+ this.removeDrawInteraction();
+ }
+ },
+ render() {
+ return null;
+ },
+ addDrawInteraction: function(newProps) {
+ // cleanup old interaction
+ if (this.drawInteraction) {
+ this.removeDrawInteraction();
+ }
+ // create a layer to draw on
+ let source = new ol.source.Vector();
+ let vector = new ol.layer.Vector({
+ source: source,
+ zIndex: 1000000,
+ style: new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: 'rgba(255, 255, 255, 0.2)'
+ }),
+ stroke: new ol.style.Stroke({
+ color: '#ffcc33',
+ width: 2
+ }),
+ image: new ol.style.Circle({
+ radius: 7,
+ fill: new ol.style.Fill({
+ color: '#ffcc33'
+ })
+ })
+ })
+ });
+
+ this.props.map.addLayer(vector);
+
+ // create an interaction to draw with
+ let draw = new ol.interaction.Draw({
+ source: source,
+ type: /** @type {ol.geom.GeometryType} */ newProps.selection.geomType,
+ style: new ol.style.Style({
+ fill: new ol.style.Fill({
+ color: 'rgba(255, 255, 255, 0.2)'
+ }),
+ stroke: new ol.style.Stroke({
+ color: 'rgba(0, 0, 0, 0.5)',
+ lineDash: [10, 10],
+ width: 2
+ }),
+ image: new ol.style.Circle({
+ radius: 5,
+ stroke: new ol.style.Stroke({
+ color: 'rgba(0, 0, 0, 0.7)'
+ }),
+ fill: new ol.style.Fill({
+ color: 'rgba(255, 255, 255, 0.2)'
+ })
+ })
+ })
+ });
+
+ draw.on('drawstart', function(evt) {
+ // preserv the sketch feature of the draw controller
+ // to update length/area on drawing a new vertex
+ this.sketchFeature = evt.feature;
+ // clear previous sketches
+ source.clear();
+ }, this);
+ draw.on('drawend', function() {
+ this.updateSelectionState();
+ }, this);
+
+ this.props.map.addInteraction(draw);
+ this.drawInteraction = draw;
+ this.selectionLayer = vector;
+ this.setDoubleClickZoomEnabled(false);
+ },
+ removeDrawInteraction: function() {
+ if (this.drawInteraction !== null) {
+ this.props.map.removeInteraction(this.drawInteraction);
+ this.drawInteraction = null;
+ this.props.map.removeLayer(this.selectionLayer);
+ this.sketchFeature = null;
+ // Delay execution of activation of double click zoom function
+ setTimeout(() => this.setDoubleClickZoomEnabled(true), 251);
+ }
+ },
+ updateSelectionState() {
+ if (!this.sketchFeature) {
+ return;
+ }
+ const sketchCoords = this.sketchFeature.getGeometry().getCoordinates();
+
+ let newSelectionState = {
+ geomType: this.props.selection.geomType,
+ point: this.props.selection.geomType === 'Point' ?
+ [sketchCoords[0], sketchCoords[1]] : null,
+ line: this.props.selection.geomType === 'LineString' ?
+ sketchCoords.map(coo => [coo[0], coo[1]]) : null,
+ polygon: this.props.selection.geomType === 'Polygon' ?
+ this.sketchFeature.getGeometry().getLinearRing(0).getCoordinates().map(coo => [coo[0], coo[1]]) : null
+ };
+ this.props.changeSelectionState(newSelectionState);
+ },
+ setDoubleClickZoomEnabled(enabled) {
+ let interactions = this.props.map.getInteractions();
+ for (let i = 0; i < interactions.getLength(); i++) {
+ let interaction = interactions.item(i);
+ if (interaction instanceof ol.interaction.DoubleClickZoom) {
+ interaction.setActive(enabled);
+ break;
+ }
+ }
+ }
+});
+
+module.exports = SelectionSupport;
diff --git a/web/client/components/map/openlayers/plugins/GoogleLayer.js b/web/client/components/map/openlayers/plugins/GoogleLayer.js
index d5d3beab94..c2bf636634 100644
--- a/web/client/components/map/openlayers/plugins/GoogleLayer.js
+++ b/web/client/components/map/openlayers/plugins/GoogleLayer.js
@@ -40,17 +40,16 @@ Layers.registerType('google', {
});
}
gmaps[mapId].setMapTypeId(layersMap[options.name]);
- let view = map.getView();
let mapContainer = document.getElementById(mapId + 'gmaps');
let setCenter = function() {
if (mapContainer.style.visibility !== 'hidden') {
- const center = ol.proj.transform(view.getCenter(), 'EPSG:3857', 'EPSG:4326');
+ const center = ol.proj.transform(map.getView().getCenter(), 'EPSG:3857', 'EPSG:4326');
gmaps[mapId].setCenter(new google.maps.LatLng(center[1], center[0]));
}
};
let setZoom = function() {
if (mapContainer.style.visibility !== 'hidden') {
- gmaps[mapId].setZoom(view.getZoom());
+ gmaps[mapId].setZoom(map.getView().getZoom());
}
};
@@ -103,18 +102,22 @@ Layers.registerType('google', {
let setRotation = function() {
if (mapContainer.style.visibility !== 'hidden') {
- const rotation = view.getRotation() * 180 / Math.PI;
+ const rotation = map.getView().getRotation() * 180 / Math.PI;
mapContainer.style.transform = "rotate(" + rotation + "deg)";
google.maps.event.trigger(gmaps[mapId], "resize");
}
};
- view.on('change:center', setCenter);
- view.on('change:resolution', setZoom);
- view.on('change:rotation', setRotation);
-
+ let setViewEventListeners = function() {
+ let view = map.getView();
+ view.on('change:center', setCenter);
+ view.on('change:resolution', setZoom);
+ view.on('change:rotation', setRotation);
+ };
+ map.on('change:view', setViewEventListeners);
+ setViewEventListeners();
setCenter();
setZoom();
diff --git a/web/client/components/map/openlayers/plugins/VectorLayer.js b/web/client/components/map/openlayers/plugins/VectorLayer.js
index 803021f372..6f5ea51532 100644
--- a/web/client/components/map/openlayers/plugins/VectorLayer.js
+++ b/web/client/components/map/openlayers/plugins/VectorLayer.js
@@ -21,25 +21,25 @@ const image = new ol.style.Circle({
});
const defaultStyles = {
- 'Point': [new ol.style.Style({
+ 'Point': () => [new ol.style.Style({
image: image
})],
- 'LineString': [new ol.style.Style({
+ 'LineString': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
})],
- 'MultiLineString': [new ol.style.Style({
+ 'MultiLineString': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'green',
width: 1
})
})],
- 'MultiPoint': [new ol.style.Style({
+ 'MultiPoint': () => [new ol.style.Style({
image: image
})],
- 'MultiPolygon': [new ol.style.Style({
+ 'MultiPolygon': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
lineDash: [4],
@@ -49,7 +49,7 @@ const defaultStyles = {
color: 'rgba(0, 0, 255, 0.1)'
})
})],
- 'Polygon': [new ol.style.Style({
+ 'Polygon': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'blue',
lineDash: [4],
@@ -59,7 +59,7 @@ const defaultStyles = {
color: 'rgba(0, 0, 255, 0.1)'
})
})],
- 'GeometryCollection': [new ol.style.Style({
+ 'GeometryCollection': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'magenta',
width: 2
@@ -75,7 +75,7 @@ const defaultStyles = {
})
})
})],
- 'Circle': [new ol.style.Style({
+ 'Circle': () => [new ol.style.Style({
stroke: new ol.style.Stroke({
color: 'red',
width: 2
@@ -84,25 +84,32 @@ const defaultStyles = {
color: 'rgba(255,0,0,0.2)'
})
})],
- 'marker': [new ol.style.Style({
- image: new ol.style.Icon(({
+ 'marker': (options) => [new ol.style.Style({
+ image: new ol.style.Icon({
anchor: [14, 41],
anchorXUnits: 'pixels',
anchorYUnits: 'pixels',
src: markerShadow
- }))
+ })
}), new ol.style.Style({
- image: new ol.style.Icon(({
+ image: new ol.style.Icon({
anchor: [0.5, 1],
anchorXUnits: 'fraction',
anchorYUnits: 'fraction',
src: markerIcon
- }))
+ }),
+ text: new ol.style.Text({
+ text: options.label,
+ scale: 1.25,
+ offsetY: 8,
+ fill: new ol.style.Fill({color: '#000000'}),
+ stroke: new ol.style.Stroke({color: '#FFFFFF', width: 2})
+ })
})]
};
-var styleFunction = function(feature) {
- return defaultStyles[feature.getGeometry().getType()];
+var styleFunction = function(feature, options) {
+ return defaultStyles[feature.getGeometry().getType()](options);
};
function getStyle(options) {
@@ -163,12 +170,12 @@ function getStyle(options) {
switch (type) {
case "Point":
case "MultiPoint":
- return defaultStyles.marker;
+ return defaultStyles.marker(options);
default:
break;
}
}
- return defaultStyles[options.styleName];
+ return defaultStyles[options.styleName](options);
} : style || styleFunction;
}
Layers.registerType('vector', {
@@ -197,6 +204,9 @@ Layers.registerType('vector', {
f.getGeometry().transform(oldCrs, newCrs);
});
}
+ if (!newOptions.overrideOLStyle) {
+ layer.setStyle((feature) => styleFunction(feature, newOptions));
+ }
if (!isEqual(oldOptions.style, newOptions.style)) {
layer.setStyle(getStyle(newOptions));
}
diff --git a/web/client/components/map/openlayers/plugins/WMSLayer.js b/web/client/components/map/openlayers/plugins/WMSLayer.js
index f1aa5fcb74..1c40e7bdb0 100644
--- a/web/client/components/map/openlayers/plugins/WMSLayer.js
+++ b/web/client/components/map/openlayers/plugins/WMSLayer.js
@@ -55,7 +55,8 @@ Layers.registerType('wms', {
zIndex: options.zIndex,
source: new ol.source.ImageWMS({
url: urls[0],
- params: queryParameters
+ params: queryParameters,
+ ratio: options.ratio
})
});
}
diff --git a/web/client/components/map/openlayers/plugins/WMTSLayer.js b/web/client/components/map/openlayers/plugins/WMTSLayer.js
index 3812af626f..b6ba3844ab 100644
--- a/web/client/components/map/openlayers/plugins/WMTSLayer.js
+++ b/web/client/components/map/openlayers/plugins/WMTSLayer.js
@@ -44,7 +44,8 @@ Layers.registerType('wmts', {
],
extent: extent,
resolutions: resolutions,
- matrixIds: matrixIds
+ matrixIds: matrixIds,
+ tileSize: options.tileSize || [256, 256]
}),
style: options.style || '',
wrapX: true
diff --git a/web/client/plugins/map/index.js b/web/client/plugins/map/index.js
index f904d18680..1d5e5ab865 100644
--- a/web/client/plugins/map/index.js
+++ b/web/client/plugins/map/index.js
@@ -12,8 +12,9 @@ const {changeMapView, clickOnMap} = require('../../actions/map');
const {layerLoading, layerLoad, layerError, invalidLayer} = require('../../actions/layers');
const {changeMousePosition} = require('../../actions/mousePosition');
const {changeMeasurementState} = require('../../actions/measurement');
+const {changeSelectionState} = require('../../actions/selection');
const {changeLocateState, onLocateError} = require('../../actions/locate');
-const {changeDrawingStatus, endDrawing} = require('../../actions/draw');
+const {changeDrawingStatus, endDrawing, setCurrentStyle} = require('../../actions/draw');
const {updateHighlighted} = require('../../actions/highlight');
const {connect} = require('react-redux');
@@ -48,7 +49,8 @@ module.exports = (mapType, actions) => {
})(components.MeasurementSupport || Empty);
const Locate = connect((state) => ({
- status: state.locate && state.locate.state
+ status: state.locate && state.locate.state,
+ messages: state.locale && state.locale.messages ? state.locale.messages.locate : undefined
}), {
changeLocateState,
onLocateError
@@ -57,12 +59,19 @@ module.exports = (mapType, actions) => {
const DrawSupport = connect((state) => (
state.draw || {}), {
onChangeDrawingStatus: changeDrawingStatus,
- onEndDrawing: endDrawing
+ onEndDrawing: endDrawing,
+ setCurrentStyle: setCurrentStyle
})( components.DrawSupport || Empty);
const HighlightSupport = connect((state) => (
state.highlight || {}), {updateHighlighted})( components.HighlightFeatureSupport || Empty);
+ const SelectionSupport = connect((state) => ({
+ selection: state.selection || {}
+ }), {
+ changeSelectionState
+ })(components.SelectionSupport || Empty);
+
require('../../components/map/' + mapType + '/plugins/index');
return {
@@ -75,7 +84,8 @@ module.exports = (mapType, actions) => {
overview: components.Overview || Empty,
scalebar: components.ScaleBar || Empty,
draw: DrawSupport,
- highlight: HighlightSupport
+ highlight: HighlightSupport,
+ selection: SelectionSupport
}
};
};
diff --git a/web/client/plugins/map/openlayers/index.js b/web/client/plugins/map/openlayers/index.js
index 7307198218..ef46b01af8 100644
--- a/web/client/plugins/map/openlayers/index.js
+++ b/web/client/plugins/map/openlayers/index.js
@@ -15,5 +15,6 @@ module.exports = {
Overview: require('../../../components/map/openlayers/Overview'),
ScaleBar: require('../../../components/map/openlayers/ScaleBar'),
DrawSupport: require('../../../components/map/openlayers/DrawSupport'),
- HighlightFeatureSupport: require('../../../components/map/openlayers/HighlightFeatureSupport')
+ HighlightFeatureSupport: require('../../../components/map/openlayers/HighlightFeatureSupport'),
+ SelectionSupport: require('../../../components/map/openlayers/SelectionSupport')
};
diff --git a/web/client/reducers/draw.js b/web/client/reducers/draw.js
index 293e036056..51442af2b2 100644
--- a/web/client/reducers/draw.js
+++ b/web/client/reducers/draw.js
@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree.
*/
-const {CHANGE_DRAWING_STATUS} = require('../actions/draw');
+const {CHANGE_DRAWING_STATUS, SET_CURRENT_STYLE} = require('../actions/draw');
const assign = require('object-assign');
@@ -28,6 +28,10 @@ function draw(state = initialState, action) {
options: action.options,
features: action.features
});
+ case SET_CURRENT_STYLE:
+ return assign({}, state, {
+ currentStyle: action.currentStyle
+ });
default:
return state;
}
diff --git a/web/client/reducers/map.js b/web/client/reducers/map.js
index 99dbaf836c..35098957ff 100644
--- a/web/client/reducers/map.js
+++ b/web/client/reducers/map.js
@@ -8,7 +8,7 @@
var {CHANGE_MAP_VIEW, CHANGE_MOUSE_POINTER,
CHANGE_ZOOM_LVL, CHANGE_MAP_CRS, CHANGE_MAP_SCALES, ZOOM_TO_EXTENT, PAN_TO,
- CHANGE_MAP_STYLE, CHANGE_ROTATION} = require('../actions/map');
+ CHANGE_MAP_STYLE, CHANGE_ROTATION, ZOOM_TO_POINT} = require('../actions/map');
const {isArray} = require('lodash');
@@ -43,7 +43,8 @@ function mapConfig(state = null, action) {
mapOptions: assign({}, state && state.mapOptions,
{
view: assign({}, state && state.mapOptions && state.mapOptions.view, {
- resolutions: resolutions
+ resolutions: resolutions,
+ scales: action.scales
})
})
});
@@ -109,6 +110,13 @@ function mapConfig(state = null, action) {
}
return state;
}
+ case ZOOM_TO_POINT: {
+ return assign({}, state, {
+ center: CoordinatesUtils.reproject(action.pos, action.crs, 'EPSG:4326'),
+ zoom: action.zoom,
+ mapStateSource: null
+ });
+ }
case PAN_TO: {
const center = CoordinatesUtils.reproject(
action.center,
diff --git a/web/client/reducers/search.js b/web/client/reducers/search.js
index 1b146b1643..0bcafe30f5 100644
--- a/web/client/reducers/search.js
+++ b/web/client/reducers/search.js
@@ -7,7 +7,7 @@
*/
var {TEXT_SEARCH_RESULTS_LOADED, TEXT_SEARCH_RESULTS_PURGE, TEXT_SEARCH_RESET, TEXT_SEARCH_ADD_MARKER, TEXT_SEARCH_TEXT_CHANGE, TEXT_SEARCH_LOADING, TEXT_SEARCH_ERROR,
- TEXT_SEARCH_NESTED_SERVICES_SELECTED, TEXT_SEARCH_CANCEL_ITEM} = require('../actions/search');
+ TEXT_SEARCH_NESTED_SERVICES_SELECTED, TEXT_SEARCH_CANCEL_ITEM, TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE} = require('../actions/search');
var {RESET_CONTROLS} = require('../actions/controls');
const assign = require('object-assign');
@@ -90,7 +90,9 @@ function search(state = null, action) {
case TEXT_SEARCH_RESULTS_PURGE:
return assign({}, state, { results: null, error: null});
case TEXT_SEARCH_ADD_MARKER:
- return assign({}, state, { markerPosition: action.markerPosition });
+ return assign({}, state, { markerPosition: action.markerPosition, markerLabel: action.markerLabel });
+ case TEXT_SEARCH_SET_HIGHLIGHTED_FEATURE:
+ return assign({}, state, {highlightedFeature: action.highlightedFeature});
case TEXT_SEARCH_RESET:
case RESET_CONTROLS:
return null;
diff --git a/web/client/reducers/selection.js b/web/client/reducers/selection.js
new file mode 100644
index 0000000000..c045b4f674
--- /dev/null
+++ b/web/client/reducers/selection.js
@@ -0,0 +1,31 @@
+/**
+ * Copyright 2017, Sourcepole AG.
+ * All rights reserved.
+ *
+ * This source code is licensed under the BSD-style license found in the
+ * LICENSE file in the root directory of this source tree.
+ */
+
+var {
+ CHANGE_SELECTION_STATE
+} = require('../actions/selection');
+
+const assign = require('object-assign');
+
+function selection(state = {
+ geomType: null
+}, action) {
+ switch (action.type) {
+ case CHANGE_SELECTION_STATE:
+ return assign({}, state, {
+ geomType: action.geomType,
+ point: action.point,
+ line: action.line,
+ polygon: action.polygon
+ });
+ default:
+ return state;
+ }
+}
+
+module.exports = selection;
diff --git a/web/client/selectors/layers.js b/web/client/selectors/layers.js
index c714ed941b..300e51cd24 100644
--- a/web/client/selectors/layers.js
+++ b/web/client/selectors/layers.js
@@ -13,20 +13,20 @@ const LayersUtils = require('../utils/LayersUtils');
const layersSelector = state => (state.layers && state.layers.flat) || (state.layers) || (state.config && state.config.layers);
const markerSelector = state => (state.mapInfo && state.mapInfo.showMarker && state.mapInfo.clickPoint);
-const geoColderSelector = state => (state.search && state.search.markerPosition);
+const geoColderSelector = state => (state.search && state.search);
// TODO currently loading flag causes a re-creation of the selector on any pan
// to avoid this separate loading from the layer object
const layerSelectorWithMarkers = createSelector(
[layersSelector, markerSelector, geoColderSelector],
- (layers = [], markerPosition, geocoderPosition) => {
+ (layers = [], markerPosition, geocoder) => {
let newLayers = [...layers];
if ( markerPosition ) {
newLayers.push(MapInfoUtils.getMarkerLayer("GetFeatureInfo", markerPosition.latlng));
}
- if (geocoderPosition) {
- newLayers.push(MapInfoUtils.getMarkerLayer("GeoCoder", geocoderPosition, "marker",
+ if (geocoder && geocoder.markerPosition) {
+ newLayers.push(MapInfoUtils.getMarkerLayer("GeoCoder", geocoder.markerPosition, "marker",
{
overrideOLStyle: true,
style: {
@@ -37,7 +37,7 @@ const layerSelectorWithMarkers = createSelector(
popupAnchor: [1, -34],
shadowSize: [41, 41]
}
- }
+ }, geocoder.markerLabel
));
}
diff --git a/web/client/utils/CoordinatesUtils.js b/web/client/utils/CoordinatesUtils.js
index d25fe0e347..60a331d633 100644
--- a/web/client/utils/CoordinatesUtils.js
+++ b/web/client/utils/CoordinatesUtils.js
@@ -49,11 +49,20 @@ function determineCrs(crs) {
}
return crs;
}
+
+let crsLabels = {
+ "EPSG:4326": "WGS 84",
+ "EPSG:3857": "WGS 84 / Pseudo Mercator"
+};
+
/**
* Utilities for Coordinates conversion.
* @memberof utils
*/
const CoordinatesUtils = {
+ setCrsLabels(labels) {
+ crsLabels = assign({}, crsLabels, labels);
+ },
getUnits: function(projection) {
const proj = new Proj4js.Proj(projection);
return proj.units || 'degrees';
@@ -242,7 +251,7 @@ const CoordinatesUtils = {
let crsList = {};
for (let a in Proj4js.defs) {
if (Proj4js.defs.hasOwnProperty(a)) {
- crsList[a] = {label: a};
+ crsList[a] = {label: crsLabels[a] || a};
}
}
return crsList;
diff --git a/web/client/utils/FilterUtils.jsx b/web/client/utils/FilterUtils.jsx
index 9dac1ff7f0..bf4cfd00f6 100644
--- a/web/client/utils/FilterUtils.jsx
+++ b/web/client/utils/FilterUtils.jsx
@@ -359,6 +359,16 @@ const FilterUtils = {
}
return filter;
},
+ closePolygon: function(coords) {
+ if (coords.length >= 3) {
+ const first = coords[0];
+ const last = coords[coords.length - 1];
+ if ((first[0] !== last[0]) || (first[1] !== last[1])) {
+ return coords.concat([coords[0]]);
+ }
+ }
+ return coords;
+ },
getGmlPolygonElement: function(coordinates, srsName, version) {
let gmlPolygon = ' {
- let coords = element.map((coordinate) => {
+ let coords = this.closePolygon(element).map((coordinate) => {
return coordinate[0] + (version === "1.0.0" ? "," : " ") + coordinate[1];
});
const exterior = (version === "1.0.0" ? "outerBoundaryIs" : "exterior");
diff --git a/web/client/utils/MapInfoUtils.js b/web/client/utils/MapInfoUtils.js
index 0bf998f6ac..ac771fb8f4 100644
--- a/web/client/utils/MapInfoUtils.js
+++ b/web/client/utils/MapInfoUtils.js
@@ -76,12 +76,13 @@ const MapInfoUtils = {
}
];
},
- getMarkerLayer(name, clickedMapPoint, styleName, otherParams) {
+ getMarkerLayer(name, clickedMapPoint, styleName, otherParams, markerLabel) {
return {
type: 'vector',
visibility: true,
name: name || "GetFeatureInfo",
styleName: styleName || "marker",
+ label: markerLabel,
features: MapInfoUtils.clickedPointToGeoJson(clickedMapPoint),
...otherParams
};
diff --git a/web/client/utils/MapUtils.js b/web/client/utils/MapUtils.js
index f0d3d716f8..cac89587cf 100644
--- a/web/client/utils/MapUtils.js
+++ b/web/client/utils/MapUtils.js
@@ -266,6 +266,22 @@ function mapUpdated(oldMap, newMap) {
return !centersEqual || (newMap.zoom !== oldMap.zoom);
}
+/* Transform width and height specified in meters to the units of the specified projection */
+function transformExtent(projection, center, width, height) {
+ let units = CoordinatesUtils.getUnits(projection);
+ if (units === 'ft') {
+ return {width: width / METERS_PER_UNIT.ft, height: height / METERS_PER_UNIT.ft};
+ } else if (units === 'us-ft') {
+ return {width: width / METERS_PER_UNIT['us-ft'], height: height / METERS_PER_UNIT['us-ft']};
+ } else if (units === 'degrees') {
+ return {
+ width: width / (111132.92 - 559.82 * Math.cos(2 * center.y) + 1.175 * Math.cos(4 * center.y)),
+ height: height / (111412.84 * Math.cos(center.y) - 93.5 * Math.cos(3 * center.y))
+ };
+ }
+ return {width, height};
+}
+
module.exports = {
EXTENT_TO_ZOOM_HOOK,
RESOLUTIONS_HOOK,
@@ -290,5 +306,6 @@ module.exports = {
getScales,
getBbox,
mapUpdated,
- getCurrentResolution
+ getCurrentResolution,
+ transformExtent
};