Giter Club home page Giter Club logo

react-leaflet-draw's Introduction

React-Leaflet-Draw

React component build on top of React-Leaflet that integrate leaflet-draw feature.

Install

npm install react-leaflet-draw

Getting started

First, include leaflet & leaflet-draw styles in your project

<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.min.css"/>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css"/>

or by including

node_modules/leaflet/dist/leaflet.css
node_modules/leaflet-draw/dist/leaflet.draw.css

You might need to add one more rule missing in the current css:

  .sr-only {
    display: none;
  }

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.

import { Map, TileLayer, FeatureGroup, Circle } from 'react-leaflet';
import { EditControl } from "react-leaflet-draw"

const Component = () => (
  <FeatureGroup>
    <EditControl
      position='topright'
      onEdited={this._onEditPath}
      onCreated={this._onCreate}
      onDeleted={this._onDeleted}
      draw={{
        rectangle: false
      }}
    />
    <Circle center={[51.51, -0.06]} radius={200} />
  </FeatureGroup>
);

For more details on how to use this plugin check out the examples example.

  • yarn example:class to compile the class example
  • yarn example:hooks to compile and run the hooks example

You can pass more options on draw object, this informations can be found here

EditControl API

Props

name type description
position string control group position
draw object enable/disable draw controls
edit object enable/disable edit controls
onEdited function hook to leaflet-draw's draw:edited event
onCreated function hook to leaflet-draw's draw:created event
onDeleted function hook to leaflet-draw's draw:deleted event
onMounted function hook to leaflet-draw's draw:mounted event
onEditStart function hook to leaflet-draw's draw:editstart event
onEditStop function hook to leaflet-draw's draw:editstop event
onDeleteStart function hook to leaflet-draw's draw:deletestart event
onDeleteStop function hook to leaflet-draw's draw:deletestop event
onDrawStart function hook to leaflet-draw's draw:drawstart event
onDrawStop function hook to leaflet-draw's draw:drawstop event
onDrawVertex function hook to leaflet-draw's draw:drawvertex event
onEditMove function hook to leaflet-draw's draw:editmove event
onEditResize function hook to leaflet-draw's draw:editresize event
onEditVertex function hook to leaflet-draw's draw:editvertex event

Links to docs

react-leaflet-draw's People

Contributors

aaronfriel avatar alex3165 avatar anajavi avatar andrewlipscomb avatar aparij avatar brennanwilkes avatar cbmasri avatar dependabot[bot] avatar dvgy avatar gpbmike avatar hoetmaaiers avatar lajtman avatar macrouch avatar markhepburn avatar menosprezzi avatar nhusher avatar nygardk avatar rexmondo avatar rotoglup avatar srghma avatar stas-lesiuk avatar tigleym avatar timhollies avatar turtiesocks avatar wedjaa avatar wyatt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

react-leaflet-draw's Issues

Should onMounted fire in componentDidMount, instead of before it has mounted?

This is arguably a question of semantics, and I've bumped into it attempting something quite ugly that I hope to avoid... nonetheless: when the onMounted callback is fired, in the middle of componentWillMount, the component has not in fact mounted yet, and thus hasn't been fully initialised if the callback relies on that.

I'm happy to put together a PR if you'd like.

Edit toolbar don't disable dynamically

I have an issue when I want to disable edit toolbar dynamically. See part of render() method below:

<FeatureGroup ref={this.saveLayerContainer}>
   <EditControl
      draw={{
         marker: false,
         rectangle: false,
         polyline: false,
      }}
      edit={{ edit: isEditable, remove: false }}
   />
   <CustomMapElement
       values={this.calculateValaues}
   />
</FeatureGroup>

I calculate value of isEditable in render() method. But it does not work because EditControl is not updated when draw props is not updated. However, if I will update draw component together with edit it will work.

I think you should add checking for edit value in componentDidUpdate too.

if (isEqual(this.props.draw, prevProps.draw) || this.props.position !== prevProps.position) {
return false;
}

Updated needed

Hi,
Nice work @alex3165 !!!!

I believe that in the new version of the react leaflet "0.11.5" your plugin does not work.
The error is "options.featureGroup must be a L.FeatureGroup".

Do you have any time to update your plugin?

THX!

Exception: Cannot add property segmentsIntersect, object is not extensible

