2014-03-24 62 views
3

我想爲Flask-Admin創建一個視圖,以在幾何字段中輸入座標。如何創建兩個文本字段並將其轉換爲幾何對象?用於幾何字段的AdminModelConvertor(LON/LAT)

這是我到目前爲止已經試過(除了無數的其他事情)

class CustomAdminConverter(AdminModelConverter): 
    @converts('geoalchemy2.types.Geometry') 
    def convert_geometry(self, field_args, **extra): 
     return WayToCoordinatesField(**field_args) 

class WayToCoordinatesField(wtf.TextAreaField): 
    def process_data(self, value): 
     print "run" #is never called?? 
     if value is None: 
      value = {} 
     else: 
      value = "test" 
     return value 

class POIView(ModelView): 
    inline_model_form_converter = MyInlineModelConverter 
    model_form_converter=CustomAdminConverter 
    can_create = True 
    def __init__(self, session, **kwargs): 
     # You can pass name and other parameters if you want to 
     super(POIView, self).__init__(POI, session, **kwargs) 

    def scaffold_form(self): 
     form_class = super(POIView, self).scaffold_form() 
     form_class.way = wtf.TextAreaField("Coordinates") 
     return form_class 

的POI對象看起來是這樣的:

class POI(db.Model): 
    __tablename__ = 'zo_poi' 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.Text()) 
    tags = db.Column(HSTORE()) 
    src = db.Column(db.Text()) 
    way = db.Column(Geometry('POINT')) 
    intern = db.Column(db.BOOLEAN()) 

非常感謝您的幫助!

回答

3

得到了一個交互式地圖的解決方案。 這裏是我做了什麼:

管理/ fields.py:

import json 
from wtforms import Field 
import geojson 
from shapely.geometry import asShape 
from geoalchemy2.shape import to_shape, from_shape 
from wtforms.widgets import html_params, HTMLString 
from geoalchemy2.elements import WKTElement, WKBElement 
from flask import render_template 
class WTFormsMapInput(object): 
    def __call__(self, field, **kwargs): 
     options = dict(name=field.name, value=field.data, height=field.height, width=field.width, 
         geometry_type=field.geometry_type) 

     return HTMLString(render_template("admin/admin_map.html", height=options['height'], width=options['width'], 
              geolayer=self.geolayer(field.data), preview=False)) 

    def geolayer(self, value): 
     if value is not None: 
      html = "" 
      subme = """var geojson = JSON.parse('%s'); 
         editableLayers.addData(geojson); 
         update() 
         map.fitBounds(editableLayers.getBounds());""" 
      # If validation in Flask-Admin fails on somethign other than 
      # the spatial column, it is never converted to geojson. Didn't 
      # spend the time to figure out why, so I just convert here. 
      if isinstance(value, (WKTElement, WKBElement)): 
       html += subme % geojson.dumps(to_shape(value)) 
      else: 
       html += subme % geojson.dumps(value) 
      return html 


class WTFormsMapField(Field): 
    widget = WTFormsMapInput() 

    def __init__(self, label='', validators=None, geometry_type=None, width=500, height=500, 
       **kwargs): 
     super(WTFormsMapField, self).__init__(label, validators, **kwargs) 
     self.width = width 
     self.height = height 
     self.geometry_type = geometry_type 

    def _value(self): 
     """ Called by widget to get GeoJSON representation of object """ 
     if self.data: 
      return self.data 
     else: 
      return json.loads(json.dumps(dict())) 

    def process_formdata(self, valuelist): 
     """ Convert GeoJSON to DB object """ 
     if valuelist: 
      geo_ob = geojson.loads(valuelist[0]) 
      self.data = from_shape(asShape(geo_ob.geometry)) 
     else: 
      self.data = None 

    def process_data(self, value): 
     """ Convert DB object to GeoJSON """ 
     if value is not None: 
      self.data = geojson.loads(geojson.dumps(to_shape(value))) 
      print self.data 
     else: 
      self.data = None 

模板/管理/ admin_map.html

<link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.css"/> 
<link rel="stylesheet" href="http://leaflet.github.io/Leaflet.draw/leaflet.draw.css"/> 
<script src="http://cdn.leafletjs.com/leaflet-0.7.2/leaflet.js"></script> 
<script src="http://leaflet.github.io/Leaflet.draw/leaflet.draw.js"></script> 
<script src="/admin/static/vendor/jquery-1.8.3.min.js" type="text/javascript"></script> 
<script src="/static/js/googleOverlay/layer/tile/Google.js"></script> 
<script src="http://maps.google.com/maps/api/js?v=3&sensor=false"></script> 

<div id="map" style="height: {{ height }}px; width: {{ width }}px;"></div> 
<input id="geojson" type="text" name="{{ name }}"/> 

