2014-03-24 4 views
3

Flask-Admin에 대한 뷰를 작성하여 기하학 필드에 좌표를 입력하려고합니다. 두 개의 Textfield를 만들고이를 Geometry 객체로 변환하려면 어떻게해야합니까?형상 필드 (LON/LAT)에 대한 AdminModelConvertor

이 (셀 수없는 다른 것들 외에) 지금까지 시도 무엇인가가

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 

관심 장소 개체는 다음과 같습니다

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 열 추가)을 지원합니다.

목록보기에서
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)) 

, 작은 미리보기 맵은 다음 각으로 표시 될 것입니다 :

가장 큰 변화는 flask-admin.contrib.geoa 대신 flask-admin.contrib.sqla에서 ModelView를 가져 오는 것입니다, 그래서 간단한 모델처럼 보일 것이다 보기 편집 또는보기에서 leaflet.draw보기.

선 또는 복잡한 다각형의 경우보다 합리적인 크기의 편집 공간을 얻으려면 으로 ModelView을 덮어 쓸 수 있습니다.

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 지원을 받으려면 TOD 목록에 있습니다. –