Code:

      <Map
        zoomControl={false}
      >
        <TileLayer
          url={tileProvider}
          attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
        />
        <FeatureGroup>
          <EditControl
            position="topright"
            onEdited={() => {}}
            onCreated={() => {}}
            onDeleted={() => {}}
            draw={{
              rectangle: false,
            }}
          />
          <Circle center={[51.51, -0.06]} radius={200} />
        </FeatureGroup>
      </Map>

ERROR (the project is using webpack based on react-boilerplate project. all the rest including other modules for react-leaflet, work as intended)

reactBoilerplateDeps.dll.js:89347 Uncaught (in promise) TypeError: Cannot add property segmentsIntersect, object is not extensible
    at Object.extend (reactBoilerplateDeps.dll.js:89347)
    at reactBoilerplateDeps.dll.js:89301
    at Object../node_modules/leaflet-draw/dist/leaflet.draw.js (reactBoilerplateDeps.dll.js:89301)

switching display text from imperial to metric

Whenever I draw a circle with the tool the radius is displayed in feet. I tried changing a few options but no luck.
draw={{ metric: true, polyline: { metric: true, }, circle: { metric: true, }, }} edit={{ metric: true, }}
Can you please let me know where I could set this feature?

Thank you!

Disabling the draw functionality in EditControl

I just want my users to be able to edit in this case. I understand I need to pass in an object to disable the draw menu, but this doesn't work:

<EditControl
position="topright"
onEdited={this._onEditPath}
onCreated={this._onCreate}
onDeleted={this._onDeleted}
draw={{draw:false}}
						/>

What should I be passing in here?

Styles missing

As can I see in the documentation, I can include styles by including this stroke:

node_modules/leaflet-draw/dist/leaflet.draw.css

But I cannot find this in dist folder in my npm-modules, version - 0.18.0

Error while loading Marker default image

Hi,

I use :

  • "leaflet": "^1.0.1"
  • "react-leaflet": "^1.0.0-rc.2"
  • "react-leaflet-draw" : "^0.10.1"

I get this error :
http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/imagesmarker-icon.png Failed to load resource: 404 (Not Found)
http://cdnjs.cloudflare.com/ajax/libs/leaflet/0.7.7/imagesmarker-icon.png Failed to load resource: 404 (Not Found)

It took me a while to understand that react-leaflet-draw had as dependencies an older version of react-leaflet with a bug which was loading the wrong images.

Any plan of updating the dependencies to newer versions ? (leaflet, leaflet-draw, react-leaflet)

React 16 support

I'm getting the following warning when try to integrate the lib with React 16

npm WARN [email protected] requires a peer of react@^15.5.2 but none was installed.

Map props not found for componentWillMount

Hey mate, been having a play with this package in a Meteor app. I'm currently hitting a bug where props.map is undefined when componentWillMount is triggered.

Meteor 1.3 with react-leaflet as a base, so it may be beyond the scope of what you're looking at. I was going to have a play around (see if pushing the map.on(...) functions back to componentDidMount makes a difference) but I can't seem to find a way to rebuild the package....got a gulp/grunt file you use?

Cheers, Tinners

How to start drawing from outside map

Hello!

I want to start the drawing action on the map from outside. I am trying to figure out what to do but I didn't come up with a solution.

screen shot 2016-02-25 at 12 36 28 am

According to the image, the controls work fine when I click on the map buttons. But I do want to start the drawing action also when clicking the sidebar buttons.

What do you recommend?

Disabling "edit" toolbar - PR proposal

I'd like to disable editing and just use the drawing features of Leaflet.Draw. In Leaflet.Draw this is done by setting the edit option to false (its default).

I'm totally happy to make a PR, just want to make sure you'd be amenable before I do anything.

What I'd propose for a first effort is an optional edit prop. By default the behavior would be the same as it is now. When edit is explicitly set to false the options object (https://github.com/alex3165/react-leaflet-draw/blob/master/src/EditControl.js#L91) would be set to an empty object, without the edit sub-object.

What do you think? Just let me know and I can make a PR.

how to select a specific feature?

I know that leaflet geojson object which have a function call onEachFeature, however react-leaflet-draw is not implemented. Anyone can help with this?

using with GeoJSON

I can use this with basic Leaflet features as in the example, but I cannot use it with the react-leaflet GeoJSON component. i.e.,

Works (from the example):
<Circle center={[51.51, -0.06]} radius={200}/>

Doesn't work:
<GeoJSON data={features} onEachFeature={setFeatureStyle}/>
The error on editing is: "Uncaught TypeError: Cannot read property 'enable' of undefined". The GeoJSON loads into the map, I just can't edit with it. I need this, so will investigate and do a PR if I find a fix.

