diff --git a/src/wkb.cpp b/src/wkb.cpp index f1937767b..79a39b510 100644 --- a/src/wkb.cpp +++ b/src/wkb.cpp @@ -81,8 +81,8 @@ public: #ifndef MAPNIK_BIG_ENDIAN needSwap_=byteOrder_?wkbXDR:wkbNDR; #else - needSwap_=byteOrder_?wkbNDR:wkbXDR; -#endif + needSwap_=byteOrder_?wkbNDR:wkbXDR; +#endif } ~wkb_reader() {} @@ -215,7 +215,7 @@ private: void read_multipoint(boost::ptr_vector & paths) { int num_points = read_integer(); - for (int i=0;i & paths) { geometry_type * pt = new geometry_type(MultiPoint); - int num_points = read_integer(); - for (int i=0;i & 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 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;iline_to(ar[i].x,ar[i].y); + } + paths.push_back(line); } - paths.push_back(line); } void read_multilinestring(boost::ptr_vector & paths) @@ -263,45 +266,51 @@ private: void read_multilinestring_2(boost::ptr_vector & paths) { - geometry_type * line = new geometry_type(MultiLineString); int num_lines=read_integer(); - unsigned capacity = 0; - for (int i=0;i 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;jline_to(ar[j].x,ar[j].y); - } + geometry_type * line = new geometry_type(MultiLineString); + unsigned capacity = 0; + for (int i=0;iset_capacity(capacity); + line->move_to(ar[0].x,ar[0].y); + for (int j=1;jline_to(ar[j].x,ar[j].y); + } + } + paths.push_back(line); } - paths.push_back(line); } void read_polygon(boost::ptr_vector & paths) { - geometry_type * poly = new geometry_type(Polygon); int num_rings=read_integer(); - unsigned capacity = 0; - for (int i=0;i 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;jline_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;jline_to(ar[j].x,ar[j].y); + } } + paths.push_back(poly); } - paths.push_back(poly); } void read_multipolygon(boost::ptr_vector & paths) @@ -316,29 +325,35 @@ private: void read_multipolygon_2(boost::ptr_vector & paths) { - geometry_type * poly = new geometry_type(MultiPolygon); int num_polys=read_integer(); - unsigned capacity = 0; - for (int i=0;i 0) { - pos_+=5; - int num_rings=read_integer(); - for (int r=0;rset_capacity(capacity); - poly->move_to(ar[0].x,ar[0].y); - for (int j=1;jline_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;jline_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 & paths) diff --git a/tests/python_tests/postgis_test.py b/tests/python_tests/postgis_test.py new file mode 100644 index 000000000..55c708996 --- /dev/null +++ b/tests/python_tests/postgis_test.py @@ -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]