diff --git a/app/demos/world/App.js b/app/demos/world/App.js index ed6d13ab..acb67cb1 100644 --- a/app/demos/world/App.js +++ b/app/demos/world/App.js @@ -11,7 +11,8 @@ import { TabbedPanelPage, BasemapContainer, VectorLayer, - DrawContainer + DrawContainer, + FeatureEditor } from '@bayer/ol-kit' import { fromLonLat } from 'ol/proj' import olFeature from 'ol/Feature' @@ -20,26 +21,33 @@ import olSourceVector from 'ol/source/Vector' import Welcome from '../../Welcome' +const labs = new olFeature({ + feature_type: ['1904Labs HQ'], + title: '1904Labs HQ', + name: '1904Labs HQ', + geometry: new olGeomPoint(fromLonLat([-90.24618, 38.636069])) +}) + class App extends React.Component { onMapInit = async (map) => { // create a vector layer and add to the map const layer = new VectorLayer({ title: '1904Labs HQ', source: new olSourceVector({ - features: [new olFeature({ - feature_type: ['1904Labs HQ'], - title: '1904Labs HQ', - name: '1904Labs HQ', - geometry: new olGeomPoint(fromLonLat([-90.24618, 38.636069])) - })] + features: [labs] }) }) + labs.set('_ol_kit_parent', layer) + map.addLayer(layer) const dataLayer = await loadDataLayer(map, 'https://data.nasa.gov/api/geospatial/7zbq-j77a?method=export&format=KML') - dataLayer.getSource().getFeatures().forEach(f => f.set('title', f.get('name'))) + dataLayer.getSource().getFeatures().forEach(f => { + f.set('_ol_kit_parent', dataLayer) + f.set('title', f.get('name')) + }) window.map = map } @@ -47,6 +55,7 @@ class App extends React.Component { render () { return ( + diff --git a/docs/BasemapBingMaps.html b/docs/BasemapBingMaps.html index 31f191d1..c3b738d0 100644 --- a/docs/BasemapBingMaps.html +++ b/docs/BasemapBingMaps.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapBlankWhite.html b/docs/BasemapBlankWhite.html index ef979599..5dca36b3 100644 --- a/docs/BasemapBlankWhite.html +++ b/docs/BasemapBlankWhite.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapContainer.html b/docs/BasemapContainer.html index 9fb02905..9f1089d2 100644 --- a/docs/BasemapContainer.html +++ b/docs/BasemapContainer.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapManager.html b/docs/BasemapManager.html index 6a0298d0..5baa6ebc 100644 --- a/docs/BasemapManager.html +++ b/docs/BasemapManager.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapOpenStreetMap.html b/docs/BasemapOpenStreetMap.html index d3c8b12f..b7e7356f 100644 --- a/docs/BasemapOpenStreetMap.html +++ b/docs/BasemapOpenStreetMap.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapStamenTerrain.html b/docs/BasemapStamenTerrain.html index 7b9b3c18..cd80fbbf 100644 --- a/docs/BasemapStamenTerrain.html +++ b/docs/BasemapStamenTerrain.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapStamenTonerDark.html b/docs/BasemapStamenTonerDark.html index 3904e4ac..a4ae1b23 100644 --- a/docs/BasemapStamenTonerDark.html +++ b/docs/BasemapStamenTonerDark.html @@ -65,7 +65,7 @@ diff --git a/docs/BasemapStamenTonerLite.html b/docs/BasemapStamenTonerLite.html index c40d0b20..e2ef8d58 100644 --- a/docs/BasemapStamenTonerLite.html +++ b/docs/BasemapStamenTonerLite.html @@ -65,7 +65,7 @@ diff --git a/docs/Basemaps_BasemapContainer.js.html b/docs/Basemaps_BasemapContainer.js.html index b1ef5ea3..31952819 100644 --- a/docs/Basemaps_BasemapContainer.js.html +++ b/docs/Basemaps_BasemapContainer.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_BasemapManager.js.html b/docs/Basemaps_BasemapManager.js.html index c5640a59..8b1a9613 100644 --- a/docs/Basemaps_BasemapManager.js.html +++ b/docs/Basemaps_BasemapManager.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_BingMaps.js.html b/docs/Basemaps_BingMaps.js.html index dcbc8222..21d4d93d 100644 --- a/docs/Basemaps_BingMaps.js.html +++ b/docs/Basemaps_BingMaps.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_BlankWhite.js.html b/docs/Basemaps_BlankWhite.js.html index d8a11bb9..b67fb6ca 100644 --- a/docs/Basemaps_BlankWhite.js.html +++ b/docs/Basemaps_BlankWhite.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_OpenStreetMap.js.html b/docs/Basemaps_OpenStreetMap.js.html index 56475cf7..a11354a5 100644 --- a/docs/Basemaps_OpenStreetMap.js.html +++ b/docs/Basemaps_OpenStreetMap.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_StamenTerrain.js.html b/docs/Basemaps_StamenTerrain.js.html index f8857823..d7825303 100644 --- a/docs/Basemaps_StamenTerrain.js.html +++ b/docs/Basemaps_StamenTerrain.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_StamenTonerDark.js.html b/docs/Basemaps_StamenTonerDark.js.html index d2cbcd90..9d726fc2 100644 --- a/docs/Basemaps_StamenTonerDark.js.html +++ b/docs/Basemaps_StamenTonerDark.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_StamenTonerLite.js.html b/docs/Basemaps_StamenTonerLite.js.html index ceb191fe..3e792c7d 100644 --- a/docs/Basemaps_StamenTonerLite.js.html +++ b/docs/Basemaps_StamenTonerLite.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Basemaps_utils.js.html b/docs/Basemaps_utils.js.html index 993033e2..17cc8655 100644 --- a/docs/Basemaps_utils.js.html +++ b/docs/Basemaps_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Compass.html b/docs/Compass.html index a522e7ba..d5bc157f 100644 --- a/docs/Compass.html +++ b/docs/Compass.html @@ -65,7 +65,7 @@ diff --git a/docs/ContextMenu.html b/docs/ContextMenu.html index 22bb7086..8fa8126c 100644 --- a/docs/ContextMenu.html +++ b/docs/ContextMenu.html @@ -65,7 +65,7 @@ diff --git a/docs/ContextMenuCoords.html b/docs/ContextMenuCoords.html index fa29634e..07ac6765 100644 --- a/docs/ContextMenuCoords.html +++ b/docs/ContextMenuCoords.html @@ -65,7 +65,7 @@ diff --git a/docs/ContextMenuListItem.html b/docs/ContextMenuListItem.html index b2af912b..00a1a9fc 100644 --- a/docs/ContextMenuListItem.html +++ b/docs/ContextMenuListItem.html @@ -65,7 +65,7 @@ diff --git a/docs/ContextMenu_ContextMenu.js.html b/docs/ContextMenu_ContextMenu.js.html index 50405345..18ff7ece 100644 --- a/docs/ContextMenu_ContextMenu.js.html +++ b/docs/ContextMenu_ContextMenu.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ContextMenu_ContextMenuCoords.js.html b/docs/ContextMenu_ContextMenuCoords.js.html index 8400c30d..fa427a66 100644 --- a/docs/ContextMenu_ContextMenuCoords.js.html +++ b/docs/ContextMenu_ContextMenuCoords.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ContextMenu_ContextMenuListItem.js.html b/docs/ContextMenu_ContextMenuListItem.js.html index b6cf77da..552cebe0 100644 --- a/docs/ContextMenu_ContextMenuListItem.js.html +++ b/docs/ContextMenu_ContextMenuListItem.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ControlGroup.html b/docs/ControlGroup.html index 51640d3a..1dad08b0 100644 --- a/docs/ControlGroup.html +++ b/docs/ControlGroup.html @@ -65,7 +65,7 @@ diff --git a/docs/ControlGroupButton.html b/docs/ControlGroupButton.html index 44309342..41fb2e30 100644 --- a/docs/ControlGroupButton.html +++ b/docs/ControlGroupButton.html @@ -65,7 +65,7 @@ diff --git a/docs/Controls.html b/docs/Controls.html index 94a46ce6..5231d0f6 100644 --- a/docs/Controls.html +++ b/docs/Controls.html @@ -65,7 +65,7 @@ diff --git a/docs/Controls_Compass.js.html b/docs/Controls_Compass.js.html index c7dc41a3..48038519 100644 --- a/docs/Controls_Compass.js.html +++ b/docs/Controls_Compass.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ControlGroup.js.html b/docs/Controls_ControlGroup.js.html index a42e8545..dd32fcce 100644 --- a/docs/Controls_ControlGroup.js.html +++ b/docs/Controls_ControlGroup.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ControlGroupButton.js.html b/docs/Controls_ControlGroupButton.js.html index c30f8e3f..b2d480b2 100644 --- a/docs/Controls_ControlGroupButton.js.html +++ b/docs/Controls_ControlGroupButton.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_Controls.js.html b/docs/Controls_Controls.js.html index ac7e9ef5..8fc50bba 100644 --- a/docs/Controls_Controls.js.html +++ b/docs/Controls_Controls.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_CurrentLocation.js.html b/docs/Controls_CurrentLocation.js.html index ab38d6ad..c60ee85a 100644 --- a/docs/Controls_CurrentLocation.js.html +++ b/docs/Controls_CurrentLocation.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ScaleLine.js.html b/docs/Controls_ScaleLine.js.html index a3144c43..937d066c 100644 --- a/docs/Controls_ScaleLine.js.html +++ b/docs/Controls_ScaleLine.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ZoomControls.js.html b/docs/Controls_ZoomControls.js.html index d79a7215..21602954 100644 --- a/docs/Controls_ZoomControls.js.html +++ b/docs/Controls_ZoomControls.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ZoomIn.js.html b/docs/Controls_ZoomIn.js.html index fed6cd68..213cb308 100644 --- a/docs/Controls_ZoomIn.js.html +++ b/docs/Controls_ZoomIn.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Controls_ZoomOut.js.html b/docs/Controls_ZoomOut.js.html index 3a9fcd50..7722c049 100644 --- a/docs/Controls_ZoomOut.js.html +++ b/docs/Controls_ZoomOut.js.html @@ -67,7 +67,7 @@ diff --git a/docs/CurrentLocation.html b/docs/CurrentLocation.html index d2cfc407..23ac1a85 100644 --- a/docs/CurrentLocation.html +++ b/docs/CurrentLocation.html @@ -65,7 +65,7 @@ diff --git a/docs/DataLayers_utils.js.html b/docs/DataLayers_utils.js.html index 124a84b5..d54cd842 100644 --- a/docs/DataLayers_utils.js.html +++ b/docs/DataLayers_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw.html b/docs/Draw.html index 9b54db5f..6c719465 100644 --- a/docs/Draw.html +++ b/docs/Draw.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawBox.html b/docs/DrawBox.html index 8fbacbf6..bbfa8fd0 100644 --- a/docs/DrawBox.html +++ b/docs/DrawBox.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawCircle.html b/docs/DrawCircle.html index 82e4ccf9..532b37c3 100644 --- a/docs/DrawCircle.html +++ b/docs/DrawCircle.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawContainer.html b/docs/DrawContainer.html index 0051ebf7..879db84f 100644 --- a/docs/DrawContainer.html +++ b/docs/DrawContainer.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawFreehand.html b/docs/DrawFreehand.html index 17898443..cee37354 100644 --- a/docs/DrawFreehand.html +++ b/docs/DrawFreehand.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawLine.html b/docs/DrawLine.html index 803af447..5bf69a75 100644 --- a/docs/DrawLine.html +++ b/docs/DrawLine.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawPin.html b/docs/DrawPin.html index b549e912..3baeb466 100644 --- a/docs/DrawPin.html +++ b/docs/DrawPin.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawPoint.html b/docs/DrawPoint.html index bb4c371c..4adc3bcd 100644 --- a/docs/DrawPoint.html +++ b/docs/DrawPoint.html @@ -65,7 +65,7 @@ diff --git a/docs/DrawPolygon.html b/docs/DrawPolygon.html index 61b49248..c9021b32 100644 --- a/docs/DrawPolygon.html +++ b/docs/DrawPolygon.html @@ -65,7 +65,7 @@ diff --git a/docs/Draw_Box.js.html b/docs/Draw_Box.js.html index 483b7edd..f4c9fbcc 100644 --- a/docs/Draw_Box.js.html +++ b/docs/Draw_Box.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Circle.js.html b/docs/Draw_Circle.js.html index e832a534..7643ce5c 100644 --- a/docs/Draw_Circle.js.html +++ b/docs/Draw_Circle.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Draw.js.html b/docs/Draw_Draw.js.html index be35f2a9..e2c4ff18 100644 --- a/docs/Draw_Draw.js.html +++ b/docs/Draw_Draw.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_DrawContainer.js.html b/docs/Draw_DrawContainer.js.html index 883b618c..fab59fae 100644 --- a/docs/Draw_DrawContainer.js.html +++ b/docs/Draw_DrawContainer.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Freehand.js.html b/docs/Draw_Freehand.js.html index 2d73c08c..5159925d 100644 --- a/docs/Draw_Freehand.js.html +++ b/docs/Draw_Freehand.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Line.js.html b/docs/Draw_Line.js.html index 5cad416d..ecf204de 100644 --- a/docs/Draw_Line.js.html +++ b/docs/Draw_Line.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Pin.js.html b/docs/Draw_Pin.js.html index 05d8a470..734257d8 100644 --- a/docs/Draw_Pin.js.html +++ b/docs/Draw_Pin.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Point.js.html b/docs/Draw_Point.js.html index df6c75a6..2d864d1c 100644 --- a/docs/Draw_Point.js.html +++ b/docs/Draw_Point.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_Polygon.js.html b/docs/Draw_Polygon.js.html index fb1a1817..1d2f830b 100644 --- a/docs/Draw_Polygon.js.html +++ b/docs/Draw_Polygon.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Draw_utils.js.html b/docs/Draw_utils.js.html index a7e6f56a..8cfa57ec 100644 --- a/docs/Draw_utils.js.html +++ b/docs/Draw_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/FeatureEditor.html b/docs/FeatureEditor.html new file mode 100644 index 00000000..55862a38 --- /dev/null +++ b/docs/FeatureEditor.html @@ -0,0 +1,495 @@ + + + + + ol-kit | FeatureEditor + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+ +
+
+ + + + + +
+ +
+ +

+ <FeatureEditor /> +

+ +
A component to edit geometries
+ + +
+ +
+
+ + +
+

Constructor

+
+
+ + + +

+ # + + + + <FeatureEditor editOpts editFeature addEditFeatureToContext map onEditBegin onEditFinish onEditCancel editStyle areaUOM distanceUOM translations /> + + +

+ + + + + + + + + + + + +
PropTypes:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeRequiredDescriptionDefault
editOpts + exact + No{}
editFeature + object + No
addEditFeatureToContext + func + No
map + object + No
onEditBegin + func + No(feature) => { + feature.setStyle(new olStyleStyle({})) +}
onEditFinish + func + No(feature, updatedFeature, addEditFeatureToContext, style) => { + const geom = updatedFeature.getGeometry() + + if (!feature) return + + feature.setGeometry(geom) + + feature.setStyle(style || null) // restore the original feature's style + addEditFeatureToContext(null) +}
onEditCancel + func + No(feature, addEditFeatureToContext, style) => { + feature.setStyle(style || null) // restore the original feature's style + addEditFeatureToContext(null) +}
editStyle + union + No(feature, map, showMeasurements = false, { areaUOM, distanceUOM }, translations) => { // eslint-disable-line + return immediateEditStyle( + { areaUOM, distanceUOM, showMeasurements, map, translations, language: navigator.language }, + feature, + map.getView().getResolution() + ) +}
areaUOM + string + No
distanceUOM + string + No
translations + object + No
+ + + + + + + + +
+ + + + +
Since:
+
  • 1.16.0
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + FeatureEditor/FeatureEditor.js, line 60 + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
+
+ + +
+ + + + + + + + + + + + + + + + +
+

Members

+
+ +
+ +

+ # + + + _renderFeature + + +

+ + + + +
+ In the past we've used temporary layers added to the map to avoid modifying the original features directly. + However, this leads to a lot of cleanup since those layers need to be added/removed from both the map and state. + That is fine as long as everything goes smoothly but if there is additional logic listening to the map for changes to it's features + or layers than we can get left in a broken state that requires a reload. Using vectorContext.drawFeature instead of + a layer added to the map alleviates at least some of this risk. +
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + FeatureEditor/FeatureEditor.js, line 77 + +

+ +
+ + + + + +
+ +
+
+ + + + + + + + + + +
+ +
+ + + + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/docs/FeatureEditor_FeatureEditor.js.html b/docs/FeatureEditor_FeatureEditor.js.html new file mode 100644 index 00000000..53c0389d --- /dev/null +++ b/docs/FeatureEditor_FeatureEditor.js.html @@ -0,0 +1,446 @@ + + + + + + + ol-kit | FeatureEditor/FeatureEditor.js + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+ +
+
+ + + + + +
+
+
import React, { Component } from 'react'
+import PropTypes from 'prop-types'
+import Translate from 'classes/Translate'
+import { immediateEditStyle } from './styles'
+import { getVectorContext } from 'ol/render'
+import { Toolbar } from 'Toolbar'
+import { Knob } from 'react-rotary-knob'
+import { connectToContext } from 'Provider'
+import { Card, Grid, CardActions, Button, FormControlLabel } from '@material-ui/core'
+import { withStyles } from '@material-ui/core/styles'
+import centroid from '@turf/centroid'
+import { olKitTurf } from './utils'
+
+import olInteractionModify from 'ol/interaction/Modify'
+import olCollection from 'ol/Collection'
+import olStyleStyle from 'ol/style/Style'
+
+const ButtonCardActions = withStyles(() => ({
+  root: {
+    padding: '4px 4px 3px 4px'
+  }
+}))(CardActions)
+
+const LeftCard = withStyles(() => ({
+  root: {
+    borderTopLeftRadius: '4px',
+    borderBottomLeftRadius: '4px',
+    borderBottomRightRadius: '0px',
+    borderTopRightRadius: '0px',
+    height: '38px'
+  }
+}))(Card)
+
+const CenterCard = withStyles(() => ({
+  root: {
+    borderRadius: '0px',
+    paddingLeft: '20px',
+    marginLeft: '0px',
+    height: '38px'
+  }
+}))(Card)
+
+const RightCard = withStyles(() => ({
+  root: {
+    borderTopRightRadius: '4px',
+    borderBottomRightRadius: '4px',
+    borderTopLeftRadius: '0px',
+    borderBottomLeftRadius: '0px',
+    marginLeft: '0px !important',
+    height: '38px'
+  }
+}))(Card)
+
+/**
+ * A component to edit geometries
+ * @component
+ * @category FeatureEditor
+ * @since 1.16.0
+ */
+class FeatureEditor extends Component {
+  constructor (props) {
+    super(props)
+    this.state = {
+      interactions: [],
+      editingFeature: null,
+      showMeasurements: false,
+      rotation: 0,
+      style: null
+    }
+  }
+
+  /** In the past we've used temporary layers added to the map to avoid modifying the original features directly.
+  However, this leads to a lot of cleanup since those layers need to be added/removed from both the map and state.
+  That is fine as long as everything goes smoothly but if there is additional logic listening to the map for changes to it's features
+   or layers than we can get left in a broken state that requires a reload.  Using vectorContext.drawFeature instead of
+   a layer added to the map alleviates at least some of this risk.*/
+  _renderFeature = (vectorContext, feature, editStyle = this.props.editStyle) => { // vectorContext.drawFeature only respects a style object and since it is common to have style functions and arrays in Openlayers we need to break the other formats down into objects
+    const { map, areaUOM, distanceUOM, translations } = this.props
+    const { showMeasurements } = this.state
+    const measurementStyles = showMeasurements ? editStyle(feature, map, showMeasurements, { areaUOM, distanceUOM }, translations) : editStyle // eslint-disable-line
+    const styleType = Array.isArray(measurementStyles) ? 'array' : typeof editStyle
+
+    try {
+      switch (styleType) {
+        case 'array':
+          measurementStyles.map(style => {
+            const geom = style.getGeometry() ? style.getGeometry() : feature.getGeometry()
+
+            vectorContext.setStyle(style)
+            vectorContext.drawGeometry(geom)
+          }) // Arrays of style objects require a feature to be drawn for each style object in the array.  This could also be recursively called but that would add extra complexity.
+          break
+        case 'function':
+          this._renderFeature(
+            vectorContext,
+            feature,
+            editStyle(feature, map, showMeasurements, { areaUOM, distanceUOM }, translations)
+          ) // Openlayers style functions return style objects or arrays of style objects so we can call functions recursively.
+          break
+        default: // style object
+          vectorContext.drawFeature(feature, editStyle)
+      }
+    } catch (e) {
+      console.warn(`Geokit was unable to draw the features to the map, this is most likely due to an invalid style object: ${e.message}`, e) // eslint-disable-line
+    }
+  }
+
+  _renderEditOverlay = (e) => {
+    const { map, editStyle } = this.props
+    const { editingFeature } = this.state
+
+    const vectorContext = getVectorContext(e)
+
+    if (!editingFeature) return // to avoid using a setStateCallback we just check for editFeatures first
+
+    this._renderFeature(vectorContext, editingFeature, editStyle)
+
+    return map.render() // render the results asynchronously
+  }
+
+  _addPostComposeListener = () => {
+    const { editingFeature } = this.state
+
+    editingFeature?.get('_ol_kit_parent')?.on('postrender', this._renderEditOverlay) // eslint-disable-line
+  }
+
+  _removePostComposeListener = () => {
+    const { editingFeature } = this.state
+
+    editingFeature?.get('_ol_kit_parent')?.un('postrender', this._renderEditOverlay) // eslint-disable-line
+  }
+
+  _end = () => { // this function cleans up our state and map.  If this does not execute correctly we could get stuck in a corrupted map state.
+    try {
+      const { map } = this.props
+      const { interactions } = this.state
+
+      this._removePostComposeListener()
+
+      interactions.forEach(i => map.removeInteraction(i))
+      this.setState({ editingFeature: null, style: null, interactions: [] })
+    } catch (err) {
+      console.warn(`Geokit encountered a problem while editing a feature: ${err.message}. \n`, err) // eslint-disable-line no-console
+    }
+  }
+
+  showMeasurements = () => {
+    this.setState({ showMeasurements: !this.state.showMeasurements })
+  }
+
+  cancelEdit = () => {
+    const { onEditCancel, editFeature, addEditFeatureToContext } = this.props
+    const { style } = this.state
+
+    this.setState(
+      { canceled: true, editingFeature: null },
+      () => onEditCancel(editFeature, addEditFeatureToContext, style)
+    )
+  }
+
+  finishEdit = () => {
+    const { onEditFinish, addEditFeatureToContext, editFeature } = this.props
+    const { editingFeature, style } = this.state
+
+    this.setState(
+      { editingFeature: null },
+      () => onEditFinish(editFeature, editingFeature, addEditFeatureToContext, style)
+    )
+  }
+
+  init () {
+    const { editOpts, map, onEditBegin, editFeature } = this.props
+
+    const clonedFeature = editFeature.clone() // create a collection of clones of the features in props, this avoids modifying the existing features
+
+    const opts = Object.assign({}, editOpts, {
+      pixelTolerance: 10,
+      features: new olCollection([clonedFeature]),
+      deleteCondition: ({ originalEvent, type }) => {
+        const { altKey, ctrlKey, shiftKey, metaKey } = originalEvent
+        const modifierKeyActive = altKey || ctrlKey || shiftKey || metaKey
+        const altClick = type === 'click' && modifierKeyActive
+        const rightClick = (type === 'pointerdown' || type === 'click') && originalEvent.button === 2
+
+        return rightClick || altClick
+      }
+    })
+
+    const translateInteraction = new Translate({ features: new olCollection([clonedFeature]) }) // ol/interaction/translate only checks for features on the map and since we are not adding these to the map (see additional comments) we use our own that knows to look for the features we pass to it whether or not they're on the map.
+    const modifyInteraction = new olInteractionModify(opts) // ol/interaction/modify doesn't care about the features being on the map or not so it's good to go
+    const style = clonedFeature.getStyle()
+
+    this.setState({
+      anchor: olKitTurf(centroid, [clonedFeature.getGeometry()]).getGeometry().getCoordinates(),
+      interactions: [modifyInteraction, translateInteraction],
+      editingFeature: clonedFeature,
+      style
+    }, () => {
+      this._addPostComposeListener()
+    })
+    map.addInteraction(translateInteraction)
+    map.addInteraction(modifyInteraction)
+    onEditBegin(clonedFeature) // callback function for IAs.  FeatureEditor doesn't do anything to the original features so we tell the IA which features they passed in as props and what features we are editing.  This should help if they want to add custom logic around these features.
+  }
+
+  componentDidMount () {
+    if (!this.props.editFeature) return
+
+    this.init()
+  }
+
+  componentDidUpdate (prevProps) {
+    const { editFeature } = this.props
+    const { interactions } = this.state
+
+    if (prevProps.editFeature && !editFeature) return this._end()
+
+    if (interactions.length === 2 || !editFeature) return
+
+    this.init()
+  }
+
+  componentWillUnmount = () => {
+    const { editingFeature } = this.state
+
+    if (editingFeature) console.warn(`Geokit FeatureEditor has been unmounted unexpectedly.  This may lead undesirable behaviour in your application.`) // eslint-disable-line no-console
+
+    return this._end()
+  }
+
+  rotate = (val) => {
+    const { editingFeature, rotation, anchor } = this.state
+    const geometry = editingFeature.getGeometry()
+    const rotationDiff = val - rotation
+
+    this.setState({ rotation: val }, () => geometry.rotate(-rotationDiff * (Math.PI / 180), anchor))
+  }
+
+  render () {
+    const { translations } = this.props
+    const { editingFeature } = this.state
+    const knobStyle = {
+      width: '35px',
+      height: '35px',
+      padding: '2px'
+    }
+
+    if (!editingFeature) return null
+
+    return (
+      <Toolbar>
+        <Grid item>
+          <ButtonCardActions>
+            <LeftCard>
+              <Button color='secondary' onClick={this.cancelEdit}>
+                {translations['_ol_kit.edit.cancel']}
+              </Button>
+            </LeftCard>
+            <CenterCard style={{ paddingLeft: '20px', marginLeft: '0px' }}>
+              <FormControlLabel
+                style={{ marginBottom: '0px' }}
+                control={
+                  <Knob style={knobStyle} unlockDistance={0} defaultValue={0} max={360} onChange={this.rotate} />
+                }
+                label={translations['_ol_kit.edit.rotate']}
+              />
+            </CenterCard>
+            <RightCard>
+              <Button color='primary' onClick={this.finishEdit}>
+                {translations['_ol_kit.edit.finish']}
+              </Button>
+            </RightCard>
+          </ButtonCardActions>
+        </Grid>
+      </Toolbar>
+    )
+  }
+}
+
+FeatureEditor.propTypes = {
+  editOpts: PropTypes.exact({
+    condition: PropTypes.string,
+    deleteCondition: PropTypes.string,
+    insertVertexCondition: PropTypes.string,
+    pixelTolerance: PropTypes.number,
+    style: PropTypes.object,
+    source: PropTypes.object,
+    wrapX: PropTypes.bool
+  }).isRequired,
+  editFeature: PropTypes.object,
+  addEditFeatureToContext: PropTypes.func,
+  map: PropTypes.object,
+  onEditBegin: PropTypes.func,
+  onEditFinish: PropTypes.func,
+  onEditCancel: PropTypes.func,
+  editStyle: PropTypes.oneOfType([
+    PropTypes.func,
+    PropTypes.object,
+    PropTypes.array
+  ]),
+  areaUOM: PropTypes.string,
+  distanceUOM: PropTypes.string,
+  translations: PropTypes.object
+}
+
+FeatureEditor.defaultProps = {
+  editOpts: {},
+  onEditFinish: (feature, updatedFeature, addEditFeatureToContext, style) => {
+    const geom = updatedFeature.getGeometry()
+
+    if (!feature) return
+
+    feature.setGeometry(geom)
+
+    feature.setStyle(style || null) // restore the original feature's style
+    addEditFeatureToContext(null)
+  },
+  onEditBegin: (feature) => {
+    feature.setStyle(new olStyleStyle({}))
+  },
+  onEditCancel: (feature, addEditFeatureToContext, style) => {
+    feature.setStyle(style || null) // restore the original feature's style
+    addEditFeatureToContext(null)
+  },
+  editStyle: (feature, map, showMeasurements = false, { areaUOM, distanceUOM }, translations) => { // eslint-disable-line
+    return immediateEditStyle(
+      { areaUOM, distanceUOM, showMeasurements, map, translations, language: navigator.language },
+      feature,
+      map.getView().getResolution()
+    )
+  }
+}
+
+export default connectToContext(FeatureEditor)
+
+
+
+ + + + +
+
+
+
+ + + + + + + + diff --git a/docs/FeatureEditor_styles.js.html b/docs/FeatureEditor_styles.js.html new file mode 100644 index 00000000..f898b3c0 --- /dev/null +++ b/docs/FeatureEditor_styles.js.html @@ -0,0 +1,450 @@ + + + + + + + ol-kit | FeatureEditor/styles.js + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+ +
+
+ + + + + +
+
+
import olFeature from 'ol/Feature'
+import olGeomLineString from 'ol/geom/LineString'
+import olGeomMultiPoint from 'ol/geom/MultiPoint'
+import olStyleFill from 'ol/style/Fill'
+import olStyleStroke from 'ol/style/Stroke'
+import olStyleStyle from 'ol/style/Style'
+import olStyleCircle from 'ol/style/Circle'
+import olStyleText from 'ol/style/Text'
+import olPoint from 'ol/geom/Point'
+import centroid from '@turf/centroid'
+
+import { pointsFromVertices, olKitTurf, pairCoordinates, getCoordinates, getText, calculateScale } from './utils'
+
+function resolveStyleFunctionArgs (args) {
+  // Using function.prototype.bind with additional arguments injects those arguments at the zeroth index of the arguements object and since opts is optional we need to handle a variable arguement object
+  const argLength = args.length
+  const feature = args[argLength - 2] || args
+  const resolution = args[argLength - 1]
+  const opts = argLength >= 3 ? args[0] : {}
+
+  return { feature, resolution, opts }
+}
+
+/**
+ * Style ol/Features
+ * @function
+ * @param {object} opts - The config object
+ * @param {ol/Feature} feature - The feature you want to style
+ * @param {number} resolution - the resolution of the map
+ * @returns {object} The style object for the passed feature
+ */
+function styleText (...args) {
+  const { feature, resolution, opts } = resolveStyleFunctionArgs(args)
+  const label = feature.get('_vmf_label')
+  const isMeasurement = feature.get('_vmf_type') === '_vmf_measurement'
+  const isCentroidLabel = feature.get('_ol_kit_needs_centroid_label')
+  const offsetY = isCentroidLabel ? 20 : isMeasurement ? 0 : (label.fontSize || 16) / 2 // eslint-disable-line
+  const styleOpts = {
+    placement: opts.placement || 'point',
+    textAlign: opts.textAlign,
+    textBaseline: opts.textBaseLine || 'top',
+    maxAngle: opts.maxAngle || Infinity,
+    // These weird calculations provide the most accurate placement relative to the textarea where people edit their text
+    offsetX: isMeasurement ? 0 : (label.fontSize || 16) / 2,
+    offsetY: offsetY,
+    rotation: -feature.get('_vmf_rotation') || 0,
+    font: `bold ${label.fontSize || 16}px sans-serif`,
+    stroke: new olStyleStroke({
+      // show a black outline unless the text color is black; then show a white outline
+      color: label.color === '#000000' ? '#ffffff' : '#000000',
+      width: 3
+    }),
+    text: getText(label, resolution, opts),
+    scale: calculateScale(opts.store.map, feature),
+    fill: new olStyleFill({
+      color: label.color || '#ffffff'
+    }),
+    image: new olStyleCircle({
+      radius: 7,
+      fill: new olStyleFill({
+        color: '#ffcc33'
+      })
+    }),
+    rotateWithView: false,
+    overflow: true
+  }
+
+  return new olStyleText(styleOpts)
+}
+
+function coordinateLabels (multiPoint, resolution, opts) {
+  return multiPoint.getPoints().map(point => coordinateLabel(point, resolution, opts))
+}
+
+function coordinateLabel (pointGeometry, resolution, opts) {
+  const geom = pointGeometry.clone()
+  const pointFeature = new olFeature({
+    geometry: geom,
+    _vmf_label: {
+      fontSize: 16
+    }
+  })
+
+  return new olStyleStyle({
+    text: styleText({
+      store: opts,
+      placement: 'point',
+      maxAngle: Math.PI / 4,
+      textAlign: undefined,
+      textBaseline: 'hanging'
+    }, pointFeature, resolution),
+    geometry: geom
+  })
+}
+
+function lengthLabel (lineGeometry, resolution, opts) {
+  const geom = lineGeometry.clone()
+  const perimeterFeature = new olFeature({
+    geometry: geom,
+    _vmf_type: '_vmf_measurement',
+    _vmf_label: {
+      fontSize: 16
+    }
+  })
+
+  return new olStyleStyle({
+    text: styleText({
+      store: opts,
+      placement: 'line',
+      maxAngle: Math.PI / 4,
+      textAlign: undefined,
+      textBaseline: 'hanging'
+    }, perimeterFeature, resolution),
+    geometry: geom
+  })
+}
+
+function perimeterSegmentLabels (polygonGeometry, resolution, opts) {
+  const labelStyles = []
+  const clonedGeom = polygonGeometry.clone()
+  const perimeterCoords = clonedGeom.getLinearRing(0).getCoordinates()
+
+  for (let i = 0; i < perimeterCoords.length - 1;) {
+    const segment = [perimeterCoords[i], perimeterCoords[i += 1]]
+
+    if (segment.flat(Infinity).includes(undefined)) break // exit the loop if we get any undefined values.  It's better to not label a segment than to break draw entirely.
+    const segmentGeom = new olGeomLineString(segment)
+    const segmentFeature = new olFeature({
+      geometry: segmentGeom,
+      _vmf_type: '_vmf_measurement',
+      _vmf_label: {
+        fontSize: 16
+      }
+    })
+
+    labelStyles.push(new olStyleStyle({
+      text: styleText({
+        store: opts,
+        placement: 'line',
+        textBaseline: 'hanging'
+      }, segmentFeature, resolution),
+      geometry: segmentGeom
+    }))
+  }
+
+  return labelStyles
+}
+
+function areaLabel (polygonGeometry, resolution, opts) {
+  const areaGeometry = polygonGeometry.clone()
+  const areaFeature = new olFeature({
+    geometry: areaGeometry,
+    _vmf_type: '_vmf_measurement',
+    _vmf_label: {
+      fontSize: 16
+    }
+  })
+
+  return new olStyleStyle({
+    text: styleText({
+      store: opts
+    }, areaFeature, resolution),
+    geometry: areaGeometry
+  })
+}
+
+function centroidLabel (geometry, resolution, opts) {
+  const point = geometry.getType() === 'Circle ' ? new olPoint(geometry.clone().getCenter()) : olKitTurf(centroid, [geometry]).getGeometry()
+  const pointFeature = new olFeature({
+    geometry: point,
+    _vmf_type: '_vmf_measurement', // styleText determines the type of label to render based on the feature's type so we need this temporary feature to be a 'measurement' feature
+    _vmf_label: {
+      fontSize: 16
+    },
+    _ol_kit_needs_centroid_label: true
+  })
+
+  return new olStyleStyle({
+    text: styleText({
+      store: opts,
+      placement: 'point',
+      maxAngle: Math.PI / 4,
+      textAlign: undefined,
+      textBaseline: 'hanging'
+    }, pointFeature, resolution),
+    geometry: point
+  })
+}
+
+function getVertices (args) {
+  const { feature } = resolveStyleFunctionArgs(args)
+  const geometry = feature.getGeometry()
+  const layout = geometry.getLayout()
+
+  switch (geometry.getType()) {
+    case 'MultiPolygon': {
+      const coordinates = geometry.getCoordinates()
+      const flatCoords = coordinates.flat(Infinity)
+      const pairedCoords = pairCoordinates(flatCoords, layout.length)
+
+      return new olGeomMultiPoint(pairedCoords, layout)
+    }
+    case 'GeometryCollection': {
+      const deepCoords = getCoordinates(geometry)
+      const flatCoords = deepCoords.flat(Infinity)
+      const pairedCoords = pairCoordinates(flatCoords, layout.length)
+
+      return new olGeomMultiPoint(pairedCoords)
+    }
+    default:
+      return new olGeomMultiPoint(pointsFromVertices(geometry))
+  }
+}
+
+/**
+ * Style an ol/Feature with orange circle vertices, a blue outline, an area label, and a perimeter length label. Can be used with individual features as a style function or call it directly to get a style object for use with `vectorContext.drawFeature`
+ * @function
+ * @param {object} opts - The config object
+ * @param {ol/Feature} feature - The feature you want to style
+ * @param {number} resolution - the resolution of the map
+ * @returns {object} The style object for the passed feature
+ */
+export function immediateEditStyle (...args) {
+  const { feature, resolution, opts = {} } = resolveStyleFunctionArgs(args)
+  const fill = new olStyleFill({
+    color: 'rgba(0, 0, 255, 0.2)'
+  })
+  const stroke = new olStyleStroke({
+    color: 'blue',
+    width: 3
+  })
+  const image = new olStyleCircle({
+    radius: 7,
+    fill: new olStyleFill({
+      color: '#ffcc33'
+    })
+  })
+  const vertexGeometry = getVertices(args)
+  const vertices = [new olStyleStyle({
+    image: new olStyleCircle({
+      radius: 5,
+      fill: new olStyleFill({
+        color: 'orange'
+      })
+    }),
+    geometry: vertexGeometry
+  })]
+
+  // checking to see if opts.geometry is defined allows us to call this function recursively for geometry collections
+  const geometry = opts.geometry || feature.clone().getGeometry()
+  const areaLabelsFlag = feature.get('_ol_kit_area_labels')
+  const distanceLabelsFlag = feature.get('_ol_kit_distance_labels')
+  const needsVertexLabels = feature.get('_ol_kit_coordinate_labels') !== undefined
+  const needsCentroidLabels = feature.get('_ol_kit_needs_centroid_label') !== undefined
+  const needsAreaLabels = opts.showMeasurements && areaLabelsFlag
+  const needsDistanceLabels = opts.showMeasurements && distanceLabelsFlag
+  const isNotCircle = feature.get('_ol_kit_draw_mode') !== 'circle'
+  const vertexLabels = (needsVertexLabels && opts.showMeasurements && isNotCircle) ? coordinateLabels(getVertices(feature), resolution, opts) : [] // eslint-disable-line
+  const type = geometry.getType()
+
+  switch (type) {
+    case 'Point':
+      return [new olStyleStyle({
+        image
+      }), ...vertexLabels]
+    case 'LineString': {
+      const lengthLabels = needsDistanceLabels ? [lengthLabel(geometry, resolution, opts)] : []
+
+      return [new olStyleStyle({
+        stroke,
+        image
+      }), ...vertices, ...lengthLabels, ...vertexLabels]
+    }
+    case 'MultiLineString': {
+      const lineStrings = geometry.getLineStrings()
+      const lengthLabels = needsDistanceLabels
+        ? lineStrings.map(lineString => lengthLabel(lineString, resolution, opts)) : []
+
+      return [new olStyleStyle({
+        stroke,
+        image
+      }), ...vertices, ...lengthLabels, ...vertexLabels]
+    }
+    case 'MultiPolygon': {
+      const polygons = geometry.getPolygons()
+      // create a label for each polygon
+      const perimeterLabels = needsDistanceLabels
+        ? polygons.map(polygon => perimeterSegmentLabels(polygon, resolution, opts)) : []
+      const areaLabels = needsAreaLabels
+        ? polygons.map(polygon => areaLabel(polygon, resolution, opts)) : []
+
+      return [new olStyleStyle({
+        stroke,
+        fill,
+        image
+      }), ...vertices, ...areaLabels, ...perimeterLabels.flat(Infinity), ...vertexLabels]
+    }
+    case 'Polygon': {
+      let labels = vertexLabels // eslint-disable-line
+
+      if (needsAreaLabels) labels.push(areaLabel(geometry, resolution, opts))
+      if (needsDistanceLabels && isNotCircle) labels = [...labels, ...perimeterSegmentLabels(geometry, resolution, opts)] //eslint-disable-line
+      if (needsCentroidLabels) labels.push(centroidLabel(geometry, resolution, opts))
+
+      return [new olStyleStyle({
+        stroke,
+        fill,
+        image
+      }), ...vertices, ...labels]
+    }
+    case 'GeometryCollection': {
+      // Recursive.  Since feature stores our metadata it needs to be preserved.  Therefore we pass the geometry we want to use through the opts object.
+      const componentStyles = geometry.getGeometries().map(geom => {
+        return immediateEditStyle.apply(this, [Object.assign(opts, { geometry: geom }), feature, resolution])
+      })
+      const flatStyles = componentStyles.flat(Infinity)
+
+      return flatStyles
+    }
+    case 'Circle': {
+      const labels = vertexLabels
+
+      if (needsAreaLabels) labels.push(areaLabel(geometry, resolution, opts))
+      if (needsCentroidLabels) labels.push(centroidLabel(geometry, resolution, opts))
+
+      return [new olStyleStyle({
+        stroke,
+        fill
+      }), ...labels]
+    }
+    default:
+      return [new olStyleStyle({
+        stroke,
+        fill,
+        image
+      }), ...vertices]
+  }
+}
+
+
+
+ + + + +
+
+
+
+ + + + + + + + diff --git a/docs/GoogleDirections.html b/docs/GoogleDirections.html index c180924d..f0ffc516 100644 --- a/docs/GoogleDirections.html +++ b/docs/GoogleDirections.html @@ -65,7 +65,7 @@ diff --git a/docs/GoogleDirections_GoogleDirections.js.html b/docs/GoogleDirections_GoogleDirections.js.html index 967630d5..7a94de2d 100644 --- a/docs/GoogleDirections_GoogleDirections.js.html +++ b/docs/GoogleDirections_GoogleDirections.js.html @@ -67,7 +67,7 @@ diff --git a/docs/GooglePlacesSearch.html b/docs/GooglePlacesSearch.html index 42409efa..4633b12c 100644 --- a/docs/GooglePlacesSearch.html +++ b/docs/GooglePlacesSearch.html @@ -65,7 +65,7 @@ diff --git a/docs/GooglePlacesSearch_GooglePlacesSearch.js.html b/docs/GooglePlacesSearch_GooglePlacesSearch.js.html index 559c7edc..159c4bd3 100644 --- a/docs/GooglePlacesSearch_GooglePlacesSearch.js.html +++ b/docs/GooglePlacesSearch_GooglePlacesSearch.js.html @@ -67,7 +67,7 @@ diff --git a/docs/HeatmapControls.html b/docs/HeatmapControls.html index ebe8142b..08f3a8db 100644 --- a/docs/HeatmapControls.html +++ b/docs/HeatmapControls.html @@ -65,7 +65,7 @@ diff --git a/docs/Heatmap_HeatmapControls.js.html b/docs/Heatmap_HeatmapControls.js.html index afce39ed..48a75522 100644 --- a/docs/Heatmap_HeatmapControls.js.html +++ b/docs/Heatmap_HeatmapControls.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ImageExif_ImageExif.js.html b/docs/ImageExif_ImageExif.js.html index 442b4b8b..a093396f 100644 --- a/docs/ImageExif_ImageExif.js.html +++ b/docs/ImageExif_ImageExif.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel.html b/docs/LayerPanel.html index deda92a0..2b101065 100644 --- a/docs/LayerPanel.html +++ b/docs/LayerPanel.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionDuplicate.html b/docs/LayerPanelActionDuplicate.html index 76f5e81f..39d05572 100644 --- a/docs/LayerPanelActionDuplicate.html +++ b/docs/LayerPanelActionDuplicate.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionExport.html b/docs/LayerPanelActionExport.html index c368c79d..6bf69283 100644 --- a/docs/LayerPanelActionExport.html +++ b/docs/LayerPanelActionExport.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionExtent.html b/docs/LayerPanelActionExtent.html index f2e77851..4ac5a093 100644 --- a/docs/LayerPanelActionExtent.html +++ b/docs/LayerPanelActionExtent.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionHeatmap.html b/docs/LayerPanelActionHeatmap.html index 05e5ddf1..bdf94e23 100644 --- a/docs/LayerPanelActionHeatmap.html +++ b/docs/LayerPanelActionHeatmap.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionImport.html b/docs/LayerPanelActionImport.html index c2b40d93..8a9d3029 100644 --- a/docs/LayerPanelActionImport.html +++ b/docs/LayerPanelActionImport.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionMerge.html b/docs/LayerPanelActionMerge.html index 06849c48..a9ae92c6 100644 --- a/docs/LayerPanelActionMerge.html +++ b/docs/LayerPanelActionMerge.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionOpacity.html b/docs/LayerPanelActionOpacity.html index 265fbb97..b42e00c6 100644 --- a/docs/LayerPanelActionOpacity.html +++ b/docs/LayerPanelActionOpacity.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActionRemove.html b/docs/LayerPanelActionRemove.html index c52a173f..0ae48ecf 100644 --- a/docs/LayerPanelActionRemove.html +++ b/docs/LayerPanelActionRemove.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelActions.html b/docs/LayerPanelActions.html index abaf5b30..df1a7a8e 100644 --- a/docs/LayerPanelActions.html +++ b/docs/LayerPanelActions.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelBase.html b/docs/LayerPanelBase.html index c21dd854..1ea44940 100644 --- a/docs/LayerPanelBase.html +++ b/docs/LayerPanelBase.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelCheckbox.html b/docs/LayerPanelCheckbox.html index a1731a2b..700576c0 100644 --- a/docs/LayerPanelCheckbox.html +++ b/docs/LayerPanelCheckbox.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelContent.html b/docs/LayerPanelContent.html index 02be66bb..13142b3f 100644 --- a/docs/LayerPanelContent.html +++ b/docs/LayerPanelContent.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelHeader.html b/docs/LayerPanelHeader.html index 017cf56b..553a0fe8 100644 --- a/docs/LayerPanelHeader.html +++ b/docs/LayerPanelHeader.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelLayersPage.html b/docs/LayerPanelLayersPage.html index ea402032..82b3ed16 100644 --- a/docs/LayerPanelLayersPage.html +++ b/docs/LayerPanelLayersPage.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelList.html b/docs/LayerPanelList.html index 06a51003..fd98d3ad 100644 --- a/docs/LayerPanelList.html +++ b/docs/LayerPanelList.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelListItem.html b/docs/LayerPanelListItem.html index a94d0ee9..07c44ed7 100644 --- a/docs/LayerPanelListItem.html +++ b/docs/LayerPanelListItem.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelMenu.html b/docs/LayerPanelMenu.html index d3044044..dd63fe62 100644 --- a/docs/LayerPanelMenu.html +++ b/docs/LayerPanelMenu.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanelPage.html b/docs/LayerPanelPage.html index ccecc0c2..b38c333f 100644 --- a/docs/LayerPanelPage.html +++ b/docs/LayerPanelPage.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerPanel_LayerPanel.js.html b/docs/LayerPanel_LayerPanel.js.html index 537f28ff..d633cca3 100644 --- a/docs/LayerPanel_LayerPanel.js.html +++ b/docs/LayerPanel_LayerPanel.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionDuplicate_index.js.html b/docs/LayerPanel_LayerPanelActionDuplicate_index.js.html index 9dc78f95..f501dcfc 100644 --- a/docs/LayerPanel_LayerPanelActionDuplicate_index.js.html +++ b/docs/LayerPanel_LayerPanelActionDuplicate_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionExport_index.js.html b/docs/LayerPanel_LayerPanelActionExport_index.js.html index 030199c3..28d3765b 100644 --- a/docs/LayerPanel_LayerPanelActionExport_index.js.html +++ b/docs/LayerPanel_LayerPanelActionExport_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionExport_utils.js.html b/docs/LayerPanel_LayerPanelActionExport_utils.js.html index 032fd644..21d39ede 100644 --- a/docs/LayerPanel_LayerPanelActionExport_utils.js.html +++ b/docs/LayerPanel_LayerPanelActionExport_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionExtent_index.js.html b/docs/LayerPanel_LayerPanelActionExtent_index.js.html index 57f45415..8be3754a 100644 --- a/docs/LayerPanel_LayerPanelActionExtent_index.js.html +++ b/docs/LayerPanel_LayerPanelActionExtent_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionHeatmap_index.js.html b/docs/LayerPanel_LayerPanelActionHeatmap_index.js.html index 6116533d..1c524411 100644 --- a/docs/LayerPanel_LayerPanelActionHeatmap_index.js.html +++ b/docs/LayerPanel_LayerPanelActionHeatmap_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionHeatmap_utils.js.html b/docs/LayerPanel_LayerPanelActionHeatmap_utils.js.html index 20a16b8c..4aafa636 100644 --- a/docs/LayerPanel_LayerPanelActionHeatmap_utils.js.html +++ b/docs/LayerPanel_LayerPanelActionHeatmap_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionImport_index.js.html b/docs/LayerPanel_LayerPanelActionImport_index.js.html index d1f362b7..4868035d 100644 --- a/docs/LayerPanel_LayerPanelActionImport_index.js.html +++ b/docs/LayerPanel_LayerPanelActionImport_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionImport_utils.js.html b/docs/LayerPanel_LayerPanelActionImport_utils.js.html index ac0d2ec7..8052a96f 100644 --- a/docs/LayerPanel_LayerPanelActionImport_utils.js.html +++ b/docs/LayerPanel_LayerPanelActionImport_utils.js.html @@ -67,7 +67,7 @@ @@ -109,7 +109,6 @@ * Converts the given file into a layer * @function convertFileToLayer * @category LayerPanel - * @since 6.5.0 * @param {Blob} [file] - the file to be converted. Accepts, 'kmz', 'kml', 'geojson', 'wkt', 'csv', 'zip', and 'json' file types. * @param {olMap} [map] - the openlayers map */ diff --git a/docs/LayerPanel_LayerPanelActionMerge_index.js.html b/docs/LayerPanel_LayerPanelActionMerge_index.js.html index ee9f75ab..c7271948 100644 --- a/docs/LayerPanel_LayerPanelActionMerge_index.js.html +++ b/docs/LayerPanel_LayerPanelActionMerge_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionMerge_utils.js.html b/docs/LayerPanel_LayerPanelActionMerge_utils.js.html index ae82ad7e..6f265db8 100644 --- a/docs/LayerPanel_LayerPanelActionMerge_utils.js.html +++ b/docs/LayerPanel_LayerPanelActionMerge_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionOpacity_index.js.html b/docs/LayerPanel_LayerPanelActionOpacity_index.js.html index eb86889c..e71ae1cd 100644 --- a/docs/LayerPanel_LayerPanelActionOpacity_index.js.html +++ b/docs/LayerPanel_LayerPanelActionOpacity_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActionRemove_index.js.html b/docs/LayerPanel_LayerPanelActionRemove_index.js.html index 996933e8..cacd012b 100644 --- a/docs/LayerPanel_LayerPanelActionRemove_index.js.html +++ b/docs/LayerPanel_LayerPanelActionRemove_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelActions_index.js.html b/docs/LayerPanel_LayerPanelActions_index.js.html index 7d02533e..a2db399d 100644 --- a/docs/LayerPanel_LayerPanelActions_index.js.html +++ b/docs/LayerPanel_LayerPanelActions_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelBase_index.js.html b/docs/LayerPanel_LayerPanelBase_index.js.html index 9c84aadb..e2cf9621 100644 --- a/docs/LayerPanel_LayerPanelBase_index.js.html +++ b/docs/LayerPanel_LayerPanelBase_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelCheckbox_index.js.html b/docs/LayerPanel_LayerPanelCheckbox_index.js.html index 48448cb8..975b408b 100644 --- a/docs/LayerPanel_LayerPanelCheckbox_index.js.html +++ b/docs/LayerPanel_LayerPanelCheckbox_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelContent_index.js.html b/docs/LayerPanel_LayerPanelContent_index.js.html index e0b77534..3399ded4 100644 --- a/docs/LayerPanel_LayerPanelContent_index.js.html +++ b/docs/LayerPanel_LayerPanelContent_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelHeader_index.js.html b/docs/LayerPanel_LayerPanelHeader_index.js.html index 1a0965c2..d6760c35 100644 --- a/docs/LayerPanel_LayerPanelHeader_index.js.html +++ b/docs/LayerPanel_LayerPanelHeader_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelLayersPage_index.js.html b/docs/LayerPanel_LayerPanelLayersPage_index.js.html index 6d341338..e6230149 100644 --- a/docs/LayerPanel_LayerPanelLayersPage_index.js.html +++ b/docs/LayerPanel_LayerPanelLayersPage_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelListItem_index.js.html b/docs/LayerPanel_LayerPanelListItem_index.js.html index 77f2bde9..8d245202 100644 --- a/docs/LayerPanel_LayerPanelListItem_index.js.html +++ b/docs/LayerPanel_LayerPanelListItem_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelList_index.js.html b/docs/LayerPanel_LayerPanelList_index.js.html index 0ba213ab..86943cbe 100644 --- a/docs/LayerPanel_LayerPanelList_index.js.html +++ b/docs/LayerPanel_LayerPanelList_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelMenu_index.js.html b/docs/LayerPanel_LayerPanelMenu_index.js.html index bc84671a..cce2e0a6 100644 --- a/docs/LayerPanel_LayerPanelMenu_index.js.html +++ b/docs/LayerPanel_LayerPanelMenu_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerPanel_LayerPanelPage_index.js.html b/docs/LayerPanel_LayerPanelPage_index.js.html index ca5318bb..9e315833 100644 --- a/docs/LayerPanel_LayerPanelPage_index.js.html +++ b/docs/LayerPanel_LayerPanelPage_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler.html b/docs/LayerStyler.html index c7d5e5b3..859d6ee6 100644 --- a/docs/LayerStyler.html +++ b/docs/LayerStyler.html @@ -65,7 +65,7 @@ diff --git a/docs/LayerStyler_LayerStyler.js.html b/docs/LayerStyler_LayerStyler.js.html index d625c0e9..c2951b0b 100644 --- a/docs/LayerStyler_LayerStyler.js.html +++ b/docs/LayerStyler_LayerStyler.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler_StyleManager_index.js.html b/docs/LayerStyler_StyleManager_index.js.html index 8f7b8ca0..e167fa6a 100644 --- a/docs/LayerStyler_StyleManager_index.js.html +++ b/docs/LayerStyler_StyleManager_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__AttributesFilter_index.js.html b/docs/LayerStyler__AttributesFilter_index.js.html index 4546fe57..c4ce9de9 100644 --- a/docs/LayerStyler__AttributesFilter_index.js.html +++ b/docs/LayerStyler__AttributesFilter_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__ColorPicker_index.js.html b/docs/LayerStyler__ColorPicker_index.js.html index 57aa61ec..2aa86f3d 100644 --- a/docs/LayerStyler__ColorPicker_index.js.html +++ b/docs/LayerStyler__ColorPicker_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__LabelStyler_index.js.html b/docs/LayerStyler__LabelStyler_index.js.html index 14e5e306..a5ca91d4 100644 --- a/docs/LayerStyler__LabelStyler_index.js.html +++ b/docs/LayerStyler__LabelStyler_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__LayerStyler__StyleGroup__GenericSymbolizer_index.js.html b/docs/LayerStyler__LayerStyler__StyleGroup__GenericSymbolizer_index.js.html index 59d044a9..85cc735e 100644 --- a/docs/LayerStyler__LayerStyler__StyleGroup__GenericSymbolizer_index.js.html +++ b/docs/LayerStyler__LayerStyler__StyleGroup__GenericSymbolizer_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__LayerStyler__StyleGroup_index.js.html b/docs/LayerStyler__LayerStyler__StyleGroup_index.js.html index 34e0f90e..9c3fc360 100644 --- a/docs/LayerStyler__LayerStyler__StyleGroup_index.js.html +++ b/docs/LayerStyler__LayerStyler__StyleGroup_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__LayerStyler_index.js.html b/docs/LayerStyler__LayerStyler_index.js.html index 40473c73..7c4f80b2 100644 --- a/docs/LayerStyler__LayerStyler_index.js.html +++ b/docs/LayerStyler__LayerStyler_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__Popover_index.js.html b/docs/LayerStyler__Popover_index.js.html index 193840d5..999594e1 100644 --- a/docs/LayerStyler__Popover_index.js.html +++ b/docs/LayerStyler__Popover_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__SelectTabs_index.js.html b/docs/LayerStyler__SelectTabs_index.js.html index 98beea2a..099440a2 100644 --- a/docs/LayerStyler__SelectTabs_index.js.html +++ b/docs/LayerStyler__SelectTabs_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/LayerStyler__Selector_index.js.html b/docs/LayerStyler__Selector_index.js.html index 17014c77..ed81ddf7 100644 --- a/docs/LayerStyler__Selector_index.js.html +++ b/docs/LayerStyler__Selector_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Map.html b/docs/Map.html index 17256853..130f2cb3 100644 --- a/docs/Map.html +++ b/docs/Map.html @@ -65,7 +65,7 @@ diff --git a/docs/Map_Map.js.html b/docs/Map_Map.js.html index 9e116345..9b9d168e 100644 --- a/docs/Map_Map.js.html +++ b/docs/Map_Map.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Map_utils.js.html b/docs/Map_utils.js.html index 9f1b4fb1..f18f15fa 100644 --- a/docs/Map_utils.js.html +++ b/docs/Map_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Measure_utils.js.html b/docs/Measure_utils.js.html index 9709b4bc..d8476bb4 100644 --- a/docs/Measure_utils.js.html +++ b/docs/Measure_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/MultiMapManager.html b/docs/MultiMapManager.html index dd7875e7..2361083a 100644 --- a/docs/MultiMapManager.html +++ b/docs/MultiMapManager.html @@ -65,7 +65,7 @@ diff --git a/docs/MultiMapManager_MultiMapManager.js.html b/docs/MultiMapManager_MultiMapManager.js.html index 8719f946..e9a7758a 100644 --- a/docs/MultiMapManager_MultiMapManager.js.html +++ b/docs/MultiMapManager_MultiMapManager.js.html @@ -67,7 +67,7 @@ diff --git a/docs/MultiMapManager_SafeParent.js.html b/docs/MultiMapManager_SafeParent.js.html index 10a3858a..bf675533 100644 --- a/docs/MultiMapManager_SafeParent.js.html +++ b/docs/MultiMapManager_SafeParent.js.html @@ -67,7 +67,7 @@ diff --git a/docs/MultiMapManager_utils.js.html b/docs/MultiMapManager_utils.js.html index b0787ae6..77e2dec0 100644 --- a/docs/MultiMapManager_utils.js.html +++ b/docs/MultiMapManager_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Pdf_utils.js.html b/docs/Pdf_utils.js.html index d0552df1..866bd8a8 100644 --- a/docs/Pdf_utils.js.html +++ b/docs/Pdf_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup.html b/docs/Popup.html index 74f6d733..2a8ce79f 100644 --- a/docs/Popup.html +++ b/docs/Popup.html @@ -65,7 +65,7 @@ diff --git a/docs/PopupActionCopyWkt.html b/docs/PopupActionCopyWkt.html index 3ef1e415..0e262d92 100644 --- a/docs/PopupActionCopyWkt.html +++ b/docs/PopupActionCopyWkt.html @@ -65,7 +65,7 @@ diff --git a/docs/PopupActionGoogleMaps.html b/docs/PopupActionGoogleMaps.html index 16fc1491..b729752e 100644 --- a/docs/PopupActionGoogleMaps.html +++ b/docs/PopupActionGoogleMaps.html @@ -65,7 +65,7 @@ diff --git a/docs/PopupActionGroup.html b/docs/PopupActionGroup.html index 16cc248a..fd4f1a4c 100644 --- a/docs/PopupActionGroup.html +++ b/docs/PopupActionGroup.html @@ -65,7 +65,7 @@ @@ -244,13 +244,13 @@
Example
-
+
diff --git a/docs/PopupActionItem.html b/docs/PopupActionItem.html index 17e90abf..2fe89380 100644 --- a/docs/PopupActionItem.html +++ b/docs/PopupActionItem.html @@ -65,7 +65,7 @@ @@ -277,13 +277,13 @@
Example
-
+
diff --git a/docs/PopupActionLink.html b/docs/PopupActionLink.html index f14ce22e..a4ccc9de 100644 --- a/docs/PopupActionLink.html +++ b/docs/PopupActionLink.html @@ -65,7 +65,7 @@ @@ -312,13 +312,13 @@
Example
-
+
diff --git a/docs/PopupBase.html b/docs/PopupBase.html index 0fdc1ed9..e7d85d82 100644 --- a/docs/PopupBase.html +++ b/docs/PopupBase.html @@ -65,7 +65,7 @@ diff --git a/docs/PopupDataList.html b/docs/PopupDataList.html index 395987ef..0dbc5652 100644 --- a/docs/PopupDataList.html +++ b/docs/PopupDataList.html @@ -65,7 +65,7 @@ @@ -222,13 +222,13 @@
Example
-
+
diff --git a/docs/PopupDefaultInsert.html b/docs/PopupDefaultInsert.html index 8212ce73..fe37f0d0 100644 --- a/docs/PopupDefaultInsert.html +++ b/docs/PopupDefaultInsert.html @@ -65,7 +65,7 @@ @@ -103,7 +103,7 @@

- <PopupDefaultInsert actions features loading onClose onSelectFeature onSettingsClick propertiesFilter selectedIdx selectInteraction translations /> + <PopupDefaultInsert actions features loading onClose onSelectFeature onSettingsClick propertiesFilter selectedIdx selectInteraction translations onEdit />

@@ -245,6 +245,17 @@
PropTypes:
+ + onEdit + + func + + No + + () => false + + + @@ -290,7 +301,7 @@
PropTypes:

View Source - Popup/PopupInsert/PopupDefaultInsert.js, line 30 + Popup/PopupInsert/PopupDefaultInsert.js, line 31

diff --git a/docs/PopupDefaultPage.html b/docs/PopupDefaultPage.html index 3cb5cd65..fc153ad0 100644 --- a/docs/PopupDefaultPage.html +++ b/docs/PopupDefaultPage.html @@ -65,7 +65,7 @@ @@ -354,13 +354,13 @@
Example
-
+
diff --git a/docs/PopupPageLayout.html b/docs/PopupPageLayout.html index 60717120..1fb901cb 100644 --- a/docs/PopupPageLayout.html +++ b/docs/PopupPageLayout.html @@ -65,7 +65,7 @@ @@ -255,13 +255,13 @@
Example
-
+
diff --git a/docs/PopupPageLayoutChild.html b/docs/PopupPageLayoutChild.html index 1e8756de..2d7f3198 100644 --- a/docs/PopupPageLayoutChild.html +++ b/docs/PopupPageLayoutChild.html @@ -65,7 +65,7 @@ @@ -266,13 +266,13 @@
Example
-
+
diff --git a/docs/PopupTabs.html b/docs/PopupTabs.html index 175f7928..0e36c3d7 100644 --- a/docs/PopupTabs.html +++ b/docs/PopupTabs.html @@ -65,7 +65,7 @@ @@ -244,13 +244,13 @@
Example
-
+
diff --git a/docs/Popup_Popup.js.html b/docs/Popup_Popup.js.html index 14de1941..614168e7 100644 --- a/docs/Popup_Popup.js.html +++ b/docs/Popup_Popup.js.html @@ -67,7 +67,7 @@ @@ -246,7 +246,8 @@ actions={actions} features={features} loading={loading} - onClose={this.hidePopup} /> + onClose={this.hidePopup} + /> )} </PopupBase>, map.getTargetElement() diff --git a/docs/Popup_PopupActions_PopupActionCopyWkt_PopupActionCopyWkt.js.html b/docs/Popup_PopupActions_PopupActionCopyWkt_PopupActionCopyWkt.js.html index 7bb53c92..337bc14c 100644 --- a/docs/Popup_PopupActions_PopupActionCopyWkt_PopupActionCopyWkt.js.html +++ b/docs/Popup_PopupActions_PopupActionCopyWkt_PopupActionCopyWkt.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionCopyWkt_utils.js.html b/docs/Popup_PopupActions_PopupActionCopyWkt_utils.js.html index c8b40fae..f0952188 100644 --- a/docs/Popup_PopupActions_PopupActionCopyWkt_utils.js.html +++ b/docs/Popup_PopupActions_PopupActionCopyWkt_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionCut_PopupActionCut.js.html b/docs/Popup_PopupActions_PopupActionCut_PopupActionCut.js.html index 83ca287b..16913a75 100644 --- a/docs/Popup_PopupActions_PopupActionCut_PopupActionCut.js.html +++ b/docs/Popup_PopupActions_PopupActionCut_PopupActionCut.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionDuplicate_PopupActionDuplicate.js.html b/docs/Popup_PopupActions_PopupActionDuplicate_PopupActionDuplicate.js.html index e0a5fe74..d11ed642 100644 --- a/docs/Popup_PopupActions_PopupActionDuplicate_PopupActionDuplicate.js.html +++ b/docs/Popup_PopupActions_PopupActionDuplicate_PopupActionDuplicate.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionGoogleMaps_PopupActionGoogleMaps.js.html b/docs/Popup_PopupActions_PopupActionGoogleMaps_PopupActionGoogleMaps.js.html index acb908dc..5027ecb2 100644 --- a/docs/Popup_PopupActions_PopupActionGoogleMaps_PopupActionGoogleMaps.js.html +++ b/docs/Popup_PopupActions_PopupActionGoogleMaps_PopupActionGoogleMaps.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionRemove_PopupActionRemove.js.html b/docs/Popup_PopupActions_PopupActionRemove_PopupActionRemove.js.html index 33494572..07269e8a 100644 --- a/docs/Popup_PopupActions_PopupActionRemove_PopupActionRemove.js.html +++ b/docs/Popup_PopupActions_PopupActionRemove_PopupActionRemove.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupActions_PopupActionZoomToExtent_PopupActionZoomToExtent.js.html b/docs/Popup_PopupActions_PopupActionZoomToExtent_PopupActionZoomToExtent.js.html index 303df53d..a39f207a 100644 --- a/docs/Popup_PopupActions_PopupActionZoomToExtent_PopupActionZoomToExtent.js.html +++ b/docs/Popup_PopupActions_PopupActionZoomToExtent_PopupActionZoomToExtent.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupBase.js.html b/docs/Popup_PopupBase.js.html index 00908583..5ad6649d 100644 --- a/docs/Popup_PopupBase.js.html +++ b/docs/Popup_PopupBase.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupActionGroup_index.js.html b/docs/Popup_PopupInsert_PopupActionGroup_index.js.html index 1d9ac7e7..ae19fde5 100644 --- a/docs/Popup_PopupInsert_PopupActionGroup_index.js.html +++ b/docs/Popup_PopupInsert_PopupActionGroup_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupActionItem_index.js.html b/docs/Popup_PopupInsert_PopupActionItem_index.js.html index cf1802a0..eaa8f455 100644 --- a/docs/Popup_PopupInsert_PopupActionItem_index.js.html +++ b/docs/Popup_PopupInsert_PopupActionItem_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupActionLink_index.js.html b/docs/Popup_PopupInsert_PopupActionLink_index.js.html index 5058641d..aaba04e4 100644 --- a/docs/Popup_PopupInsert_PopupActionLink_index.js.html +++ b/docs/Popup_PopupInsert_PopupActionLink_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupDataList_index.js.html b/docs/Popup_PopupInsert_PopupDataList_index.js.html index e72ee1ae..d9f9a95e 100644 --- a/docs/Popup_PopupInsert_PopupDataList_index.js.html +++ b/docs/Popup_PopupInsert_PopupDataList_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupDefaultInsert.js.html b/docs/Popup_PopupInsert_PopupDefaultInsert.js.html index d540da14..b8566c98 100644 --- a/docs/Popup_PopupInsert_PopupDefaultInsert.js.html +++ b/docs/Popup_PopupInsert_PopupDefaultInsert.js.html @@ -67,7 +67,7 @@ @@ -91,6 +91,7 @@ import { PopupActionRemove } from 'Popup/PopupActions/PopupActionRemove' import { PopupActionDuplicate } from 'Popup/PopupActions/PopupActionDuplicate' import { PopupActionCut } from 'Popup/PopupActions/PopupActionCut' +import { PopupActionEdit } from 'Popup/PopupActions/PopupActionEdit' import { PopupActionZoomToExtent } from 'Popup/PopupActions/PopupActionZoomToExtent' import PopupDefaultPage from './PopupDefaultPage' import PopupPageLayout from './PopupPageLayout' @@ -114,7 +115,7 @@ super(props) this.state = { - selectedIdx: 0 + selectedIdx: 0, } } @@ -168,7 +169,7 @@ } render () { - const { actions, features, loading, onClose, onSettingsClick, propertiesFilter, translations } = this.props + const { actions, features, loading, onClose, onSettingsClick, propertiesFilter, translations, onEdit } = this.props const { selectedIdx } = this.state const getChildren = feature => { @@ -178,6 +179,7 @@ <PopupActionDuplicate key='dupe' />, <PopupActionRemove key='remove' />, <PopupActionGoogleMaps key='nav' />, + <PopupActionEdit key='edit' onEdit={onEdit} />, <PopupActionZoomToExtent key='zoom' />] if (!pointGeom) defaultActions = [...defaultActions, <PopupActionCut key='cut' />] @@ -220,7 +222,8 @@ onClose: () => {}, onSelectFeature: () => {}, propertiesFilter: sanitizeProperties, - translations: en + translations: en, + onEdit: () => false, } PopupDefaultInsert.propTypes = { @@ -250,7 +253,8 @@ '_ol_kit.PopupDefaultPage.details': PropTypes.string, '_ol_kit.PopupDefaultPage.actions': PropTypes.string, '_ol_kit.PopupDefaultPage.customize': PropTypes.string - }).isRequired + }).isRequired, + onEdit: PropTypes.func, } export default connectToContext(PopupDefaultInsert) diff --git a/docs/Popup_PopupInsert_PopupDefaultPage_index.js.html b/docs/Popup_PopupInsert_PopupDefaultPage_index.js.html index 056b1457..57742f29 100644 --- a/docs/Popup_PopupInsert_PopupDefaultPage_index.js.html +++ b/docs/Popup_PopupInsert_PopupDefaultPage_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupPageLayoutChild_index.js.html b/docs/Popup_PopupInsert_PopupPageLayoutChild_index.js.html index 6e103c51..951d65fa 100644 --- a/docs/Popup_PopupInsert_PopupPageLayoutChild_index.js.html +++ b/docs/Popup_PopupInsert_PopupPageLayoutChild_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupPageLayout_index.js.html b/docs/Popup_PopupInsert_PopupPageLayout_index.js.html index ac296874..a8c70b8d 100644 --- a/docs/Popup_PopupInsert_PopupPageLayout_index.js.html +++ b/docs/Popup_PopupInsert_PopupPageLayout_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert_PopupTabs_index.js.html b/docs/Popup_PopupInsert_PopupTabs_index.js.html index df13ff19..f26ad031 100644 --- a/docs/Popup_PopupInsert_PopupTabs_index.js.html +++ b/docs/Popup_PopupInsert_PopupTabs_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_PopupInsert__LoadingSpinner_index.js.html b/docs/Popup_PopupInsert__LoadingSpinner_index.js.html index 4a6a17ea..4efe097f 100644 --- a/docs/Popup_PopupInsert__LoadingSpinner_index.js.html +++ b/docs/Popup_PopupInsert__LoadingSpinner_index.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Popup_utils.js.html b/docs/Popup_utils.js.html index de97c6bc..3d0aad4e 100644 --- a/docs/Popup_utils.js.html +++ b/docs/Popup_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ProjectMenu.html b/docs/ProjectMenu.html index 471cc7dd..9328444c 100644 --- a/docs/ProjectMenu.html +++ b/docs/ProjectMenu.html @@ -65,7 +65,7 @@ diff --git a/docs/Project_Project.js.html b/docs/Project_Project.js.html index c41b76e6..c7e484b4 100644 --- a/docs/Project_Project.js.html +++ b/docs/Project_Project.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Project_utils.js.html b/docs/Project_utils.js.html index 81d32a34..cd96a34c 100644 --- a/docs/Project_utils.js.html +++ b/docs/Project_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Provider.html b/docs/Provider.html index b5e0ab71..22360445 100644 --- a/docs/Provider.html +++ b/docs/Provider.html @@ -65,7 +65,7 @@ @@ -105,7 +105,7 @@

