Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
flask-admin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
JIRA
JIRA
Merge Requests
0
Merge Requests
0
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Python-Dev
flask-admin
Commits
bc0f282f
Commit
bc0f282f
authored
Mar 26, 2016
by
Paul Brown
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1231 from jmagnusson/mapbox-flip-coordinates
Allow flipping mapbox coordinates to/from db
parents
02084e12
86d2542b
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
128 additions
and
38 deletions
+128
-38
fields.py
flask_admin/contrib/geoa/fields.py
+53
-14
test_basic.py
flask_admin/tests/geoa/test_basic.py
+75
-24
No files found.
flask_admin/contrib/geoa/fields.py
View file @
bc0f282f
import
json
import
json
import
warnings
import
geoalchemy2
from
flask
import
current_app
from
shapely.geometry
import
shape
from
sqlalchemy
import
func
from
wtforms.fields
import
TextAreaField
from
wtforms.fields
import
TextAreaField
from
shapely.geometry
import
shape
,
mapping
from
.widgets
import
LeafletWidget
from
.widgets
import
LeafletWidget
from
sqlalchemy
import
func
import
geoalchemy2
#from types import NoneType
#from .. import db how do you get db.session in a Field?
class
JSONField
(
TextAreaField
):
class
JSONField
(
TextAreaField
):
...
@@ -37,32 +39,69 @@ class JSONField(TextAreaField):
...
@@ -37,32 +39,69 @@ class JSONField(TextAreaField):
class
GeoJSONField
(
JSONField
):
class
GeoJSONField
(
JSONField
):
widget
=
LeafletWidget
()
widget
=
LeafletWidget
()
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
geometry_type
=
"GEOMETRY"
,
srid
=
'-1'
,
session
=
None
,
**
kwargs
):
def
__init__
(
self
,
label
=
None
,
validators
=
None
,
geometry_type
=
"GEOMETRY"
,
srid
=
'-1'
,
session
=
None
,
**
kwargs
):
super
(
GeoJSONField
,
self
)
.
__init__
(
label
,
validators
,
**
kwargs
)
super
(
GeoJSONField
,
self
)
.
__init__
(
label
,
validators
,
**
kwargs
)
self
.
web_srid
=
4326
self
.
web_srid
=
4326
self
.
srid
=
srid
self
.
srid
=
srid
if
self
.
srid
is
-
1
:
if
self
.
srid
is
-
1
:
self
.
transform_srid
=
self
.
web_srid
self
.
transform_srid
=
self
.
web_srid
else
:
else
:
self
.
transform_srid
=
self
.
srid
self
.
transform_srid
=
self
.
srid
self
.
geometry_type
=
geometry_type
.
upper
()
self
.
geometry_type
=
geometry_type
.
upper
()
self
.
session
=
session
self
.
session
=
session
def
_flip_coordinates
(
self
,
other_func
):
if
current_app
.
config
.
get
(
'MAPBOX_FIX_COORDINATES_ORDER'
):
return
func
.
ST_FlipCoordinates
(
other_func
)
else
:
warnings
.
warn
(
'Consider setting the Flask config option '
'MAPBOX_FIX_COORDINATES_ORDER as the current implementation '
'passes lng/lat coordinates in the wrong order to '
'Leaflet. Without this setting any coordinates saved will '
'have flipped coordinates in your database. '
'Please note that this will become the standard behavior in '
'the next major version of Flask-Admin.'
)
return
other_func
def
_value
(
self
):
def
_value
(
self
):
if
self
.
raw_data
:
if
self
.
raw_data
:
return
self
.
raw_data
[
0
]
return
self
.
raw_data
[
0
]
if
type
(
self
.
data
)
is
geoalchemy2
.
elements
.
WKBElement
:
if
type
(
self
.
data
)
is
geoalchemy2
.
elements
.
WKBElement
:
if
self
.
srid
is
-
1
:
if
self
.
srid
is
-
1
:
self
.
data
=
self
.
session
.
scalar
(
func
.
ST_AsGeoJson
(
self
.
data
))
self
.
data
=
self
.
session
.
scalar
(
func
.
ST_AsGeoJson
(
self
.
_flip_coordinates
(
self
.
data
)
)
)
else
:
else
:
self
.
data
=
self
.
session
.
scalar
(
func
.
ST_AsGeoJson
(
func
.
ST_Transform
(
self
.
data
,
self
.
web_srid
)))
self
.
data
=
self
.
session
.
scalar
(
func
.
ST_AsGeoJson
(
self
.
_flip_coordinates
(
func
.
ST_Transform
(
self
.
data
,
self
.
web_srid
)
)
)
)
return
super
(
GeoJSONField
,
self
)
.
_value
()
return
super
(
GeoJSONField
,
self
)
.
_value
()
def
process_formdata
(
self
,
valuelist
):
def
process_formdata
(
self
,
valuelist
):
super
(
GeoJSONField
,
self
)
.
process_formdata
(
valuelist
)
super
(
GeoJSONField
,
self
)
.
process_formdata
(
valuelist
)
if
str
(
self
.
data
)
is
''
:
if
str
(
self
.
data
)
is
''
:
self
.
data
=
None
self
.
data
=
None
if
self
.
data
is
not
None
:
if
self
.
data
is
not
None
:
web_shape
=
self
.
session
.
scalar
(
func
.
ST_AsText
(
func
.
ST_Transform
(
func
.
ST_GeomFromText
(
shape
(
self
.
data
)
.
wkt
,
self
.
web_srid
),
self
.
transform_srid
)))
web_shape
=
self
.
session
.
scalar
(
self
.
data
=
'SRID='
+
str
(
self
.
srid
)
+
';'
+
str
(
web_shape
)
func
.
ST_AsText
(
self
.
_flip_coordinates
(
func
.
ST_Transform
(
func
.
ST_GeomFromText
(
shape
(
self
.
data
)
.
wkt
,
self
.
web_srid
),
self
.
transform_srid
)
)
)
)
self
.
data
=
'SRID='
+
str
(
self
.
srid
)
+
';'
+
str
(
web_shape
)
flask_admin/tests/geoa/test_basic.py
View file @
bc0f282f
from
__future__
import
unicode_literals
from
__future__
import
unicode_literals
from
nose.tools
import
eq_
,
ok_
import
json
import
re
from
flask_admin.contrib.geoa
import
ModelView
from
flask_admin.contrib.geoa
import
ModelView
from
flask_admin.contrib.geoa.fields
import
GeoJSONField
from
geoalchemy2
import
Geometry
from
geoalchemy2
import
Geometry
from
geoalchemy2.shape
import
to_shape
from
geoalchemy2.shape
import
to_shape
from
flask_admin.contrib.geoa.fields
import
GeoJSONField
from
nose.tools
import
eq_
from
.
import
setup
from
.
import
setup
...
@@ -30,6 +32,8 @@ def test_model():
...
@@ -30,6 +32,8 @@ def test_model():
app
,
db
,
admin
=
setup
()
app
,
db
,
admin
=
setup
()
GeoModel
=
create_models
(
db
)
GeoModel
=
create_models
(
db
)
db
.
create_all
()
db
.
create_all
()
GeoModel
.
query
.
delete
()
db
.
session
.
commit
()
view
=
ModelView
(
GeoModel
,
db
.
session
)
view
=
ModelView
(
GeoModel
,
db
.
session
)
admin
.
add_view
(
view
)
admin
.
add_view
(
view
)
...
@@ -81,37 +85,84 @@ def test_model():
...
@@ -81,37 +85,84 @@ def test_model():
rv
=
client
.
get
(
'/admin/geomodel/'
)
rv
=
client
.
get
(
'/admin/geomodel/'
)
eq_
(
rv
.
status_code
,
200
)
eq_
(
rv
.
status_code
,
200
)
point_opt_1
=
'>{"type": "Point", "coordinates": [125.8, 10.0]}</textarea>'
point_opt_2
=
'>{"coordinates": [125.8, 10.0], "type": "Point"}</textarea>'
point_opt_3
=
'>{"type":"Point","coordinates":[125.8,10]}</textarea>'
html
=
rv
.
data
.
decode
(
'utf-8'
)
html
=
rv
.
data
.
decode
(
'utf-8'
)
ok_
(
point_opt_1
in
html
or
point_opt_2
in
html
or
point_opt_3
in
html
,
html
)
pattern
=
r'(.|\n)+({.*"type": ?"Point".*})</textarea>(.|\n)+'
group
=
re
.
match
(
pattern
,
html
)
.
group
(
2
)
p
=
json
.
loads
(
group
)
eq_
(
p
[
'coordinates'
][
0
],
125.8
)
eq_
(
p
[
'coordinates'
][
1
],
10.0
)
url
=
'/admin/geomodel/edit/?id=
%
s'
%
model
.
id
url
=
'/admin/geomodel/edit/?id=
%
s'
%
model
.
id
rv
=
client
.
get
(
url
)
rv
=
client
.
get
(
url
)
eq_
(
rv
.
status_code
,
200
)
eq_
(
rv
.
status_code
,
200
)
#rv = client.post(url, data={
#
rv = client.post(url, data={
# "name": "edited",
#
"name": "edited",
# "point": '{"type": "Point", "coordinates": [99.9, 10.5]}',
#
"point": '{"type": "Point", "coordinates": [99.9, 10.5]}',
# "line": '', # set to NULL in the database
#
"line": '', # set to NULL in the database
#})
#
})
#eq_(rv.status_code, 302)
#
eq_(rv.status_code, 302)
#
#
#model = db.session.query(GeoModel).first()
#
model = db.session.query(GeoModel).first()
#eq_(model.name, "edited")
#
eq_(model.name, "edited")
#eq_(to_shape(model.point).geom_type, "Point")
#
eq_(to_shape(model.point).geom_type, "Point")
#eq_(list(to_shape(model.point).coords), [(99.9, 10.5)])
#
eq_(list(to_shape(model.point).coords), [(99.9, 10.5)])
#eq_(to_shape(model.line), None)
#
eq_(to_shape(model.line), None)
#eq_(to_shape(model.polygon).geom_type, "Polygon")
#
eq_(to_shape(model.polygon).geom_type, "Polygon")
#eq_(list(to_shape(model.polygon).exterior.coords),
#
eq_(list(to_shape(model.polygon).exterior.coords),
# [(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)])
#
[(100.0, 0.0), (101.0, 0.0), (101.0, 1.0), (100.0, 1.0), (100.0, 0.0)])
#eq_(to_shape(model.multi).geom_type, "MultiPoint")
#
eq_(to_shape(model.multi).geom_type, "MultiPoint")
#eq_(len(to_shape(model.multi).geoms), 2)
#
eq_(len(to_shape(model.multi).geoms), 2)
#eq_(list(to_shape(model.multi).geoms[0].coords), [(100.0, 0.0)])
#
eq_(list(to_shape(model.multi).geoms[0].coords), [(100.0, 0.0)])
#eq_(list(to_shape(model.multi).geoms[1].coords), [(101.0, 1.0)])
#
eq_(list(to_shape(model.multi).geoms[1].coords), [(101.0, 1.0)])
url
=
'/admin/geomodel/delete/?id=
%
s'
%
model
.
id
url
=
'/admin/geomodel/delete/?id=
%
s'
%
model
.
id
rv
=
client
.
post
(
url
)
rv
=
client
.
post
(
url
)
eq_
(
rv
.
status_code
,
302
)
eq_
(
rv
.
status_code
,
302
)
eq_
(
db
.
session
.
query
(
GeoModel
)
.
count
(),
0
)
eq_
(
db
.
session
.
query
(
GeoModel
)
.
count
(),
0
)
def
test_mapbox_fix_point_coordinates
():
app
,
db
,
admin
=
setup
()
app
.
config
[
'MAPBOX_FIX_COORDINATES_ORDER'
]
=
True
GeoModel
=
create_models
(
db
)
db
.
create_all
()
GeoModel
.
query
.
delete
()
db
.
session
.
commit
()
view
=
ModelView
(
GeoModel
,
db
.
session
)
admin
.
add_view
(
view
)
# Make some test clients
client
=
app
.
test_client
()
rv
=
client
.
post
(
'/admin/geomodel/new/'
,
data
=
{
"name"
:
"test1"
,
"point"
:
'{"type": "Point", "coordinates": [125.8, 10.0]}'
,
"line"
:
'{"type": "LineString", "coordinates": [[50.2345, 94.2], [50.21, 94.87]]}'
,
"polygon"
:
'{"type": "Polygon", "coordinates": [[[100.0, 0.0], [101.0, 0.0], [101.0, 1.0], [100.0, 1.0], [100.0, 0.0]]]}'
,
"multi"
:
'{"type": "MultiPoint", "coordinates": [[100.0, 0.0], [101.0, 1.0]]}'
,
})
model
=
db
.
session
.
query
(
GeoModel
)
.
first
()
# Notice how the coordinates are reversed here, i.e. longitude first which
# is the way it's stored in PostGIS columns.
eq_
(
list
(
to_shape
(
model
.
point
)
.
coords
),
[(
10.0
,
125.8
)])
eq_
(
list
(
to_shape
(
model
.
line
)
.
coords
),
[(
94.2
,
50.2345
),
(
94.87
,
50.21
)])
eq_
(
list
(
to_shape
(
model
.
polygon
)
.
exterior
.
coords
),
[(
0.0
,
100.0
),
(
0.0
,
101.0
),
(
1.0
,
101.0
),
(
1.0
,
100.0
),
(
0.0
,
100.0
)])
eq_
(
list
(
to_shape
(
model
.
multi
)
.
geoms
[
0
]
.
coords
),
[(
0.0
,
100.0
)])
eq_
(
list
(
to_shape
(
model
.
multi
)
.
geoms
[
1
]
.
coords
),
[(
1.0
,
101.0
)])
rv
=
client
.
get
(
'/admin/geomodel/'
)
eq_
(
rv
.
status_code
,
200
)
html
=
rv
.
data
.
decode
(
'utf-8'
)
pattern
=
r'(.|\n)+({.*"type": ?"Point".*})</textarea>(.|\n)+'
group
=
re
.
match
(
pattern
,
html
)
.
group
(
2
)
p
=
json
.
loads
(
group
)
# Reversed order again, so that it's parsed correctly by leaflet
eq_
(
p
[
'coordinates'
][
0
],
10.0
)
eq_
(
p
[
'coordinates'
][
1
],
125.8
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment