handle empty geometries from postgis and add postgis tests - refs #1333
This commit is contained in:
parent
fa23cda22f
commit
daeecb39e0
2 changed files with 342 additions and 56 deletions
113
src/wkb.cpp
113
src/wkb.cpp
|
@ -238,17 +238,20 @@ private:
|
|||
|
||||
void read_linestring(boost::ptr_vector<geometry_type> & paths)
|
||||
{
|
||||
geometry_type * line = new geometry_type(LineString);
|
||||
int num_points=read_integer();
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
line->set_capacity(num_points);
|
||||
line->move_to(ar[0].x,ar[0].y);
|
||||
for (int i=1;i<num_points;++i)
|
||||
if (num_points > 0)
|
||||
{
|
||||
line->line_to(ar[i].x,ar[i].y);
|
||||
geometry_type * line = new geometry_type(LineString);
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
line->set_capacity(num_points);
|
||||
line->move_to(ar[0].x,ar[0].y);
|
||||
for (int i=1;i<num_points;++i)
|
||||
{
|
||||
line->line_to(ar[i].x,ar[i].y);
|
||||
}
|
||||
paths.push_back(line);
|
||||
}
|
||||
paths.push_back(line);
|
||||
}
|
||||
|
||||
void read_multilinestring(boost::ptr_vector<geometry_type> & paths)
|
||||
|
@ -263,45 +266,51 @@ private:
|
|||
|
||||
void read_multilinestring_2(boost::ptr_vector<geometry_type> & paths)
|
||||
{
|
||||
geometry_type * line = new geometry_type(MultiLineString);
|
||||
int num_lines=read_integer();
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_lines;++i)
|
||||
if (num_lines > 0)
|
||||
{
|
||||
pos_+=5;
|
||||
int num_points=read_integer();
|
||||
capacity+=num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
line->set_capacity(capacity);
|
||||
line->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
geometry_type * line = new geometry_type(MultiLineString);
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_lines;++i)
|
||||
{
|
||||
line->line_to(ar[j].x,ar[j].y);
|
||||
pos_+=5;
|
||||
int num_points=read_integer();
|
||||
capacity+=num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
line->set_capacity(capacity);
|
||||
line->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
{
|
||||
line->line_to(ar[j].x,ar[j].y);
|
||||
}
|
||||
}
|
||||
paths.push_back(line);
|
||||
}
|
||||
paths.push_back(line);
|
||||
}
|
||||
|
||||
void read_polygon(boost::ptr_vector<geometry_type> & paths)
|
||||
{
|
||||
geometry_type * poly = new geometry_type(Polygon);
|
||||
int num_rings=read_integer();
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_rings;++i)
|
||||
if (num_rings > 0)
|
||||
{
|
||||
int num_points=read_integer();
|
||||
capacity+=num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
poly->set_capacity(capacity);
|
||||
poly->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
geometry_type * poly = new geometry_type(Polygon);
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_rings;++i)
|
||||
{
|
||||
poly->line_to(ar[j].x,ar[j].y);
|
||||
int num_points=read_integer();
|
||||
capacity+=num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
poly->set_capacity(capacity);
|
||||
poly->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
{
|
||||
poly->line_to(ar[j].x,ar[j].y);
|
||||
}
|
||||
}
|
||||
paths.push_back(poly);
|
||||
}
|
||||
paths.push_back(poly);
|
||||
}
|
||||
|
||||
void read_multipolygon(boost::ptr_vector<geometry_type> & paths)
|
||||
|
@ -316,29 +325,35 @@ private:
|
|||
|
||||
void read_multipolygon_2(boost::ptr_vector<geometry_type> & paths)
|
||||
{
|
||||
geometry_type * poly = new geometry_type(MultiPolygon);
|
||||
int num_polys=read_integer();
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_polys;++i)
|
||||
if (num_polys > 0)
|
||||
{
|
||||
pos_+=5;
|
||||
int num_rings=read_integer();
|
||||
for (int r=0;r<num_rings;++r)
|
||||
geometry_type * poly = new geometry_type(MultiPolygon);
|
||||
unsigned capacity = 0;
|
||||
for (int i=0;i<num_polys;++i)
|
||||
{
|
||||
int num_points=read_integer();
|
||||
capacity += num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
poly->set_capacity(capacity);
|
||||
poly->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
pos_+=5;
|
||||
int num_rings=read_integer();
|
||||
for (int r=0;r<num_rings;++r)
|
||||
{
|
||||
poly->line_to(ar[j].x,ar[j].y);
|
||||
int num_points=read_integer();
|
||||
if (num_points > 0)
|
||||
{
|
||||
capacity += num_points;
|
||||
CoordinateArray ar(num_points);
|
||||
read_coords(ar);
|
||||
poly->set_capacity(capacity);
|
||||
poly->move_to(ar[0].x,ar[0].y);
|
||||
for (int j=1;j<num_points;++j)
|
||||
{
|
||||
poly->line_to(ar[j].x,ar[j].y);
|
||||
}
|
||||
poly->line_to(ar[0].x,ar[0].y);
|
||||
}
|
||||
}
|
||||
poly->line_to(ar[0].x,ar[0].y);
|
||||
}
|
||||
paths.push_back(poly);
|
||||
}
|
||||
paths.push_back(poly);
|
||||
}
|
||||
|
||||
void read_collection(boost::ptr_vector<geometry_type> & paths)
|
||||
|
|
271
tests/python_tests/postgis_test.py
Normal file
271
tests/python_tests/postgis_test.py
Normal file
|
@ -0,0 +1,271 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from nose.tools import *
|
||||
import atexit
|
||||
import time
|
||||
from utilities import execution_path
|
||||
from subprocess import Popen, PIPE
|
||||
import os, mapnik
|
||||
|
||||
MAPNIK_TEST_DBNAME = 'mapnik-tmp-postgis-test-db'
|
||||
POSTGIS_TEMPLATE_DBNAME = 'template_postgis'
|
||||
SHAPEFILE = os.path.join(execution_path('.'),'../data/shp/world_merc.shp')
|
||||
SQL_DUMPS = os.path.join(execution_path('.'),'../data/sql')
|
||||
|
||||
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('.'))
|
||||
|
||||
def call(cmd,silent=False):
|
||||
stdin, stderr = Popen(cmd, shell=True, stdout=PIPE, stderr=PIPE).communicate()
|
||||
if not stderr:
|
||||
return stdin.strip()
|
||||
elif not silent and not 'NOTICE' in stderr:
|
||||
raise RuntimeError(stderr.strip())
|
||||
|
||||
def psql_can_connect():
|
||||
"""Test ability to connect to a postgis template db with no options.
|
||||
|
||||
Basically, to run these tests your user must have full read
|
||||
access over unix sockets without supplying a password. This
|
||||
keeps these tests simple and focused on postgis not on postgres
|
||||
auth issues.
|
||||
"""
|
||||
try:
|
||||
call('psql %s -c "select postgis_version()"' % POSTGIS_TEMPLATE_DBNAME)
|
||||
return True
|
||||
except RuntimeError, e:
|
||||
print 'Notice: skipping postgis tests (connection)'
|
||||
return False
|
||||
|
||||
def shp2pgsql_on_path():
|
||||
"""Test for presence of shp2pgsql on the user path.
|
||||
|
||||
We require this program to load test data into a temporarily database.
|
||||
"""
|
||||
try:
|
||||
call('shp2pgsql')
|
||||
return True
|
||||
except RuntimeError, e:
|
||||
print 'Notice: skipping postgis tests (shp2pgsql)'
|
||||
return False
|
||||
|
||||
def createdb_and_dropdb_on_path():
|
||||
"""Test for presence of dropdb/createdb on user path.
|
||||
|
||||
We require these programs to setup and teardown the testing db.
|
||||
"""
|
||||
try:
|
||||
call('createdb --help')
|
||||
call('dropdb --help')
|
||||
return True
|
||||
except RuntimeError, e:
|
||||
print 'Notice: skipping postgis tests (createdb/dropdb)'
|
||||
return False
|
||||
|
||||
insert_table_1 = """
|
||||
CREATE TABLE test(gid serial PRIMARY KEY, geom geometry);
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;POINT(-2 2)'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;MULTIPOINT(2 1,1 2)'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;LINESTRING(0 0,1 1,1 2)'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;MULTILINESTRING((1 0,0 1,3 2),(3 2,5 4))'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;MULTIPOLYGON(((1 1,3 1,3 3,1 3,1 1),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))'));
|
||||
INSERT INTO test(geom) values (GeomFromEWKT('SRID=4326;GEOMETRYCOLLECTION(POLYGON((1 1, 2 1, 2 2, 1 2,1 1)),POINT(2 3),LINESTRING(2 3,3 4))'));
|
||||
"""
|
||||
|
||||
insert_table_2 = """
|
||||
CREATE TABLE test2(manual_id int4 PRIMARY KEY, geom geometry);
|
||||
INSERT INTO test2(manual_id, geom) values (0, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test2(manual_id, geom) values (1, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test2(manual_id, geom) values (1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test2(manual_id, geom) values (-1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test2(manual_id, geom) values (2147483647, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test2(manual_id, geom) values (-2147483648, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
"""
|
||||
|
||||
insert_table_3 = """
|
||||
CREATE TABLE test3(non_id bigint, manual_id int4, geom geometry);
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, 0, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, 1, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, 1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, -1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, 2147483647, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test3(non_id, manual_id, geom) values (9223372036854775807, -2147483648, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
"""
|
||||
|
||||
insert_table_4 = """
|
||||
CREATE TABLE test4(non_id int4, manual_id int8 PRIMARY KEY, geom geometry);
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, 0, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, 1, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, 1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, -1000, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, 2147483647, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test4(non_id, manual_id, geom) values (0, -2147483648, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
"""
|
||||
|
||||
insert_table_5 = """
|
||||
CREATE TABLE test5(non_id int4, manual_id numeric PRIMARY KEY, geom geometry);
|
||||
INSERT INTO test5(non_id, manual_id, geom) values (0, -1, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
INSERT INTO test5(non_id, manual_id, geom) values (0, 1, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
"""
|
||||
|
||||
insert_table_6 = '''
|
||||
CREATE TABLE "tableWithMixedCase"(gid serial PRIMARY KEY, geom geometry);
|
||||
INSERT INTO "tableWithMixedCase"(geom) values (ST_MakePoint(0,0));
|
||||
INSERT INTO "tableWithMixedCase"(geom) values (ST_MakePoint(0,1));
|
||||
INSERT INTO "tableWithMixedCase"(geom) values (ST_MakePoint(1,0));
|
||||
INSERT INTO "tableWithMixedCase"(geom) values (ST_MakePoint(1,1));
|
||||
'''
|
||||
|
||||
insert_table_7 = '''
|
||||
CREATE TABLE test6(first_id int4, second_id int4,PRIMARY KEY (first_id,second_id), geom geometry);
|
||||
INSERT INTO test6(first_id, second_id, geom) values (0, 0, GeomFromEWKT('SRID=4326;POINT(0 0)'));
|
||||
'''
|
||||
|
||||
insert_table_8 = '''
|
||||
CREATE TABLE test7(gid serial PRIMARY KEY, geom geometry);
|
||||
INSERT INTO test7(gid, geom) values (1, GeomFromEWKT('SRID=4326;GEOMETRYCOLLECTION(MULTILINESTRING((10 10,20 20,10 40),(40 40,30 30,40 20,30 10)),LINESTRING EMPTY)'));
|
||||
'''
|
||||
|
||||
def postgis_setup():
|
||||
call('dropdb %s' % MAPNIK_TEST_DBNAME,silent=True)
|
||||
call('createdb -T %s %s' % (POSTGIS_TEMPLATE_DBNAME,MAPNIK_TEST_DBNAME),silent=False)
|
||||
call('shp2pgsql -s 3857 -g geom -W LATIN1 %s world_merc | psql -q %s' % (SHAPEFILE,MAPNIK_TEST_DBNAME), silent=True)
|
||||
call('''psql -q %s -c "CREATE TABLE \"empty\" (key serial);SELECT AddGeometryColumn('','empty','geom','-1','GEOMETRY',4);"''' % MAPNIK_TEST_DBNAME,silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_1),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_2),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_3),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_4),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_5),silent=False)
|
||||
call("""psql -q %s -c '%s'""" % (MAPNIK_TEST_DBNAME,insert_table_6),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_7),silent=False)
|
||||
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_8),silent=False)
|
||||
|
||||
def postgis_takedown():
|
||||
pass
|
||||
# fails as the db is in use: https://github.com/mapnik/mapnik/issues/960
|
||||
#call('dropdb %s' % MAPNIK_TEST_DBNAME)
|
||||
|
||||
if 'postgis' in mapnik.DatasourceCache.instance().plugin_names() \
|
||||
and createdb_and_dropdb_on_path() \
|
||||
and psql_can_connect() \
|
||||
and shp2pgsql_on_path():
|
||||
|
||||
# initialize test database
|
||||
postgis_setup()
|
||||
|
||||
def test_feature():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='world_merc')
|
||||
fs = ds.featureset()
|
||||
feature = fs.next()
|
||||
eq_(feature['gid'],1)
|
||||
eq_(feature['fips'],u'AC')
|
||||
eq_(feature['iso2'],u'AG')
|
||||
eq_(feature['iso3'],u'ATG')
|
||||
eq_(feature['un'],28)
|
||||
eq_(feature['name'],u'Antigua and Barbuda')
|
||||
eq_(feature['area'],44)
|
||||
eq_(feature['pop2005'],83039)
|
||||
eq_(feature['region'],19)
|
||||
eq_(feature['subregion'],29)
|
||||
eq_(feature['lon'],-61.783)
|
||||
eq_(feature['lat'],17.078)
|
||||
|
||||
def test_subquery():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='(select * from world_merc) as w')
|
||||
fs = ds.featureset()
|
||||
feature = fs.next()
|
||||
eq_(feature['gid'],1)
|
||||
eq_(feature['fips'],u'AC')
|
||||
eq_(feature['iso2'],u'AG')
|
||||
eq_(feature['iso3'],u'ATG')
|
||||
eq_(feature['un'],28)
|
||||
eq_(feature['name'],u'Antigua and Barbuda')
|
||||
eq_(feature['area'],44)
|
||||
eq_(feature['pop2005'],83039)
|
||||
eq_(feature['region'],19)
|
||||
eq_(feature['subregion'],29)
|
||||
eq_(feature['lon'],-61.783)
|
||||
eq_(feature['lat'],17.078)
|
||||
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='(select gid,geom,fips as _fips from world_merc) as w')
|
||||
fs = ds.featureset()
|
||||
feature = fs.next()
|
||||
eq_(feature['gid'],1)
|
||||
eq_(feature['_fips'],u'AC')
|
||||
eq_(len(feature),2)
|
||||
|
||||
def test_geometry_detection():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='test',
|
||||
geometry_field='geom')
|
||||
|
||||
def test_disabled_auto_detection_and_subquery():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='''(select geom, 'a'::varchar as name from test2) as t''',
|
||||
geometry_field='geom')
|
||||
fs = ds.featureset()
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),1)
|
||||
eq_(feat['name'],'a')
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),2)
|
||||
eq_(feat['name'],'a')
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),3)
|
||||
eq_(feat['name'],'a')
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),4)
|
||||
eq_(feat['name'],'a')
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),5)
|
||||
eq_(feat['name'],'a')
|
||||
feat = fs.next()
|
||||
eq_(feat.id(),6)
|
||||
eq_(feat['name'],'a')
|
||||
|
||||
|
||||
def test_manually_specified_feature_id_field():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='test4',
|
||||
geometry_field='geom',
|
||||
key_field='manual_id')
|
||||
fs = ds.featureset()
|
||||
eq_(fs.next()['manual_id'],0)
|
||||
eq_(fs.next()['manual_id'],1)
|
||||
eq_(fs.next()['manual_id'],1000)
|
||||
eq_(fs.next()['manual_id'],-1000)
|
||||
eq_(fs.next()['manual_id'],2147483647)
|
||||
eq_(fs.next()['manual_id'],-2147483648)
|
||||
|
||||
fs = ds.featureset()
|
||||
eq_(fs.next().id(),0)
|
||||
eq_(fs.next().id(),1)
|
||||
eq_(fs.next().id(),1000)
|
||||
eq_(fs.next().id(),-1000)
|
||||
eq_(fs.next().id(),2147483647)
|
||||
eq_(fs.next().id(),-2147483648)
|
||||
|
||||
def test_numeric_type_feature_id_field():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='test5',
|
||||
geometry_field='geom')
|
||||
fs = ds.featureset()
|
||||
eq_(fs.next()['manual_id'],-1)
|
||||
eq_(fs.next()['manual_id'],1)
|
||||
|
||||
fs = ds.featureset()
|
||||
eq_(fs.next().id(),1)
|
||||
eq_(fs.next().id(),2)
|
||||
|
||||
def test_empty_geom():
|
||||
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='test7',
|
||||
geometry_field='geom')
|
||||
fs = ds.featureset()
|
||||
eq_(fs.next()['gid'],1)
|
||||
|
||||
atexit.register(postgis_takedown)
|
||||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
||||
#test_auto_detection_and_subquery()
|
||||
[eval(run)() for run in dir() if 'test_' in run]
|
Loading…
Reference in a new issue