Skip to content
Permalink
Browse files

Clean up SWIG file and add new __geo_interface__ protocol and tests

  • Loading branch information...
geographika committed Nov 8, 2018
1 parent fecac40 commit 3ed8857466f49c9910500e9ad70b4fe62dd8b737
Showing with 149 additions and 32 deletions.
  1. +137 −32 mapscript/python/pyextend.i
  2. +4 −0 mapscript/python/tests/cases/line_test.py
  3. +8 −0 mapscript/python/tests/cases/point_test.py
@@ -1,22 +1,21 @@
/******************************************************************************
* $Id$
*
* Project: MapServer
* Purpose: Python-specific extensions to MapScript objects
* Author: Sean Gillies, sgillies@frii.com
* Author: Seth Girvin, sethg@geographika.co.uk
*
******************************************************************************
*
* Python-specific mapscript code has been moved into this
* SWIG interface file to improve the readibility of the main
* SWIG interface file to improve the readability of the main
* interface file. The main mapscript.i file includes this
* file when SWIGPYTHON is defined (via 'swig -python ...').
*
*****************************************************************************/

/* fromstring: Factory for mapfile objects */

%pythoncode {
%pythoncode %{
def fromstring(data, mappath=None):
"""Creates map objects from mapfile strings.

@@ -50,22 +49,7 @@ def fromstring(data, mappath=None):
return ob
else:
raise ValueError("No map, layer, class, or style found. Can not load from provided string")
}

/* ===========================================================================
Python rectObj extensions
======================================================================== */

%extend pointObj {

%pythoncode {

def __str__(self):
return self.toString()

}

}
%}


/* ===========================================================================
@@ -74,11 +58,11 @@ def fromstring(data, mappath=None):

%extend rectObj {

%pythoncode {
%pythoncode %{

def __str__(self):
return self.toString()

def __contains__(self, item):
item_type = item.__class__.__name__
if item_type == "pointObj":
@@ -89,11 +73,128 @@ def fromstring(data, mappath=None):
return False
else:
raise TypeError('__contains__ does not yet handle %s' % (item_type))


%}
}

/* ===========================================================================
Python pointObj extensions
======================================================================== */

%extend pointObj {

%pythoncode %{

def __str__(self):
return self.toString()

@property
def __geo_interface__(self):

if hasattr(self, "z"):
coords = (self.x, self.y, self.z)
else:
coords = (self.x, self.y)

return {"type": "Point", "coordinates": coords}

%}
}


/* ===========================================================================
Python lineObj extensions
======================================================================== */

%extend lineObj {

%pythoncode %{

@property
def __geo_interface__(self):

coords = []

for idx in range(0, self.numpoints):
pt = self.get(idx)
geom = pt.__geo_interface__
coords.append(geom["coordinates"])

return {"type": "LineString", "coordinates": coords}

%}
}


/* ===========================================================================
Python shapeObj extensions
======================================================================== */

%extend shapeObj {

%pythoncode %{

@property
def __geo_interface__(self):

bounds = self.bounds
geom_type = self.type

# see https://tools.ietf.org/html/rfc7946 for GeoJSON types

if ms_geom_type == MS_SHAPE_POINT or ms_geom_type == MS_SHP_POINTZ or ms_geom_type == MS_SHP_POINTM:
geom_type = "Point"
elif ms_geom_type == MS_SHP_MULTIPOINTZ or ms_geom_type == MS_SHP_MULTIPOINTM:
geom_type = "MultiPoint"
elif ms_geom_type == MS_SHAPE_LINE or ms_geom_type == MS_SHP_ARCZ or ms_geom_type == MS_SHP_ARCM:
if self.numlines == 1:
geom_type = "LineString"
else:
geom_type = "MultiLineString"
elif ms_geom_type == MS_SHAPE_POLYGON or ms_geom_type == MS_SHP_POLYGONZ or ms_geom_type == MS_SHP_POLYGONM:
if self.numlines == 1:
geom_type = "Polygon"
else:
geom_type = "MultiPolygon"
elif ms_geom_type == MS_SHAPE_NULL:
return None
else:
raise TypeError("Shape type {} not supported".format(geom_type))

properties = {}



coords = []

# property names are stored at the layer level
# https://github.com/mapserver/mapserver/issues/130
property_names = [for self.getValue(idx) in range(0, self.numvalues)]


property_values = [for self.getValue(idx) in range(0, self.numvalues)]

properties = zip(property_names, property_values)

for idx in range(0, self.numlines):
line = self.get(idx)
geom = line.__geo_interface__
coords.append(geom["coordinates"])

return {
"type": "Feature",
"bbox": (bounds.minx, bounds.miny, bounds.maxx, bounds.maxxy),
"properties": properties
"geometry": {
"type": geom_type,
"coordinates": coords
}
}

%}
}


/******************************************************************************
* Extensions to mapObj
*****************************************************************************/
@@ -129,22 +230,25 @@ def fromstring(data, mappath=None):
PyTuple_SetItem(output,0,PyInt_FromLong((long)self->width));
PyTuple_SetItem(output,1,PyInt_FromLong((long)self->height));
return output;
}
%pythoncode {
}
%pythoncode %{

def get_height(self):
return self.getSize()[1] # <-- second member is the height

def get_width(self):
return self.getSize()[0] # <-- first member is the width

def set_height(self, value):
return self.setSize(self.getSize()[0], value)

def set_width(self, value):
return self.setSize(value, self.getSize()[1])

width = property(get_width, set_width)
height = property(get_height, set_height)

}


%}
}

/******************************************************************************
@@ -261,7 +365,7 @@ def fromstring(data, mappath=None):
msSetError(MS_MISCERR, "pattern is read-only", "patternlength_set()");
}

%pythoncode {
%pythoncode %{

__swig_setmethods__["patternlength"] = _mapscript.styleObj_patternlength_set2
__swig_getmethods__["patternlength"] = _mapscript.styleObj_patternlength_get
@@ -270,7 +374,7 @@ def fromstring(data, mappath=None):
__swig_setmethods__["pattern"] = _mapscript.styleObj_pattern_set
__swig_getmethods__["pattern"] = _mapscript.styleObj_pattern_get
if _newclass:pattern = _swig_property(_mapscript.styleObj_pattern_get, _mapscript.styleObj_pattern_set)
}
%}

}

@@ -280,6 +384,7 @@ def fromstring(data, mappath=None):
*****************************************************************************/

%extend hashTableObj{

%pythoncode %{

def __getitem__(self, key):
@@ -311,5 +416,5 @@ def fromstring(data, mappath=None):

return keys

%}
};
%}
}
@@ -74,6 +74,10 @@ def xtestAlterNumPoints(self):

assert error_thrown is True

def testLineGeoInterface(self):
"""return point using the __geo_interface__ protocol"""
assert self.line.__geo_interface__ == {"type": "LineString", "coordinates": [(0.0, 1.0), (2.0, 3.0)]}


if __name__ == '__main__':
unittest.main()
@@ -90,6 +90,14 @@ def testPointToString(self):

assert p.toString() == p_str, p.toString()

def testPointGeoInterface(self):
"""return point using the __geo_interface__ protocol"""
p = mapscript.pointObj(1.0, 1.0, 0.002, 15.0)
if hasattr(p, 'z'):
assert p.__geo_interface__ == {"type": "Point", "coordinates": (1.0, 1.0, 0.002)}
else:
assert p.__geo_interface__ == {"type": "Point", "coordinates": (1.0, 1.0)}


if __name__ == '__main__':
unittest.main()

0 comments on commit 3ed8857

Please sign in to comment.
You can’t perform that action at this time.