Unable to delete layers (shapes)

Hi, While trying to delete a layer, it seems like when clicking on the shape it doesn't recognize the layer and therefore unable to delete the layer from the map.
I am able to edit the layers and save it.

Any suggestion why this happens?

Editing a shape

I have a shape and click on the "edit" button to edit it. When I drag the corner markers of the shape, the whole map is dragged, instead of the shape being edited. What could be the reason for this behavior. Any ideas?

popup with event handling

Hello,

when im adding markers to the map im trying to create a popup for each marker with event handling, but keep running into issues. when i try to generate a popup with a div and bind it I get type error message (appendchild on node) if i convert the whole popup into string then the onclick or onchange event doesnt work.

Is there any way to work around it ?

trying to populate FG with data, but it doesn't work

i am trying to import some GeoJSON to the FeatureGroup in _onFeatureGroupReady handler, but it doesn't appear to be rendered into the map. strangely, the edit menu becomes usable, indicating that the data is maybe there just not being rendered. i'm not sure whats happening, as i'm quite the noob to maps in general. was hoping maybe someone could help me with this. the relevant code is in the else if(this.props.data) block. the console log statements all show the data being there.

here is a mock of the data:

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [
              37.79,
              -122.45
            ],
            [
              37.79,
              -122.42999999999999
            ],
            [
              37.77,
              -122.42999999999999
            ],
            [
              37.77,
              -122.45
            ],
            [
              37.79,
              -122.45
            ]
          ]
        ]
      }
    }
  ]
}

here is the code where i'm trying to import this data

_onFeatureGroupReady = (ref) => {
    console.log('_onFeatureGroupReady');
    console.log(ref);
    if(ref===null) {
        return;//bug???
    }
    this._editableFG = ref; 
    // populate the leaflet FeatureGroup with the geoJson layers
    if(this.state.data) {
        console.log('importing service area from state');
        let leafletGeoJSON = new L.GeoJSON(this.state.data);
        let leafletFG = this._editableFG.leafletElement;
        leafletGeoJSON.eachLayer( layer =>leafletFG.addLayer(layer));
    }else if(this.props.data) {
        console.log('importing service area from props');
        this.setState({data:this.props.data},()=>{
            console.log(this.state.data);
            let leafletGeoJSON = new L.GeoJSON(this.state.data);
            console.log(leafletGeoJSON);
            let leafletFG = this._editableFG.leafletElement;
            console.log(leafletFG);
            leafletGeoJSON.eachLayer( layer =>leafletFG.addLayer(layer));
        })
    }
        
}

any ideas what i might be doing wrong (or even better, any way i can achieve this?). thank you.

Error: TypeError: Super expression must either be null or a function, not object

Just importing the library like this in the App.js file:

import { Map, TileLayer, FeatureGroup } from 'react-leaflet';
import { EditControl } from "react-leaflet-draw"

I get the error. To test, I have just created a react app using create-react-app , which has installed react 16.4.1, and installed the latest leaflet, leaflet-draw, react-leaflet and react-leaflet-draw.

The error is:

Uncaught TypeError: Super expression must either be null or a function, not object
    at _inherits (react-leaflet-draw.js:108)
    at react-leaflet-draw.js:127
    at Object.<anonymous> (react-leaflet-draw.js:233)
    at __webpack_require__ (react-leaflet-draw.js:30)
    at Object.defineProperty.value (react-leaflet-draw.js:64)
    at __webpack_require__ (react-leaflet-draw.js:30)
    at Object.defineProperty.value (react-leaflet-draw.js:50)
    at react-leaflet-draw.js:53
    at webpackUniversalModuleDefinition (react-leaflet-draw.js:3)
    at Object../node_modules/react-leaflet-draw/dist/react-leaflet-draw.js (react-leaflet-draw.js:10)
    at __webpack_require__ (bootstrap 542f1d0713b5b5361f40:678)
    at fn (bootstrap 542f1d0713b5b5361f40:88)
    at Object../src/App.js (App.css?9a66:26)
    at __webpack_require__ (bootstrap 542f1d0713b5b5361f40:678)
    at fn (bootstrap 542f1d0713b5b5361f40:88)
    at Object../src/index.js (index.css?f255:26)
    at __webpack_require__ (bootstrap 542f1d0713b5b5361f40:678)
    at fn (bootstrap 542f1d0713b5b5361f40:88)
    at Object.0 (registerServiceWorker.js:117)
    at __webpack_require__ (bootstrap 542f1d0713b5b5361f40:678)
    at ./node_modules/ansi-regex/index.js.module.exports (bootstrap 542f1d0713b5b5361f40:724)
    at bootstrap 542f1d0713b5b5361f40:724

