diff --git a/package-lock.json b/package-lock.json index 6139953dcac7fb8c6e4e70fcce3acdb9a6119dc7..a5848437d7f6b2b08a9cbd23acc1605d3eaa8322 100644 --- a/package-lock.json +++ b/package-lock.json @@ -849,9 +849,9 @@ } }, "@babel/runtime": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", - "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", + "version": "7.4.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", + "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", "requires": { "regenerator-runtime": "^0.13.2" } @@ -2232,6 +2232,14 @@ "semver": "^5.5.0" } }, + "@babel/runtime": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz", + "integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==", + "requires": { + "regenerator-runtime": "^0.13.2" + } + }, "semver": { "version": "5.7.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", @@ -7671,6 +7679,11 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.5.1.tgz", "integrity": "sha512-ekM9KAeG99tYisNBg0IzEywAlp0hYI5XRipsqRXyRTeuU8jcuntilpp+eFf5gaE0xubc9RuSNIVtByEKwqFV0w==" }, + "leaflet-draw": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/leaflet-draw/-/leaflet-draw-1.0.4.tgz", + "integrity": "sha512-rsQ6saQO5ST5Aj6XRFylr5zvarWgzWnrg46zQ1MEOEIHsppdC/8hnN8qMoFvACsPvTioAuysya/TVtog15tyAQ==" + }, "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", @@ -7786,6 +7799,11 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, + "lodash-es": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.11.tgz", + "integrity": "sha512-DHb1ub+rMjjrxqlB3H56/6MXtm1lSksDp2rA2cNWjG8mlDUYFhUj3Di2Zn5IwSU87xLv8tNIQ7sSwE/YOX/D/Q==" + }, "lodash._reinterpolate": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz", @@ -10149,16 +10167,14 @@ "fast-deep-equal": "^2.0.1", "hoist-non-react-statics": "^3.3.0", "warning": "^4.0.3" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.4.5", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.5.tgz", - "integrity": "sha512-TuI4qpWZP6lGOGIuGWtp9sPluqYICmbk8T/1vpSysqJxRPkudh/ofFWyqdcMsDf2s7KvDL4/YHgKyvcS3g9CJQ==", - "requires": { - "regenerator-runtime": "^0.13.2" - } - } + } + }, + "react-leaflet-draw": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/react-leaflet-draw/-/react-leaflet-draw-0.19.0.tgz", + "integrity": "sha512-aOB7Nqgl79l62L7vHxhdyKJD6ep+1Q+qTfnrYfmcgF+yK0A1lQA2fUv9N4C0HCbejcyiqx1XYchSCw9Q+Vtc3A==", + "requires": { + "lodash-es": "^4.17.10" } }, "react-scripts": { diff --git a/package.json b/package.json index fd4f1ef5ed723b41f0a52817ea2c0c87396333a4..af5b9cd24a2375a6c95fc5b8708d9bc55264571f 100644 --- a/package.json +++ b/package.json @@ -3,13 +3,17 @@ "version": "0.1.0", "private": true, "dependencies": { + "@babel/runtime": "7.4.5", "leaflet": "^1.5.1", + "leaflet-draw": "^1.0.4", "react": "^16.8.6", "react-cookie": "^4.0.0", "react-dom": "^16.8.6", "react-leaflet": "^2.3.0", - "react-scripts": "3.0.1", + "react-leaflet-draw": "0.19.0", "universal-cookie": "^4.0.0" + "react-scripts": "3.0.1" + }, "scripts": { "start": "react-scripts start", diff --git a/src/App.js b/src/App.js index 4ee86192799077399a359cda79b0a7408303bbe6..b5ee0cf4c7ef5a434a32cde764f4c1b03cc231ee 100644 --- a/src/App.js +++ b/src/App.js @@ -1,4 +1,8 @@ import React, { Component } from 'react'; +import '../node_modules/leaflet-draw/dist/leaflet.draw.css' +import './App.css'; + +import UserMap from './components/UserMap.js' import styles from './App.css'; import UserMap from './components/UserMap'; diff --git a/src/App_3.js b/src/App_3.js new file mode 100644 index 0000000000000000000000000000000000000000..b2a585516702a125c7e11dd28aa609985aa4a686 --- /dev/null +++ b/src/App_3.js @@ -0,0 +1,367 @@ +import React, { Component } from 'react'; +import { Map, TileLayer, Circle, FeatureGroup } from 'react-leaflet'; +import L from 'leaflet'; +import { EditControl } from 'react-leaflet-draw'; + +// work around broken icons when using webpack, see https://github.com/PaulLeCam/react-leaflet/issues/255 + +delete L.Icon.Default.prototype._getIconUrl; +L.Icon.Default.mergeOptions({ + iconRetinaUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', + iconUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-icon.png', + shadowUrl: 'https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.0/images/marker-shadow.png', +}); + +// + +let polyline; + +export default class EditControlExample extends Component { + + // see http://leaflet.github.io/Leaflet.draw/docs/leaflet-draw-latest.html#l-draw-event for leaflet-draw events doc + + _onEdited = (e) => { + + let numEdited = 0; + e.layers.eachLayer( (layer) => { + numEdited += 1; + }); + console.log(`_onEdited: edited ${numEdited} layers`, e); + + this._onChange(); + } + + _onCreated = (e) => { + let type = e.layerType; + let layer = e.layer; + if (type === 'marker') { + // Do marker specific actions + console.log("_onCreated: marker created", e); + } + else { + console.log("_onCreated: something else created:", type, e); + } + // Do whatever else you need to. (save to db; etc) + + this._onChange(); + } + + _onDeleted = (e) => { + + let numDeleted = 0; + e.layers.eachLayer( (layer) => { + numDeleted += 1; + }); + console.log(`onDeleted: removed ${numDeleted} layers`, e); + + this._onChange(); + } + + _onMounted = (drawControl) => { + console.log('_onMounted', drawControl); + } + + _onEditStart = (e) => { + console.log('_onEditStart', e); + } + + _onEditStop = (e) => { + console.log('_onEditStop', e); + } + + _onDeleteStart = (e) => { + console.log('_onDeleteStart', e); + } + + _onDeleteStop = (e) => { + console.log('_onDeleteStop', e); + } + + render() { + return ( + <Map className='map' center={[37.8189, -122.4786]} zoom={13} zoomControl={false}> + <TileLayer + attribution='© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' + url="http://{s}.tile.osm.org/{z}/{x}/{y}.png" + /> + <FeatureGroup ref={ (reactFGref) => {this._onFeatureGroupReady(reactFGref);} }> + <EditControl + position='topright' + onEdited={this._onEdited} + onCreated={this._onCreated} + onDeleted={this._onDeleted} + onMounted={this._onMounted} + onEditStart={this._onEditStart} + onEditStop={this._onEditStop} + onDeleteStart={this._onDeleteStart} + onDeleteStop={this._onDeleteStop} + draw={{ + rectangle: false + }} + /> + </FeatureGroup> + </Map> + ); + } + + _editableFG = null + + _onFeatureGroupReady = (reactFGref) => { + + // populate the leaflet FeatureGroup with the geoJson layers + + let leafletGeoJSON = new L.GeoJSON(getGeoJson()); + let leafletFG = reactFGref.leafletElement; + + leafletGeoJSON.eachLayer( (layer) => { + leafletFG.addLayer(layer); + }); + + // store the ref for future access to content + + this._editableFG = reactFGref; + } + + _onChange = () => { + + // this._editableFG contains the edited geometry, which can be manipulated through the leaflet API + + const { onChange } = this.props; + + if (!this._editableFG || !onChange) { + return; + } + + const geojsonData = this._editableFG.leafletElement.toGeoJSON(); + onChange(geojsonData); + } +} + +// data taken from the example in https://github.com/PaulLeCam/react-leaflet/issues/176 + +function getGeoJson() { + return { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + -122.47979164123535, + 37.830124319877235 + ], + [ + -122.47721672058105, + 37.809377088502615 + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [ + -122.46923446655273, + 37.80293476836673 + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [ + -122.48399734497069, + 37.83466623607849 + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Point", + "coordinates": [ + -122.47867584228514, + 37.81893781173967 + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.48069286346434, + 37.800637436707525 + ], + [ + -122.48069286346434, + 37.803104310307276 + ], + [ + -122.47950196266174, + 37.803104310307276 + ], + [ + -122.47950196266174, + 37.800637436707525 + ], + [ + -122.48069286346434, + 37.800637436707525 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.48103886842728, + 37.833075326166274 + ], + [ + -122.48065531253813, + 37.832558431940114 + ], + [ + -122.4799284338951, + 37.8322660885204 + ], + [ + -122.47963070869446, + 37.83231693093747 + ], + [ + -122.47948586940764, + 37.832467339549524 + ], + [ + -122.47945636510849, + 37.83273426112019 + ], + [ + -122.47959315776825, + 37.83289737938241 + ], + [ + -122.48004108667372, + 37.833109220743104 + ], + [ + -122.48058557510376, + 37.83328293020496 + ], + [ + -122.48080283403395, + 37.83332529830436 + ], + [ + -122.48091548681259, + 37.83322785163939 + ], + [ + -122.48103886842728, + 37.833075326166274 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + -122.48043537139893, + 37.82564992009924 + ], + [ + -122.48129367828368, + 37.82629397920697 + ], + [ + -122.48240947723389, + 37.82544653184479 + ], + [ + -122.48373985290527, + 37.82632787689904 + ], + [ + -122.48425483703613, + 37.82680244295304 + ], + [ + -122.48605728149415, + 37.82639567223645 + ], + [ + -122.4898338317871, + 37.82663295542695 + ], + [ + -122.4930953979492, + 37.82415839321614 + ], + [ + -122.49700069427489, + 37.821887146654376 + ], + [ + -122.4991464614868, + 37.82171764783966 + ], + [ + -122.49850273132326, + 37.81798857543524 + ], + [ + -122.50923156738281, + 37.82090404811055 + ], + [ + -122.51232147216798, + 37.823344820392535 + ], + [ + -122.50150680541992, + 37.8271414168374 + ], + [ + -122.48743057250977, + 37.83093781796035 + ], + [ + -122.48313903808594, + 37.82822612280363 + ], + [ + -122.48043537139893, + 37.82564992009924 + ] + ] + ] + } + } + ] + } +} \ No newline at end of file diff --git a/src/components/DrawTools.js b/src/components/DrawTools.js new file mode 100644 index 0000000000000000000000000000000000000000..791233b3996d525e4c1da055f69011f66546aa46 --- /dev/null +++ b/src/components/DrawTools.js @@ -0,0 +1,63 @@ +import React, {Component} from 'react'; +import {Â EditControl } from "react-leaflet-draw" +import { + FeatureGroup, +} from 'react-leaflet' + +class DrawTools extends Component { + _onCreated = (e) => { + let type = e.layerType; // from the example; isn't used right now, but may be relevant in the future + let layer = e.layer; + let geoJSON = layer.toGeoJSON(); + console.log(JSON.stringify(geoJSON, null, 4)); // makes the output readable in the console + } + + render() { + return ( + // "It's important to wrap EditControl component into FeatureGroup component from react-leaflet. The elements you draw will be added to this FeatureGroup layer, when you hit edit button only items in this layer will be edited." + <FeatureGroup> + <EditControl + position='topright' + onCreated={this._onCreated} + draw={{ + circle: { + repeatMode: true, // allows using the tool again after finishing the previous shape + shapeOptions: { + color: '#f9f10c', + opacity: 100 + } + }, + rectangle: { + repeatMode: true + }, + polygon: { + repeatMode: true, + allowIntersection: false, // Restricts shapes to simple polygons + drawError: { + color: '#e1e100', // Color the shape will turn when intersects + message: '<strong>Oh snap!<strong> you can\'t draw that!' // Message that will show when intersect + }, + shapeOptions: { + color: '#ed2572', + opacity: 100 + } + }, + polyline: { + repeatMode: true, + shapeOptions: { + color: '#ed2572', + opacity: 100 + } + }, + marker: { + repeatMode: true + }, + circlemarker: false + }} + /> + </FeatureGroup> + ) + } +} + +export default DrawTools; \ No newline at end of file diff --git a/src/components/UserMap.js b/src/components/UserMap.js index 3f7e559b1b63f29a1f3840f0aae50234590db1de..cb64d1aeadbed37593062118c46849b28c37b1bc 100644 --- a/src/components/UserMap.js +++ b/src/components/UserMap.js @@ -1,22 +1,29 @@ -import React, { Component } from 'react'; -import { Map, TileLayer, Marker, Popup } from 'react-leaflet'; +import React, {Component} from 'react'; +import { + Map, + TileLayer, + ZoomControl +} from 'react-leaflet' +import DrawTools from './DrawTools.js' + class UserMap extends Component { - render() { - return ( - <Map className='map' center={this.props.position} zoom={this.props.zoom}> - <TileLayer - attribution='Maanmittauslaitoksen kartta' - url=' https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg' - /> - <Marker position={this.props.position}> - <Popup> - Se on perjantai, my dudes <br /> - </Popup> - </Marker> - </Map> - ); - } + render() { + return ( + <Map + className="map" + center={this.props.position} + zoom={this.props.zoom} + > + <TileLayer + attribution='Maanmittauslaitoksen kartta' + url=" https://tiles.kartat.kapsi.fi/peruskartta/{z}/{x}/{y}.png" + />' + <ZoomControl position='topright' /> + <DrawTools position={this.props.position} /> + </Map> + ) + } } -export default UserMap; +export default UserMap; \ No newline at end of file