- <Provider children contextProps map maps translations /> + <Provider children contextProps map maps translations editFeature />

@@ -192,6 +192,17 @@
PropTypes:
+ + editFeature + + object + + No + An Ol feature object that is being edited + + + + @@ -271,13 +282,13 @@
Example
-
+
diff --git a/docs/Provider_Provider.js.html b/docs/Provider_Provider.js.html index b9401aa4..05bcb748 100644 --- a/docs/Provider_Provider.js.html +++ b/docs/Provider_Provider.js.html @@ -67,7 +67,7 @@ @@ -117,6 +117,10 @@ this.setState({ mapContext }) } + addEditFeatureToContext = editFeature => { + this.setState({ editFeature }) + } + getContextValue = () => { const { contextProps, map: mapProp, maps: mapsProp, translations } = this.props const { mapContext } = this.state @@ -134,6 +138,8 @@ addMapToContext: this.addMapToContext, map, maps, + editFeature: this.state.editFeature, + addEditFeatureToContext: this.addEditFeatureToContext, persistedState: this.state, persistState: this.persistState, translations, @@ -170,7 +176,9 @@ /** Array of Openlayers map objects for components that support multi-map (this will override anything passed to map prop) */ maps: PropTypes.array, /** Object with key/value pairs for component translation strings */ - translations: PropTypes.object + translations: PropTypes.object, + /** An Ol feature object that is being edited */ + editFeature: PropTypes.object } export default Provider diff --git a/docs/Provider_utils.js.html b/docs/Provider_utils.js.html index 82d6df93..afc2f359 100644 --- a/docs/Provider_utils.js.html +++ b/docs/Provider_utils.js.html @@ -67,7 +67,7 @@ diff --git a/docs/TabbedPanel.html b/docs/TabbedPanel.html index 43a39556..d3e395f1 100644 --- a/docs/TabbedPanel.html +++ b/docs/TabbedPanel.html @@ -65,7 +65,7 @@ diff --git a/docs/TabbedPanel_TabbedPanel.js.html b/docs/TabbedPanel_TabbedPanel.js.html index 3098eca0..1fd06455 100644 --- a/docs/TabbedPanel_TabbedPanel.js.html +++ b/docs/TabbedPanel_TabbedPanel.js.html @@ -67,7 +67,7 @@ diff --git a/docs/TimeSlider.html b/docs/TimeSlider.html index 3a40a2c7..d3f19cce 100644 --- a/docs/TimeSlider.html +++ b/docs/TimeSlider.html @@ -65,7 +65,7 @@ diff --git a/docs/TimeSliderBase.html b/docs/TimeSliderBase.html index 7caa24e5..6418a816 100644 --- a/docs/TimeSliderBase.html +++ b/docs/TimeSliderBase.html @@ -65,7 +65,7 @@ diff --git a/docs/TimeSlider_TimeSlider.js.html b/docs/TimeSlider_TimeSlider.js.html index 0478275d..de263ba2 100644 --- a/docs/TimeSlider_TimeSlider.js.html +++ b/docs/TimeSlider_TimeSlider.js.html @@ -67,7 +67,7 @@ diff --git a/docs/TimeSlider_TimeSliderBase.js.html b/docs/TimeSlider_TimeSliderBase.js.html index 0df11517..d9c69454 100644 --- a/docs/TimeSlider_TimeSliderBase.js.html +++ b/docs/TimeSlider_TimeSliderBase.js.html @@ -67,7 +67,7 @@ diff --git a/docs/Toolbar_Toolbar.js.html b/docs/Toolbar_Toolbar.js.html index fb9c4adf..570ce941 100644 --- a/docs/Toolbar_Toolbar.js.html +++ b/docs/Toolbar_Toolbar.js.html @@ -67,7 +67,7 @@ diff --git a/docs/ZoomControls.html b/docs/ZoomControls.html index 7057602b..714ad3d1 100644 --- a/docs/ZoomControls.html +++ b/docs/ZoomControls.html @@ -65,7 +65,7 @@ diff --git a/docs/ZoomIn.html b/docs/ZoomIn.html index bc2daf13..09ef19fb 100644 --- a/docs/ZoomIn.html +++ b/docs/ZoomIn.html @@ -65,7 +65,7 @@ diff --git a/docs/ZoomOut.html b/docs/ZoomOut.html index 24effe31..c94fcfdd 100644 --- a/docs/ZoomOut.html +++ b/docs/ZoomOut.html @@ -65,7 +65,7 @@ diff --git a/docs/classes_VectorLayer.js.html b/docs/classes_VectorLayer.js.html index 682b1074..57b94390 100644 --- a/docs/classes_VectorLayer.js.html +++ b/docs/classes_VectorLayer.js.html @@ -67,7 +67,7 @@ diff --git a/docs/classes_VectorTileLayer.js.html b/docs/classes_VectorTileLayer.js.html index c6452ef3..928c1f10 100644 --- a/docs/classes_VectorTileLayer.js.html +++ b/docs/classes_VectorTileLayer.js.html @@ -67,7 +67,7 @@ diff --git a/docs/docs.html b/docs/docs.html index d559451b..3a522cf9 100644 --- a/docs/docs.html +++ b/docs/docs.html @@ -65,7 +65,7 @@ diff --git a/docs/entry.js b/docs/entry.js index 8cc2eb90..260cc986 100644 --- a/docs/entry.js +++ b/docs/entry.js @@ -97,140 +97,143 @@ reactComponents['DrawPoint'] = Component23; import Component24 from '../src/Draw/Polygon.js'; reactComponents['DrawPolygon'] = Component24; -import Component25 from '../src/GoogleDirections/GoogleDirections.js'; -reactComponents['GoogleDirections'] = Component25; +import Component25 from '../src/FeatureEditor/FeatureEditor.js'; +reactComponents['FeatureEditor'] = Component25; -import Component26 from '../src/GooglePlacesSearch/GooglePlacesSearch.js'; -reactComponents['GooglePlacesSearch'] = Component26; +import Component26 from '../src/GoogleDirections/GoogleDirections.js'; +reactComponents['GoogleDirections'] = Component26; -import Component27 from '../src/Heatmap/HeatmapControls.js'; -reactComponents['HeatmapControls'] = Component27; +import Component27 from '../src/GooglePlacesSearch/GooglePlacesSearch.js'; +reactComponents['GooglePlacesSearch'] = Component27; -import Component28 from '../src/LayerPanel/LayerPanel.js'; -reactComponents['LayerPanel'] = Component28; +import Component28 from '../src/Heatmap/HeatmapControls.js'; +reactComponents['HeatmapControls'] = Component28; -import Component29 from '../src/LayerPanel/LayerPanelActionDuplicate/index.js'; -reactComponents['LayerPanelActionDuplicate'] = Component29; +import Component29 from '../src/LayerPanel/LayerPanel.js'; +reactComponents['LayerPanel'] = Component29; -import Component30 from '../src/LayerPanel/LayerPanelActionExport/index.js'; -reactComponents['LayerPanelActionExport'] = Component30; +import Component30 from '../src/LayerPanel/LayerPanelActionDuplicate/index.js'; +reactComponents['LayerPanelActionDuplicate'] = Component30; -import Component31 from '../src/LayerPanel/LayerPanelActionExtent/index.js'; -reactComponents['LayerPanelActionExtent'] = Component31; +import Component31 from '../src/LayerPanel/LayerPanelActionExport/index.js'; +reactComponents['LayerPanelActionExport'] = Component31; -import Component32 from '../src/LayerPanel/LayerPanelActionHeatmap/index.js'; -reactComponents['LayerPanelActionHeatmap'] = Component32; +import Component32 from '../src/LayerPanel/LayerPanelActionExtent/index.js'; +reactComponents['LayerPanelActionExtent'] = Component32; -import Component33 from '../src/LayerPanel/LayerPanelActionImport/index.js'; -reactComponents['LayerPanelActionImport'] = Component33; +import Component33 from '../src/LayerPanel/LayerPanelActionHeatmap/index.js'; +reactComponents['LayerPanelActionHeatmap'] = Component33; -import Component34 from '../src/LayerPanel/LayerPanelActionMerge/index.js'; -reactComponents['LayerPanelActionMerge'] = Component34; +import Component34 from '../src/LayerPanel/LayerPanelActionImport/index.js'; +reactComponents['LayerPanelActionImport'] = Component34; -import Component35 from '../src/LayerPanel/LayerPanelActionOpacity/index.js'; -reactComponents['LayerPanelActionOpacity'] = Component35; +import Component35 from '../src/LayerPanel/LayerPanelActionMerge/index.js'; +reactComponents['LayerPanelActionMerge'] = Component35; -import Component36 from '../src/LayerPanel/LayerPanelActionRemove/index.js'; -reactComponents['LayerPanelActionRemove'] = Component36; +import Component36 from '../src/LayerPanel/LayerPanelActionOpacity/index.js'; +reactComponents['LayerPanelActionOpacity'] = Component36; -import Component37 from '../src/LayerPanel/LayerPanelActions/index.js'; -reactComponents['LayerPanelActions'] = Component37; +import Component37 from '../src/LayerPanel/LayerPanelActionRemove/index.js'; +reactComponents['LayerPanelActionRemove'] = Component37; -import Component38 from '../src/LayerPanel/LayerPanelBase/index.js'; -reactComponents['LayerPanelBase'] = Component38; +import Component38 from '../src/LayerPanel/LayerPanelActions/index.js'; +reactComponents['LayerPanelActions'] = Component38; -import Component39 from '../src/LayerPanel/LayerPanelCheckbox/index.js'; -reactComponents['LayerPanelCheckbox'] = Component39; +import Component39 from '../src/LayerPanel/LayerPanelBase/index.js'; +reactComponents['LayerPanelBase'] = Component39; -import Component40 from '../src/LayerPanel/LayerPanelContent/index.js'; -reactComponents['LayerPanelContent'] = Component40; +import Component40 from '../src/LayerPanel/LayerPanelCheckbox/index.js'; +reactComponents['LayerPanelCheckbox'] = Component40; -import Component41 from '../src/LayerPanel/LayerPanelHeader/index.js'; -reactComponents['LayerPanelHeader'] = Component41; +import Component41 from '../src/LayerPanel/LayerPanelContent/index.js'; +reactComponents['LayerPanelContent'] = Component41; -import Component42 from '../src/LayerPanel/LayerPanelLayersPage/index.js'; -reactComponents['LayerPanelLayersPage'] = Component42; +import Component42 from '../src/LayerPanel/LayerPanelHeader/index.js'; +reactComponents['LayerPanelHeader'] = Component42; -import Component43 from '../src/LayerPanel/LayerPanelList/index.js'; -reactComponents['LayerPanelList'] = Component43; +import Component43 from '../src/LayerPanel/LayerPanelLayersPage/index.js'; +reactComponents['LayerPanelLayersPage'] = Component43; -import Component44 from '../src/LayerPanel/LayerPanelListItem/index.js'; -reactComponents['LayerPanelListItem'] = Component44; +import Component44 from '../src/LayerPanel/LayerPanelList/index.js'; +reactComponents['LayerPanelList'] = Component44; -import Component45 from '../src/LayerPanel/LayerPanelMenu/index.js'; -reactComponents['LayerPanelMenu'] = Component45; +import Component45 from '../src/LayerPanel/LayerPanelListItem/index.js'; +reactComponents['LayerPanelListItem'] = Component45; -import Component46 from '../src/LayerPanel/LayerPanelPage/index.js'; -reactComponents['LayerPanelPage'] = Component46; +import Component46 from '../src/LayerPanel/LayerPanelMenu/index.js'; +reactComponents['LayerPanelMenu'] = Component46; -import Component47 from '../src/LayerStyler/LayerStyler.js'; -reactComponents['LayerStyler'] = Component47; +import Component47 from '../src/LayerPanel/LayerPanelPage/index.js'; +reactComponents['LayerPanelPage'] = Component47; -import Component48 from '../src/Map/Map.js'; -reactComponents['Map'] = Component48; +import Component48 from '../src/LayerStyler/LayerStyler.js'; +reactComponents['LayerStyler'] = Component48; -import Component49 from '../src/MultiMapManager/MultiMapManager.js'; -reactComponents['MultiMapManager'] = Component49; +import Component49 from '../src/Map/Map.js'; +reactComponents['Map'] = Component49; -import Component50 from '../src/Popup/Popup.js'; -reactComponents['Popup'] = Component50; +import Component50 from '../src/MultiMapManager/MultiMapManager.js'; +reactComponents['MultiMapManager'] = Component50; -import Component51 from '../src/Popup/PopupActions/PopupActionCopyWkt/PopupActionCopyWkt.js'; -reactComponents['PopupActionCopyWkt'] = Component51; +import Component51 from '../src/Popup/Popup.js'; +reactComponents['Popup'] = Component51; -import Component52 from '../src/Popup/PopupActions/PopupActionGoogleMaps/PopupActionGoogleMaps.js'; -reactComponents['PopupActionGoogleMaps'] = Component52; +import Component52 from '../src/Popup/PopupActions/PopupActionCopyWkt/PopupActionCopyWkt.js'; +reactComponents['PopupActionCopyWkt'] = Component52; -import Component53 from '../src/Popup/PopupInsert/PopupActionGroup/index.js'; -reactComponents['PopupActionGroup'] = Component53; +import Component53 from '../src/Popup/PopupActions/PopupActionGoogleMaps/PopupActionGoogleMaps.js'; +reactComponents['PopupActionGoogleMaps'] = Component53; -import Component54 from '../src/Popup/PopupInsert/PopupActionItem/index.js'; -reactComponents['PopupActionItem'] = Component54; +import Component54 from '../src/Popup/PopupInsert/PopupActionGroup/index.js'; +reactComponents['PopupActionGroup'] = Component54; -import Component55 from '../src/Popup/PopupInsert/PopupActionLink/index.js'; -reactComponents['PopupActionLink'] = Component55; +import Component55 from '../src/Popup/PopupInsert/PopupActionItem/index.js'; +reactComponents['PopupActionItem'] = Component55; -import Component56 from '../src/Popup/PopupBase.js'; -reactComponents['PopupBase'] = Component56; +import Component56 from '../src/Popup/PopupInsert/PopupActionLink/index.js'; +reactComponents['PopupActionLink'] = Component56; -import Component57 from '../src/Popup/PopupInsert/PopupDataList/index.js'; -reactComponents['PopupDataList'] = Component57; +import Component57 from '../src/Popup/PopupBase.js'; +reactComponents['PopupBase'] = Component57; -import Component58 from '../src/Popup/PopupInsert/PopupDefaultInsert.js'; -reactComponents['PopupDefaultInsert'] = Component58; +import Component58 from '../src/Popup/PopupInsert/PopupDataList/index.js'; +reactComponents['PopupDataList'] = Component58; -import Component59 from '../src/Popup/PopupInsert/PopupDefaultPage/index.js'; -reactComponents['PopupDefaultPage'] = Component59; +import Component59 from '../src/Popup/PopupInsert/PopupDefaultInsert.js'; +reactComponents['PopupDefaultInsert'] = Component59; -import Component60 from '../src/Popup/PopupInsert/PopupPageLayout/index.js'; -reactComponents['PopupPageLayout'] = Component60; +import Component60 from '../src/Popup/PopupInsert/PopupDefaultPage/index.js'; +reactComponents['PopupDefaultPage'] = Component60; -import Component61 from '../src/Popup/PopupInsert/PopupPageLayoutChild/index.js'; -reactComponents['PopupPageLayoutChild'] = Component61; +import Component61 from '../src/Popup/PopupInsert/PopupPageLayout/index.js'; +reactComponents['PopupPageLayout'] = Component61; -import Component62 from '../src/Popup/PopupInsert/PopupTabs/index.js'; -reactComponents['PopupTabs'] = Component62; +import Component62 from '../src/Popup/PopupInsert/PopupPageLayoutChild/index.js'; +reactComponents['PopupPageLayoutChild'] = Component62; -import Component63 from '../src/Project/Project.js'; -reactComponents['ProjectMenu'] = Component63; +import Component63 from '../src/Popup/PopupInsert/PopupTabs/index.js'; +reactComponents['PopupTabs'] = Component63; -import Component64 from '../src/Provider/Provider.js'; -reactComponents['Provider'] = Component64; +import Component64 from '../src/Project/Project.js'; +reactComponents['ProjectMenu'] = Component64; -import Component65 from '../src/TabbedPanel/TabbedPanel.js'; -reactComponents['TabbedPanel'] = Component65; +import Component65 from '../src/Provider/Provider.js'; +reactComponents['Provider'] = Component65; -import Component66 from '../src/TimeSlider/TimeSlider.js'; -reactComponents['TimeSlider'] = Component66; +import Component66 from '../src/TabbedPanel/TabbedPanel.js'; +reactComponents['TabbedPanel'] = Component66; -import Component67 from '../src/TimeSlider/TimeSliderBase.js'; -reactComponents['TimeSliderBase'] = Component67; +import Component67 from '../src/TimeSlider/TimeSlider.js'; +reactComponents['TimeSlider'] = Component67; -import Component68 from '../src/Controls/ZoomControls.js'; -reactComponents['ZoomControls'] = Component68; +import Component68 from '../src/TimeSlider/TimeSliderBase.js'; +reactComponents['TimeSliderBase'] = Component68; -import Component69 from '../src/Controls/ZoomIn.js'; -reactComponents['ZoomIn'] = Component69; +import Component69 from '../src/Controls/ZoomControls.js'; +reactComponents['ZoomControls'] = Component69; -import Component70 from '../src/Controls/ZoomOut.js'; -reactComponents['ZoomOut'] = Component70; \ No newline at end of file +import Component70 from '../src/Controls/ZoomIn.js'; +reactComponents['ZoomIn'] = Component70; + +import Component71 from '../src/Controls/ZoomOut.js'; +reactComponents['ZoomOut'] = Component71; \ No newline at end of file diff --git a/docs/global.html b/docs/global.html index 68f36798..c6912f3a 100644 --- a/docs/global.html +++ b/docs/global.html @@ -65,7 +65,7 @@ @@ -1954,7 +1954,7 @@
Parameters:

