From ebce119d0cef2a60f12ffecb60cc5ec4d1a162e9 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Sat, 5 Feb 2011 03:39:35 +0000 Subject: [PATCH] osx: add modification to __init__.py to work with multiple version --- osx/python/__init__.py | 788 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 788 insertions(+) create mode 100644 osx/python/__init__.py diff --git a/osx/python/__init__.py b/osx/python/__init__.py new file mode 100644 index 000000000..1d1e96246 --- /dev/null +++ b/osx/python/__init__.py @@ -0,0 +1,788 @@ +# +# This file is part of Mapnik (C++/Python mapping toolkit) +# Copyright (C) 2009 Artem Pavlenko, Dane Springmeyer +# +# Mapnik is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +"""Mapnik Python module. + +Boost Python bindings to the Mapnik C++ shared library. + +Several things happen when you do: + + >>> import mapnik + + 1) Mapnik C++ objects are imported via the '__init__.py' from the '_mapnik.so' shared object + (_mapnik.pyd on win) which references libmapnik.so (linux), libmapnik.dylib (mac), or + mapnik.dll (win32). + + 2) The paths to the input plugins and font directories are imported from the 'paths.py' + file which was constructed and installed during SCons installation. + + 3) All available input plugins and TrueType fonts are automatically registered. + + 4) Boost Python metaclass injectors are used in the '__init__.py' to extend several + objects adding extra convenience when accessed via Python. + +""" + +import sys + +ver_int = int('%s%s' % (sys.version_info[0],sys.version_info[1])) +ver_str = '%s.%s' % (sys.version_info[0],sys.version_info[1]) + +path_insert = '/Library/Frameworks/Mapnik.framework/Versions/2.0/unix/lib/python%s/site-packages/mapnik2' + +if ver_int < 25: + raise ImportError('Mapnik bindings are only available for python versions >= 2.5') +elif ver_int in (25,26,27,31): + sys.path.insert(0, path_insert % ver_str) + from _mapnik2 import * +elif ver_int > 31: + raise ImportError('Mapnik bindings are only available for python versions <= 3.1') +else: + raise ImportError('Mapnik bindings are only available for python versions 2.5, 2.6, 2.7, and 3.1') + + +import os +import warnings + +try: + from ctypes import RTLD_NOW, RTLD_GLOBAL +except ImportError: + try: + from DLFCN import RTLD_NOW, RTLD_GLOBAL + except ImportError: + RTLD_NOW = 2 + RTLD_GLOBAL = 256 + +flags = sys.getdlopenflags() +sys.setdlopenflags(RTLD_NOW | RTLD_GLOBAL) + +from paths import inputpluginspath, fontscollectionpath + +# The base Boost.Python class +BoostPythonMetaclass = Coord.__class__ + +class _MapnikMetaclass(BoostPythonMetaclass): + def __init__(self, name, bases, dict): + for b in bases: + if type(b) not in (self, type): + for k,v in list(dict.items()): + if hasattr(b, k): + setattr(b, '_c_'+k, getattr(b, k)) + setattr(b,k,v) + return type.__init__(self, name, bases, dict) + +# metaclass injector compatible with both python 2 and 3 +# http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/ +_injector = _MapnikMetaclass('_injector', (object, ), {}) + + +def Filter(*args,**kwargs): + warnings.warn("'Filter' is deprecated and will be removed in Mapnik 0.8.1, use 'Expression' instead", + DeprecationWarning, 2) + return Expression(*args, **kwargs) + +class Envelope(Box2d): + def __init__(self, *args, **kwargs): + warnings.warn("'Envelope' is deprecated and will be removed in Mapnik 0.8.1, use 'Box2d' instead", + DeprecationWarning, 2) + Box2d.__init__(self, *args, **kwargs) + +class _Coord(Coord,_injector): + """ + Represents a point with two coordinates (either lon/lat or x/y). + + Following operators are defined for Coord: + + Addition and subtraction of Coord objects: + + >>> Coord(10, 10) + Coord(20, 20) + Coord(30.0, 30.0) + >>> Coord(10, 10) - Coord(20, 20) + Coord(-10.0, -10.0) + + Addition, subtraction, multiplication and division between + a Coord and a float: + + >>> Coord(10, 10) + 1 + Coord(11.0, 11.0) + >>> Coord(10, 10) - 1 + Coord(-9.0, -9.0) + >>> Coord(10, 10) * 2 + Coord(20.0, 20.0) + >>> Coord(10, 10) / 2 + Coord(5.0, 5.0) + + Equality of coords (as pairwise equality of components): + >>> Coord(10, 10) is Coord(10, 10) + False + >>> Coord(10, 10) == Coord(10, 10) + True + """ + def __repr__(self): + return 'Coord(%s,%s)' % (self.x, self.y) + + def forward(self, projection): + """ + Projects the point from the geographic coordinate + space into the cartesian space. The x component is + considered to be longitude, the y component the + latitude. + + Returns the easting (x) and northing (y) as a + coordinate pair. + + Example: Project the geographic coordinates of the + city center of Stuttgart into the local + map projection (GK Zone 3/DHDN, EPSG 31467) + >>> p = Projection('+init=epsg:31467') + >>> Coord(9.1, 48.7).forward(p) + Coord(3507360.12813,5395719.2749) + """ + return forward_(self, projection) + + def inverse(self, projection): + """ + Projects the point from the cartesian space + into the geographic space. The x component is + considered to be the easting, the y component + to be the northing. + + Returns the longitude (x) and latitude (y) as a + coordinate pair. + + Example: Project the cartesian coordinates of the + city center of Stuttgart in the local + map projection (GK Zone 3/DHDN, EPSG 31467) + into geographic coordinates: + >>> p = Projection('+init=epsg:31467') + >>> Coord(3507360.12813,5395719.2749).inverse(p) + Coord(9.1, 48.7) + """ + return inverse_(self, projection) + +class _Box2d(Box2d,_injector): + """ + Represents a spatial envelope (i.e. bounding box). + + + Following operators are defined for Box2d: + + Addition: + e1 + e2 is equvalent to e1.expand_to_include(e2) but yields + a new envelope instead of modifying e1 + + Subtraction: + Currently e1 - e2 returns e1. + + Multiplication and division with floats: + Multiplication and division change the width and height of the envelope + by the given factor without modifying its center.. + + That is, e1 * x is equivalent to: + e1.width(x * e1.width()) + e1.height(x * e1.height()), + except that a new envelope is created instead of modifying e1. + + e1 / x is equivalent to e1 * (1.0/x). + + Equality: two envelopes are equal if their corner points are equal. + """ + + def __repr__(self): + return 'Box2d(%s,%s,%s,%s)' % \ + (self.minx,self.miny,self.maxx,self.maxy) + + def forward(self, projection): + """ + Projects the envelope from the geographic space + into the cartesian space by projecting its corner + points. + + See also: + Coord.forward(self, projection) + """ + return forward_(self, projection) + + def inverse(self, projection): + """ + Projects the envelope from the cartesian space + into the geographic space by projecting its corner + points. + + See also: + Coord.inverse(self, projection). + """ + return inverse_(self, projection) + +class _Projection(Projection,_injector): + + def __repr__(self): + return "Projection('%s')" % self.params() + + def forward(self,obj): + """ + Projects the given object (Box2d or Coord) + from the geographic space into the cartesian space. + + See also: + Box2d.forward(self, projection), + Coord.forward(self, projection). + """ + return forward_(obj,self) + + def inverse(self,obj): + """ + Projects the given object (Box2d or Coord) + from the cartesian space into the geographic space. + + See also: + Box2d.inverse(self, projection), + Coord.inverse(self, projection). + """ + return inverse_(obj,self) + +def get_types(num): + dispatch = {1: int, + 2: float, + 3: float, + 4: str, + 5: Geometry2d, + 6: object} + return dispatch.get(num) + +class _Datasource(Datasource,_injector): + + def describe(self): + return Describe(self) + + def field_types(self): + return map(get_types,self._field_types()) + + def all_features(self): + query = Query(self.envelope()) + for fld in self.fields(): + query.add_property_name(fld) + return self.features(query).features + +class _DeprecatedFeatureProperties(object): + + def __init__(self, feature): + self._feature = feature + + def __getitem__(self, name): + warnings.warn("indexing feature.properties is deprecated, index the " + "feature object itself for the same effect", DeprecationWarning, 2) + return self._feature[name] + + def __iter__(self): + warnings.warn("iterating feature.properties is deprecated, iterate the " + "feature object itself for the same effect", DeprecationWarning, 2) + return iter(self._feature) + +class _Feature(Feature, _injector): + """ + A Feature. + + TODO: docs + """ + @property + def properties(self): + return _DeprecatedFeatureProperties(self) + + @property + def attributes(self): + #XXX Returns a copy! changes to it won't affect feat.'s attrs. + # maybe deprecate? + return dict(self) + + @property + def geometry(self): + if self.num_geometries() > 0: + return self.get_geometry(0) + + @property + def geometries(self): + return [self.get_geometry(i) for i in xrange(self.num_geometries())] + + def __init__(self, id, geometry=None, **properties): + Feature._c___init__(self, id) + if geometry is not None: + self.add_geometry(geometry) + for k, v in properties.iteritems(): + self[k] = v + + def add_geometry(self, geometry): + geometry = self._as_wkb(geometry) + Feature._c_add_geometry(self, geometry) + + def _as_wkb(self, geometry): + if hasattr(geometry, 'wkb'): + # a shapely.geometry.Geometry + geometry = geometry.wkb + if isinstance(geometry, str): + # ignoring unicode un purpose + for type_ in ('POINT', 'POLYGON', 'LINE'): + if type_ in geometry: + # A WKT encoded string + from shapely import wkt + geometry = wkt.loads(geometry).wkb + return geometry + raise TypeError("%r (%s) not supported" % (geometry, type(geometry))) + +class _Color(Color,_injector): + + def __repr__(self): + return "Color(%r)" % self.to_hex_string() + +class _Symbolizers(Symbolizers,_injector): + + def __getitem__(self, idx): + sym = Symbolizers._c___getitem__(self, idx) + return sym.symbol() + +def _add_symbol_method_to_symbolizers(vars=globals()): + + def symbol_for_subcls(self): + return self + + def symbol_for_cls(self): + return getattr(self,self.type())() + + for name, obj in vars.items(): + if name.endswith('Symbolizer') and not name.startswith('_'): + if name == 'Symbolizer': + symbol = symbol_for_cls + else: + symbol = symbol_for_subcls + type('dummy', (obj,_injector), {'symbol': symbol}) +_add_symbol_method_to_symbolizers() + +def Datasource(**keywords): + """Wrapper around CreateDatasource. + + Create a Mapnik Datasource using a dictionary of parameters. + + Keywords must include: + + type='plugin_name' # e.g. type='gdal' + + See the convenience factory methods of each input plugin for + details on additional required keyword arguments. + + """ + + return CreateDatasource(keywords) + +# convenience factory methods + +def Shapefile(**keywords): + """Create a Shapefile Datasource. + + Required keyword arguments: + file -- path to shapefile without extension + + Optional keyword arguments: + base -- path prefix (default None) + encoding -- file encoding (default 'utf-8') + + >>> from mapnik import Shapefile, Layer + >>> shp = Shapefile(base='/home/mapnik/data',file='world_borders') + >>> lyr = Layer('Shapefile Layer') + >>> lyr.datasource = shp + + """ + keywords['type'] = 'shape' + return CreateDatasource(keywords) + +def PostGIS(**keywords): + """Create a PostGIS Datasource. + + Required keyword arguments: + dbname -- database name to connect to + table -- table name or subselect query + + *Note: if using subselects for the 'table' value consider also + passing the 'geometry_field' and 'srid' and 'extent_from_subquery' + options and/or specifying the 'geometry_table' option. + + Optional db connection keyword arguments: + user -- database user to connect as (default: see postgres docs) + password -- password for database user (default: see postgres docs) + host -- portgres hostname (default: see postgres docs) + port -- postgres port (default: see postgres docs) + initial_size -- integer size of connection pool (default: 1) + max_size -- integer max of connection pool (default: 10) + persist_connection -- keep connection open (default: True) + + Optional table-level keyword arguments: + extent -- manually specified data extent (comma delimited string, default: None) + estimate_extent -- boolean, direct PostGIS to use the faster, less accurate `estimate_extent` over `extent` (default: False) + extent_from_subquery -- boolean, direct Mapnik to query Postgis for the extent of the raw 'table' value (default: uses 'geometry_table') + geometry_table -- specify geometry table to use to look up metadata (default: automatically parsed from 'table' value) + geometry_field -- specify geometry field to use (default: first entry in geometry_columns) + srid -- specify srid to use (default: auto-detected from geometry_field) + row_limit -- integer limit of rows to return (default: 0) + cursor_size -- integer size of binary cursor to use (default: 0, no binary cursor is used) + multiple_geometries -- boolean, direct the Mapnik wkb reader to interpret as multigeometries (default False) + + >>> from mapnik import PostGIS, Layer + >>> params = dict(dbname='mapnik',table='osm',user='postgres',password='gis') + >>> params['estimate_extent'] = False + >>> params['extent'] = '-20037508,-19929239,20037508,19929239' + >>> postgis = PostGIS(**params) + >>> lyr = Layer('PostGIS Layer') + >>> lyr.datasource = postgis + + """ + keywords['type'] = 'postgis' + return CreateDatasource(keywords) + + +def Raster(**keywords): + """Create a Raster (Tiff) Datasource. + + Required keyword arguments: + file -- path to stripped or tiled tiff + lox -- lowest (min) x/longitude of tiff extent + loy -- lowest (min) y/latitude of tiff extent + hix -- highest (max) x/longitude of tiff extent + hiy -- highest (max) y/latitude of tiff extent + + Hint: lox,loy,hix,hiy make a Mapnik Box2d + + Optional keyword arguments: + base -- path prefix (default None) + + >>> from mapnik import Raster, Layer + >>> raster = Raster(base='/home/mapnik/data',file='elevation.tif',lox=-122.8,loy=48.5,hix=-122.7,hiy=48.6) + >>> lyr = Layer('Tiff Layer') + >>> lyr.datasource = raster + + """ + keywords['type'] = 'raster' + return CreateDatasource(keywords) + +def Gdal(**keywords): + """Create a GDAL Raster Datasource. + + Required keyword arguments: + file -- path to GDAL supported dataset + + Optional keyword arguments: + base -- path prefix (default None) + shared -- boolean, open GdalDataset in shared mode (default: False) + bbox -- tuple (minx, miny, maxx, maxy). If specified, overrides the bbox detected by GDAL. + + >>> from mapnik import Gdal, Layer + >>> dataset = Gdal(base='/home/mapnik/data',file='elevation.tif') + >>> lyr = Layer('GDAL Layer from TIFF file') + >>> lyr.datasource = dataset + + """ + keywords['type'] = 'gdal' + if 'bbox' in keywords: + if isinstance(keywords['bbox'], (tuple, list)): + keywords['bbox'] = ','.join([str(item) for item in keywords['bbox']]) + return CreateDatasource(keywords) + +def Occi(**keywords): + """Create a Oracle Spatial (10g) Vector Datasource. + + Required keyword arguments: + user -- database user to connect as + password -- password for database user + host -- oracle host to connect to (does not refer to SID in tsnames.ora) + table -- table name or subselect query + + Optional keyword arguments: + initial_size -- integer size of connection pool (default 1) + max_size -- integer max of connection pool (default 10) + extent -- manually specified data extent (comma delimited string, default None) + estimate_extent -- boolean, direct Oracle to use the faster, less accurate estimate_extent() over extent() (default False) + encoding -- file encoding (default 'utf-8') + geometry_field -- specify geometry field (default 'GEOLOC') + use_spatial_index -- boolean, force the use of the spatial index (default True) + multiple_geometries -- boolean, direct the Mapnik wkb reader to interpret as multigeometries (default False) + + >>> from mapnik import Occi, Layer + >>> params = dict(host='myoracle',user='scott',password='tiger',table='test') + >>> params['estimate_extent'] = False + >>> params['extent'] = '-20037508,-19929239,20037508,19929239' + >>> oracle = Occi(**params) + >>> lyr = Layer('Oracle Spatial Layer') + >>> lyr.datasource = oracle + """ + keywords['type'] = 'occi' + return CreateDatasource(keywords) + +def Ogr(**keywords): + """Create a OGR Vector Datasource. + + Required keyword arguments: + file -- path to OGR supported dataset + layer -- name of layer to use within datasource (optional if layer_by_index is used) + + Optional keyword arguments: + layer_by_index -- choose layer by index number instead of by layer name. + base -- path prefix (default None) + encoding -- file encoding (default 'utf-8') + multiple_geometries -- boolean, direct the Mapnik wkb reader to interpret as multigeometries (default False) + + >>> from mapnik import Ogr, Layer + >>> datasource = Ogr(base='/home/mapnik/data',file='rivers.geojson',layer='OGRGeoJSON') + >>> lyr = Layer('OGR Layer from GeoJSON file') + >>> lyr.datasource = datasource + + """ + keywords['type'] = 'ogr' + return CreateDatasource(keywords) + +def SQLite(**keywords): + """Create a SQLite Datasource. + + Required keyword arguments: + file -- path to SQLite database file + table -- table name or subselect query + + Optional keyword arguments: + base -- path prefix (default None) + encoding -- file encoding (default 'utf-8') + extent -- manually specified data extent (comma delimited string, default None) + metadata -- name of auxillary table containing record for table with xmin, ymin, xmax, ymax, and f_table_name + geometry_field -- name of geometry field (default 'the_geom') + key_field -- name of primary key field (default 'OGC_FID') + row_offset -- specify a custom integer row offset (default 0) + row_limit -- specify a custom integer row limit (default 0) + wkb_format -- specify a wkb type of 'spatialite' (default None) + multiple_geometries -- boolean, direct the Mapnik wkb reader to interpret as multigeometries (default False) + use_spatial_index -- boolean, instruct sqlite plugin to use Rtree spatial index (default True) + + >>> from mapnik import SQLite, Layer + >>> sqlite = SQLite(base='/home/mapnik/data',file='osm.db',table='osm',extent='-20037508,-19929239,20037508,19929239') + >>> lyr = Layer('SQLite Layer') + >>> lyr.datasource = sqlite + + """ + keywords['type'] = 'sqlite' + return CreateDatasource(keywords) + +def Rasterlite(**keywords): + """Create a Rasterlite Datasource. + + Required keyword arguments: + file -- path to Rasterlite database file + table -- table name or subselect query + + Optional keyword arguments: + base -- path prefix (default None) + extent -- manually specified data extent (comma delimited string, default None) + + >>> from mapnik import Rasterlite, Layer + >>> rasterlite = Rasterlite(base='/home/mapnik/data',file='osm.db',table='osm',extent='-20037508,-19929239,20037508,19929239') + >>> lyr = Layer('Rasterlite Layer') + >>> lyr.datasource = rasterlite + + """ + keywords['type'] = 'rasterlite' + return CreateDatasource(keywords) + +def Osm(**keywords): + """Create a Osm Datasource. + + Required keyword arguments: + file -- path to OSM file + + Optional keyword arguments: + encoding -- file encoding (default 'utf-8') + url -- url to fetch data (default None) + bbox -- data bounding box for fetching data (default None) + + >>> from mapnik import Osm, Layer + >>> datasource = Osm(file='test.osm') + >>> lyr = Layer('Osm Layer') + >>> lyr.datasource = datasource + + """ + # note: parser only supports libxml2 so not exposing option + # parser -- xml parser to use (default libxml2) + keywords['type'] = 'osm' + return CreateDatasource(keywords) + +def Kismet(**keywords): + """Create a Kismet Datasource. + + Required keyword arguments: + host -- kismet hostname + port -- kismet port + + Optional keyword arguments: + encoding -- file encoding (default 'utf-8') + extent -- manually specified data extent (comma delimited string, default None) + + >>> from mapnik import Kismet, Layer + >>> datasource = Kismet(host='localhost',port=2501,extent='-179,-85,179,85') + >>> lyr = Layer('Kismet Server Layer') + >>> lyr.datasource = datasource + + """ + keywords['type'] = 'kismet' + return CreateDatasource(keywords) + +def Geos(**keywords): + """Create a GEOS Vector Datasource. + + Required keyword arguments: + wkt -- inline WKT text of the geometry + + Optional keyword arguments: + multiple_geometries -- boolean, direct the GEOS wkt reader to interpret as multigeometries (default False) + extent -- manually specified data extent (comma delimited string, default None) + + >>> from mapnik import Geos, Layer + >>> datasource = Geos(wkt='MULTIPOINT(100 100, 50 50, 0 0)') + >>> lyr = Layer('GEOS Layer from WKT string') + >>> lyr.datasource = datasource + + """ + keywords['type'] = 'geos' + return CreateDatasource(keywords) + +def mapnik_version_string(version=mapnik_version()): + """Return the Mapnik version as a string.""" + patch_level = version % 100 + minor_version = version / 100 % 1000 + major_version = version / 100000 + return '%s.%s.%s' % ( major_version, minor_version,patch_level) + +def mapnik_version_from_string(version_string): + """Return the Mapnik version from a string.""" + n = version_string.split('.') + return (int(n[0]) * 100000) + (int(n[1]) * 100) + (int(n[2])); + +def register_plugins(path=inputpluginspath): + """Register plugins located by specified path""" + DatasourceCache.instance().register_datasources(path) + +def register_fonts(path=fontscollectionpath,valid_extensions=['.ttf','.otf','.ttc','.pfa','.pfb','.ttc','.dfont']): + """Recursively register fonts using path argument as base directory""" + for dirpath, _, filenames in os.walk(path): + for filename in filenames: + if os.path.splitext(filename)[1] in valid_extensions: + FontEngine.instance().register_font(os.path.join(dirpath, filename)) + +# auto-register known plugins and fonts +register_plugins() +register_fonts() + +#set dlopen flags back to the original +sys.setdlopenflags(flags) + +# Explicitly export API members to avoid namespace pollution +# and ensure correct documentation processing +__all__ = [ + # classes + 'Color', + 'Coord', + 'ColorBand', + 'CompositeOp', + 'DatasourceCache', + 'Box2d', + 'Feature', + 'Featureset', + 'FontEngine', + 'Geometry2d', + 'GlyphSymbolizer', + 'Image', + 'ImageView', + 'Layer', + 'Layers', + 'LinePatternSymbolizer', + 'LineSymbolizer', + 'Map', + 'MarkersSymbolizer', + 'Names', + 'Parameter', + 'Parameters', + 'PointDatasource', + 'PointSymbolizer', + 'PolygonPatternSymbolizer', + 'PolygonSymbolizer', + 'ProjTransform', + 'Projection', + 'Query', + 'RasterSymbolizer', + 'RasterColorizer', + 'Rule', 'Rules', + 'ShieldSymbolizer', + 'Singleton', + 'Stroke', + 'Style', + 'Symbolizer', + 'Symbolizers', + 'TextSymbolizer', + 'ViewTransform', + # enums + 'aspect_fix_mode', + 'label_placement', + 'line_cap', + 'line_join', + 'text_transform', + 'vertical_alignment', + 'horizontal_alignment', + 'justify_alignment', + 'pattern_alignment', + # functions + # datasources + 'Datasource', + 'CreateDatasource', + 'Shapefile', + 'PostGIS', + 'Raster', + 'Gdal', + 'Occi', + 'Ogr', + 'SQLite', + 'Osm', + 'Kismet', + 'Describe', + # version and environment + 'mapnik_version_string', + 'mapnik_version', + 'mapnik_svn_revision', + 'has_cairo', + 'has_pycairo', + # factory methods + 'Expression', + 'PathExpression', + # load/save/render + 'load_map', + 'load_map_from_string', + 'save_map', + 'save_map_to_string', + 'render', + 'render_tile_to_file', + 'render_to_file', + # other + 'register_plugins', + 'register_fonts', + 'scale_denominator', + # deprecated + 'Filter', + 'Envelope', + ]