Module not found: Can't resolve 'leaflet-draw'

Hi guys,
I got this error when I copied the Get Start example.
./node_modules/react-leaflet-draw/dist/esm/EditControl.js
Module not found: Can't resolve 'leaflet-draw'
I'm using "react-leaflet-draw": "^0.19.0", "leaflet": "^1.4.0", "react-leaflet": "^2.1.4" and "react": "^16.6.3".

Trouble creating a codepen / jsfiddle

I'm not really sure why this occurs, but I cannot create a codepen / jsfiddle for a cutdown version of this library's example.

I was trying to do this for another issue I will create.

The codepen is here: https://codepen.io/andrewdodd/pen/pVrqwd

I have:

  • Added all the required CSS & JS libraries
  • Introduced a cutdown copy of the example

Some things to note:

  • the console has an error "Uncaught TypeError: Cannot read property 'LayersControl' of undefined
    at Object. (VM288 react-leaflet-draw.js:233)"
  • If you remove the <EditControl /> the example works correctly, albeit without the draw components
  • If you add editControl={true} to the LeafletMap component, the draw tools are displayed (but my understanding is that this is being done directly by the leaflet.draw.js plugin)

Any advice would be great.

`draw.rectangle` equality check in `componentDidUpdate` broken

    if (isEqual(this.props.draw, prevProps.draw) || this.props.position !== prevProps.position) {
      return false;
    }

If draw is set to something, this equality check will fail causing re-rendering every time there is an update.

$ prevProps.draw
rectangle: shapeOptions: clickable: truecolor: "#3388ff"fill: truefillColor: nullfillOpacity: 0.2opacity: 0.5showArea: truestroke: trueweight: 4__proto__: Object__proto__: Object__proto__: Object

$ this.props.draw
rectangle: shapeOptions: {clickable: true}__proto__: Object__proto__: Object

The issue is in updateDrawControls. Looks like a reference to this.props.draw is being passed directly to options.draw which is then mutated by leaflet-draw.

Props should be immutable.

  updateDrawControls = () => {
    const { layerContainer } = this.context;
    const { draw, edit, position } = this.props;
    const options = {
      edit: {
        ...edit,
        featureGroup: layerContainer
      }
    };

    if (draw) {
      options.draw = draw;
    }

    if (position) {
      options.position = position;
    }

    this.leafletElement = new L.Control.Draw(options); // eslint-disable-line
  };

Styling messed up on toolbar

I get "Draw" text on all my controls in the toolbar, when I add this...

ss

  • is there extra CSS I should have to add?
  • is the "global" L in your code importable? Is there a chance that's our problem, since I don't have L in global scope anywhere? (es6 import modules all the way)
  • I'm trying to set this up in react-storybook which is awesome - but it's been a bit of a pain getting leaflet + css in place... thoughts?
    <Map {...pickMapProps(mapData)}>
      <TileLayer
        url='http://{s}.tile.osm.org/{z}/{x}/{y}.png'
        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
      />
      <FeatureGroup>
        <EditControl
          position='topright'
          onEdited={props.onChanged}
          onCreated={props.onChanged}
          onDeleted={props.onChanged}
          draw={{
            rectangle: false
          }}
        />
          <Circle center={[51.51, -0.06]} radius={200} />
      </FeatureGroup>
    </Map>

Saving/Redrawing shapes?

Might be a stupid question but I'm a little confused on how to save/redraw the shapes that are created?

this is my render

 <Map center={position} zoom={this.state.zoom} ref={node => this.map = node} >
            <LayersDropDown />
            <FeatureGroup>
              <EditControl
                position='topright'
                onEdited={this.props.updateDrawLayers}
                onCreated={this.props.updateDrawLayers}
                onDeleted={this.props.updateDrawLayers}
                draw={{ marker: false }}
              />
            </FeatureGroup>
             {/* Need to put shapes here like circle/line/etc*/}
</Map>

the layer object thats passed via the edit/create/delete functions is a pretty giant object so what information do I need to grab? is there an easy was to transform it to only what you need?
Then how do i load the drawn objects back in? I see the one example of the <Circle ..../> I'm assuming there's API for all the shapes then?

Adding/Removing EditControl Does not update Callbacks correctly