<script> 
    var map = new L.Map('map', { 
       center: new L.LatLng(47.3682, 8.879), 
       zoom: 11 
       {% if preview %} 
       , 
        dragging: false, 
        touchzoom: false, 
        scrollWheelZoom: false, 
        doubleClickZoom: false, 
        boxZoom: false, 
        tap: false, 
        keyboard: false, 
        zoomControl: false 

       {% endif %} 
      } 
    ); 
    var ggl = new L.Google('ROADMAP'); 
    map.addLayer(ggl); 
    var osm = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'); 
    map.addControl(new L.Control.Layers({'OpenStreetMap': osm, 'Google Maps': ggl}, {})); 

    var editableLayers = L.geoJson().addTo(map); 

    {{ geolayer |safe }} 
    {% if not preview %} 
    var drawControl = new L.Control.Draw({ 
     position: 'topright', 
     draw: { 
      polyline: false, 
      circle: false, 
      rectangle: false, 
      polygon: true, 
      marker: true, 
     }, 
     edit: { 
      featureGroup: editableLayers 
     } 
    }); 
    {% endif %} 
    map.addControl(drawControl); 

    map.on('draw:created', function (e) { 
     editableLayers.addLayer(e.layer); 
     update(); 
    }); 

    map.on('draw:edited', function (e) { 
     // Just use the first layer 
     update(); 
    }) 

    map.on('draw:deleted', function (e) { 
     update(); 
    }) 

    function update() { 
     if (editableLayers.getLayers().length > 0) { 
      $("#geojson").val(JSON.stringify(editableLayers.getLayers()[0].toGeoJSON())); 
     } else { 
      $("#geojson").val(null); 
     } 
    } 

</script> 

管理/ views.py

class POIView(ModelView): 
    can_create = True 
    form_overrides = dict(location=WTFormsMapField) 
    form_args = dict(
     way=dict(
      geometry_type='Polygon', height=500, width=500 
     ) 
    ) 
    column_formatters = dict(tags=lambda v, c, m, p: (u', '.join(u"=".join([k, v]) for k, v in m.tags.items())), 
          ) 

    def __init__(self, session, **kwargs): 
     super(POIView, self).__init__(POI, session, **kwargs) 

    def scaffold_form(self): 
     form_class = super(POIView, self).scaffold_form() 
     form_class.way = WTFormsMapField() 
     form_class.tags = MySelect2TagsField("Tags",None) 
     return form_class 

admin/models.py

class POI(db.Model): 
    __tablename__ = 'zo_poi' 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.Text()) 
    tags = db.Column(HSTORE()) 
    src = db.Column(db.Text()) 
    way = db.Column(Geometry('point', srid=4326)) 
    intern = db.Column(db.BOOLEAN()) 
2

從Flask-Admin版本1.0.9開始,它現在支持Geoalchemy2 Geometry列(並且1.1.0添加了Geography列)。

最大的變化是從flask-admin.contrib.geoa而不是flask-admin.contrib.sqla進口ModelView,所以一個簡單的模型看起來像:

from geoalchemy2 import Geometry 
from flask-admin.contrib.geoa import ModelView 

app.config['MAPBOX_MAP_ID'] = 'example.abc123'  

class Location(db.Model): 
    id = db.Column(db.Integer, primary_key=True) 
    name = db.Column(db.String(64), unique=True) 
    point = db.Column(Geometry("Point", 4326)) 

admin = Admin(app) 
admin.add_view(ModelView(Location, db.session)) 

在列表視圖中,一個小的預覽圖將與每個顯示的,然後編輯或創建視圖中的leaflet.draw視圖。

對於線條或複雜的多邊形,您可能想要覆蓋ModelViewform_widget_args以獲得更合理的編輯空間。

class Polygon(db.Model): 
    ... 
    polygon = db.Column(Geometry("Polygon", 4326) 

class PolygonView(ModelView): 
    form_widget_args = {'polgon': {'data-height': 400; 'data-width': 400}} 

admin.add_view(PolygonView(Polygon, db.session)) 
+0

哇..確定將檢查了這一點,當我再次在該項目上工作:) thx。 –

+0

在當前版本的Flask-Admin(1.1.0)中,自從Mapbox API升級到v4以來,對幾何列的支持已被破壞。 https://github.com/mrjoes/flask-admin/blob/ac5fe084fb9803d791d6c6441af4c456b9eeaf3b/flask_admin/static/admin/js/form-1.0.0.js#L158 – blurrcat

+1

@blurrcat對於新創建的地圖,不支持v3 API ,但對於2015年3月15日之前創建的地圖,尚未關閉支持。在我的待辦事項列表中,可以使v4 API支持起作用。 –