basic feature adding in editor
This commit is contained in:
parent
96abc31a6c
commit
9f79e26671
8 changed files with 169 additions and 18 deletions
16
src/c3nav/editor/forms.py
Normal file
16
src/c3nav/editor/forms.py
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
from django.forms import ModelForm
|
||||||
|
from django.forms.widgets import HiddenInput
|
||||||
|
|
||||||
|
from ..mapdata.models import Feature
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureForm(ModelForm):
|
||||||
|
def __init__(self, *args, feature_type, **kwargs):
|
||||||
|
self.feature_type = feature_type
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
self.fields['level'].widget = HiddenInput()
|
||||||
|
self.fields['geometry'].widget = HiddenInput()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Feature
|
||||||
|
fields = ['name', 'package', 'level', 'geometry']
|
|
@ -32,16 +32,49 @@
|
||||||
color:#000000;
|
color:#000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#mapeditcontrols {
|
#mapeditcontrols {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top:54px;
|
top:54px;
|
||||||
bottom:0;
|
bottom:0;
|
||||||
|
padding:0;
|
||||||
|
width:350px;
|
||||||
|
right:0;
|
||||||
|
overflow:hidden;
|
||||||
|
background-image:url('/static/img/loader.gif');
|
||||||
|
background-repeat:no-repeat;
|
||||||
|
background-position:center;
|
||||||
|
}
|
||||||
|
#mapeditcontrols > div {
|
||||||
|
position:absolute;
|
||||||
|
top:0;
|
||||||
|
bottom:0;
|
||||||
|
background-color:white;
|
||||||
padding:8px;
|
padding:8px;
|
||||||
width:350px;
|
width:350px;
|
||||||
right:0;
|
right:0;
|
||||||
overflow:auto;
|
overflow:auto;
|
||||||
}
|
}
|
||||||
#mapeditcontrols legend .btn {
|
#mapeditcontrols > #mapeditdetail {
|
||||||
|
right:-350px;
|
||||||
|
transition: right 300ms;
|
||||||
|
-webkit-transition: right 300ms;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
#mapeditcontrols.detail #mapeditdetail {
|
||||||
|
right:0;
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
#mapeditcontrols > #mapeditlist {
|
||||||
|
display:none;
|
||||||
|
}
|
||||||
|
#mapeditcontrols.list > #mapeditlist {
|
||||||
|
display:block;
|
||||||
|
}
|
||||||
|
#mapeditdetail h3 {
|
||||||
|
margin-top:5px;
|
||||||
|
}
|
||||||
|
#mapeditlist legend .btn {
|
||||||
padding-left:5.5px;
|
padding-left:5.5px;
|
||||||
padding-right:5.5px;
|
padding-right:5.5px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ editor = {
|
||||||
minZoom: 1,
|
minZoom: 1,
|
||||||
crs: L.CRS.Simple,
|
crs: L.CRS.Simple,
|
||||||
editable: true,
|
editable: true,
|
||||||
closePopupOnClick: false,
|
closePopupOnClick: false
|
||||||
});
|
});
|
||||||
|
|
||||||
L.control.scale({imperial: false}).addTo(editor.map);
|
L.control.scale({imperial: false}).addTo(editor.map);
|
||||||
|
@ -18,12 +18,41 @@ editor = {
|
||||||
editor.get_packages();
|
editor.get_packages();
|
||||||
editor.get_sources();
|
editor.get_sources();
|
||||||
editor.get_levels();
|
editor.get_levels();
|
||||||
|
|
||||||
|
$('#mapeditdetail').on('click', '#btn_abort', function() {
|
||||||
|
if (editor._adding !== null) {
|
||||||
|
editor._adding.remove();
|
||||||
|
editor._adding = null;
|
||||||
|
editor._drawing = null;
|
||||||
|
$('#mapeditcontrols').removeClass('detail').addClass('list');
|
||||||
|
$('#mapeditdetail').html('');
|
||||||
|
$('.start-drawing').prop('disabled', false);
|
||||||
|
}
|
||||||
|
}).on('submit', 'form', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var data = $(this).serialize();
|
||||||
|
var action = $(this).attr('action');
|
||||||
|
$('#mapeditcontrols').removeClass('detail');
|
||||||
|
$('#mapeditdetail').html('');
|
||||||
|
$.post(action, data, function(data) {
|
||||||
|
var content = $(data);
|
||||||
|
if ($('<div>').append(content).find('form').length > 0) {
|
||||||
|
$('#mapeditdetail').html(content);
|
||||||
|
$('#mapeditcontrols').addClass('detail');
|
||||||
|
} else {
|
||||||
|
editor._adding = null;
|
||||||
|
editor._drawing = null;
|
||||||
|
$('.start-drawing').prop('disabled', false);
|
||||||
|
$('#mapeditcontrols').addClass('list');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
get_feature_types: function() {
|
get_feature_types: function() {
|
||||||
$.getJSON('/api/v1/featuretypes/', function(feature_types) {
|
$.getJSON('/api/v1/featuretypes/', function(feature_types) {
|
||||||
var feature_type;
|
var feature_type;
|
||||||
var editcontrols = $('#mapeditcontrols');
|
var editcontrols = $('#mapeditlist');
|
||||||
for(var i=0;i<feature_types.length;i++) {
|
for(var i=0;i<feature_types.length;i++) {
|
||||||
feature_type = feature_types[i];
|
feature_type = feature_types[i];
|
||||||
editor.feature_types[feature_type.name] = feature_type;
|
editor.feature_types[feature_type.name] = feature_type;
|
||||||
|
@ -72,33 +101,48 @@ editor = {
|
||||||
|
|
||||||
level_layers: {},
|
level_layers: {},
|
||||||
levels: {},
|
levels: {},
|
||||||
|
_level: null,
|
||||||
get_levels: function() {
|
get_levels: function() {
|
||||||
$.getJSON('/api/v1/levels/?ordering=-altitude', function(levels) {
|
$.getJSON('/api/v1/levels/?ordering=-altitude', function(levels) {
|
||||||
L.LevelControl = L.Control.extend({
|
L.LevelControl = L.Control.extend({
|
||||||
options: {
|
options: {
|
||||||
position: 'bottomright'
|
position: 'bottomright'
|
||||||
},
|
},
|
||||||
onAdd: function (map) {
|
onAdd: function () {
|
||||||
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link;
|
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-levels'), link;
|
||||||
var level;
|
var level;
|
||||||
for(var i=0;i<levels.length;i++) {
|
for(var i=0;i<levels.length;i++) {
|
||||||
level = levels[i];
|
level = levels[i];
|
||||||
link = L.DomUtil.create('a', (i == levels.length-1) ? 'current' : '', container);
|
link = L.DomUtil.create('a', (i == levels.length-1) ? 'current' : '', container);
|
||||||
|
link.name = level.name;
|
||||||
link.innerHTML = level.name;
|
link.innerHTML = level.name;
|
||||||
|
link.href = '';
|
||||||
}
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
editor.map.addControl(new L.LevelControl());
|
editor.map.addControl(new L.LevelControl());
|
||||||
|
|
||||||
var level_layer, feature_layer, level;
|
$('.leaflet-levels').on('click', 'a', function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (editor._drawing !== null || editor._adding !== null) return;
|
||||||
|
editor.level_layers[editor._level].remove();
|
||||||
|
editor._level = $(this).attr('name');
|
||||||
|
editor.level_layers[editor._level].addTo(editor.map);
|
||||||
|
$('.leaflet-levels .current').removeClass('current');
|
||||||
|
$(this).addClass('current');
|
||||||
|
});
|
||||||
|
|
||||||
|
var level;
|
||||||
for(var i=0;i<levels.length;i++) {
|
for(var i=0;i<levels.length;i++) {
|
||||||
level = levels[i];
|
level = levels[i];
|
||||||
editor.levels[level.name] = level;
|
editor.levels[level.name] = level;
|
||||||
level_layer = L.layerGroup().addTo(editor.map);
|
editor.level_layers[level.name] = L.layerGroup();
|
||||||
editor.level_layers[level.name] = level_layer;
|
|
||||||
}
|
}
|
||||||
editor.init_drawing();
|
editor.init_drawing();
|
||||||
|
|
||||||
|
editor._level = levels[levels.length-1].name;
|
||||||
|
editor.level_layers[editor._level].addTo(editor.map);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -111,7 +155,7 @@ editor = {
|
||||||
options: {
|
options: {
|
||||||
position: 'topleft'
|
position: 'topleft'
|
||||||
},
|
},
|
||||||
onAdd: function (map) {
|
onAdd: function() {
|
||||||
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-drawbar');
|
var container = L.DomUtil.create('div', 'leaflet-control leaflet-bar leaflet-drawbar');
|
||||||
$('<a href="#" id="drawcancel">').appendTo(container).text('cancel').attr({
|
$('<a href="#" id="drawcancel">').appendTo(container).text('cancel').attr({
|
||||||
href: '#',
|
href: '#',
|
||||||
|
@ -126,21 +170,24 @@ editor = {
|
||||||
});
|
});
|
||||||
editor.map.addControl(new L.DrawControl());
|
editor.map.addControl(new L.DrawControl());
|
||||||
|
|
||||||
$('#mapeditcontrols').on('click', '.start-drawing', function() {
|
$('#mapeditlist').on('click', '.start-drawing', function() {
|
||||||
console.log($(this).closest('fieldset'));
|
console.log($(this).closest('fieldset'));
|
||||||
editor.start_drawing($(this).closest('fieldset').attr('name'));
|
editor.start_drawing($(this).closest('fieldset').attr('name'));
|
||||||
});
|
});
|
||||||
|
|
||||||
editor.map.on('editable:drawing:commit', function (e) {
|
editor.map.on('editable:drawing:commit', function (e) {
|
||||||
editor._drawing = null;
|
|
||||||
editor._adding = e.layer;
|
editor._adding = e.layer;
|
||||||
|
|
||||||
e.layer.disableEdit();
|
e.layer.disableEdit();
|
||||||
L.popup({
|
|
||||||
closeButton: false,
|
|
||||||
autoClose: false,
|
|
||||||
}).setContent('<img src="/static/img/loader.gif">').setLatLng(e.layer.getCenter()).openOn(editor.map);
|
|
||||||
$('.leaflet-drawbar').hide();
|
$('.leaflet-drawbar').hide();
|
||||||
|
var path = '/editor/features/'+editor._drawing+'/add';
|
||||||
|
$('#mapeditcontrols').removeClass('list');
|
||||||
|
$('#mapeditdetail').html('<img src="/static/img/loader.gif">').load(path, function() {
|
||||||
|
$('#mapeditcontrols').addClass('detail');
|
||||||
|
$('#id_level').val(editor._level);
|
||||||
|
$('#id_geometry').val(JSON.stringify(editor._adding.toGeoJSON().geometry));
|
||||||
|
});
|
||||||
|
|
||||||
}).on('editable:drawing:cancel', function (e) {
|
}).on('editable:drawing:cancel', function (e) {
|
||||||
if (editor._drawing !== null && editor._adding === null) {
|
if (editor._drawing !== null && editor._adding === null) {
|
||||||
e.layer.remove();
|
e.layer.remove();
|
||||||
|
@ -166,10 +213,10 @@ editor = {
|
||||||
editor.map.editTools.stopDrawing();
|
editor.map.editTools.stopDrawing();
|
||||||
editor._drawing = null;
|
editor._drawing = null;
|
||||||
$('.leaflet-drawbar').hide();
|
$('.leaflet-drawbar').hide();
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
if ($('#mapeditcontrols').length) {
|
if ($('#mapeditlist').length) {
|
||||||
editor.init();
|
editor.init();
|
||||||
}
|
}
|
||||||
|
|
15
src/c3nav/editor/templates/editor/feature.html
Normal file
15
src/c3nav/editor/templates/editor/feature.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
{% load bootstrap3 %}
|
||||||
|
|
||||||
|
<h3>{% if new %}New{% else %}Edit{% endif %} {{ feature_type.title }}</h3>
|
||||||
|
<form action="{{ path }}" method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% bootstrap_form form %}
|
||||||
|
{% buttons %}
|
||||||
|
<button id="btn_abort" class="btn btn-danger">
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" class="btn btn-primary pull-right">
|
||||||
|
Submit
|
||||||
|
</button>
|
||||||
|
{% endbuttons %}
|
||||||
|
</form>
|
0
src/c3nav/editor/templates/editor/feature_success.html
Normal file
0
src/c3nav/editor/templates/editor/feature_success.html
Normal file
|
@ -2,5 +2,8 @@
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div id="map"></div>
|
<div id="map"></div>
|
||||||
<div id="mapeditcontrols"></div>
|
<div id="mapeditcontrols" class="list">
|
||||||
|
<div id="mapeditlist"></div>
|
||||||
|
<div id="mapeditdetail"></div>
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
from django.conf.urls import url
|
from django.conf.urls import url
|
||||||
from django.views.generic import TemplateView
|
from django.views.generic import TemplateView
|
||||||
|
|
||||||
|
from c3nav.editor.views import add_feature
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index')
|
url(r'^$', TemplateView.as_view(template_name='editor/map.html'), name='editor.index'),
|
||||||
|
url(r'^features/(?P<feature_type>[^/]+)/add/$', add_feature, name='editor.feature.add')
|
||||||
]
|
]
|
||||||
|
|
34
src/c3nav/editor/views.py
Normal file
34
src/c3nav/editor/views.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from django.db import transaction
|
||||||
|
from django.http.response import Http404
|
||||||
|
from django.shortcuts import render
|
||||||
|
|
||||||
|
from c3nav.editor.forms import FeatureForm
|
||||||
|
from c3nav.mapdata.models.feature import FEATURE_TYPES
|
||||||
|
from c3nav.settings import DIRECT_EDITING
|
||||||
|
|
||||||
|
|
||||||
|
def add_feature(request, feature_type):
|
||||||
|
feature_type = FEATURE_TYPES.get(feature_type)
|
||||||
|
if feature_type is None:
|
||||||
|
raise Http404()
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
form = FeatureForm(request.POST, feature_type=feature_type)
|
||||||
|
if form.is_valid():
|
||||||
|
if not DIRECT_EDITING:
|
||||||
|
return render(request, 'editor/feature_success.html', {})
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
feature = form.instance
|
||||||
|
feature.feature_type = feature_type.name
|
||||||
|
feature.save()
|
||||||
|
return render(request, 'editor/feature_success.html', {})
|
||||||
|
else:
|
||||||
|
form = FeatureForm(feature_type=feature_type)
|
||||||
|
|
||||||
|
return render(request, 'editor/feature.html', {
|
||||||
|
'form': form,
|
||||||
|
'feature_type': feature_type,
|
||||||
|
'path': request.path,
|
||||||
|
'new': True
|
||||||
|
})
|
Loading…
Add table
Add a link
Reference in a new issue