improve the map.query_point/query_map_point implementation, now throwing for invalid coords or projection transformations
This commit is contained in:
parent
a4b3daf181
commit
5767c65470
4 changed files with 160 additions and 67 deletions
|
@ -9,6 +9,8 @@ For a complete change history, see the SVN log.
|
|||
|
||||
## Mapnik 2.1.0
|
||||
|
||||
- Improved error feedback for invalid values passed to map.query_point
|
||||
|
||||
- Fixed rendering of thin svg lines (#1129)
|
||||
|
||||
- Improved logging/debugging system with release logs and file redirection (#937 and partially #986, #467)
|
||||
|
|
107
src/map.cpp
107
src/map.cpp
|
@ -564,85 +564,66 @@ CoordTransform Map::view_transform() const
|
|||
|
||||
featureset_ptr Map::query_point(unsigned index, double x, double y) const
|
||||
{
|
||||
if ( index< layers_.size())
|
||||
if (!current_extent_.valid())
|
||||
{
|
||||
throw std::runtime_error("query_point: map extent is not intialized, you need to set a valid extent before querying");
|
||||
}
|
||||
if (!current_extent_.intersects(x,y))
|
||||
{
|
||||
throw std::runtime_error("query_point: x,y coords do not intersect map extent");
|
||||
}
|
||||
if (index < layers_.size())
|
||||
{
|
||||
mapnik::layer const& layer = layers_[index];
|
||||
try
|
||||
mapnik::datasource_ptr ds = layer.datasource();
|
||||
if (ds)
|
||||
{
|
||||
double z = 0;
|
||||
mapnik::projection dest(srs_);
|
||||
mapnik::projection source(layer.srs());
|
||||
proj_transform prj_trans(source,dest);
|
||||
prj_trans.backward(x,y,z);
|
||||
|
||||
double minx = current_extent_.minx();
|
||||
double miny = current_extent_.miny();
|
||||
double maxx = current_extent_.maxx();
|
||||
double maxy = current_extent_.maxy();
|
||||
|
||||
prj_trans.backward(minx,miny,z);
|
||||
prj_trans.backward(maxx,maxy,z);
|
||||
double tol = (maxx - minx) / width_ * 3;
|
||||
mapnik::datasource_ptr ds = layer.datasource();
|
||||
if (ds)
|
||||
double z = 0;
|
||||
if (!prj_trans.equal() && !prj_trans.backward(x,y,z))
|
||||
{
|
||||
throw std::runtime_error("query_point: could not project x,y into layer srs");
|
||||
}
|
||||
// TODO - pass tolerance to features_at_point as well
|
||||
featureset_ptr fs = ds->features_at_point(mapnik::coord2d(x,y));
|
||||
if (fs)
|
||||
{
|
||||
mapnik::box2d<double> map_ex = current_extent_;
|
||||
if (maximum_extent_) {
|
||||
map_ex.clip(*maximum_extent_);
|
||||
}
|
||||
if (!prj_trans.backward(map_ex,PROJ_ENVELOPE_POINTS))
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << "query_point: could not project map extent '" << map_ex
|
||||
<< "' into layer srs for tolerance calculation";
|
||||
throw std::runtime_error(s.str());
|
||||
}
|
||||
double tol = (map_ex.maxx() - map_ex.minx()) / width_ * 3;
|
||||
MAPNIK_LOG_DEBUG(map) << "map: Query at point tol=" << tol << "(" << x << "," << y << ")";
|
||||
|
||||
featureset_ptr fs = ds->features_at_point(mapnik::coord2d(x,y));
|
||||
if (fs)
|
||||
return boost::make_shared<filter_featureset<hit_test_filter> >(fs,
|
||||
hit_test_filter(x,y,tol));
|
||||
return boost::make_shared<filter_featureset<hit_test_filter> >(fs,
|
||||
hit_test_filter(x,y,tol));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
MAPNIK_LOG_ERROR(map) << "Exception caught in \"query_point\"";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::ostringstream s;
|
||||
s << "Invalid layer index passed to query_point: '" << index << "'";
|
||||
if (layers_.size() > 0) s << " for map with " << layers_.size() << " layers(s)";
|
||||
else s << " (map has no layers)";
|
||||
throw std::out_of_range(s.str());
|
||||
}
|
||||
return featureset_ptr();
|
||||
}
|
||||
|
||||
featureset_ptr Map::query_map_point(unsigned index, double x, double y) const
|
||||
{
|
||||
if ( index< layers_.size())
|
||||
{
|
||||
mapnik::layer const& layer = layers_[index];
|
||||
CoordTransform tr = view_transform();
|
||||
tr.backward(&x,&y);
|
||||
|
||||
try
|
||||
{
|
||||
mapnik::projection dest(srs_);
|
||||
mapnik::projection source(layer.srs());
|
||||
proj_transform prj_trans(source,dest);
|
||||
double z = 0;
|
||||
prj_trans.backward(x,y,z);
|
||||
|
||||
double minx = current_extent_.minx();
|
||||
double miny = current_extent_.miny();
|
||||
double maxx = current_extent_.maxx();
|
||||
double maxy = current_extent_.maxy();
|
||||
|
||||
prj_trans.backward(minx,miny,z);
|
||||
prj_trans.backward(maxx,maxy,z);
|
||||
double tol = (maxx - minx) / width_ * 3;
|
||||
mapnik::datasource_ptr ds = layer.datasource();
|
||||
if (ds)
|
||||
{
|
||||
MAPNIK_LOG_DEBUG(map) << "map: Query at point tol=" << tol << "(" << x << "," << y << ")";
|
||||
|
||||
featureset_ptr fs = ds->features_at_point(mapnik::coord2d(x,y));
|
||||
if (fs)
|
||||
return boost::make_shared<filter_featureset<hit_test_filter> >(fs,
|
||||
hit_test_filter(x,y,tol));
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
MAPNIK_LOG_ERROR(map) << "Exception caught in \"query_map_point\"";
|
||||
}
|
||||
}
|
||||
return featureset_ptr();
|
||||
CoordTransform tr = view_transform();
|
||||
tr.backward(&x,&y);
|
||||
return query_point(index,x,y);
|
||||
}
|
||||
|
||||
Map::~Map() {}
|
||||
|
|
107
tests/python_tests/map_query_test.py
Normal file
107
tests/python_tests/map_query_test.py
Normal file
|
@ -0,0 +1,107 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from nose.tools import *
|
||||
from utilities import execution_path
|
||||
from copy import deepcopy
|
||||
|
||||
import os, mapnik
|
||||
|
||||
def setup():
|
||||
# All of the paths used are relative, if we run the tests
|
||||
# from another directory we need to chdir()
|
||||
os.chdir(execution_path('.'))
|
||||
|
||||
# map has no layers
|
||||
@raises(IndexError)
|
||||
def test_map_query_throw1():
|
||||
m = mapnik.Map(256,256)
|
||||
m.zoom_to_box(mapnik.Box2d(-1,-1,0,0))
|
||||
m.query_point(0,0,0)
|
||||
|
||||
# only positive indexes
|
||||
@raises(IndexError)
|
||||
def test_map_query_throw2():
|
||||
m = mapnik.Map(256,256)
|
||||
m.query_point(-1,0,0)
|
||||
|
||||
# map has never been zoomed (nodata)
|
||||
@raises(RuntimeError)
|
||||
def test_map_query_throw3():
|
||||
m = mapnik.Map(256,256)
|
||||
m.query_point(0,0,0)
|
||||
|
||||
# map has never been zoomed (even with data)
|
||||
@raises(RuntimeError)
|
||||
def test_map_query_throw4():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/agg_poly_gamma_map.xml')
|
||||
m.query_point(0,0,0)
|
||||
|
||||
# invalid coords in general (do not intersect)
|
||||
@raises(RuntimeError)
|
||||
def test_map_query_throw5():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/agg_poly_gamma_map.xml')
|
||||
m.zoom_all()
|
||||
m.query_point(0,9999999999999999,9999999999999999)
|
||||
|
||||
# invalid coords for back projecting
|
||||
@raises(RuntimeError)
|
||||
def test_map_query_throw6():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/merc2wgs84_reprojection.xml')
|
||||
wgs84_bounds = mapnik.Box2d(-180,-90,180,90)
|
||||
m.maximum_extent = wgs84_bounds
|
||||
m.zoom_all()
|
||||
m.query_point(0,-180,-90)
|
||||
|
||||
def test_map_query_works1():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')
|
||||
merc_bounds = mapnik.Box2d(-20037508.34,-20037508.34,20037508.34,20037508.34)
|
||||
m.maximum_extent = merc_bounds
|
||||
m.zoom_all()
|
||||
fs = m.query_point(0,-11012435.5376, 4599674.6134) # somewhere in kansas
|
||||
feat = fs.next()
|
||||
eq_(feat.attributes['NAME_FORMA'],u'United States of America')
|
||||
|
||||
def test_map_query_works2():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/merc2wgs84_reprojection.xml')
|
||||
wgs84_bounds = mapnik.Box2d(-179.999999975,-85.0511287776,179.999999975,85.0511287776)
|
||||
m.maximum_extent = wgs84_bounds
|
||||
# caution - will go square due to evil aspect_fix_mode backhandedness
|
||||
m.zoom_all()
|
||||
#mapnik.render_to_file(m,'works2.png')
|
||||
# valid that aspec_fix_mode modified the bbox
|
||||
eq_(m.envelope(),mapnik.Box2d(-179.999999975,-179.999999975,179.999999975,179.999999975))
|
||||
fs = m.query_point(0,-98.9264, 38.1432) # somewhere in kansas
|
||||
feat = fs.next()
|
||||
eq_(feat.attributes['NAME'],u'United States')
|
||||
|
||||
def test_map_query_in_pixels_works1():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/wgs842merc_reprojection.xml')
|
||||
merc_bounds = mapnik.Box2d(-20037508.34,-20037508.34,20037508.34,20037508.34)
|
||||
m.maximum_extent = merc_bounds
|
||||
m.zoom_all()
|
||||
fs = m.query_map_point(0,55,100) # somewhere in middle of us
|
||||
feat = fs.next()
|
||||
eq_(feat.attributes['NAME_FORMA'],u'United States of America')
|
||||
|
||||
def test_map_query_in_pixels_works2():
|
||||
m = mapnik.Map(256,256)
|
||||
mapnik.load_map(m,'../data/good_maps/merc2wgs84_reprojection.xml')
|
||||
wgs84_bounds = mapnik.Box2d(-179.999999975,-85.0511287776,179.999999975,85.0511287776)
|
||||
m.maximum_extent = wgs84_bounds
|
||||
# caution - will go square due to evil aspect_fix_mode backhandedness
|
||||
m.zoom_all()
|
||||
# valid that aspec_fix_mode modified the bbox
|
||||
eq_(m.envelope(),mapnik.Box2d(-179.999999975,-179.999999975,179.999999975,179.999999975))
|
||||
fs = m.query_map_point(0,55,100) # somewhere in middle of us
|
||||
feat = fs.next()
|
||||
eq_(feat.attributes['NAME'],u'United States')
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
||||
[eval(run)() for run in dir() if 'test_' in run]
|
|
@ -69,17 +69,20 @@ def test_dataraster_query_point():
|
|||
_map = mapnik.Map(256,256, srs)
|
||||
_map.layers.append(lyr)
|
||||
|
||||
# point inside raster extent with valid data
|
||||
x, y = 427417, 4477517
|
||||
x, y = 556113.0,4381428.0 # center of extent of raster
|
||||
_map.zoom_all()
|
||||
features = _map.query_point(0,x,y).features
|
||||
assert len(features) == 1
|
||||
feat = features[0]
|
||||
center = feat.envelope().center()
|
||||
assert center.x==x and center.y==y, center
|
||||
value = feat['value']
|
||||
assert value == 21.0, value
|
||||
assert value == 18.0, value
|
||||
|
||||
# point outside raster extent
|
||||
# point inside map extent but outside raster extent
|
||||
current_box = _map.envelope()
|
||||
current_box.expand_to_include(-427417,4477517)
|
||||
_map.zoom_to_box(current_box)
|
||||
features = _map.query_point(0,-427417,4477517).features
|
||||
assert len(features) == 0
|
||||
|
||||
|
|
Loading…
Reference in a new issue