View Source - LayerPanel/LayerPanelActionImport/utils.js, line 48 + LayerPanel/LayerPanelActionImport/utils.js, line 47

@@ -2118,9 +2118,6 @@
Parameters:
-
Since:
-
  • 6.5.0
- @@ -5180,6 +5177,228 @@
Parameters:
+ + + +
+ + + +

+ # + + + + immediateEditStyle(opts, feature, resolution) → {object} + + +

+ + + + +
+ Style an ol/Feature with orange circle vertices, a blue outline, an area label, and a perimeter length label. Can be used with individual features as a style function or call it directly to get a style object for use with `vectorContext.drawFeature` +
+ + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +object + + + + The config object
feature + + +ol/Feature + + + + The feature you want to style
resolution + + +number + + + + the resolution of the map
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + FeatureEditor/styles.js, line 223 + +

+ +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + +
+ +
The style object for the passed feature
+ + +
+ + +object + + +
+ +
+ + +
+
+ + +
@@ -6825,6 +7044,228 @@
Parameters:
+
+
+
+ + + +
+ +
The style object for the passed feature
+ + +
+ + +object + + +
+ +
+ + +
+
+ + + + + + +
+ + + +

+ # + + + + styleText(opts, feature, resolution) → {object} + + +

+ + + + +
+ Style ol/Features +
+ + + + + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
opts + + +object + + + + The config object
feature + + +ol/Feature + + + + The feature you want to style
resolution + + +number + + + + the resolution of the map
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ View Source + + FeatureEditor/styles.js, line 32 + +