Got the bug to work with a minimal example
Use this file instead of editcontrol.js on the minimal example in the repo
https://gist.github.com/ALTinners/6a15e4f1ad2653ee23d1d15132f46a8f

To replicate:

  1. Start app and press Toggle Edit button. Controls should be enabled, and drawing works as normal.
  2. Toggle Edit twice so that the Edit Controls are deleted and created again. Draw anything, and the create function will be triggered twice.
  3. Toggling each time adds another extra trigger for the function, so I'm guessing that the bindings for draw:create (and possibly other signals) are not being destroyed properly when the React component is destroyed.

I can't see any code in the react-leaflet-draw source that relates to React component destructors; my understanding was that this was abstracted by React (or possibly by react-leaflet).

Deprecated warnings

Hi!
I get deprecated warnings with last version of react-leaflet

"leaflet": "^1.2.0",
    "leaflet-draw": "0.4.9",
    "react": "^16.1.1",
    "react-leaflet": "^1.7.6",
    "react-leaflet-draw": "^0.17.0",

2017-11-27 13 08 24

Strange behavior editing a polygon

There is an interesting behavior in leaflet-draw, possibly in combination with the reselect-package.

The expected behavior is that the polygon changes when editing it.

expected

The real behavior is, that its only changing the outline of it and not the real polygon.

reality

I have debugged a lot and tried to reproduce the error.

I wrote a Component based on the example and added it to my codebase.

https://github.com/inowas/inowas-dss-client/blob/error/leaflet-edit-polygon/src/containers/tools/EditAreaMap.jsx

Adding it here: https://github.com/inowas/inowas-dss-client/blob/error/leaflet-edit-polygon/src/containers/tools/TestLeaflet.js it works as expected.

Adding it to a connected component: https://github.com/inowas/inowas-dss-client/blob/error/leaflet-edit-polygon/src/t03/containers/mainTest.jsx it does not work as expected.

In both cases, the same sample-data is rendered (no state, no props no store-data).

When I'm changing the line https://github.com/inowas/inowas-dss-client/blob/error/leaflet-edit-polygon/src/t03/containers/mainTest.jsx#L36

from

T03T = connect( makeMapStateToProps, mapDispatchToProps )( T03T );

to

T03T = connect( mapStateToProps, mapDispatchToProps )( T03T );

and it's not depending anymore to the selectors: https://github.com/inowas/inowas-dss-client/blob/error/leaflet-edit-polygon/src/t03/selectors/mapState.js#L40 it works well.

Can you imagine why this happens? How con I debug further this situation?

Best regards
Ralf

Custom toolbar

Hi,
I see that using leaflet-draw a toolbar is created on map, using which we can draw on the map. I want to create a customer toolbar outside the map and be able to use it to draw on the map. Is there a way to do that?

Trouble understanding how to interact with underlying map/objects

I am unsure if this is an issue with this library or just an issue with my understanding. I would really appreciate if anyone can give me some tips.

I am trying to use the react-leaflet and react-leaflet-draw library, as the draw functionality really looks great (and I prefer to use existing libs where possible).

I have a situation where:

  • I have polygons in my application that I want to draw on a leaflet map
  • I would like to use the leaflet.draw functionality to be able to both:
    • Add new polygons
    • Edit existing polygons

As far as I can understand, the issues I am having are because the react-leaflet and react-leaflet-draw libraries are really just shims around leaflet.js & leaflet.draw.js, so integrating the 'react' objects with the ones managed by the underlying library is not really working.

The example provided has got me part of the way, but I can't seem to get things to work the way I think I need them to.

My files are as follows:

Example.js

import React, { Component } from 'react';
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import 'leaflet-draw/dist/leaflet.draw.css';
import {Map,  GeoJSON} from 'react-leaflet';

import EditControlWrapper from './EditControlWrapper';


function getInitPolys(offset) {
  return {
    "type": "FeatureCollection",
    "features": [
      {
        "type": "Feature", "properties": {},
        "geometry": {
          "type": "Polygon",
          "coordinates": [
            [[200+offset, 200+offset],
             [100+offset, 0+offset],
             [100+offset, 100+offset]]
          ]
        }
      }
    ]
  }
}

export default class Example extends Component {
  constructor() {
    super();
    this.state = getInitPolys(0);
    this.handleButton = this.handleButton.bind(this);
    this.drawingChanged = this.drawingChanged.bind(this);
  }
  handleButton() {
    // forcibly change the state
    this.setState(getInitPolys(25));
  }
  drawingChanged(e) {
    console.log('Received new GeoJSON', e);
    this.setState(e);
  }

