add support for rendering (vs skipping) 3D and 4D postgis geometries like POINTZM, LINESTRINGZM, and POLYGONZM - closes #44

This commit is contained in:
Dane Springmeyer 2014-04-09 19:40:14 -04:00
parent d577b0814b
commit 83c0db36cb
2 changed files with 402 additions and 2 deletions

View file

@ -62,14 +62,31 @@ public:
wkbMultiLineString=5, wkbMultiLineString=5,
wkbMultiPolygon=6, wkbMultiPolygon=6,
wkbGeometryCollection=7, wkbGeometryCollection=7,
// Z
wkbPointZ=1001, wkbPointZ=1001,
wkbLineStringZ=1002, wkbLineStringZ=1002,
wkbPolygonZ=1003, wkbPolygonZ=1003,
wkbMultiPointZ=1004, wkbMultiPointZ=1004,
wkbMultiLineStringZ=1005, wkbMultiLineStringZ=1005,
wkbMultiPolygonZ=1006, wkbMultiPolygonZ=1006,
wkbGeometryCollectionZ=1007 wkbGeometryCollectionZ=1007,
}; // M
wkbPointM=2001,
wkbLineStringM=2002,
wkbPolygonM=2003,
wkbMultiPointM=2004,
wkbMultiLineStringM=2005,
wkbMultiPolygonM=2006,
wkbGeometryCollectionM=2007,
// ZM
wkbPointZM=3001,
wkbLineStringZM=3002,
wkbPolygonZM=3003,
wkbMultiPointZM=3004,
wkbMultiLineStringZM=3005,
wkbMultiPolygonZM=3006,
wkbGeometryCollectionZM=3007
};
wkb_reader(const char* wkb, std::size_t size, wkbFormat format) wkb_reader(const char* wkb, std::size_t size, wkbFormat format)
: wkb_(wkb), : wkb_(wkb),
@ -142,24 +159,50 @@ public:
read_collection(paths); read_collection(paths);
break; break;
case wkbPointZ: case wkbPointZ:
case wkbPointM:
read_point_xyz(paths); read_point_xyz(paths);
break; break;
case wkbPointZM:
read_point_xyzm(paths);
break;
case wkbLineStringZ: case wkbLineStringZ:
case wkbLineStringM:
read_linestring_xyz(paths); read_linestring_xyz(paths);
break; break;
case wkbLineStringZM:
read_linestring_xyzm(paths);
break;
case wkbPolygonZ: case wkbPolygonZ:
case wkbPolygonM:
read_polygon_xyz(paths); read_polygon_xyz(paths);
break; break;
case wkbPolygonZM:
read_polygon_xyzm(paths);
break;
case wkbMultiPointZ: case wkbMultiPointZ:
case wkbMultiPointM:
read_multipoint_xyz(paths); read_multipoint_xyz(paths);
break; break;
case wkbMultiPointZM:
read_multipoint_xyzm(paths);
break;
case wkbMultiLineStringZ: case wkbMultiLineStringZ:
case wkbMultiLineStringM:
read_multilinestring_xyz(paths); read_multilinestring_xyz(paths);
break; break;
case wkbMultiLineStringZM:
read_multilinestring_xyzm(paths);
break;
case wkbMultiPolygonZ: case wkbMultiPolygonZ:
case wkbMultiPolygonM:
read_multipolygon_xyz(paths); read_multipolygon_xyz(paths);
break; break;
case wkbMultiPolygonZM:
read_multipolygon_xyzm(paths);
break;
case wkbGeometryCollectionZ: case wkbGeometryCollectionZ:
case wkbGeometryCollectionM:
case wkbGeometryCollectionZM:
read_collection(paths); read_collection(paths);
break; break;
default: default:
@ -245,6 +288,27 @@ private:
} }
} }
void read_coords_xyzm(CoordinateArray& ar)
{
if (! needSwap_)
{
for (unsigned i = 0; i < ar.size(); ++i)
{
read_double_ndr(wkb_ + pos_, ar[i].x);
read_double_ndr(wkb_ + pos_ + 8, ar[i].y);
pos_ += 32; // skip XYZM
}
}
else
{
for (unsigned i = 0; i < ar.size(); ++i)
{
read_double_xdr(wkb_ + pos_, ar[i].x);
read_double_xdr(wkb_ + pos_ + 8, ar[i].y);
pos_ += 32; // skip XYZM
}
}
}
void read_point(boost::ptr_vector<geometry_type> & paths) void read_point(boost::ptr_vector<geometry_type> & paths)
{ {
@ -275,6 +339,16 @@ private:
paths.push_back(pt); paths.push_back(pt);
} }
void read_point_xyzm(boost::ptr_vector<geometry_type> & paths)
{
double x = read_double();
double y = read_double();
std::auto_ptr<geometry_type> pt(new geometry_type(Point));
pos_ += 16;
pt->move_to(x, y);
paths.push_back(pt);
}
void read_multipoint_xyz(boost::ptr_vector<geometry_type> & paths) void read_multipoint_xyz(boost::ptr_vector<geometry_type> & paths)
{ {
int num_points = read_integer(); int num_points = read_integer();
@ -285,6 +359,16 @@ private:
} }
} }
void read_multipoint_xyzm(boost::ptr_vector<geometry_type> & paths)
{
int num_points = read_integer();
for (int i = 0; i < num_points; ++i)
{
pos_ += 5;
read_point_xyzm(paths);
}
}
void read_linestring(boost::ptr_vector<geometry_type> & paths) void read_linestring(boost::ptr_vector<geometry_type> & paths)
{ {
int num_points = read_integer(); int num_points = read_integer();
@ -329,6 +413,23 @@ private:
} }
} }
void read_linestring_xyzm(boost::ptr_vector<geometry_type> & paths)
{
int num_points = read_integer();
if (num_points > 0)
{
CoordinateArray ar(num_points);
read_coords_xyzm(ar);
std::auto_ptr<geometry_type> line(new geometry_type(LineString));
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);
}
}
void read_multilinestring_xyz(boost::ptr_vector<geometry_type> & paths) void read_multilinestring_xyz(boost::ptr_vector<geometry_type> & paths)
{ {
int num_lines = read_integer(); int num_lines = read_integer();
@ -339,6 +440,15 @@ private:
} }
} }
void read_multilinestring_xyzm(boost::ptr_vector<geometry_type> & paths)
{
int num_lines = read_integer();
for (int i = 0; i < num_lines; ++i)
{
pos_ += 5;
read_linestring_xyzm(paths);
}
}
void read_polygon(boost::ptr_vector<geometry_type> & paths) void read_polygon(boost::ptr_vector<geometry_type> & paths)
{ {
@ -402,6 +512,32 @@ private:
} }
} }
void read_polygon_xyzm(boost::ptr_vector<geometry_type> & paths)
{
int num_rings = read_integer();
if (num_rings > 0)
{
std::auto_ptr<geometry_type> poly(new geometry_type(Polygon));
for (int i = 0; i < num_rings; ++i)
{
int num_points = read_integer();
if (num_points > 0)
{
CoordinateArray ar(num_points);
read_coords_xyzm(ar);
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->close_path();
}
}
if (poly->size() > 2) // ignore if polygon has less than 3 vertices
paths.push_back(poly);
}
}
void read_multipolygon_xyz(boost::ptr_vector<geometry_type> & paths) void read_multipolygon_xyz(boost::ptr_vector<geometry_type> & paths)
{ {
int num_polys = read_integer(); int num_polys = read_integer();
@ -412,6 +548,16 @@ private:
} }
} }
void read_multipolygon_xyzm(boost::ptr_vector<geometry_type> & paths)
{
int num_polys = read_integer();
for (int i = 0; i < num_polys; ++i)
{
pos_ += 5;
read_polygon_xyzm(paths);
}
}
void read_collection(boost::ptr_vector<geometry_type> & paths) void read_collection(boost::ptr_vector<geometry_type> & paths)
{ {
int num_geometries = read_integer(); int num_geometries = read_integer();

View file

@ -164,6 +164,34 @@ INSERT INTO test11(label,geom) values ('label_7',GeomFromEWKT('SRID=4326;MULTIPO
INSERT INTO test11(label,geom) values ('label_8',GeomFromEWKT('SRID=4326;GEOMETRYCOLLECTION(POLYGON((1 1, 2 1, 2 2, 1 2,1 1)),POINT(2 3),LINESTRING(2 3,3 4))')); INSERT INTO test11(label,geom) values ('label_8',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_12 = """
CREATE TABLE test12(gid serial PRIMARY KEY, name varchar(40), geom geometry);
INSERT INTO test12(name,geom) values ('Point',GeomFromEWKT('SRID=4326;POINT(0 0)'));
INSERT INTO test12(name,geom) values ('PointZ',GeomFromEWKT('SRID=4326;POINTZ(0 0 0)'));
INSERT INTO test12(name,geom) values ('PointM',GeomFromEWKT('SRID=4326;POINTM(0 0 0)'));
INSERT INTO test12(name,geom) values ('PointZM',GeomFromEWKT('SRID=4326;POINTZM(0 0 0 0)'));
INSERT INTO test12(name,geom) values ('MultiPoint',GeomFromEWKT('SRID=4326;MULTIPOINT(0 0, 1 1)'));
INSERT INTO test12(name,geom) values ('MultiPointZ',GeomFromEWKT('SRID=4326;MULTIPOINTZ(0 0 0, 1 1 1)'));
INSERT INTO test12(name,geom) values ('MultiPointM',GeomFromEWKT('SRID=4326;MULTIPOINTM(0 0 0, 1 1 1)'));
INSERT INTO test12(name,geom) values ('MultiPointZM',GeomFromEWKT('SRID=4326;MULTIPOINTZM(0 0 0 0, 1 1 1 1)'));
INSERT INTO test12(name,geom) values ('LineString',GeomFromEWKT('SRID=4326;LINESTRING(0 0, 1 1)'));
INSERT INTO test12(name,geom) values ('LineStringZ',GeomFromEWKT('SRID=4326;LINESTRINGZ(0 0 0, 1 1 1)'));
INSERT INTO test12(name,geom) values ('LineStringM',GeomFromEWKT('SRID=4326;LINESTRINGM(0 0 0, 1 1 1)'));
INSERT INTO test12(name,geom) values ('LineStringZM',GeomFromEWKT('SRID=4326;LINESTRINGZM(0 0 0 0, 1 1 1 1)'));
INSERT INTO test12(name,geom) values ('Polygon',GeomFromEWKT('SRID=4326;POLYGON((0 0, 1 1, 2 2, 0 0))'));
INSERT INTO test12(name,geom) values ('PolygonZ',GeomFromEWKT('SRID=4326;POLYGONZ((0 0 0, 1 1 1, 2 2 2, 0 0 0))'));
INSERT INTO test12(name,geom) values ('PolygonM',GeomFromEWKT('SRID=4326;POLYGONZ((0 0 0, 1 1 1, 2 2 2, 0 0 0))'));
INSERT INTO test12(name,geom) values ('PolygonZM',GeomFromEWKT('SRID=4326;POLYGONZM((0 0 0 0, 1 1 1 1, 2 2 2 2, 0 0 0 0))'));
INSERT INTO test12(name,geom) values ('MultiLineString',GeomFromEWKT('SRID=4326;MULTILINESTRING((0 0, 1 1),(2 2, 3 3))'));
INSERT INTO test12(name,geom) values ('MultiLineStringZ',GeomFromEWKT('SRID=4326;MULTILINESTRINGZ((0 0 0, 1 1 1),(2 2 2, 3 3 3))'));
INSERT INTO test12(name,geom) values ('MultiLineStringM',GeomFromEWKT('SRID=4326;MULTILINESTRINGM((0 0 0, 1 1 1),(2 2 2, 3 3 3))'));
INSERT INTO test12(name,geom) values ('MultiLineStringZM',GeomFromEWKT('SRID=4326;MULTILINESTRINGZM((0 0 0 0, 1 1 1 1),(2 2 2 2, 3 3 3 3))'));
INSERT INTO test12(name,geom) values ('MultiPolygon',GeomFromEWKT('SRID=4326;MULTIPOLYGON(((0 0, 1 1, 2 2, 0 0)),((0 0, 1 1, 2 2, 0 0)))'));
INSERT INTO test12(name,geom) values ('MultiPolygonZ',GeomFromEWKT('SRID=4326;MULTIPOLYGONZ(((0 0 0, 1 1 1, 2 2 2, 0 0 0)),((0 0 0, 1 1 1, 2 2 2, 0 0 0)))'));
INSERT INTO test12(name,geom) values ('MultiPolygonM',GeomFromEWKT('SRID=4326;MULTIPOLYGONM(((0 0 0, 1 1 1, 2 2 2, 0 0 0)),((0 0 0, 1 1 1, 2 2 2, 0 0 0)))'));
INSERT INTO test12(name,geom) values ('MultiPolygonZM',GeomFromEWKT('SRID=4326;MULTIPOLYGONZM(((0 0 0 0, 1 1 1 1, 2 2 2 2, 0 0 0 0)),((0 0 0 0, 1 1 1 1, 2 2 2 2, 0 0 0 0)))'));
"""
def postgis_setup(): def postgis_setup():
call('dropdb %s' % MAPNIK_TEST_DBNAME,silent=True) call('dropdb %s' % MAPNIK_TEST_DBNAME,silent=True)
@ -182,6 +210,7 @@ def postgis_setup():
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_9),silent=False) call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_9),silent=False)
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_10),silent=False) call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_10),silent=False)
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_11),silent=False) call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_11),silent=False)
call('''psql -q %s -c "%s"''' % (MAPNIK_TEST_DBNAME,insert_table_12),silent=False)
def postgis_takedown(): def postgis_takedown():
pass pass
@ -757,6 +786,231 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \
# This used to raise an exception before correction of issue 2042 # This used to raise an exception before correction of issue 2042
mapnik.render_to_file(map2,'world2.png', 'png') mapnik.render_to_file(map2,'world2.png', 'png')
def test_handling_of_zm_dimensions():
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,
table='(select gid,ST_CoordDim(geom) as dim,name,geom from test12) as tmp',
geometry_field='geom')
eq_(len(ds.fields()),3)
eq_(ds.fields(),['gid', 'dim', 'name'])
eq_(ds.field_types(),['int', 'int', 'str'])
fs = ds.featureset()
# Point (2d)
feat = fs.next()
eq_(feat.id(),1)
eq_(feat['gid'],1)
eq_(feat['dim'],2)
eq_(feat['name'],'Point')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Point(0 0)')
# PointZ
feat = fs.next()
eq_(feat.id(),2)
eq_(feat['gid'],2)
eq_(feat['dim'],3)
eq_(feat['name'],'PointZ')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Point(0 0)')
# PointM
feat = fs.next()
eq_(feat.id(),3)
eq_(feat['gid'],3)
eq_(feat['dim'],3)
eq_(feat['name'],'PointM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Point(0 0)')
# PointZM
feat = fs.next()
eq_(feat.id(),4)
eq_(feat['gid'],4)
eq_(feat['dim'],4)
eq_(feat['name'],'PointZM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Point(0 0)')
# MultiPoint
feat = fs.next()
eq_(feat.id(),5)
eq_(feat['gid'],5)
eq_(feat['dim'],2)
eq_(feat['name'],'MultiPoint')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Point(0 0)')
eq_(geoms[1].to_wkt(),'Point(1 1)')
# MultiPointZ
feat = fs.next()
eq_(feat.id(),6)
eq_(feat['gid'],6)
eq_(feat['dim'],3)
eq_(feat['name'],'MultiPointZ')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Point(0 0)')
eq_(geoms[1].to_wkt(),'Point(1 1)')
# MultiPointM
feat = fs.next()
eq_(feat.id(),7)
eq_(feat['gid'],7)
eq_(feat['dim'],3)
eq_(feat['name'],'MultiPointM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Point(0 0)')
eq_(geoms[1].to_wkt(),'Point(1 1)')
# MultiPointZM
feat = fs.next()
eq_(feat.id(),8)
eq_(feat['gid'],8)
eq_(feat['dim'],4)
eq_(feat['name'],'MultiPointZM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Point(0 0)')
eq_(geoms[1].to_wkt(),'Point(1 1)')
# LineString
feat = fs.next()
eq_(feat.id(),9)
eq_(feat['gid'],9)
eq_(feat['dim'],2)
eq_(feat['name'],'LineString')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
# LineStringZ
feat = fs.next()
eq_(feat.id(),10)
eq_(feat['gid'],10)
eq_(feat['dim'],3)
eq_(feat['name'],'LineStringZ')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
# LineStringM
feat = fs.next()
eq_(feat.id(),11)
eq_(feat['gid'],11)
eq_(feat['dim'],3)
eq_(feat['name'],'LineStringM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
# LineStringZM
feat = fs.next()
eq_(feat.id(),12)
eq_(feat['gid'],12)
eq_(feat['dim'],4)
eq_(feat['name'],'LineStringZM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
# Polygon
feat = fs.next()
eq_(feat.id(),13)
eq_(feat['gid'],13)
eq_(feat['name'],'Polygon')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# PolygonZ
feat = fs.next()
eq_(feat.id(),14)
eq_(feat['gid'],14)
eq_(feat['name'],'PolygonZ')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# PolygonM
feat = fs.next()
eq_(feat.id(),15)
eq_(feat['gid'],15)
eq_(feat['name'],'PolygonM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# PolygonZM
feat = fs.next()
eq_(feat.id(),16)
eq_(feat['gid'],16)
eq_(feat['name'],'PolygonZM')
geoms = feat.geometries()
eq_(len(geoms),1)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# MultiLineString
feat = fs.next()
eq_(feat.id(),17)
eq_(feat['gid'],17)
eq_(feat['name'],'MultiLineString')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
eq_(geoms[1].to_wkt(),'LineString(2 2,3 3)')
# MultiLineStringZ
feat = fs.next()
eq_(feat.id(),18)
eq_(feat['gid'],18)
eq_(feat['name'],'MultiLineStringZ')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
eq_(geoms[1].to_wkt(),'LineString(2 2,3 3)')
# MultiLineStringM
feat = fs.next()
eq_(feat.id(),19)
eq_(feat['gid'],19)
eq_(feat['name'],'MultiLineStringM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
eq_(geoms[1].to_wkt(),'LineString(2 2,3 3)')
# MultiLineStringZM
feat = fs.next()
eq_(feat.id(),20)
eq_(feat['gid'],20)
eq_(feat['name'],'MultiLineStringZM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'LineString(0 0,1 1)')
eq_(geoms[1].to_wkt(),'LineString(2 2,3 3)')
# MultiPolygon
feat = fs.next()
eq_(feat.id(),21)
eq_(feat['gid'],21)
eq_(feat['name'],'MultiPolygon')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
eq_(geoms[1].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# MultiPolygonZ
feat = fs.next()
eq_(feat.id(),22)
eq_(feat['gid'],22)
eq_(feat['name'],'MultiPolygonZ')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
eq_(geoms[1].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# MultiPolygonM
feat = fs.next()
eq_(feat.id(),23)
eq_(feat['gid'],23)
eq_(feat['name'],'MultiPolygonM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
eq_(geoms[1].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
# MultiPolygonZM
feat = fs.next()
eq_(feat.id(),24)
eq_(feat['gid'],24)
eq_(feat['name'],'MultiPolygonZM')
geoms = feat.geometries()
eq_(len(geoms),2)
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
eq_(geoms[1].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
atexit.register(postgis_takedown) atexit.register(postgis_takedown)