summaryrefslogtreecommitdiff
path: root/chimere
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@peacefrogs.net>2012-11-22 00:26:57 +0100
committerÉtienne Loks <etienne.loks@peacefrogs.net>2012-11-22 00:33:22 +0100
commitb9b767af4dec1bfd13f09fac4c863c80db450090 (patch)
treec061bfe6d200a0ee103c97800000607f12dd7000 /chimere
parenta1176e9e6dbcd8af30fc32248fec5748dabfcbe9 (diff)
downloadChimère-b9b767af4dec1bfd13f09fac4c863c80db450090.tar.bz2
Chimère-b9b767af4dec1bfd13f09fac4c863c80db450090.zip
Admin import: xapi specific interface (refs #449, refs #458)
Diffstat (limited to 'chimere')
-rw-r--r--chimere/forms.py13
-rw-r--r--chimere/models.py5
-rw-r--r--chimere/static/chimere/css/forms.css16
-rw-r--r--chimere/static/chimere/js/importer_interface.js66
-rw-r--r--chimere/widgets.py158
5 files changed, 210 insertions, 48 deletions
diff --git a/chimere/forms.py b/chimere/forms.py
index 8fafaba..63e8c52 100644
--- a/chimere/forms.py
+++ b/chimere/forms.py
@@ -34,8 +34,8 @@ from django.core.mail import EmailMessage, BadHeaderError
from chimere.models import Marker, Route, PropertyModel, Property, Area,\
News, Category, SubCategory, RouteFile, MultimediaFile, MultimediaType, \
PictureFile, Importer, IMPORTER_CHOICES
-from chimere.widgets import AreaField, PointField, TextareaWidget, \
- TextareaAdminWidget, DatePickerWidget, ImporterChoicesWidget
+from chimere.widgets import AreaField, PointField, TextareaWidget, ImportFiltrWidget,\
+ TextareaAdminWidget, DatePickerWidget, ImporterChoicesWidget, RE_XAPI
from datetime import timedelta, datetime, tzinfo
@@ -98,6 +98,7 @@ class NewsAdminForm(forms.ModelForm):
model = News
class ImporterAdminForm(forms.ModelForm):
+ filtr = forms.CharField(widget=ImportFiltrWidget, required=False)
importer_type = forms.ChoiceField(widget=ImporterChoicesWidget,
choices=[('', '--')]+list(IMPORTER_CHOICES))
class Meta:
@@ -112,6 +113,14 @@ class ImporterAdminForm(forms.ModelForm):
Verify that only one type of source is provided
Verify that shapefiles are zipped
'''
+ if self.cleaned_data.get('importer_type') == 'OSM' and \
+ not self.cleaned_data.get('filtr'):
+ raise forms.ValidationError(_(u"For OSM import you must be "\
+ u"provide a filter. Select an area and node/way filter."))
+ if self.cleaned_data.get('importer_type') == 'OSM' and \
+ not RE_XAPI.match(self.cleaned_data.get('filtr')):
+ raise forms.ValidationError(_(u"For OSM import you must be "\
+ u"provide a filter. Select an area and node/way filter."))
if self.cleaned_data.get('importer_type') == 'SHP' and \
not self.cleaned_data.get('zipped'):
raise forms.ValidationError(_(u"Shapefiles must be provided in a "\
diff --git a/chimere/models.py b/chimere/models.py
index 924fd54..fe38e11 100644
--- a/chimere/models.py
+++ b/chimere/models.py
@@ -229,13 +229,12 @@ class Importer(models.Model):
'''
importer_type = models.CharField(_(u"Importer type"), max_length=4,
choices=IMPORTER_CHOICES)
- # URL of a KML file or a XAPI service for OSM
+ filtr = models.CharField(_(u"Filter"), max_length=200,
+ blank=True, null=True)
source = models.CharField(_(u"Source"), max_length=200,
blank=True, null=True)
source_file = models.FileField(_(u"Source file"),
upload_to='import_files', blank=True, null=True)
- filtr = models.CharField(_(u"Filter"), max_length=200,
- blank=True, null=True)
default_name = models.CharField(_(u"Name by default"), max_length=200,
blank=True, null=True)
srid = models.IntegerField(_(u"SRID"), blank=True, null=True)
diff --git a/chimere/static/chimere/css/forms.css b/chimere/static/chimere/css/forms.css
index 46acdb9..b75f947 100644
--- a/chimere/static/chimere/css/forms.css
+++ b/chimere/static/chimere/css/forms.css
@@ -39,7 +39,7 @@ div.bottomform{
margin:0;
border: 1px solid black;
width:100%;
- height:500px;
+ height:350px;
}
@@ -129,3 +129,17 @@ div.bottomform{
overflow:hidden;
height:16px;
}
+
+.form-row.field-filtr.field-map{
+ float:right;
+ width:50%;
+}
+
+#id_filtr{
+ width:400px;
+}
+
+.aligned .input-osm label{
+ display:inline;
+ float:none;
+}
diff --git a/chimere/static/chimere/js/importer_interface.js b/chimere/static/chimere/js/importer_interface.js
index 4eaacb7..fc52b4c 100644
--- a/chimere/static/chimere/js/importer_interface.js
+++ b/chimere/static/chimere/js/importer_interface.js
@@ -1,8 +1,9 @@
django.jQuery(function($) {
var importer_form_filter = {
- OSM:new Array('field-source', 'field-default_name', 'field-categories'),
+ OSM:new Array('field-filtr', 'field-default_name', 'field-categories',
+ 'field-source'),
KML:new Array('field-source', 'field-source_file', 'field-default_name',
- 'filed-filtr', 'field-zipped', 'field-origin',
+ 'field-filtr', 'field-zipped', 'field-origin',
'field-license', 'field-categories'),
SHP:new Array('field-source', 'field-source_file', 'field-default_name',
'field-zipped', 'field-origin', 'field-srid',
@@ -13,26 +14,63 @@ django.jQuery(function($) {
'field-origin', 'field-srid', 'field-license',
'field-categories')
}
-/*
- field-source
- field-source_file
- field-filtr
- field-default_name
- field-srid
- field-zipped
- field-origin
- field-license
- field-categories
-*/
+ var map_initialized;
function refresh_importer_form(){
$('.form-row').not('.field-importer_type').hide();
- var importer_val = $('.field-importer_type select').val()
+ var importer_val = $('.field-importer_type select').val();
if (!importer_val) return;
var form_filters = importer_form_filter[importer_val];
for (k=0; k<form_filters.length;k++){
$('.form-row.'+form_filters[k]).show();
}
+ if (importer_val == 'OSM'){
+ $('.form-row.field-filtr').addClass('field-map');
+ $('#map_edit').show();
+ if(!$('#id_source').val()){
+ $('#id_source').val(default_xapi);
+ }
+ $('#id_filtr').attr('readonly', true);
+ $('.help-osm').show();
+ $('.input-osm').show();
+ if (!map_initialized){
+ init_map_form();
+ map_initialized = true;
+ }
+ } else {
+ $('.form-row.field-filtr').removeClass('field-map');
+ $('#id_filtr').attr('readonly', false);
+ $('#map_edit').hide();
+ $('.help-osm').hide();
+ $('.input-osm').hide();
+ if($('#id_source').val() == default_xapi) $('#id_source').val('');
+ }
}
refresh_importer_form();
$('.field-importer_type select').change(refresh_importer_form);
+ function refresh_filtr_form(){
+ if (!$('#upper_left_lat').val() ||
+ !parseFloat($('#upper_left_lat').val())){
+ alert(msg_missing_area);
+ return false;
+ }
+ if (!$('input[name=id_filtr_type]:checked').val()){
+ alert(msg_missing_type);
+ return false;
+ }
+ if (!$('#id_filtr_tag').val()){
+ alert(msg_missing_filtr);
+ return false;
+ }
+ value = $('input[name=id_filtr_type]:checked').val();
+ value += '[' + $("#id_filtr_tag").val() + ']';
+ value += '[bbox=';
+ value += $('#upper_left_lon').val() + ',';
+ value += $('#upper_left_lat').val() + ',';
+ value += $('#lower_right_lon').val() + ',';
+ value += $('#lower_right_lat').val();
+ value += ']';
+ $('#id_filtr').val(value);
+ return false;
+ }
+ $('#id_refresh_filtr').click(refresh_filtr_form);
});
diff --git a/chimere/widgets.py b/chimere/widgets.py
index 00f5b79..219f30d 100644
--- a/chimere/widgets.py
+++ b/chimere/widgets.py
@@ -30,6 +30,8 @@ from django.core.urlresolvers import reverse
from django.utils.safestring import mark_safe
from django.utils.translation import ugettext as _
+import re
+
def getMapJS(area_name=''):
'''Variable initialization for drawing the map
'''
@@ -385,34 +387,44 @@ class AreaWidget(forms.TextInput):
"%schimere/js/edit_area.js" % settings.STATIC_URL,
"%schimere/js/base.js" % settings.STATIC_URL,]
- def render(self, name, value, attrs=None):
+ def get_bounding_box_from_value(self, value):
+ '''
+ Return upper left lat/lon and lower lat/lon from the input value
+ '''
+ upper_left_lat, upper_left_lon = 0, 0
+ lower_right_lat, lower_right_lon = 0, 0
+ if not value:
+ return upper_left_lat, upper_left_lon, lower_right_lat, \
+ lower_right_lon
+ if len(value) == 2:
+ upper_left = value[0]
+ lower_right = value[1]
+ if hasattr(upper_left, 'x') and hasattr(upper_left, 'y'):
+ upper_left_lon, upper_left_lat = upper_left.x, upper_left.y
+ elif len(upper_left) == 2:
+ try:
+ upper_left_lon = float(upper_left[0])
+ upper_left_lat = float(upper_left[1])
+ except ValueError:
+ pass
+ if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'):
+ lower_right_lon, lower_right_lat = lower_right.x, \
+ lower_right.y
+ elif len(lower_right) == 2:
+ lower_right_lon, lower_right_lat = lower_right
+ try:
+ lower_right_lon = float(lower_right[0])
+ lower_right_lat = float(lower_right[1])
+ except ValueError:
+ pass
+ return upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon
+
+ def render(self, name, value, attrs=None, initialized=True):
"""
Render a map
"""
- upper_left_lat, upper_left_lon = 0, 0
- lower_right_lat, lower_right_lon = 0, 0
- if value:
- if len(value) == 2:
- upper_left = value[0]
- lower_right = value[1]
- if hasattr(upper_left, 'x') and hasattr(upper_left, 'y'):
- upper_left_lon, upper_left_lat = upper_left.x, upper_left.y
- elif len(upper_left) == 2:
- try:
- upper_left_lon = float(upper_left[0])
- upper_left_lat = float(upper_left[1])
- except ValueError:
- pass
- if hasattr(lower_right, 'x') and hasattr(lower_right, 'y'):
- lower_right_lon, lower_right_lat = lower_right.x, \
- lower_right.y
- elif len(lower_right) == 2:
- lower_right_lon, lower_right_lat = lower_right
- try:
- lower_right_lon = float(lower_right[0])
- lower_right_lat = float(lower_right[1])
- except ValueError:
- pass
+ upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon = \
+ self.get_bounding_box_from_value(value)
tpl = getMapJS()
tpl += u"</div>\n"\
u"<input type='hidden' name='upper_left_lat' id='upper_left_lat' "\
@@ -425,9 +437,9 @@ class AreaWidget(forms.TextInput):
u"value='%f'/>\n" % (
upper_left_lat, upper_left_lon, lower_right_lat, lower_right_lon)
help_msg = _(u"Hold CTRL, click and drag to select area on the map")
- tpl += u"<p>%s</p>\n" % help_msg
+ tpl += u"<p class='help-osm'>%s</p>\n" % help_msg
tpl += u"<script type='text/javascript'>\n"
- tpl += u"$(document).ready(function($) {\ninit();\n"
+ tpl += u"function init_map_form (){\ninit();\n"
if value:
tpl += u"var extent = new OpenLayers.Bounds(%f, %f, %f, %f);\n"\
u"extent.transform(epsg_display_projection, epsg_projection);\n"\
@@ -435,7 +447,10 @@ class AreaWidget(forms.TextInput):
u"map.zoomToExtent(extent, true);\n"\
u"map.zoomOut();" % (upper_left_lon, upper_left_lat,
lower_right_lon, lower_right_lat)
- tpl += u"});\n</script>\n<hr class='spacer'/>\n"
+ tpl += u"}\n"
+ if initialized:
+ tpl += u"$(document).ready(function($) {init_map_form()});\n"
+ tpl += u"</script>\n"
tpl += u"<div id='map_edit'>\n"
return mark_safe(tpl)
@@ -455,6 +470,93 @@ class AreaWidget(forms.TextInput):
values.append(value)
return values
+RE_XAPI = re.compile('(node|way)\[(.*=.*)\]\[bbox='\
+ '(-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*,-*[0-9]*.[0-9]*)\]')
+
+class ImportFiltrWidget(AreaWidget):
+ """
+ Manage the edition of the import source field
+ """
+ class Media:
+ css = {
+ "all": settings.OSM_CSS_URLS + \
+ ["%schimere/css/forms.css" % settings.STATIC_URL,]
+ }
+ js = settings.OSM_JS_URLS + [
+ "%schimere/js/edit_area.js" % settings.STATIC_URL,
+ "%schimere/js/base.js" % settings.STATIC_URL,]
+
+ def render(self, name, value, attrs=None):
+ """
+ Render a map
+ """
+ tpl = super(ImportFiltrWidget, self).render(name, value, attrs,
+ initialized=False)
+ tpl += u"</div><hr class='spacer'/>"
+ vals = {'lbl':_(u"Type:"), 'name':name, 'node':_(u"Node"),
+ 'way':_(u"Way")}
+ vals['way_selected'] = ' checked="checked"'\
+ if self.xapi_type == 'way' else ''
+ vals['node_selected'] = ' checked="checked"'\
+ if self.xapi_type == 'node' else ''
+ tpl += u"<div class='input-osm'><label>%(lbl)s</label>"\
+ u"<input type='radio' name='id_%(name)s_type' id='id_%(name)s_node'"\
+ u" value='node'%(node_selected)s/> <label for='id_%(name)s_node'>"\
+ u"%(node)s</label> <input type='radio' name='id_%(name)s_type' "\
+ u"id='id_%(name)s_way' value='way'%(way_selected)s/> <label "\
+ u"for='id_%(name)s_way'>%(way)s</label></div>" % vals
+ help_msg = _(u"Enter an OSM \"tag=value\" string such as "\
+ u"\"amenity=pub\". A list of common tag is available "\
+ u"<a href='https://wiki.openstreetmap.org/wiki/Map_Features' "\
+ u" target='_blank'>here</a>.")
+ tpl += u"<p class='help-osm'>%s</p>\n" % help_msg
+ tpl += u"<div class='input-osm'><label for='id_%s_tag'>%s</label>"\
+ u"<input type='text' id='id_%s_tag' value=\"%s\"/></div>" % (
+ name, _(u"Tag:"), name, self.xapi_tag)
+ tpl += u"<script type='text/javascript'>\n"
+ tpl += u"var default_xapi='%s';" % settings.CHIMERE_XAPI_URL
+ tpl += u'var msg_missing_area = "%s";' % \
+ _(u"You have to select an area.")
+ tpl += u'var msg_missing_type = "%s";' % \
+ _(u"You have to select a type.")
+ tpl += u'var msg_missing_filtr = "%s";' % \
+ _(u"You have to insert a filter tag.")
+ tpl += u"</script>\n"
+ help_msg = _(u"Don't forget to refresh before submit")
+ tpl += u"<p class='help-osm'>%s</p>\n" % help_msg
+ if not value:
+ value = ''
+ tpl += u"<div><input type='text' id='id_%s' name='id_%s' "\
+ u"value=\"%s\"/> <input type='button' id='id_refresh_%s' "\
+ u"value='%s' class='input-osm'/>" % (name, name, value, name,
+ _(u"Refresh"))
+ return mark_safe(tpl)
+
+ def value_from_datadict(self, data, files, name):
+ """
+ Return the appropriate values
+ """
+ return data.get('id_'+name, None)
+
+ def get_bounding_box_from_value(self, value):
+ '''
+ Return upper left lat/lon, lower lat/lon from the input value.
+ Get also xapi type and xapi tag
+ '''
+ upper_left_lat, upper_left_lon = 0, 0
+ lower_right_lat, lower_right_lon = 0, 0
+ self.xapi_type, self.xapi_tag, self.bounding_box = None, None, None
+ xapi_m = RE_XAPI.match(value)
+ if not xapi_m:
+ return upper_left_lat, upper_left_lon, lower_right_lat, \
+ lower_right_lon
+ # as the regexp pass, we could be pretty confident
+ self.xapi_type, self.xapi_tag, self.bounding_box = xapi_m.groups()
+ upper_left_lon, upper_left_lat, lower_right_lon, lower_right_lat = \
+ self.bounding_box.split(',')
+ return float(upper_left_lat), float(upper_left_lon), \
+ float(lower_right_lat), float(lower_right_lon)
+
class AreaField(forms.MultiValueField):
'''
Set the widget for the form field