  render() {
    const style={height:"300px",width:"100%",position:"relative"};
    const bounds = [[0,0], [1000,1000]];
    const theGeoJSONs = <GeoJSON data={this.state} />;

    return (
      <div>
        <Map 
          crs={ L.CRS.Simple } 
          bounds={ bounds }
          style={style}
          minZoom={-4}
        >
          <EditControlWrapper onChange={this.drawingChanged}>
            {theGeoJSONs}
          </EditControlWrapper>
      </Map>
      <button onClick={this.handleClick}>Change Input</button>
      </div>
    )
  }
}

EditControlWrapper.js

import React, { Component } from 'react';
import 'leaflet-draw/dist/leaflet.draw.css';
import {FeatureGroup, Circle} from 'react-leaflet';
import { EditControl } from 'react-leaflet-draw';

export default class CustomEditControl extends React.Component {
  render() {
    return (
      <FeatureGroup ref={ (reactFGref) => {this._onFeatureGroupReady(reactFGref);} }>
        <EditControl 
          position='topright'
          onEdited={this._onChange}
          onCreated={this._onChange}
          onDeleted={this._onChange}
        />
        {this.props.children}
      </FeatureGroup>
    );
  }

  _editableFG = null
  _onFeatureGroupReady = (reactFGref) => {
    if (reactFGref) {
      this._editableFG = reactFGref;
   }
  }
  _onChange = () => {
    const { onChange } = this.props;

    if (!onChange || !this._editableFG) {
      return;
    }

    const geojsonData = this._editableFG.leafletElement.toGeoJSON();
    onChange(geojsonData);
  }
}

My understanding is that the flow should go something like this:

1. Data flow
   Example ---> pushes in polygons through children --> Wrapper --> FeatureGroup

2. Callback flow
   LeafetReactDraw.onEdit/onCreate/onEdit ---> Wrapper --> Example --> setState()
                                      [via callbacks]

3. State change causes React to invalidate components which causes data flow again

Because the react-leaflet library is really just a shim around the real leaflet, I'm guessing that we have to remove all of the polygons from the underlying map when one of these components gets updated/unmounted. But where!

Also, this does other weird things like not allowing edits, but it is probably not worth diving into just yet.

Am I on the right path? Or is there an easier way?

Also, if anyone can help with #44 then I can make a codepen for this too.

Duplicated polygons onCreated when using them on State

Hi there,

I'm having trouble trying to create new polygons. The way that I'm using this library in conjunction with react-leaflet is that I save all the polygons in the component's state, with some additional info for a pop-up with text, when the area is clicked. I show all those polygons in the respective layer in use, where the is also used.

The unwanted behavior is that upon creating a new shape, it duplicates the new shape, rendering the shape that was saved in the state, and also the shape created with the draw:created.

I could refresh the page and only show the polygons contained in the state but that is plain stupid, or I could leave the new shapes out the state, but that way I'm not able to inject the popup with the text.

I'm trying to remove the polygon that is automatically created, so that only the one that I copy to the state is shown, but with no luck so far.

I'll leave here the code of the component in question, which is still "in construction" and using demo values, for reference.

Any help is much appreciated.
Thank you

import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {withStyles} from 'material-ui/styles'
import TextField from 'material-ui/TextField'
import Typography from 'material-ui/Typography'
import {Map, TileLayer, Marker, Popup, LayersControl, Polygon, FeatureGroup} from 'react-leaflet'
import L from 'leaflet'
import {EditControl} from 'react-leaflet-draw'
import update from 'react-addons-update'

const styles = theme => ({
  container: {
    display: 'flex',
    flexWrap: 'wrap'
  },
  textField: {
    marginLeft: theme.spacing.unit,
    marginRight: theme.spacing.unit,
    width: '200px',
    fontSize: ''
  },
  menu: {
    width: 200
  }
})

const mapData = {
  name: 'My Working Place',
  location: {
    lat: 37.050300,
    lng: -7.881713
  },
  areas: [{
    id: 1,
    name: 'My working area 1',
    crop: 'Citrinos',
    area: 1025,
    points: [
      [37.050698940024866, -7.882744073867799],
      [37.04947874365056, -7.8816014528274545],
      [37.050163768591695, -7.880088686943055],
      [37.050942976945635, -7.882298827171327],
      [37.050698940024866, -7.882744073867799]
    ]
  },
  {
    id: 2,
    name: 'My working area 2',
    crop: 'Citrinos',
    area: 1025,
    points: [
      [37.04626760682191, -7.8815263509750375],
      [37.04565961628692, -7.8798043727874765],
      [37.04571527759473, -7.879697084426881],
      [37.04739794247914, -7.879133820533753],
      [37.04763342695574, -7.879659533500672],
      [37.04754779632151, -7.879825830459596],
      [37.04766767918239, -7.8803032636642465],
      [37.04626760682191, -7.8815263509750375]
    ]
  }
  ]

}