+ +
+ + + + + + + + + + + + + + + + + +
diff --git a/docs/tutorial-Getting Started.html b/docs/tutorial-Getting Started.html index dcd4f4ca..0cd73264 100644 --- a/docs/tutorial-Getting Started.html +++ b/docs/tutorial-Getting Started.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-LayerPanel_.html b/docs/tutorial-LayerPanel_.html index 9d726a36..0eca536a 100644 --- a/docs/tutorial-LayerPanel_.html +++ b/docs/tutorial-LayerPanel_.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-Popup_.html b/docs/tutorial-Popup_.html index b91285ad..9cec5198 100644 --- a/docs/tutorial-Popup_.html +++ b/docs/tutorial-Popup_.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-TimeSlider_.html b/docs/tutorial-TimeSlider_.html index 0fce7738..a4a09ffc 100644 --- a/docs/tutorial-TimeSlider_.html +++ b/docs/tutorial-TimeSlider_.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-connectToContext.html b/docs/tutorial-connectToContext.html index 4651740d..5eefceab 100644 --- a/docs/tutorial-connectToContext.html +++ b/docs/tutorial-connectToContext.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-contextProps.html b/docs/tutorial-contextProps.html index 78c8916d..7411c3ba 100644 --- a/docs/tutorial-contextProps.html +++ b/docs/tutorial-contextProps.html @@ -65,7 +65,7 @@ diff --git a/docs/tutorial-i18n Support.html b/docs/tutorial-i18n Support.html index 6f3f601e..c7e43f27 100644 --- a/docs/tutorial-i18n Support.html +++ b/docs/tutorial-i18n Support.html @@ -65,7 +65,7 @@ diff --git a/package-lock.json b/package-lock.json index 83fbe81b..1c0ab1a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@bayer/ol-kit", - "version": "1.15.0", + "version": "1.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1309,7 +1309,6 @@ "version": "7.7.7", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.7.tgz", "integrity": "sha512-uCnC2JEVAu8AKB5do1WRIsvrdJ0flYx/A/9f/6chdacnEZ7LmavjdsDXr5ksYBegxtuTPR5Va9/+13QF/kFkCA==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.2" } @@ -9646,9 +9645,9 @@ } }, "caniuse-lite": { - "version": "1.0.30001150", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001150.tgz", - "integrity": "sha512-kiNKvihW0m36UhAFnl7bOAv0i1K1f6wpfVtTF5O5O82XzgtBnb05V0XeV3oZ968vfg2sRNChsHw8ASH2hDfoYQ==", + "version": "1.0.30001265", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz", + "integrity": "sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==", "dev": true }, "canvg": { @@ -11011,20 +11010,36 @@ "d3-collection": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==", - "dev": true + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" }, "d3-color": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==", - "dev": true + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" }, "d3-format": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==", - "dev": true + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" }, "d3-geo": { "version": "1.7.1", @@ -11038,7 +11053,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", - "dev": true, "requires": { "d3-color": "1" } @@ -11053,7 +11067,6 @@ "version": "2.2.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", - "dev": true, "requires": { "d3-array": "^1.2.0", "d3-collection": "1", @@ -11063,6 +11076,11 @@ "d3-time-format": "2" } }, + "d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, "d3-shape": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", @@ -11075,18 +11093,21 @@ "d3-time": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==", - "dev": true + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" }, "d3-time-format": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", - "dev": true, "requires": { "d3-time": "1" } }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, "d3-voronoi": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.2.tgz", @@ -23475,8 +23496,36 @@ "react-lifecycles-compat": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", - "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", - "dev": true + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==" + }, + "react-motion": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/react-motion/-/react-motion-0.5.2.tgz", + "integrity": "sha512-9q3YAvHoUiWlP3cK0v+w1N5Z23HXMj4IF4YuvjvWegWqNPfLXsOBE/V7UvQGpXxHFKRQQcNcVQE31g9SB/6qgQ==", + "requires": { + "performance-now": "^0.2.0", + "prop-types": "^15.5.8", + "raf": "^3.1.0" + }, + "dependencies": { + "performance-now": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-0.2.0.tgz", + "integrity": "sha1-M+8wxcd9TqIcWlOGnZG1bY8lVeU=" + } + } + }, + "react-move": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/react-move/-/react-move-2.9.1.tgz", + "integrity": "sha512-5qKYsJrKKpSypEaaYyR2HBbBgX65htRqKDa8o5OGDkq2VfklmTCbLawtYFpdmcJRqbz4jCYpzo2Rrsazq9HA8Q==", + "requires": { + "@babel/runtime": "^7.2.0", + "d3-interpolate": "^1.3.2", + "d3-timer": "^1.0.9", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } }, "react-overlays": { "version": "4.1.0", @@ -23512,6 +23561,26 @@ "resize-observer-polyfill": "^1.5.0" } }, + "react-rotary-knob": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/react-rotary-knob/-/react-rotary-knob-1.1.8.tgz", + "integrity": "sha512-K/RfMCM1OcDUMPdP7uR/uDBcg84rFgvC1HkgS89EGvZzaHwURCV/9JQTmJNxWeaCZhd2piFKFPoej3QAu6DAWQ==", + "requires": { + "d3-drag": "^1.2.1", + "d3-scale": "^2.0.0", + "d3-selection": "^1.3.0", + "prop-types": "^15.6.0", + "react-svgmt": "^1.1.7", + "uuid": "^3.2.1" + }, + "dependencies": { + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, "react-router": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.2.0.tgz", @@ -23637,6 +23706,16 @@ "react-virtualized": "^9.21.2" } }, + "react-svgmt": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/react-svgmt/-/react-svgmt-1.1.11.tgz", + "integrity": "sha512-y85s2dy37AJ0z+Sjsnwpkt/5md9nUFh2gkcTZL68uG5ZqIIXf7Na2CrinXWm6anY5JmnMQAXsoMlozcro1H8DA==", + "requires": { + "d3-ease": "^1.0.3", + "react-motion": "^0.5.2", + "react-move": "^2.7.0" + } + }, "react-test-renderer": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.14.0.tgz", diff --git a/package.json b/package.json index a8073616..d3cea055 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bayer/ol-kit", - "version": "1.15.0", + "version": "1.16.0", "license": "BSD", "description": "Mapping components & utils built with openlayers + react", "keywords": [ @@ -103,6 +103,7 @@ "react-draggable": "~4.4.3", "react-dropzone": "~11.3.1", "react-hook-form": "~5.7.2", + "react-rotary-knob": "^1.1.8", "react-select": "~3.1.0", "react-viewer": "~3.2.2", "react-virtuoso": "~1.10.8", diff --git a/src/Draw/__snapshots__/DrawContainer.test.js.snap b/src/Draw/__snapshots__/DrawContainer.test.js.snap index 23cc662f..b2e4181d 100644 --- a/src/Draw/__snapshots__/DrawContainer.test.js.snap +++ b/src/Draw/__snapshots__/DrawContainer.test.js.snap @@ -18,7 +18,7 @@ exports[` should render a basic prebuilt DrawContainer componen class=\\"sc-gtssRu fBppYk\\" href=\\"https://ol-kit.com/\\" target=\\"_blank\\" - title=\\"Powered by ol-kit v1.15.0\\" + title=\\"Powered by ol-kit v1.16.0\\" >  ({ + root: { + padding: '4px 4px 3px 4px' + } +}))(CardActions) + +const LeftCard = withStyles(() => ({ + root: { + borderTopLeftRadius: '4px', + borderBottomLeftRadius: '4px', + borderBottomRightRadius: '0px', + borderTopRightRadius: '0px', + height: '38px' + } +}))(Card) + +const CenterCard = withStyles(() => ({ + root: { + borderRadius: '0px', + paddingLeft: '20px', + marginLeft: '0px', + height: '38px' + } +}))(Card) + +const RightCard = withStyles(() => ({ + root: { + borderTopRightRadius: '4px', + borderBottomRightRadius: '4px', + borderTopLeftRadius: '0px', + borderBottomLeftRadius: '0px', + marginLeft: '0px !important', + height: '38px' + } +}))(Card) + +/** + * A component to edit geometries + * @component + * @category FeatureEditor + * @since 1.16.0 + */ +class FeatureEditor extends Component { + constructor (props) { + super(props) + this.state = { + interactions: [], + editingFeature: null, + showMeasurements: false, + rotation: 0, + style: null + } + } + + /** In the past we've used temporary layers added to the map to avoid modifying the original features directly. + However, this leads to a lot of cleanup since those layers need to be added/removed from both the map and state. + That is fine as long as everything goes smoothly but if there is additional logic listening to the map for changes to it's features + or layers than we can get left in a broken state that requires a reload. Using vectorContext.drawFeature instead of + a layer added to the map alleviates at least some of this risk.*/ + _renderFeature = (vectorContext, feature, editStyle = this.props.editStyle) => { // vectorContext.drawFeature only respects a style object and since it is common to have style functions and arrays in Openlayers we need to break the other formats down into objects + const { map, areaUOM, distanceUOM, translations } = this.props + const { showMeasurements } = this.state + const measurementStyles = showMeasurements ? editStyle(feature, map, showMeasurements, { areaUOM, distanceUOM }, translations) : editStyle // eslint-disable-line + const styleType = Array.isArray(measurementStyles) ? 'array' : typeof editStyle + + try { + switch (styleType) { + case 'array': + measurementStyles.map(style => { + const geom = style.getGeometry() ? style.getGeometry() : feature.getGeometry() + + vectorContext.setStyle(style) + vectorContext.drawGeometry(geom) + }) // Arrays of style objects require a feature to be drawn for each style object in the array. This could also be recursively called but that would add extra complexity. + break + case 'function': + this._renderFeature( + vectorContext, + feature, + editStyle(feature, map, showMeasurements, { areaUOM, distanceUOM }, translations) + ) // Openlayers style functions return style objects or arrays of style objects so we can call functions recursively. + break + default: // style object + vectorContext.drawFeature(feature, editStyle) + } + } catch (e) { + console.warn(`Geokit was unable to draw the features to the map, this is most likely due to an invalid style object: ${e.message}`, e) // eslint-disable-line + } + } + + _renderEditOverlay = (e) => { + const { map, editStyle } = this.props + const { editingFeature } = this.state + + const vectorContext = getVectorContext(e) + + if (!editingFeature) return // to avoid using a setStateCallback we just check for editFeatures first + + this._renderFeature(vectorContext, editingFeature, editStyle) + + return map.render() // render the results asynchronously + } + + _addPostComposeListener = () => { + const { editingFeature } = this.state + + editingFeature?.get('_ol_kit_parent')?.on('postrender', this._renderEditOverlay) // eslint-disable-line + } + + _removePostComposeListener = () => { + const { editingFeature } = this.state + + editingFeature?.get('_ol_kit_parent')?.un('postrender', this._renderEditOverlay) // eslint-disable-line + } + + _end = () => { // this function cleans up our state and map. If this does not execute correctly we could get stuck in a corrupted map state. + try { + const { map } = this.props + const { interactions } = this.state + + this._removePostComposeListener() + + interactions.forEach(i => map.removeInteraction(i)) + this.setState({ editingFeature: null, style: null, interactions: [] }) + } catch (err) { + console.warn(`Geokit encountered a problem while editing a feature: ${err.message}. \n`, err) // eslint-disable-line no-console + } + } + + showMeasurements = () => { + this.setState({ showMeasurements: !this.state.showMeasurements }) + } + + cancelEdit = () => { + const { onEditCancel, editFeature, addEditFeatureToContext } = this.props + const { style } = this.state + + this.setState( + { canceled: true, editingFeature: null }, + () => onEditCancel(editFeature, addEditFeatureToContext, style) + ) + } + + finishEdit = () => { + const { onEditFinish, addEditFeatureToContext, editFeature } = this.props + const { editingFeature, style } = this.state + + this.setState( + { editingFeature: null }, + () => onEditFinish(editFeature, editingFeature, addEditFeatureToContext, style) + ) + } + + init () { + const { editOpts, map, onEditBegin, editFeature } = this.props + + const clonedFeature = editFeature.clone() // create a collection of clones of the features in props, this avoids modifying the existing features + + const opts = Object.assign({}, editOpts, { + pixelTolerance: 10, + features: new olCollection([clonedFeature]), + deleteCondition: ({ originalEvent, type }) => { + const { altKey, ctrlKey, shiftKey, metaKey } = originalEvent + const modifierKeyActive = altKey || ctrlKey || shiftKey || metaKey + const altClick = type === 'click' && modifierKeyActive + const rightClick = (type === 'pointerdown' || type === 'click') && originalEvent.button === 2 + + return rightClick || altClick + } + }) + + const translateInteraction = new Translate({ features: new olCollection([clonedFeature]) }) // ol/interaction/translate only checks for features on the map and since we are not adding these to the map (see additional comments) we use our own that knows to look for the features we pass to it whether or not they're on the map. + const modifyInteraction = new olInteractionModify(opts) // ol/interaction/modify doesn't care about the features being on the map or not so it's good to go + const style = clonedFeature.getStyle() + + this.setState({ + anchor: olKitTurf(centroid, [clonedFeature.getGeometry()]).getGeometry().getCoordinates(), + interactions: [modifyInteraction, translateInteraction], + editingFeature: clonedFeature, + style + }, () => { + this._addPostComposeListener() + }) + map.addInteraction(translateInteraction) + map.addInteraction(modifyInteraction) + onEditBegin(clonedFeature) // callback function for IAs. FeatureEditor doesn't do anything to the original features so we tell the IA which features they passed in as props and what features we are editing. This should help if they want to add custom logic around these features. + } + + componentDidMount () { + if (!this.props.editFeature) return + + this.init() + } + + componentDidUpdate (prevProps) { + const { editFeature } = this.props + const { interactions } = this.state + + if (prevProps.editFeature && !editFeature) return this._end() + + if (interactions.length === 2 || !editFeature) return + + this.init() + } + + componentWillUnmount = () => { + const { editingFeature } = this.state + + if (editingFeature) console.warn(`Geokit FeatureEditor has been unmounted unexpectedly. This may lead undesirable behaviour in your application.`) // eslint-disable-line no-console + + return this._end() + } + + rotate = (val) => { + const { editingFeature, rotation, anchor } = this.state + const geometry = editingFeature.getGeometry() + const rotationDiff = val - rotation + + this.setState({ rotation: val }, () => geometry.rotate(-rotationDiff * (Math.PI / 180), anchor)) + } + + render () { + const { translations } = this.props + const { editingFeature } = this.state + const knobStyle = { + width: '35px', + height: '35px', + padding: '2px' + } + + if (!editingFeature) return null + + return ( + + + + + + + + + } + label={translations['_ol_kit.edit.rotate']} + /> + + + + + + + + ) + } +} + +FeatureEditor.propTypes = { + editOpts: PropTypes.exact({ + condition: PropTypes.string, + deleteCondition: PropTypes.string, + insertVertexCondition: PropTypes.string, + pixelTolerance: PropTypes.number, + style: PropTypes.object, + source: PropTypes.object, + wrapX: PropTypes.bool + }).isRequired, + editFeature: PropTypes.object, + addEditFeatureToContext: PropTypes.func, + map: PropTypes.object, + onEditBegin: PropTypes.func, + onEditFinish: PropTypes.func, + onEditCancel: PropTypes.func, + editStyle: PropTypes.oneOfType([ + PropTypes.func, + PropTypes.object, + PropTypes.array + ]), + areaUOM: PropTypes.string, + distanceUOM: PropTypes.string, + translations: PropTypes.object +} + +FeatureEditor.defaultProps = { + editOpts: {}, + onEditFinish: (feature, updatedFeature, addEditFeatureToContext, style) => { + const geom = updatedFeature.getGeometry() + + if (!feature) return + + feature.setGeometry(geom) + + feature.setStyle(style || null) // restore the original feature's style + addEditFeatureToContext(null) + }, + onEditBegin: (feature) => { + feature.setStyle(new olStyleStyle({})) + }, + onEditCancel: (feature, addEditFeatureToContext, style) => { + feature.setStyle(style || null) // restore the original feature's style + addEditFeatureToContext(null) + }, + editStyle: (feature, map, showMeasurements = false, { areaUOM, distanceUOM }, translations) => { // eslint-disable-line + return immediateEditStyle( + { areaUOM, distanceUOM, showMeasurements, map, translations, language: navigator.language }, + feature, + map.getView().getResolution() + ) + } +} + +export default connectToContext(FeatureEditor) diff --git a/src/FeatureEditor/index.js b/src/FeatureEditor/index.js new file mode 100644 index 00000000..d1ab7a10 --- /dev/null +++ b/src/FeatureEditor/index.js @@ -0,0 +1 @@ +export { default as FeatureEditor } from './FeatureEditor' diff --git a/src/FeatureEditor/styles.js b/src/FeatureEditor/styles.js new file mode 100644 index 00000000..5a1937a0 --- /dev/null +++ b/src/FeatureEditor/styles.js @@ -0,0 +1,338 @@ +import olFeature from 'ol/Feature' +import olGeomLineString from 'ol/geom/LineString' +import olGeomMultiPoint from 'ol/geom/MultiPoint' +import olStyleFill from 'ol/style/Fill' +import olStyleStroke from 'ol/style/Stroke' +import olStyleStyle from 'ol/style/Style' +import olStyleCircle from 'ol/style/Circle' +import olStyleText from 'ol/style/Text' +import olPoint from 'ol/geom/Point' +import centroid from '@turf/centroid' + +import { pointsFromVertices, olKitTurf, pairCoordinates, getCoordinates, getText, calculateScale } from './utils' + +function resolveStyleFunctionArgs (args) { + // Using function.prototype.bind with additional arguments injects those arguments at the zeroth index of the arguements object and since opts is optional we need to handle a variable arguement object + const argLength = args.length + const feature = args[argLength - 2] || args + const resolution = args[argLength - 1] + const opts = argLength >= 3 ? args[0] : {} + + return { feature, resolution, opts } +} + +/** + * Style ol/Features + * @function + * @param {object} opts - The config object + * @param {ol/Feature} feature - The feature you want to style + * @param {number} resolution - the resolution of the map + * @returns {object} The style object for the passed feature + */ +function styleText (...args) { + const { feature, resolution, opts } = resolveStyleFunctionArgs(args) + const label = feature.get('_vmf_label') + const isMeasurement = feature.get('_vmf_type') === '_vmf_measurement' + const isCentroidLabel = feature.get('_ol_kit_needs_centroid_label') + const offsetY = isCentroidLabel ? 20 : isMeasurement ? 0 : (label.fontSize || 16) / 2 // eslint-disable-line + const styleOpts = { + placement: opts.placement || 'point', + textAlign: opts.textAlign, + textBaseline: opts.textBaseLine || 'top', + maxAngle: opts.maxAngle || Infinity, + // These weird calculations provide the most accurate placement relative to the textarea where people edit their text + offsetX: isMeasurement ? 0 : (label.fontSize || 16) / 2, + offsetY: offsetY, + rotation: -feature.get('_vmf_rotation') || 0, + font: `bold ${label.fontSize || 16}px sans-serif`, + stroke: new olStyleStroke({ + // show a black outline unless the text color is black; then show a white outline + color: label.color === '#000000' ? '#ffffff' : '#000000', + width: 3 + }), + text: getText(label, resolution, opts), + scale: calculateScale(opts.store.map, feature), + fill: new olStyleFill({ + color: label.color || '#ffffff' + }), + image: new olStyleCircle({ + radius: 7, + fill: new olStyleFill({ + color: '#ffcc33' + }) + }), + rotateWithView: false, + overflow: true + } + + return new olStyleText(styleOpts) +} + +function coordinateLabels (multiPoint, resolution, opts) { + return multiPoint.getPoints().map(point => coordinateLabel(point, resolution, opts)) +} + +function coordinateLabel (pointGeometry, resolution, opts) { + const geom = pointGeometry.clone() + const pointFeature = new olFeature({ + geometry: geom, + _vmf_label: { + fontSize: 16 + } + }) + + return new olStyleStyle({ + text: styleText({ + store: opts, + placement: 'point', + maxAngle: Math.PI / 4, + textAlign: undefined, + textBaseline: 'hanging' + }, pointFeature, resolution), + geometry: geom + }) +} + +function lengthLabel (lineGeometry, resolution, opts) { + const geom = lineGeometry.clone() + const perimeterFeature = new olFeature({ + geometry: geom, + _vmf_type: '_vmf_measurement', + _vmf_label: { + fontSize: 16 + } + }) + + return new olStyleStyle({ + text: styleText({ + store: opts, + placement: 'line', + maxAngle: Math.PI / 4, + textAlign: undefined, + textBaseline: 'hanging' + }, perimeterFeature, resolution), + geometry: geom + }) +} + +function perimeterSegmentLabels (polygonGeometry, resolution, opts) { + const labelStyles = [] + const clonedGeom = polygonGeometry.clone() + const perimeterCoords = clonedGeom.getLinearRing(0).getCoordinates() + + for (let i = 0; i < perimeterCoords.length - 1;) { + const segment = [perimeterCoords[i], perimeterCoords[i += 1]] + + if (segment.flat(Infinity).includes(undefined)) break // exit the loop if we get any undefined values. It's better to not label a segment than to break draw entirely. + const segmentGeom = new olGeomLineString(segment) + const segmentFeature = new olFeature({ + geometry: segmentGeom, + _vmf_type: '_vmf_measurement', + _vmf_label: { + fontSize: 16 + } + }) + + labelStyles.push(new olStyleStyle({ + text: styleText({ + store: opts, + placement: 'line', + textBaseline: 'hanging' + }, segmentFeature, resolution), + geometry: segmentGeom + })) + } + + return labelStyles +} + +function areaLabel (polygonGeometry, resolution, opts) { + const areaGeometry = polygonGeometry.clone() + const areaFeature = new olFeature({ + geometry: areaGeometry, + _vmf_type: '_vmf_measurement', + _vmf_label: { + fontSize: 16 + } + }) + + return new olStyleStyle({ + text: styleText({ + store: opts + }, areaFeature, resolution), + geometry: areaGeometry + }) +} + +function centroidLabel (geometry, resolution, opts) { + const point = geometry.getType() === 'Circle ' ? new olPoint(geometry.clone().getCenter()) : olKitTurf(centroid, [geometry]).getGeometry() + const pointFeature = new olFeature({ + geometry: point, + _vmf_type: '_vmf_measurement', // styleText determines the type of label to render based on the feature's type so we need this temporary feature to be a 'measurement' feature + _vmf_label: { + fontSize: 16 + }, + _ol_kit_needs_centroid_label: true + }) + + return new olStyleStyle({ + text: styleText({ + store: opts, + placement: 'point', + maxAngle: Math.PI / 4, + textAlign: undefined, + textBaseline: 'hanging' + }, pointFeature, resolution), + geometry: point + }) +} + +function getVertices (args) { + const { feature } = resolveStyleFunctionArgs(args) + const geometry = feature.getGeometry() + const layout = geometry.getLayout() + + switch (geometry.getType()) { + case 'MultiPolygon': { + const coordinates = geometry.getCoordinates() + const flatCoords = coordinates.flat(Infinity) + const pairedCoords = pairCoordinates(flatCoords, layout.length) + + return new olGeomMultiPoint(pairedCoords, layout) + } + case 'GeometryCollection': { + const deepCoords = getCoordinates(geometry) + const flatCoords = deepCoords.flat(Infinity) + const pairedCoords = pairCoordinates(flatCoords, layout.length) + + return new olGeomMultiPoint(pairedCoords) + } + default: + return new olGeomMultiPoint(pointsFromVertices(geometry)) + } +} + +/** + * Style an ol/Feature with orange circle vertices, a blue outline, an area label, and a perimeter length label. Can be used with individual features as a style function or call it directly to get a style object for use with `vectorContext.drawFeature` + * @function + * @param {object} opts - The config object + * @param {ol/Feature} feature - The feature you want to style + * @param {number} resolution - the resolution of the map + * @returns {object} The style object for the passed feature + */ +export function immediateEditStyle (...args) { + const { feature, resolution, opts = {} } = resolveStyleFunctionArgs(args) + const fill = new olStyleFill({ + color: 'rgba(0, 0, 255, 0.2)' + }) + const stroke = new olStyleStroke({ + color: 'blue', + width: 3 + }) + const image = new olStyleCircle({ + radius: 7, + fill: new olStyleFill({ + color: '#ffcc33' + }) + }) + const vertexGeometry = getVertices(args) + const vertices = [new olStyleStyle({ + image: new olStyleCircle({ + radius: 5, + fill: new olStyleFill({ + color: 'orange' + }) + }), + geometry: vertexGeometry + })] + + // checking to see if opts.geometry is defined allows us to call this function recursively for geometry collections + const geometry = opts.geometry || feature.clone().getGeometry() + const areaLabelsFlag = feature.get('_ol_kit_area_labels') + const distanceLabelsFlag = feature.get('_ol_kit_distance_labels') + const needsVertexLabels = feature.get('_ol_kit_coordinate_labels') !== undefined + const needsCentroidLabels = feature.get('_ol_kit_needs_centroid_label') !== undefined + const needsAreaLabels = opts.showMeasurements && areaLabelsFlag + const needsDistanceLabels = opts.showMeasurements && distanceLabelsFlag + const isNotCircle = feature.get('_ol_kit_draw_mode') !== 'circle' + const vertexLabels = (needsVertexLabels && opts.showMeasurements && isNotCircle) ? coordinateLabels(getVertices(feature), resolution, opts) : [] // eslint-disable-line + const type = geometry.getType() + + switch (type) { + case 'Point': + return [new olStyleStyle({ + image + }), ...vertexLabels] + case 'LineString': { + const lengthLabels = needsDistanceLabels ? [lengthLabel(geometry, resolution, opts)] : [] + + return [new olStyleStyle({ + stroke, + image + }), ...vertices, ...lengthLabels, ...vertexLabels] + } + case 'MultiLineString': { + const lineStrings = geometry.getLineStrings() + const lengthLabels = needsDistanceLabels + ? lineStrings.map(lineString => lengthLabel(lineString, resolution, opts)) : [] + + return [new olStyleStyle({ + stroke, + image + }), ...vertices, ...lengthLabels, ...vertexLabels] + } + case 'MultiPolygon': { + const polygons = geometry.getPolygons() + // create a label for each polygon + const perimeterLabels = needsDistanceLabels + ? polygons.map(polygon => perimeterSegmentLabels(polygon, resolution, opts)) : [] + const areaLabels = needsAreaLabels + ? polygons.map(polygon => areaLabel(polygon, resolution, opts)) : [] + + return [new olStyleStyle({ + stroke, + fill, + image + }), ...vertices, ...areaLabels, ...perimeterLabels.flat(Infinity), ...vertexLabels] + } + case 'Polygon': { + let labels = vertexLabels // eslint-disable-line + + if (needsAreaLabels) labels.push(areaLabel(geometry, resolution, opts)) + if (needsDistanceLabels && isNotCircle) labels = [...labels, ...perimeterSegmentLabels(geometry, resolution, opts)] //eslint-disable-line + if (needsCentroidLabels) labels.push(centroidLabel(geometry, resolution, opts)) + + return [new olStyleStyle({ + stroke, + fill, + image + }), ...vertices, ...labels] + } + case 'GeometryCollection': { + // Recursive. Since feature stores our metadata it needs to be preserved. Therefore we pass the geometry we want to use through the opts object. + const componentStyles = geometry.getGeometries().map(geom => { + return immediateEditStyle.apply(this, [Object.assign(opts, { geometry: geom }), feature, resolution]) + }) + const flatStyles = componentStyles.flat(Infinity) + + return flatStyles + } + case 'Circle': { + const labels = vertexLabels + + if (needsAreaLabels) labels.push(areaLabel(geometry, resolution, opts)) + if (needsCentroidLabels) labels.push(centroidLabel(geometry, resolution, opts)) + + return [new olStyleStyle({ + stroke, + fill + }), ...labels] + } + default: + return [new olStyleStyle({ + stroke, + fill, + image + }), ...vertices] + } +} diff --git a/src/FeatureEditor/utils.js b/src/FeatureEditor/utils.js new file mode 100644 index 00000000..2e6965fa --- /dev/null +++ b/src/FeatureEditor/utils.js @@ -0,0 +1,408 @@ +import ugh from 'ugh' +import { radiansToDegrees } from '@turf/helpers' +import turfDestination from '@turf/destination' +import turfDistance from '@turf/distance' +import turfBearing from '@turf/bearing' +import * as turfAssert from '@turf/invariant' + +import olGeomPolygon, { fromCircle } from 'ol/geom/Polygon' +import * as olProj from 'ol/proj' + +import olFormatGeoJson from 'ol/format/GeoJSON' +import olFeature from 'ol/Feature' +import olCollection from 'ol/Collection' + +const defaultDataProjection = 'EPSG:4326' + +export function olKitTurf (turfFunc, argArray, projection = 'EPSG:3857') { + const transformedArgs = argArray.map((arg) => { + return transform(arg, projection, true) + }) + const turfResults = turfFunc.apply(this, transformedArgs) + + return transform(turfResults, projection, false) +} + +export function transform (value, projection, toWgs84) { + const { type } = describeType(value) + const format = new olFormatGeoJson({ + defaultDataProjection, + featureProjection: projection + }) + + return toWgs84 + ? input(value, projection, type, format) + : output(value, projection, type, format) +} + +export function input (value, projection, type, format) { + switch (type) { + case 'coordinate': + return olProj.toLonLat(value, projection) + case 'extent': + return olProj.transformExtent(value, projection, defaultDataProjection) + case 'olGeometry': + return format.writeGeometryObject(value) + case 'olFeature': + return format.writeFeatureObject(value) + case 'Array': + return format.writeFeaturesObject(value) + case 'olCollection': + return format.writeFeaturesObject(value.getArray()) + default: + return value + } +} + +export function output (value, projection, type, format) { + switch (type) { + case 'coordinate': + return olProj.transform(value, 'EPSG:4326', projection) + case 'extent': + return olProj.transformExtent(value, defaultDataProjection, projection) + case 'geojsonGeometry': + return format.readGeometry(value) + case 'geojsonFeature': + return format.readFeature(value) + case 'Array': + return value.map(item => { + const itemType = describeType(item).type + + return output(item, projection, itemType, format) + }) + case 'geojsonCollection': + return format.readFeatures(value) + default: + return value + } +} +export function describeType (query) { + if (Array.isArray(query)) { + const containsNumber = assertTurf('containsNumber', false, [query]) + + if (containsNumber) { + switch (query.length) { + case 2: + assertTurf('containsNumber', false, [query]) + + return { + type: 'coordinate' + } + case 4: + assertTurf('containsNumber', false, [query]) + + return { + type: 'extent' + } + default: + return { + type: 'Array', + contains: query.map(val => describeType(val)) + } + } + } else { + return { + type: 'Array', + contains: query.map(val => describeType(val)) + } + } + } else if (query instanceof olFeature) { + return { + type: 'olFeature', + geomType: query.getGeometry().getType() + } + } else if (query.getType) { + return { + type: 'olGeometry', + geomType: query.getType() + } + } else if (query instanceof olCollection) { + return { + type: 'olCollection', + contains: query.getArray().map(val => describeType(val)) + } + } else if (assertTurf('geojsonType', false, query, 'FeatureCollection', 'describeType')) { + return { + type: 'geojsonFeatureCollection' + } + } else if (assertTurf('geojsonType', false, query, 'Feature', 'describeType')) { + return { + type: 'geojsonFeature' + } + } else if (assertTurf('geojsonType', false, query, 'Polygon', 'describeType')) { + return { + type: 'geojsonGeometry', + geomType: 'Polygon' + } + } else if (assertTurf('geojsonType', false, query, 'LineString', 'describeType')) { + return { + type: 'geojsonGeometry', + geomType: 'LineString' + } + } else if (assertTurf('geojsonType', false, query, 'Point', 'describeType')) { + return { + type: 'geojsonGeometry', + geomType: 'Point' + } + } else { + return { + type: typeof query + } + } +} + +export function assertTurf (assertion, hard, ...args) { + try { + turfAssert[assertion].apply(null, args) + } catch (error) { + if (hard) { + throw new Error(error.message) + } else { + return false + } + } + + return true +} + +export function coordValidator (coord) { + return coord.map((val) => { + return isNaN(val) ? 0 : val || 0 + }) +} + +export function getCoordinates (geometry, optCircle = false) { + switch (geometry.getType()) { + case 'GeometryCollection': + return geometry.getGeometries().map(geom => getCoordinates(geom, optCircle)) + case 'Circle': + return optCircle ? geometry.getCenter() : fromCircle(geometry).getCoordinates() + default: + try { + return geometry.getCoordinates() + } catch (e) { + return undefined + } + } +} + +export const getHeading = (coordinate, index, allowNegative = true) => { + const absCoord = Math.abs(coordinate) + const coord = allowNegative ? coordinate : absCoord + + if (index === 0 && absCoord <= 180) { + if (coordinate > 0) { + return `${coord}˚ E` + } else { + return `${coord}˚ W` + } + } else if (coordinate > 0 && absCoord) { + return `${coord}˚ N` + } else { + return `${coord}˚ S` + } +} + +export function coordDiff (coord1, coord2, view) { + const projection = view.getProjection() + + const args = [coordValidator(coord1), coordValidator(coord2)] + + const distance = olKitTurf(turfDistance, args, projection) + const bearing = olKitTurf(turfBearing, args, projection) + + return { distance, bearing } +} + +export function targetDestination (startCoord, distance, bearing, view) { + const projection = view.getProjection() + + const coord = olProj.toLonLat(coordValidator(startCoord), projection) + const destination = turfDestination(coord, distance, bearing) + + return olProj.fromLonLat(destination.geometry.coordinates, projection) +} + +export function normalizeExtent (extent) { + const newExtent = [] + + newExtent[0] = extent[1] + newExtent[1] = extent[0] + newExtent[2] = extent[3] + newExtent[3] = extent[2] + + return newExtent.map((coords) => { + if (coords === 90) { + return 89.99 + } else if (coords === -90) { + return -89.99 + } else { + return coords + } + }) +} + +export function pairCoordinates (flatCoords, groupSize = 2) { + const pairedCoords = [] + + for (let i = 0; i < flatCoords.length - 1;) { + pairedCoords.push([flatCoords[i], flatCoords[i + 1]]) + i += groupSize + } + + return pairedCoords +} + +export function convertXYtoLatLong (map, x, y) { + const coords = map.getCoordinateFromPixel([x, y]) + const transformed = olProj.transform(coords, map.getView().getProjection().getCode(), 'EPSG:4326') + const longitude = Number((Number(transformed[0] || 0) % 180).toFixed(6)) + const latitude = Number((transformed[1] || 0).toFixed(6)) + + return { + longitude, + latitude + } +} + +export function createNewBoxGeom (map, geometry, height, width) { + const coords = geometry.getCoordinates() + const topLeft = map.getPixelFromCoordinate(coords) + + // TODO: ensure this never gets called before the map is loaded + // if this util is called before the map is loaded it's likely possible to get a null value + if (topLeft) { + const moveRight = topLeft[0] + 400 // TODO: change this when we make the width the right dimension again + const moveDown = topLeft[1] + height + const pixelShape = [ + topLeft, + [moveRight, topLeft[1]], + [moveRight, moveDown], + [topLeft[0], moveDown], + topLeft + ] + + return new olGeomPolygon([pixelShape.map(pixel => map.getCoordinateFromPixel(pixel))]) + // TODO: no need to do this if we never get a null value for topLeft (see above comments) + } else { + return geometry + } +} + +export function createTextBox (fontSize = 16, text, resolution) { + const textbox = document.createElement('div') + + textbox.class = 'text-box-size' + textbox.style = `font-size: ${fontSize}; position: absolute; visibility: hidden; height: auto; width: auto; white-space: nowrap;` + textbox.innerHTML += text + document.getElementById('_vmf_hook').appendChild(textbox) + + return { + height: textbox.clientHeight - 1, + width: textbox.clientWidth - resolution + } +} + +export function translatePoint (pointGeom, angle = 0, distance, map) { + const view = map.getView() + const res = view.getResolution() + const projection = view.getProjection() + const properAngle = -radiansToDegrees(angle) + const dist = distance * res + const oneEighty = properAngle > 180 ? properAngle - 360 : properAngle + + return olKitTurf(turfDestination, [pointGeom, dist, oneEighty], projection).getGeometry() +} + +export function pointsFromVertices (geometry) { + const featureType = geometry.getType() + const coordinates = getCoordinates(geometry, true) + + if (featureType === 'Polygon' || featureType === 'MultiLineString') { + return [].concat(...coordinates) + } else if (featureType === 'LineString' || featureType === 'MultiPoint') { + return coordinates + } else if (featureType === 'Point' || featureType === 'Circle') { + return [coordinates] + } else { + ugh.warn('Geometries of type %s are not supported', featureType) + + return coordinates + } +} + +export function getText (labelProps = { text: '' }, resolution, opts = {}) { + if (resolution > opts.maxreso) return '' + + switch (opts.type) { + case 'hide': + return '' + case 'shorten': + return labelProps.text.substring(0, 12) + case 'wrap': + return stringDivider(labelProps.text, 32, '\n') + default: + return labelProps.text + } +} + +export function getTextWidth (text = '') { + // We want the width of the longest line of text so split with \n and reverse sort by length + const lines = text.split(`\n`).sort((a, b) => b.length - a.length) + const ctx = document.createElement('canvas').getContext('2d') + const textMetrics = ctx.measureText(lines[0]) + const fontSize = ctx.font.split('px')[0] || 16 + + return { + width: textMetrics.width, + fontSize + } +} + +export function stringDivider (str, width, spaceReplacer) { + if (str.length > width) { + let p = width + + while (p > 0 && (str[p] !== ' ' && str[p] !== '-')) { + p-- + } + if (p > 0) { + let left + + if (str.substring(p, p + 1) === '-') { + left = str.substring(0, p + 1) + } else { + left = str.substring(0, p) + } + const right = str.substring(p + 1) + + return left + spaceReplacer + stringDivider(right, width, spaceReplacer) + } + } + + return str +} + +export function calculateScale (map, feature) { + const currentResolution = map.getView().getResolution() + const vmfLabel = feature.get('_vmf_label') || {} + const labelResolution = vmfLabel.resolution || currentResolution + const scaleFactor = labelResolution / currentResolution + + return vmfLabel.scaling ? scaleFactor : 1 +} + +export function formatStyleString (textStyle) { + const font = textStyle.getFont() || 14 + const scale = textStyle.getScale() || 1 + const px = font.indexOf('px') + const size = font.slice(px - 2, px) + const scaledSize = (size - 6) * scale // Size - 6 is just done to make the text appear the same relative size as it is on the map + const scaledFont = font.replace(`${size}px`, `${scaledSize}px`) + const fillColor = textStyle.getFill().getColor() + const stroke = textStyle.getStroke() + const strokeColor = stroke.getColor() + const strokeWidth = stroke.getWidth() + + return `font: ${scaledFont}; letter-spacing: 0px; paint-order: stroke; stroke: ${strokeColor}; stroke-width: ${strokeWidth}; fill: ${fillColor}` +} diff --git a/src/LayerPanel/LayerPanelActionImport/utils.js b/src/LayerPanel/LayerPanelActionImport/utils.js index f2ec3cd5..638d8e00 100644 --- a/src/LayerPanel/LayerPanelActionImport/utils.js +++ b/src/LayerPanel/LayerPanelActionImport/utils.js @@ -27,7 +27,6 @@ export function arrRegexIndexOf (arr, re) { * Converts the given file into a layer * @function convertFileToLayer * @category LayerPanel - * @since 6.5.0 * @param {Blob} [file] - the file to be converted. Accepts, 'kmz', 'kml', 'geojson', 'wkt', 'csv', 'zip', and 'json' file types. * @param {olMap} [map] - the openlayers map */ diff --git a/src/Popup/Popup.js b/src/Popup/Popup.js index 5bc9622d..d8fcdfba 100644 --- a/src/Popup/Popup.js +++ b/src/Popup/Popup.js @@ -164,7 +164,8 @@ class Popup extends Component { actions={actions} features={features} loading={loading} - onClose={this.hidePopup} /> + onClose={this.hidePopup} + /> )} , map.getTargetElement() diff --git a/src/Popup/PopupActions/PopupActionEdit/PopupActionEdit.js b/src/Popup/PopupActions/PopupActionEdit/PopupActionEdit.js new file mode 100644 index 00000000..a40a77b0 --- /dev/null +++ b/src/Popup/PopupActions/PopupActionEdit/PopupActionEdit.js @@ -0,0 +1,65 @@ +import React, { Component } from 'react' +import PropTypes from 'prop-types' +import { PopupActionItem } from 'Popup' +import { connectToContext } from 'Provider' + +class PopupActionEdit extends Component { + constructor (props) { + super(props) + + this.state = { + showFeatureEditor: false, + style: undefined + } + } + + componentDidMount () { + const { feature } = this.props + const style = feature.getStyle() // grab the original feature's style + + this.setState({ style }) // save that style to state + } + + onClick = () => { + const { addEditFeatureToContext, feature, onEdit } = this.props + + addEditFeatureToContext(feature) + onEdit(feature) + } + + render () { + const { translations } = this.props + + return ( +
+ +
+ ) + } +} + +PopupActionEdit.propTypes = { + translations: PropTypes.object, + feature: PropTypes.object, + map: PropTypes.object, + onEdit: PropTypes.func, + showPopup: PropTypes.func, + areaUOM: PropTypes.string, + distanceUOM: PropTypes.string, + persistedState: PropTypes.object, + persistedStateKey: PropTypes.string, + persistState: PropTypes.func, + onEdit: PropTypes.func, + addEditFeatureToContext: PropTypes.func +} + +PopupActionEdit.defaultProps = { + showPopup: () => false, + onEdit: () => false, + persistedStateKey: 'GeokitPopup', + translations: { + 'popup.editGeom': 'Edit Geometry' + } +} + +export default connectToContext(PopupActionEdit) diff --git a/src/Popup/PopupActions/PopupActionEdit/index.js b/src/Popup/PopupActions/PopupActionEdit/index.js new file mode 100644 index 00000000..8ac3d2d2 --- /dev/null +++ b/src/Popup/PopupActions/PopupActionEdit/index.js @@ -0,0 +1 @@ +export { default as PopupActionEdit } from './PopupActionEdit' diff --git a/src/Popup/PopupActions/index.js b/src/Popup/PopupActions/index.js index 52798031..757e40ca 100644 --- a/src/Popup/PopupActions/index.js +++ b/src/Popup/PopupActions/index.js @@ -1,2 +1,3 @@ export * from './PopupActionCopyWkt' export * from './PopupActionGoogleMaps' +export * from './PopupActionEdit' diff --git a/src/Popup/PopupInsert/PopupDefaultInsert.js b/src/Popup/PopupInsert/PopupDefaultInsert.js index 27a77d87..0cc26406 100644 --- a/src/Popup/PopupInsert/PopupDefaultInsert.js +++ b/src/Popup/PopupInsert/PopupDefaultInsert.js @@ -9,6 +9,7 @@ import { PopupActionGoogleMaps } from 'Popup/PopupActions/PopupActionGoogleMaps' import { PopupActionRemove } from 'Popup/PopupActions/PopupActionRemove' import { PopupActionDuplicate } from 'Popup/PopupActions/PopupActionDuplicate' import { PopupActionCut } from 'Popup/PopupActions/PopupActionCut' +import { PopupActionEdit } from 'Popup/PopupActions/PopupActionEdit' import { PopupActionZoomToExtent } from 'Popup/PopupActions/PopupActionZoomToExtent' import PopupDefaultPage from './PopupDefaultPage' import PopupPageLayout from './PopupPageLayout' @@ -32,7 +33,7 @@ class PopupDefaultInsert extends Component { super(props) this.state = { - selectedIdx: 0 + selectedIdx: 0, } } @@ -86,7 +87,7 @@ class PopupDefaultInsert extends Component { } render () { - const { actions, features, loading, onClose, onSettingsClick, propertiesFilter, translations } = this.props + const { actions, features, loading, onClose, onSettingsClick, propertiesFilter, translations, onEdit } = this.props const { selectedIdx } = this.state const getChildren = feature => { @@ -96,6 +97,7 @@ class PopupDefaultInsert extends Component { , , , + , ] if (!pointGeom) defaultActions = [...defaultActions, ] @@ -138,7 +140,8 @@ PopupDefaultInsert.defaultProps = { onClose: () => {}, onSelectFeature: () => {}, propertiesFilter: sanitizeProperties, - translations: en + translations: en, + onEdit: () => false, } PopupDefaultInsert.propTypes = { @@ -168,7 +171,8 @@ PopupDefaultInsert.propTypes = { '_ol_kit.PopupDefaultPage.details': PropTypes.string, '_ol_kit.PopupDefaultPage.actions': PropTypes.string, '_ol_kit.PopupDefaultPage.customize': PropTypes.string - }).isRequired + }).isRequired, + onEdit: PropTypes.func, } export default connectToContext(PopupDefaultInsert) diff --git a/src/Provider/Provider.js b/src/Provider/Provider.js index 2873f00c..6241baca 100644 --- a/src/Provider/Provider.js +++ b/src/Provider/Provider.js @@ -35,6 +35,10 @@ class Provider extends React.Component { this.setState({ mapContext }) } + addEditFeatureToContext = editFeature => { + this.setState({ editFeature }) + } + getContextValue = () => { const { contextProps, map: mapProp, maps: mapsProp, translations } = this.props const { mapContext } = this.state @@ -52,6 +56,8 @@ class Provider extends React.Component { addMapToContext: this.addMapToContext, map, maps, + editFeature: this.state.editFeature, + addEditFeatureToContext: this.addEditFeatureToContext, persistedState: this.state, persistState: this.persistState, translations, @@ -88,7 +94,9 @@ Provider.propTypes = { /** Array of Openlayers map objects for components that support multi-map (this will override anything passed to map prop) */ maps: PropTypes.array, /** Object with key/value pairs for component translation strings */ - translations: PropTypes.object + translations: PropTypes.object, + /** An Ol feature object that is being edited */ + editFeature: PropTypes.object } export default Provider diff --git a/src/__snapshots__/index.test.js.snap b/src/__snapshots__/index.test.js.snap index a21646cf..efb5d0ef 100644 --- a/src/__snapshots__/index.test.js.snap +++ b/src/__snapshots__/index.test.js.snap @@ -27,6 +27,7 @@ Object { "DrawPin": [Function], "DrawPoint": [Function], "DrawPolygon": [Function], + "FeatureEditor": [Function], "FlexMap": Object { "$$typeof": Symbol(react.forward_ref), "attrs": Array [], @@ -219,6 +220,7 @@ Object { "MultiMapManager": [Function], "Popup": [Function], "PopupActionCopyWkt": [Function], + "PopupActionEdit": [Function], "PopupActionGoogleMaps": [Function], "PopupActionGroup": [Function], "PopupActionItem": [Function], @@ -279,6 +281,7 @@ Object { "TabbedPanelPage": [Function], "TimeSlider": [Function], "TimeSliderBase": [Function], + "Translate": [Function], "VectorLayer": [Function], "VectorTileLayer": [Function], "ZoomControls": [Function], diff --git a/src/classes/Translate.js b/src/classes/Translate.js new file mode 100644 index 00000000..4ecfdff3 --- /dev/null +++ b/src/classes/Translate.js @@ -0,0 +1,35 @@ +import ugh from 'ugh' +import olInteractionTranslate from 'ol/interaction/Translate' +import * as olArray from 'ol/array' + +export default class Translate extends olInteractionTranslate { + constructor (props) { + super(props) + + return this + } + + featuresAtPixel_ = (pixel, map) => { + const mapFeature = map.forEachFeatureAtPixel(pixel, + function (feature) { + if (!this.features_ || + olArray.includes(this.features_.getArray(), feature)) { + return feature + } + }.bind(this), { + layerFilter: this.layerFilter_, + hitTolerance: this.hitTolerance_ + }) + + try { + const coordinate = map.getCoordinateFromPixel(pixel) + const intersectFeatures = this.features_.getArray().filter(f => f.getGeometry().intersectsCoordinate(coordinate)) + + return mapFeature || intersectFeatures[intersectFeatures.length - 1] + } catch (e) { + ugh.warn(`Custom functionality failed: ${e.message}`, e) + + return super.featuresAtPixel_(pixel, map) + } + } +} diff --git a/src/classes/index.js b/src/classes/index.js index c64a1de9..49c1806b 100644 --- a/src/classes/index.js +++ b/src/classes/index.js @@ -1,3 +1,4 @@ export { default as VectorLayer } from './VectorLayer' export { default as VectorTileLayer } from './VectorTileLayer' export { default as Preferences } from './Preferences' +export { default as Translate } from './Translate' diff --git a/src/index.js b/src/index.js index a80159a5..5744b20b 100644 --- a/src/index.js +++ b/src/index.js @@ -18,3 +18,4 @@ export * from './Snackbar' export * from './TabbedPanel' export * from './TimeSlider' export * from './classes' +export * from './FeatureEditor' diff --git a/src/locales/en.js b/src/locales/en.js index 3d45df27..49511542 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -125,5 +125,9 @@ export default { // Google Directions '_ol_kit.directions.placeOriginPoint': 'Place a point at your origin', '_ol_kit.directions.placeWaypoint': 'Now place a point to add a destination and click finish when you\'re done:', - '_ol_kit.directions.waypointLabel': 'Waypoint' + '_ol_kit.directions.waypointLabel': 'Waypoint', + // FeatureEditor + '_ol_kit.edit.cancel': 'Cancel', + '_ol_kit.edit.finish': 'Finish', + '_ol_kit.edit.rotate': 'Rotate' }