2017-05-08 01:31:49 +00:00
/ *
* Copyright ( c ) 2015 Dominique Cavailhez
* Leaflet extension for Leaflet . draw
* Markers , polylines , polygons , rectangles & circle editor
* Snap on others markers , lines & polygons including the edited shape itself
* Requires https : //github.com/Leaflet/Leaflet.draw & https://github.com/makinacorpus/Leaflet.Snap
* /
L . Control . Draw . Plus = L . Control . Draw . extend ( {
snapLayers : new L . FeatureGroup ( ) , // Container for layers used for snap
editLayers : new L . FeatureGroup ( ) , // Container for editable layers
options : { // Force default to false to have to declare only required commands
draw : {
marker : false , // Capability to create a marker
polyline : false , // Capability to create a polyline
polygon : false , // Capability to create a polygon
rectangle : false , // Capability to create a rectangle
circle : false // Capability to create a circle
} ,
edit : {
edit : false , // Capability to edit a feature
remove : false // Capability to remove a feature
} ,
entry : 'edit-json' , // <textarea id="edit-json">JSON</textarea> | <input type="hidden" id="edit-json" name="xxx" value="JSON"> : geoJson field to be edited
jsonOptions : { } , // Options to be used when retreiving Json from <input />
changed : 'edit-changed' // <span id="edit-changed" style="display:none">changed</span> : warn changes to be saved
} ,
initialize : function ( options ) {
// Allign drawing style on display
L . Util . extend ( L . Draw . Polyline . prototype . options . shapeOptions , L . Polyline . prototype . options ) ;
L . Util . extend ( L . Draw . Polygon . prototype . options . shapeOptions , L . Polygon . prototype . options ) ;
options . edit = L . extend ( this . options . edit , options . edit ) ; // Init false non chosen options
options . draw = L . extend ( this . options . draw , options . draw ) ;
for ( var o in options . draw )
if ( options . draw [ o ] )
options . draw [ o ] = {
guideLayers : [ this . snapLayers ] // Allow snap on creating elements
} ;
L . Control . Draw . prototype . initialize . call ( this , options ) ;
} ,
onAdd : function ( map ) {
this . _toolbars [ 'edit' ] . options . featureGroup = this . editLayers ; // Link the layers to edit
this . editLayers . addTo ( this . snapLayers ) ; // Cascade to snapLayers & add the map
this . snapLayers . addTo ( map ) ; // Make all this visble
// Add new features to the editor
map . on ( 'draw:created' , function ( e ) {
2017-05-09 00:14:25 +00:00
//this.addLayer(e.layer);
2017-05-08 01:31:49 +00:00
} , this ) ;
// Remove deleted features from the editor
map . on ( 'layerremove' , function ( e ) {
this . editLayers . removeLayer ( e . layer ) ;
} , this ) ;
// Read geoJson field to be edited
var ele = document . getElementById ( this . options . entry ) ;
if ( ele ) {
var elei = typeof ele . value != 'undefined' ? 'value' : 'innerHTML' ,
gjs = JSON . parse (
ele [ elei ] . replace ( /\s/g , '' ) || // Get & clean geoJson input
'{"type":"FeatureCollection","features":[]}' // Default
) ;
new L . GeoJSON (
this . explodeMultiFeatures ( gjs ) ,
this . options . jsonOptions
) . addTo ( this ) ;
}
// Clean features & rewrite json field
2017-05-09 00:14:25 +00:00
//map.on('draw:created draw:editvertex', this._optimSavGeom, this); // When something has changed
//this._optimSavGeom(false); // At the init
2017-05-08 01:31:49 +00:00
return L . Control . Draw . prototype . onAdd . call ( this , map ) ;
} ,
// Leaflet.draw does not work with multigeometry features such as MultiPoint, MultiLineString, MultiPolygon, or GeometryCollection.
// If you need to add multigeometry features to the draw plugin, convert them to a FeatureCollection of non-multigeometries (Points, LineStrings, or Polygons).
explodeMultiFeatures : function ( f ) {
// Prepare replacement structure
var r = {
type : 'FeatureCollection' ,
features : [ ]
} ;
switch ( f . type ) {
case 'FeatureCollection' : // Recurse in FeatureCollection
for ( var i = 0 ; i < f . features . length ; i ++ )
r . features . push ( this . explodeMultiFeatures ( f . features [ i ] ) ) ;
return r ;
case 'Feature' : // Recurse in Feature
return this . explodeMultiFeatures ( f . geometry ) ;
case 'MultiPoint' : // Convert Multi* geoms
case 'MultiLineString' :
case 'MultiPolygon' :
for ( var i = 0 ; i < f . coordinates . length ; i ++ )
r . features . push ( {
type : f . type . replace ( 'Multi' , '' ) ,
coordinates : f . coordinates [ i ]
} ) ;
return r ;
}
return f ;
} ,
addLayer : function ( layer ) {
// Récurse in GeometryCollection
if ( layer . _layers ) {
for ( var l in layer . _layers )
this . addLayer ( layer . _layers [ l ] ) ;
return ;
}
// Change color when hover (to be able to see different poly)
layer . on ( 'mouseover mouseout' , function ( e ) {
if ( typeof e . target . setStyle == 'function' )
e . target . setStyle ( {
color : e . type == 'mouseover' ? 'red' : L . Polyline . prototype . options . color
} ) ;
} ) ;
// Add snapping to vectors layers
layer . addTo ( this . editLayers ) ;
if ( layer . _latlng ) // Point
layer . snapediting = new L . Handler . MarkerSnap ( this . _map , layer ) ;
else if ( layer . _latlngs ) // Polyline, Polygon, Rectangle
layer . snapediting = new L . Handler . PolylineSnap ( this . _map , layer ) ;
else // ?? protection
return ;
layer . snapediting . addGuideLayer ( this . snapLayers ) ;
layer . snapediting . enable ( ) ;
2017-05-09 00:14:25 +00:00
//this._optimSavGeom(); // Optimize & write full json on output element
2017-05-08 01:31:49 +00:00
// Close enables edit toolbar handlers & save changes
layer . on ( 'deleted' , function ( ) {
for ( m in this . _toolbars [ 'edit' ] . _modes )
this . _toolbars [ 'edit' ] . _modes [ m ] . handler . disable ( ) ;
2017-05-09 00:14:25 +00:00
//this._optimSavGeom();
2017-05-08 01:31:49 +00:00
} , this ) ;
} ,
_optimSavGeom : function ( changed ) {
// Optimize the edited layers
var ls = this . editLayers . _layers ;
if ( ! this . _map . noOptim ) // To optimize "cut" !!
for ( var il1 in ls ) { // For all layers being edited
var ll1 = ls [ il1 ] . _latlngs ;
if ( ll1 && ! ls [ il1 ] . options . fill ) { // Only polylines
// Transform polyline whose the 2 ends match into polygon
if ( ll1 [ 0 ] . equals ( ll1 [ ll1 . length - 1 ] ) && // The 2 ends match
ll1 . length > 3 ) { // If it will make at least a triangle (4 summits line).
this . editLayers . removeLayer ( ls [ il1 ] ) ; //DCCM TODO bug : create a bug while finishing dragend after the poly removal
this . addLayer ( new L . Polygon ( ll1 ) ) ; // Create a new polygon & restart optimization from scratch
return ; // End here the current optimization
}
// Merge polylines having ends at the same position
for ( var il2 in ls ) {
var ll2 = ls [ il2 ] . _latlngs ,
lladd = null ; // List of points to move to another polyline
if ( il1 < il2 && // Not the same & only once each pair
ll2 && ! ls [ il2 ] . options . fill ) { // The 2nd is also a polyline
if ( ll1 [ 0 ] . equals ( ll2 [ 0 ] ) ) {
ll1 . reverse ( ) ;
lladd = ll2 ;
} else if ( ll1 [ 0 ] . equals ( ll2 [ ll2 . length - 1 ] ) ) {
ll1 . reverse ( ) ;
lladd = ll2 . reverse ( ) ;
} else if ( ll1 [ ll1 . length - 1 ] . equals ( ll2 [ 0 ] ) ) {
lladd = ll2 ;
} else if ( ll1 [ ll1 . length - 1 ] . equals ( ll2 [ ll2 . length - 1 ] ) ) {
lladd = ll2 . reverse ( ) ;
}
if ( lladd ) {
lladd . shift ( ) ; // We avoid the first point as it's already on the first poly
this . editLayers . removeLayer ( ls [ il1 ] ) ; // Erase the initial polylines
this . editLayers . removeLayer ( ls [ il2 ] ) ;
this . addLayer ( new L . Polyline ( ll1 . concat ( lladd ) ) ) ; // Create a new poly & restart optimization from scratch
return ; // End here the current optimization
}
}
}
}
}
// Save edited data to the json output field
var ele = document . getElementById ( this . options . entry ) ,
elc = document . getElementById ( this . options . changed ) ;
if ( ele ) {
var elei = typeof ele . value != 'undefined' ? 'value' : 'innerHTML' ;
ele [ elei ] = JSON . stringify ( this . editLayers . toGeoJSON ( ) ) ;
}
this . _map . fire ( 'draw:entry-changed' ) ; // For user's usage
// Unmask "changed" message
if ( elc )
elc . style . display = changed === false ? 'none' : '' ;
}
} ) ;
// Cut a polyline by removing a segment whose the middle marker is cliqued
// Cut a polygon by removing a segment whose the middle marker is cliqued & transform it into polyline
// Horrible hack : modify onClick action on MiddleMarkers Leaflet.draw/Edit.Poly.js & generated files
eval ( 'L.Edit.PolyVerticesEdit.prototype._createMiddleMarker = ' +
L . Edit . PolyVerticesEdit . prototype . _createMiddleMarker . toString ( )
. replace ( /'click', onClick, this|'click',[a-z],this/g , "'click',this._cut,this" )
) ;
// Resize the too big summits markers
L . Edit . PolyVerticesEdit . prototype . options . touchIcon . options . iconSize = new L . Point ( 8 , 8 ) ;
L . Edit . PolyVerticesEdit . include ( {
_cut : function ( e ) {
// Split markers on each side of the cut
2017-05-09 00:14:25 +00:00
var alt ;
2017-05-08 01:31:49 +00:00
var found = 0 ,
2017-05-09 00:14:25 +00:00
lls = [ [ ] , [ ] ] ,
times = [ [ ] , [ ] ] ;
2017-05-08 01:31:49 +00:00
for ( m in this . _markers ) {
2017-05-09 00:14:25 +00:00
if ( this . _markers [ m ] . _latlng . alt ) {
lls [ found ] . push (
[
this . _markers [ m ] . _latlng . lat ,
this . _markers [ m ] . _latlng . lng ,
this . _markers [ m ] . _latlng . alt
]
) ;
}
else {
lls [ found ] . push ( this . _markers [ m ] . _latlng ) ;
}
if ( this . _markers [ m ] . _latlng . time ) {
times [ found ] . push ( this . _markers [ m ] . _latlng . time ) ;
}
2017-05-08 01:31:49 +00:00
if ( this . _markers [ m ] . _middleRight && this . _markers [ m ] . _middleRight . _leaflet _id == e . target . _leaflet _id )
found = 1 ; // We find the cut point
}
// Remove the old poly
this . _map . removeLayer ( this . _poly ) ;
// This is a Polygon, we will remove the clicked segment & transform it into a Polyline
if ( this . _poly . options . fill )
this . _map . fire ( 'draw:created' , { // Create a new Polyline with these summits & optimize
layer : new L . Polyline ( lls [ 1 ] . concat ( lls [ 0 ] ) )
} ) ;
// This is a polyline
else
2017-05-09 00:14:25 +00:00
for ( f in lls ) {
if ( lls [ f ] . length > 1 ) {
var p = new L . Polyline ( lls [ f ] ) ;
2017-05-09 01:00:09 +00:00
p . gpxedit _id = this . _poly . gpxedit _id ;
2017-05-09 00:14:25 +00:00
if ( times [ f ] . length === p . _latlngs . length ) {
for ( var i = 0 ; i < times [ f ] . length ; i ++ ) {
if ( times [ f ] [ i ] ) {
p . _latlngs [ i ] . time = times [ f ] [ i ] ;
}
}
}
2017-05-08 01:31:49 +00:00
this . _map . fire ( 'draw:created' , { // Create a new Polyline with the splited summits if any
2017-05-09 01:00:09 +00:00
layer : p ,
layerType : 'polyline'
2017-05-08 01:31:49 +00:00
} ) ;
2017-05-09 00:14:25 +00:00
}
}
2017-05-08 01:31:49 +00:00
// Optimize
this . _map . fire ( 'draw:editvertex' ) ;
}
2017-05-09 00:14:25 +00:00
} ) ;