class RegistrationMap extends Component {
  constructor (props) {
    super(props)
    this.state = {
      zoom: 15,
      lat: mapData.location.lat,
      lng: mapData.location.lng,
      areas: mapData.areas
    }
  }

  /** > Create polygon - Calculate area and add polygon to state -------------------------------------------------- */
  _onCreate = (data) => {
    let latlngs = []
    let points = []
    let nextID = this.state.areas[this.state.areas.length - 1].id + 1
    data.layer._latlngs.map((a) => {
      latlngs.push(a)
      a.map((coordinate) => {
        points.push([coordinate.lat, coordinate.lng])
      })
    })
    let newShape = {
      'id': nextID,
      'name': '',
      'crop': '',
      'area': this._shapeArea(latlngs[0]),
      'points': points
    }
    let newAreas = update(this.state.areas, {$push: [newShape]})
    let layer = L.geoJson()
    layer.clearLayers()
    this.setState({areas: newAreas})
    console.log('onCreated', data)
    console.log('state: ', this.state)
  }
  /** > Change path - Calculate new area and update points in state ----------------------------------------------- */
  _onEditPath = (data) => {
    let areas = this.state.areas
    for (var layer in data.layers._layers) {
      console.log('edit path latlng', data.layers)
      let latlngs = []
      let points = []
      data.layers._layers[layer]._latlngs.map((a) => {
        latlngs.push(a)
        a.map((coordinate) => {
          points.push([coordinate.lat, coordinate.lng])
        })
      })
      let id = data.layers._layers[layer].options.name
      areas.find(x => x.id === id).points = points
      areas.find(x => x.id === id).area = this._shapeArea(latlngs[0])
      this.setState({areas: areas})
    }
  }

  _onEdit = (data) => {
    console.log('onEdit', data)
    console.log('state: ', this.state)
  }

  _onDeleted = (data) => {
    console.log('onDeleted', data)
    console.log('state: ', this.state)
  }
  /** > Convert an array of pair of points into an array of LatLngs ----------------------------------------------- */
  _toLatLngs = (dataPoints) => {
    let latlngs = []
    dataPoints.map((point) => {
      latlngs.push(L.latLng(point))
    })
    return (latlngs)
  }
  /** > Calculate AREA from an array of LatLng points ------------------------------------------------------------- */
  _shapeArea = (latlngs) => {
    let a = L.GeometryUtil.geodesicArea(latlngs)
    return (L.GeometryUtil.readableArea(a, true, 10))
  }

  _handleTextChange = name => e => {
    let areas = this.state.areas
    areas.find(x => x.id === e.target.form.id)[name] = e.target.value
    this.setState({areas: areas})
  }

  render () {
    const {classes} = this.props
    const position = [this.state.lat, this.state.lng]
    const osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
    const googleSat = 'http://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}'
    const googleSubdomains = ['mt0', 'mt1', 'mt2', 'mt3']
    const mapHeight = {height: '300px'}

    console.log('MAP STATE: ', this.state)
    let definedAreas = this.state.areas.map((a) => {
      let latlngs = this._toLatLngs(a.points)
      a.area = this._shapeArea(latlngs)
      return (
        <Polygon positions={a.points} name={a.id}>
          <Popup style={{width: '200'}}>
            <form id={a.id} className={this.props.container} noValidate autoComplete='off'>
              {this.props.isAdmin
                ? <div>
                  <TextField
                    id='name'
                    label='Sector'
                    className={this.props.textField}
                    value={a.name}
                    onChange={this._handleTextChange('name')}
                    margin='normal'
                    style={{fontSize: '1.5rem'}}
                  />
                  <TextField
                    id='crop'
                    label='Cultura'
                    className={this.props.textField}
                    value={a.crop}
                    onChange={this._handleTextChange('crop')}
                    margin='normal'
                    style={{fontSize: '1.5rem'}}
                  />
                </div>
                : <div>
                  <Typography variant='subheading'>
                    Sector : {a.name}
                  </Typography>
                  <Typography variant='subheading'>
                    Cultura : {a.crop}
                  </Typography>
                </div>}
              <Typography variant='subheading'>
                Area : {a.area}
              </Typography>
            </form>
          </Popup>
        </Polygon>
      )
    })

    return (
      <Map center={position} zoom={this.state.zoom} style={mapHeight} onMeasureFinish={this.handleMeasureFinish}>
        <LayersControl position='topright'>
          <LayersControl.BaseLayer name='OpenStreet Map'>
            <TileLayer
              url={osmUrl}
              attribution='&copy <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors'
              maxZoom={22}
            />
          </LayersControl.BaseLayer>
          <LayersControl.BaseLayer checked name='Google Satellite'>
            <TileLayer
              url={googleSat}
              subdomains={googleSubdomains}
              maxZoom='22'
            />
          </LayersControl.BaseLayer>
          <LayersControl.Overlay checked name='Areas'>
            <FeatureGroup>
              { this.props.isAdmin
                ? <EditControl
                  position='topleft'
                  onEdited={(e) => this._onEditPath(e)}
                  onCreated={(e) => this._onCreate(e)}
                  onDeleted={(e) => this._onDeleted(e)}
                  onEditVertex={(e) => this._onEdit(e)}
                  draw={{
                    rectangle: false,
                    polyline: false,
                    circle: false,
                    marker: false,
                    circlemarker: false,
                    polygon: {
                      allowIntersection: false,
                      showArea: true
                    }
                  }}
                /> : null}
              {definedAreas}
            </FeatureGroup>
          </LayersControl.Overlay>
        </LayersControl>
        <Marker position={position}>
          <Popup>
            <span>
              {this.props.building.name}
            </span>
          </Popup>
        </Marker>
      </Map>
    )
  }
}

RegistrationMap.propTypes = {
  classes: PropTypes.object.isRequired
}

export default withStyles(styles)(RegistrationMap)

Accessing an edited polygon's array of points on save.

I've got a component which puts an editable polygon on the map. When the user hits the "save" button, I want to access an array of the polygon's new vertices, so that I can save them. How do I do this?

My component:

<FeatureGroup>
						<EditControl
							position="topright"
							onEdited={console.log(this)}
							edit={{ remove: false }}
							draw={{
								marker: false,
								circle: false,
								rectangle: false,
								polygon: false,
								polyline: false
							}}
						/>
						<Polygon positions={polygonCoords} />;
					</FeatureGroup>

Tooltip

Hello, is it possible to add custom tooltips?

image

I know that it's possible in leaflet-draw, but I can't do it using react-leaflet-draw

How to change toolbar button title?

Hey,

is there any way to change the titles of the buttons in the draw and edit toolbar? For example I want to change 'Draw a polyline'.

Thanks!

How to use ref to programmatically activate edit

I want to activate drawing mode from outside of leaflet-draw component (without requiring the user to click on the the Edit icon). I tried to set up a ref but it's 'undefined' - understandably so the first time render() is called. How do I use ref in this case to make direct calls to draw plugin methods?

render() {
    console.log(this.editControl); // undefined
    return (<EditControl ref={(ref)=>{
          this.editControl=ref; 
          console.log(this.editControl)}}   // Object {.. }
    ...
    />);
}

draw polygon can't end

when running the example, and using the draw-polygon. after clicking the "first point to close shape", the dashed line is still there.

the console also says "Uncaught TypeError: Cannot read property '_onChange' of undefined" on edit-control.js:46

I think it's a bug.

Rendering react components on newly create layer.

Hi,

I'm trying to render some react components inside a layer created by react-leaflet-draw.

This is my attempt:

    _onCreate(e) {
        var layer = e.layer
        layer.bindPopup(
            <Content>
                <Label>Flower Name</Label>
                <Input type="text" placeholder="Flower Name"/>
                <Label>Flower Index</Label>
                <Input type="number" placeholder="Flower Index"/>
                <Label>Flower Radius</Label>
                <Input type="number" placeholder="Flower Radius"/>
                <Button color="isSuccess" >Add Flower</Button>
            </Content>
        )
    }

The components are supplied by react-bulma.

But I try this approach I get the following error:
Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.

If I make the content a simple string I get fields and a button but no access to external functions nor the actual Bulma components.

Essentially I want to be able to save the new shape on the database.

From a brief online search it seems that this is a limitation of react-leaflet, but I wanted to check here first.

Also, is this the best way to set a popup for a newly created shape? I'm having a hard time translating the regular leaflet-draw approaches to react-leaflet-draw.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.