update from master

This commit is contained in:
Dane Springmeyer 2013-01-24 00:32:25 -08:00
commit 249cb3e2ca
1390 changed files with 51563 additions and 20112 deletions

8
.gitignore vendored
View file

@ -39,8 +39,16 @@ tests/data/sqlite/*index
demo/c++/cairo-demo.pdf
demo/c++/cairo-demo.png
demo/c++/cairo-demo256.png
demo/c++/cairo-demo.svg
demo/c++/demo.tif
demo/c++/demo.jpg
demo/c++/demo.png
demo/c++/demo256.png
demo/viewer/Makefile
demo/viewer/Makefile.Debug
demo/viewer/Makefile.Release
demo/viewer/release/
demo/viewer/ui_about.h
demo/viewer/ui_info.h
demo/viewer/ui_layer_info.h
tests/cpp_tests/*-bin

21
.travis.yml Normal file
View file

@ -0,0 +1,21 @@
language: cpp
compiler: clang
before_install:
- echo 'yes' | sudo add-apt-repository ppa:mapnik/boost
- sudo apt-get update -qq
- sudo apt-get install -qq libboost-dev libboost-filesystem-dev libboost-program-options-dev libboost-python-dev libboost-regex-dev libboost-system-dev libboost-thread-dev python-nose libicu-dev libpng-dev libjpeg-dev libtiff-dev libz-dev libfreetype6-dev libxml2-dev libproj-dev
script:
- ./configure DEMO=False BINDINGS='python' CPP_TESTS=False CAIRO=False INPUT_PLUGINS='' OPTIMIZATION=1 JOBS=2 FAST=True && make
branches:
only:
- master
notifications:
irc:
channels:
- "irc.freenode.org#mapnik"
use_notice: true

View file

@ -2,60 +2,61 @@
Mapnik is written by Artem Pavlenko with contributions from:
Andy Allen
AJ Ashton
Matt Amos
Lucio Asnaghi
Justin Bronn
Christopher Brown
Jon Burgess
Toby Collet
Robert Coup
Berteun Damman
Craig de Stigter
Jean-Francois Doyon
David Eastcott
Krzysztof Godlewski
Beau Gunderson
John Hague
Dominic Hargreaves
Aubrey Holland
Tom Hughes
Konstantin Käfer
Mak Kolybabi
Peter Körner
Hermann Kraus
Stella Laurenzo
David Leaver
Carlos López
Dennis Luxen
Tom MacWright
Michal Migurski
Andrii Mishkovskyi
Ben Moores
Dražen Odobašić
Cameron Patrick
Igor Podolskiy
Reid Priedhorsky
Brian Quinion
Marcin Rudowski
Christopher Schmidt
Andreas Schneider
Vincent Schut
Ehud Shabtai
David Siegel
Steve Singer
Paul Smith
Vince Spader
Philipp Spitzer
Dane Springmeyer
Dave Stubbs
River Tarnell
Oliver Tonnhofer
Alberto Valverde
Martijn van Oosterhout
Andreas Volz
Lennard voor den Dag
Shaun Walbridge
Nick Whitelegg
Leslie Wu
* Andy Allen
* AJ Ashton
* Matt Amos
* Lucio Asnaghi
* Justin Bronn
* Christopher Brown
* Jon Burgess
* Toby Collet
* Robert Coup
* Berteun Damman
* Craig de Stigter
* Jean-Francois Doyon
* David Eastcott
* Krzysztof Godlewski
* Beau Gunderson
* John Hague
* Dominic Hargreaves
* Aubrey Holland
* Tom Hughes
* Konstantin Käfer
* Mak Kolybabi
* Peter Körner
* Hermann Kraus
* Stella Laurenzo
* David Leaver
* Carlos López
* Dennis Luxen
* Tom MacWright
* Michal Migurski
* Andrii Mishkovskyi
* Ben Moores
* Dražen Odobašić
* Cameron Patrick
* Igor Podolskiy
* Reid Priedhorsky
* Brian Quinion
* Marcin Rudowski
* Christopher Schmidt
* Andreas Schneider
* Vincent Schut
* Ehud Shabtai
* David Siegel
* Steve Singer
* Paul Smith
* Vince Spader
* Philipp Spitzer
* Dane Springmeyer
* Dave Stubbs
* River Tarnell
* Oliver Tonnhofer
* Alberto Valverde
* Martijn van Oosterhout
* Andreas Volz
* Lennard voor den Dag
* Shaun Walbridge
* Rich Wareham
* Nick Whitelegg
* Leslie Wu

View file

@ -4,24 +4,129 @@ A simple log of core changes affecting Mapnik usage.
Developers: Please commit along with changes.
For a complete change history, see the SVN log.
For a complete change history, see the git log.
## Future
- Added Layer `buffer-size` that can be used to override Map `buffer-size` to avoid
over-fetching of data that does not need to be buffered as much as other layers.
Map level `buffer-size` will be default if layers do not set the option. Renamed a
previously undocumented parameter by the same name that impacted clipping extent and
was not needed (clipping padding should likely be a symbolizer level option) (#1566)
- Fixed building symbolizer rendering to be fully sensitive to alpha (8b66128c892 / bc8ea1c5a7a)
- Added 64 bit integer support in the grid_renderer (#1662)
- `<Filter>[attr]</Filter>` now returns false if attr is an empty string (#1665)
- Added 64 bit integer support in expressions and feature ids (#1661,#1662)
- Added support for DBF `Logical` type: #1614
- Added serialization of `line-offset` to save_map (#1562)
- Enabled default input plugin directory and fonts path to be set inherited from environment settings in
python bindings to make it easier to run tests locally (#1594). New environment settings are:
- MAPNIK_INPUT_PLUGINS_DIRECTORY
- MAPNIK_FONT_DIRECTORY
- Added support for controlling rendering behavior of markers on multi-geometries `marker-multi-policy` (#1555,#1573)
- Added alternative PNG/ZLIB implementation (`miniz`) that can be enabled with `e=miniz` (#1554)
- Added support for setting zlib `Z_FIXED` strategy with format string: `png:z=fixed`
- Fixed handling of transparency level option in Octree-based PNG encoding (#1556)
- Faster rendering of rasters by reducing memory allocation of temporary buffers (#1516)
- Added ability to pass a pre-created collision detector to the cairo renderer (#1444)
- Tolerance parameter is now supported for querying datasources at a given point (#503/#1499)
- Improved detection of newlines in CSV files - now more robust in the face of mixed newline types (#1497)
- Allow style level compositing operations to work outside of featureset extents across tiled requests (#1477)
- Support for encoding `literal` postgres types as strings 69fb17cd3/#1466
- Fixed zoom_all behavior when Map maximum-extent is provided. Previously maximum-extent was used outright but
now the combined layer extents will be again respected: they will be clipped to the maximum-extent if possible
and only when back-projecting fails for all layers will the maximum-extent be used as a fallback (#1473)
## Mapnik 2.1.0
Released Aug 23, 2012
(Packaged from a25aac8)
- Feature-level compositing (comp-op) for all symbolizers (except building) in AGG and Cairo renderers (#1409)
- Style-level compositing (comp-op) (#1409) and style-level opacity for AGG renderer (#314)
- New experimental framework for image manipulation called `image-filters` to allow things to be done across entire layer canvas like burring (#1412)
- Support for recoloring stroke, fill, and opacity of SVG files (#1410 / #659)
- Support for data-driven transform expressions (#664)
- New support for offsetting geometries / parallel lines in line_symbolizer (#927/#1269)
- New support for clipping geometries - now default enabled on all symbolizers (#1116)
- Framework for chainable geometry transformations (called `vertex_converters`) so that you can do things like clip, smooth, and offset at the same time (#927)
- WKT parsing now is more robust and supports multi-geometries (#745)
- New support for outputting WKT/WKB/GeoJSON/SVG from mapnik.Geometry objects (#1411)
- New experimental python datasource plugin (#1337)
- New experimental geojson datasource plugin using in-memory rtree indexing (#1413)
- Cairo rendering is now much more similiar to AGG rendering as cairo backend now supports `scale_factor` (#1280) and other fixed have landed (#1343, #1233, #1344, #1242, #687, #737, #1006, #1071)
- mapnik::Feature objects and datasource plugins now use a `Context` to store attribute schemas to reduce the memory footprint of features (#834)
- Added Stroke `miterlimit` (#786)
- Python: exposed Map `background_image` (and aliased `background` to `background_color`)
- Python: exposed BuildingSymbolizer
- Support in the CSV plugin for reading JSON encoded geometries (#1392)
- Increased grid encoding performance (#1315)
- Added support for setting opacity dynamically on images in polygon pattern and markers symbolizers
- Added support for filtering on a features geometry type, either `point`, `linestring`, `polygon`,
or `collection` using the expression keyword of `[mapnik::geometry_type]` (#546)
- MarkersSymbolizer width and height moved to expressions (#1102)
- PostGIS: Added `simplify_geometries` option - will trigger ST_Simplify on geometries before returning to Mapnik (#1179)
- Improved error feedback for invalid values passed to map.query_point
- Fixed rendering of thin svg lines (#1129)
- Improved logging/debugging system with release logs and file redirection (https://github.com/mapnik/mapnik/wiki/Runtime-Logging) (#937 and partially #986, #467)
- GDAL: allow setting nodata value on the fly (will override value if nodata is set in data) (#1161)
- GDAL: respect nodata for paletted/colormapped images (#1160)
- PostGIS: Added a new option called 'autodetect_key_field' (by default false) that if true will
trigger autodetection of a given tables' primary key allowing for feature.id() to represent
globally unique ids. This option has no effect if the user has not manually supplied the 'key_field' option. (#804)
- PostGIS: Added a new option called `autodetect_key_field` (by default false) that if true will
trigger autodetection of the table primary key allowing for feature.id() to represent
globally unique ids. This option has no effect if the user has not manually supplied the `key_field` option. (#804)
- Cairo: Add full rendering support for markers to match AGG renderer functionality (#1071)
- Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentionally radii) (#1134)
- Added 'ignore-placement` attribute to markers-symbolizer (#1135)
- Added `ignore-placement` attribute to markers-symbolizer (#1135)
- Removed PointDatasource - use more robust MemoryDatasource instead (#1032)
@ -37,10 +142,43 @@ For a complete change history, see the SVN log.
- Added support for justify-alignment=auto. This is the new default. (#1125)
- Added support for grouped rendering using the `group-by` layer option: https://github.com/mapnik/mapnik/wiki/Grouped-rendering
## Mapnik 2.0.2
Released Aug 3, 2012
(Packaged from adb2ec741)
- Fixed handling of empty WKB geometries (#1334)
- Fixed naming of `stroke-dashoffset` in save_map (cc3cd5f63f28)
- Fixed support for boost 1.50 (8dea5a5fe239233)
- Fixed TextSymbolizer placement in Cairo backend so it respects avoid-edges and minimum-padding across all renderers (#1242)
- Fixed ShieldSymbolizer placement so it respects avoid-edges and minimum-padding across all renderers (#1242)
- Rolled back change made in 2.0.1 to marker width/height meaning that Mapnik > 2.0.2 will stick to assuming width/heigh are radii for back compatibility with 2.0.0. The reverted change is seen below as "Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentionally radii)". Issue tracking this is #1163
- XML: Fixed to avoid throwing if a `<Parameters>` element is encountered (which is supported in >= 2.1.x)
- Support for PostGIS 2.0 in the pgsql2sqlite command (e69c44e/47e5b3c)
- Fixed reference counting of Py_None when returning null attributes from Postgres during UTFGrid encoding, which could cause a Fatal Python error: deallocating None (#1221)
- Fixed possible breakage registering plugins via python if a custom PREFIX or DESTDIR was used (e.g. macports/homebrew) (#1171)
- Fixed memory leak in the case of proj >= 4.8 and a projection initialization error (#1173)
## Mapnik 2.0.1
(Packaged from 5cd3cb2efdaf7e9990a57e8e00b652a81aaa39ae)
Released April 10, 2012
(Packaged from 57347e9106)
- Support for PostGIS 2.0 (#956,#1083)
@ -54,7 +192,7 @@ For a complete change history, see the SVN log.
- Fix Markers rendering so that ellipse height/width units are pixels (previously were unintentially radii) (#1134)
- Added 'ignore-placement` attribute to markers-symbolizer (#1135)
- Added `ignore-placement` attribute to markers-symbolizer (#1135)
- Removed svn_revision info from mapnik-config and python bindings as git is now used
@ -75,11 +213,15 @@ For a complete change history, see the SVN log.
## Mapnik 2.0.0
Released September 26, 2011
(Packaged from 5b4c20eab3)
- Add minimum-path-length property to text_symbolizer to allow labels to be placed only on lines of a certain length (#865)
- Add support for png quantization using fixed palettes (#843)
- Add AlsoFilter functionality - http://trac.mapnik.org/wiki/AlsoFilter
- Add AlsoFilter functionality - https://github.com/mapnik/mapnik/wiki/AlsoFilter
- SQLite Plugin: optimize i/o using shared cache and no mutexes (#797)
@ -100,13 +242,13 @@ For a complete change history, see the SVN log.
from a file that files directory is used. And a custom value can still be passed as an argument to
load_map_from_string().
- Added python function 'render_grid' to allow conversion of grid buffer to python object containing list of grid
- Added python function `render_grid` to allow conversion of grid buffer to python object containing list of grid
pixels, list of keys, and a and dictionary of feature attributes.
- Added new rendering backend, grid_renderer, that collects the attributes of rendered features and
burns their ids into a grid buffer.
- Added optional 'maximum-extent' parameter to map object. If set will be used, instead of combined
- Added optional `maximum-extent` parameter to map object. If set will be used, instead of combined
layer extents, for return value of map.zoom_all(). Useful in cases where the combined layer extents
cannot possibly be projected into the map srs or the user wishes to control map bounds without
modifying the extents of each layer.
@ -119,9 +261,9 @@ For a complete change history, see the SVN log.
- Added support for drawing only first matching rule using filter-mode="first" in Style (#706)
- Added support to PointSymbolizer ('ignore_placement') for skipping adding placed points to collision detector (#564)
- Added support to PointSymbolizer (`ignore_placement`) for skipping adding placed points to collision detector (#564)
- Added ability to register fonts within XML using Map level 'font_directory' parameter (#168)
- Added ability to register fonts within XML using Map level `font-directory` parameter (#168)
- TextSymbolizer: Change text_convert to text_transform to better match css naming (r2211)
@ -129,8 +271,8 @@ For a complete change history, see the SVN log.
- Upgraded to the latest proj4 string literal for EPSG:4326 (WGS84) as global default projection (#333)
- Added 'mapnik_version_from_string()' function in python bindings to easily convert string representation
of version number to the integer format used in 'mapnik/version.hpp'. e.g. '0.7.1' --> 701.
- Added `mapnik_version_from_string()` function in python bindings to easily convert string representation
of version number to the integer format used in `mapnik/version.hpp`. e.g. `0.7.1` --> `701`.
- Added xinclude (http://www.w3.org/TR/xinclude/) support to libxml2-based xml parser (oldtopos) (#567)
@ -138,7 +280,7 @@ For a complete change history, see the SVN log.
- Added support for setting global alignment of polygon pattern fills (#203)
- Added support for choosing OGR layer by index number using 'layer_by_index' parameter (r1904)
- Added support for choosing OGR layer by index number using `layer_by_index` parameter (r1904)
- Added support for fractional halo widths (using FT Stroker) (#93)
@ -162,9 +304,13 @@ For a complete change history, see the SVN log.
- Implement MarkersSymbolizer in Cairo render and improve the markers placement finder. (#553)
# Mapnik 0.7.2 Release
# Mapnik 0.7.2
- Added forward compatibility for Mapnik 2.0 XML syntax (https://trac.mapnik.org/wiki/Mapnik2/Changes)
Released Oct 18, 2011
(Packaged from bc5cabeb6a)
- Added forward compatibility for Mapnik 2.0 XML syntax (https://github.com/mapnik/mapnik/wiki/Mapnik2/Changes)
- Build fixes to ensure boost_threads are not used unless THREADING=multi build option is used
@ -196,7 +342,7 @@ For a complete change history, see the SVN log.
- Various fixes to sqlite, ogr, and occi driver backported from trunk.
- Ensured that '\n' triggers linebreaks in text rendering (#584)
- Ensured that `\n` triggers linebreaks in text rendering (#584)
- Support for boost filesystem v3
@ -205,9 +351,53 @@ For a complete change history, see the SVN log.
- Fixed reading of label_position_tolerance on text_symbolizer and height for building_symbolizer
# Mapnik 0.7.0 Release
# Mapnik 0.7.1
(Packaged from r1574)
Released March 23, 2010
(Packaged from r1745/db89f1ca75)
- Rasters: Various fixes and improvements to 8bit png output ([#522](https://github.com/mapnik/mapnik/issues/522),[#475](https://github.com/mapnik/mapnik/issues/475))
- XML: Save map buffer_size when serializing map.
- SCons: Added new build options `PRIORITIZE_LINKING` and `LINK_PRIORITY`. The first is a boolean (default True)
of whether to use the new sorting implementation that gives explcit preference to custom or local paths
during compile and linking that will affect builds when duplicate libraries and include directories are on the
system. LINK_PRIORITY defaults to prioritizing internal sources of the mapnik source folder, then local/user
installed libraries over system libraries, but the option can be customized. Sorting not only ensures that
compiling and linking will more likely match the desired libraries but also gives more likelyhood to avoid
the scenario where libraries are linked that don`t match the includes libmapnik compiled against.
- XML: Fixed behavior of PolygonPatternSymbolizer and LinePatternSymbolizer whereby width, height,
and type of images is actually allowed to be optionally ommitted ([#508](https://github.com/mapnik/mapnik/issues/508)). This was added in r1543 but
only worked correctly for PointSymbolizer and ShieldSymbolizer.
- Fixed reading of PostGIS data on Big Endian systems ([#515](https://github.com/mapnik/mapnik/issues/515))
- PostGIS: Added better support for alterative schemas ([#500](https://github.com/mapnik/mapnik/issues/500))
- AGG Renderer - Enforced default gamma function on all symbolizers to ensure proper antialiasing
even when gamma is modified on the PolygonSymbolizer. ([#512](https://github.com/mapnik/mapnik/issues/512))
- PNG: fixed png256 for large images and some improvements to reduce color corruptions ([#522](https://github.com/mapnik/mapnik/issues/522))
- PNG: Added new quantization method for indexed png format using hextree with full support for alpha
channel. Also new method has some optimizations for color gradients common when using elevation based
rasters. By default old method using octree is used. (r1680, r1683, [#477](https://github.com/mapnik/mapnik/issues/477))
- PNG: Added initial support for passing options to png writter like number of colors, transparency
support, quantization method and possibly other in future using type parameter. For example
"png8:c=128:t=1:m=h" limits palette to 128 colors, uses only binary transparency (0 - none,
1 - binary, 2 - full), and new method of quantization using hextree (h - hextree, o - octree).
Existing type "png256" can be also written using "png8:c=256:m=o:t=2" (r1680, r1683, [#477](https://github.com/mapnik/mapnik/issues/477))
# Mapnik 0.7.0
Released January, 19 2010
(Packaged from r1574/a0da946be9)
- Core: Fixed linking to external libagg (r1297,r1299)
@ -217,26 +407,26 @@ For a complete change history, see the SVN log.
* Use the gdaladdo utility to add overviews to existing GDAL datasets
- PostGIS: Added an optional 'geometry_table' parameter. The 'geometry_table' used by Mapnik to look up
metadata in the geometry_columns and calculate extents (when the 'geometry_field' and 'srid' parameters
are not supplied). If 'geometry_table' is not specified Mapnik will attempt to determine the name of the
table to query based on parsing the 'table' parameter, which may fail for complex queries with more than
one 'from' keyword. Using this parameter should allow for existing metadata and table indexes to be used
while opening the door to much more complicated subqueries being passed to the 'table' parameter without
- PostGIS: Added an optional `geometry_table` parameter. The `geometry_table` used by Mapnik to look up
metadata in the geometry_columns and calculate extents (when the `geometry_field` and `srid` parameters
are not supplied). If `geometry_table` is not specified Mapnik will attempt to determine the name of the
table to query based on parsing the `table` parameter, which may fail for complex queries with more than
one `from` keyword. Using this parameter should allow for existing metadata and table indexes to be used
while opening the door to much more complicated subqueries being passed to the `table` parameter without
failing (#260, #426).
- PostGIS Plugin: Added optional 'geometry_field' and 'srid' parameters. If specified these will allow
- PostGIS Plugin: Added optional `geometry_field` and `srid` parameters. If specified these will allow
Mapnik to skip several queries to try to determine these values dynamically, and can be helpful to avoid
possible query failures during metadata lookup with complex subqueries as discussed in #260 and #436, but
also solvable by specifying the 'geometry_table' parameter. (r1300,#376)
also solvable by specifying the `geometry_table` parameter. (r1300,#376)
- PostGIS: Added an optional 'extent_from_subquery' parameter that when true (while the 'extent' parameter is
not provided and 'estimate_extent' is false) will direct Mapnik to calculate the extent upon the exact table
or sql provided in the 'table' parameter. If a sub-select is used for the table parameter then this will,
- PostGIS: Added an optional `extent_from_subquery` parameter that when true (while the `extent` parameter is
not provided and `estimate_extent` is false) will direct Mapnik to calculate the extent upon the exact table
or sql provided in the `table` parameter. If a sub-select is used for the table parameter then this will,
in cases where the subquery limits results, provide a faster and more accurate layer extent. It will have
no effect if the 'table' parameter is simply an existing table. This parameter is false by default. (#456)
no effect if the `table` parameter is simply an existing table. This parameter is false by default. (#456)
- PostGIS Plugin: Added '!bbox!' token substitution ability in sql query string. This opens the door for various
- PostGIS Plugin: Added `!bbox!` token substitution ability in sql query string. This opens the door for various
complex queries that may aggregate geometries to be kept fast by allowing proper placement of the bbox
query to be used by indexes. (#415)
@ -252,7 +442,7 @@ For a complete change history, see the SVN log.
(Select * from table where geom &amp;&amp; !bbox!) as map
</Parameter>
- PostGIS Plugin: Added 'scale_denominator' substitution ability in sql query string (#415/#465)
- PostGIS Plugin: Added `scale_denominator` substitution ability in sql query string (#415/#465)
* Pass the scale_denominator token inside a subquery like: !scale_denominator!
@ -260,7 +450,7 @@ For a complete change history, see the SVN log.
- PostGIS Plugin: Added support for quoted table names (r1454) (#393)
- PostGIS: Add a 'persist_connection' option (default true), that when false will release
- PostGIS: Add a `persist_connection` option (default true), that when false will release
the idle psql connection after datasource goes out of scope (r1337) (#433,#434)
- PostGIS: Added support for BigInt (int8) postgres type (384)
@ -281,19 +471,19 @@ For a complete change history, see the SVN log.
- PNG: Added support for semi-transparency in png256 output (#477,#202)
- PolygonSymbolizer: Added 'gamma' attribute to allow for dilation of polygon edges - a solution
- PolygonSymbolizer: Added `gamma` attribute to allow for dilation of polygon edges - a solution
to gap artifacts or "ghost lines" between adjacent polygons and allows for slight sharpening of
the edges of non overlapping polygons. Accepts any values but 0-1 is the recommended range.
- TextSymbolizer: Large set of new attributes: 'text_transform', 'line_spacing', 'character_spacing',
'wrap_character', 'wrap_before', 'horizontal_alignment', 'justify_alignment', and 'opacity'.
- TextSymbolizer: Large set of new attributes: `text_transform`, `line_spacing`, `character_spacing`,
`wrap_character`, `wrap_before`, `horizontal_alignment`, `justify_alignment`, and `opacity`.
* More details at changesets: r1254 and r1341
- SheildSymbolizer: Added special new attributes: 'unlock_image', 'VERTEX' placement, 'no_text' and many
attributes previously only supported in the TextSymbolizer: 'allow_overlap', 'vertical_alignment',
'horizontal_alignment', 'justify_alignment', 'wrap_width', 'wrap_character', 'wrap_before', 'text_transform',
'line_spacing', 'character_spacing', and 'opacity'.
- SheildSymbolizer: Added special new attributes: `unlock_image`, `VERTEX` placement, `no_text` and many
attributes previously only supported in the TextSymbolizer: `allow_overlap`, `vertical_alignment`,
`horizontal_alignment`, `justify_alignment`, `wrap_width`, `wrap_character`, `wrap_before`, `text_transform`,
`line_spacing`, `character_spacing`, and `opacity`.
* More details at changeset r1341
@ -301,42 +491,42 @@ For a complete change history, see the SVN log.
- XML: Fixed memory leak in libxml2 implementation (#473)
- XML: Added function to serialize map to string, called 'mapnik.save_map_to_string()' (#396)
- XML: Added function to serialize map to string, called `mapnik.save_map_to_string()` (#396)
- XML: Added parameter to <Map> called 'minimum_version' to allow for enforcing the minimum Mapnik version
- XML: Added parameter to <Map> called `minimum_version` to allow for enforcing the minimum Mapnik version
needed for XML features used in the mapfiles. Uses Major.Minor.Point syntax, for example
<Map minimum_version="0.6.1"> would throw an error if the user is running Mapnik less than 0.6.1.
- XML: Added support for relative paths when using entities and 'mapnik.load_map_from_string()' (#440)
- XML: Added support for relative paths when using entities and `mapnik.load_map_from_string()` (#440)
- XML: Made width and height optional for symbolizers using images (r1543)
- XML: Ensured that default values for layers are not serialized in save_map() (r1366)
- XML: Added missing serialization of PointSymbolizer 'opacity' and 'allow_overlap' attributes (r1358)
- XML: Added missing serialization of PointSymbolizer `opacity` and `allow_overlap` attributes (r1358)
- XML: Default text vertical_alignment now dependent on dy (#485, r1527)
- Python: Exposed ability to write to Cairo formats using 'mapnik.render_to_file()' and without pycairo (#381)
- Python: Exposed ability to write to Cairo formats using `mapnik.render_to_file()` and without pycairo (#381)
- Python: Fixed potential crash if pycairo support is enabled but python-cairo module is missing (#392)
- Python: Added 'mapnik.has_pycairo()' function to test for pycairo support (r1278) (#284)
- Python: Added `mapnik.has_pycairo()` function to test for pycairo support (r1278) (#284)
- Python: Added 'mapnik.register_plugins()' and 'mapnik.register_fonts()' functions (r1256)
- Python: Added `mapnik.register_plugins()` and `mapnik.register_fonts()` functions (r1256)
- Python: Pickling support for point_symbolizer (r1295) (#345)
- Python: Ensured mapnik::config_errors now throw RuntimeError exception instead of UserWarning exception (#442)
- Filters: Added support for '!=' as an alias to '<>' for not-equals filters (avoids &lt;&gt;) (r1326) (#427)
- Filters: Added support for `!=` as an alias to `<>` for not-equals filters (avoids &lt;&gt;) (r1326) (#427)
- SCons: Improved boost auto-detection (r1255,r1279)
- SCons: Fixed support for JOBS=N and FAST=True to enable faster compiling (r1440)
- SCons: Ensured that -h or --help will properly print help on custom Mapnik options before a user
has been able to properly run 'configure'. (r1514)
has been able to properly run `configure`. (r1514)
- SCons: Added ability to link to custom icu library name using ICU_LIB_NAME (r1414)
@ -345,12 +535,13 @@ For a complete change history, see the SVN log.
- Fonts: Added unifont to auto-installed fonts, which is used by the OSM styles as a fallback font (r1328)
# Mapnik 0.6.1
# Mapnik 0.6.1 Release
Released July 14, 2009
(Packaged from r1247)
(Packaged from r1247/353ff576c7)
- Plugins: expose list of registered plugins as a 'plugin_names()' method of DatasourceCache (r1180)
- Plugins: expose list of registered plugins as a `plugin_names()` method of DatasourceCache (r1180)
- XML: Fixed serialization and parsing bugs related to handling of integers and Enums (#328,#353)
@ -390,13 +581,13 @@ For a complete change history, see the SVN log.
- Python: Pickling support for raster_symbolizer (r1154) (#345)
- Python: Added 'mapnik.has_cairo()' function to test for cairo support (r1152) (#284)
- Python: Added `mapnik.has_cairo()` function to test for cairo support (r1152) (#284)
- Python: Exposed dash_array get method (r1151) (#317)
- Python: Pickling support for Coord objects (#345)
- GDAL Plugin: Added an experimental option to open files in 'shared mode' (r1143)
- GDAL Plugin: Added an experimental option to open files in `shared mode` (r1143)
- Python: Exposed RasterSymbolizer options in Python (r1139)
@ -408,13 +599,13 @@ For a complete change history, see the SVN log.
- XML: Ensured relative paths in XML are interpreted relative to XML file location (r1124) (#326)
- XML: Added ability to serialize all default symbolizer values by passing third argument to save_map(m,'file.xml',True)(r1117) (#327)
- XML: Added ability to serialize all default symbolizer values by passing third argument to save_map(m,`file.xml`,True)(r1117) (#327)
- Core: Added support for alpha transparency when writing to png256 (patch from Marcin Rudowski) (#202)
- SCons: Ensured ABI compatibility information is embedded in libmapnik.dylib on Mac OS X (#322)
- SCons: Ensured that the full 'install_name' path would be added to libmapnik.dylib on Mac OS X (#374)
- SCons: Ensured that the full `install_name` path would be added to libmapnik.dylib on Mac OS X (#374)
- Tests: Added testing framework in Python using nose (r1101-r1105)
@ -429,16 +620,17 @@ For a complete change history, see the SVN log.
- Plugins: Fixed segfault in OGR Plugin with empty geometries (r1074) (#292)
# Mapnik 0.6.0
# Mapnik 0.6.0 Release
Released April 1, 2009
(Packaged from r1066)
(Packaged from r1066/c88e03436f)
- Python: Added support for aspect_fix_mode (r1013)
- OGCServer Fixed axis-ordering for WMS 1.3.0 request (r1051) (#241)
- Plugins: Added option to all plugins to support using a 'base' path argument (r1042)
- Plugins: Added option to all plugins to support using a `base` path argument (r1042)
- Symbolizers: RasterSymbolizer now support composing modes for hillshading (r1027)
@ -459,7 +651,7 @@ For a complete change history, see the SVN log.
- Plugins: PostGIS plugin now accepts multi-line queries (r862)
- Filter parsing: Allow numbers in the filter field name.
This allows for shapefiles with columns like '1970'.
This allows for shapefiles with columns like `1970`.
- Plugins: Added OGR driver for reading all OGR supported formats (kunitoki) (r836) (#170)
@ -481,7 +673,7 @@ For a complete change history, see the SVN log.
- Core: Transformation is now skipped if srs values match exactly (r777)
- Symbolizers: 'min_distance' now honored for POINT placement using Text Symbolizer (r771)
- Symbolizers: `min_distance` now honored for POINT placement using Text Symbolizer (r771)
- Plugins: PostGIS plugin now accepts a geometry_field,record_limit, cursor_size options (r769,r872)
@ -522,3 +714,27 @@ For a complete change history, see the SVN log.
- Plugins: Use memory mapped files for reading shape file (r628)
- Core: Use streams to write images (i/o refactor) (r628) (#15)
# Mapnik 0.5.1
Released April 15, 2008
(Packaged from c29cb7386d)
# Mapnik 0.5.0
Released April 15, 2008
(Packaged from 0464a3563c)
# Mapnik 0.4.0
Released February 26, 2007
(Packaged from 8d73e3a8dc)
# Mapnik 0.3.0
Released May 22, 2006
(Packaged from 3ae046ebe2)

View file

@ -1,84 +1,83 @@
# Mapnik Installation
Mapnik is cross platform and runs on Linux, Mac OSX, Solaris, *BSD, and Windows.
## Quick Start
To configure and build mapnik do:
To configure and build Mapnik do:
./configure
make
NOTE: the above will not work on windows, rather see https://github.com/mapnik/mapnik/wiki/BuildingOnWindows
Then to run the tests locally (without needing to install):
make test-local
Install like:
sudo make install
If you need to uninstall do:
sudo make uninstall
For more details see the 'Building' Section below.
For more details see the `Building` Section below.
Platform specific install guides at http://trac.mapnik.org/wiki/MapnikInstallation
Platform specific install guides at https://github.com/mapnik/mapnik/wiki/Mapnik-Installation
For troubleshooting help see http://trac.mapnik.org/wiki/InstallationTroubleshooting
For troubleshooting help see https://github.com/mapnik/mapnik/wiki/InstallationTroubleshooting
## Depends
Mapnik is cross platform and runs on Linux, Mac OSX, Solaris, *BSD, and Windows.
The build system should work for all posix/unix systems but for windows see:
http://trac.mapnik.org/wiki/BuildingOnWindows
Build dependencies are:
Build system dependencies are:
* C++ compiler (like g++ or clang++)
* Python >= 2.4
* >= 2 GB RAM
* Python 2.4-2.7
* Scons (a copy is bundled)
Mapnik Core depends on:
* Boost
- >= 1.46 is recommended
- >= 1.45 is required if compiling with clang++
- >= 1.42 works on most systems and most compilers
- >= 1.47 is required to support wkt/wkb geometry output (optional)
- These libraries are required:
- >= 1.47 is required.
- These libraries are used:
- filesystem
- system
- thread (if mapnik threadsafe support is required, default on)
- regex (optionally built with icu regex support)
- program_options (optionally for mapnik command line programs)
* libicuuc >= 4.0 (ideally >= 4.2) - International Components for Unicode
* libpng >= 1.2.x - PNG Graphics
* libjpeg - JPEG Graphics
* libtiff - TIFF Graphics
* libz - Zlib Compression
* libfreetype - Freetype2 for Font support (Install requires freetype-config)
* libpng >= 1.2.x - PNG graphics
* libjpeg - JPEG graphics
* libtiff - TIFF graphics
* libz - Zlib compression
* libfreetype - Freetype2 for font support (Install requires freetype-config)
* libxml2 - XML parsing (Install requires xml2-config)
* libproj - PROJ.4 Projection library
* libproj - PROJ.4 projection library
Mapnik Python binding depend on:
Mapnik Python bindings depend on:
* Python >= 2.4
* Python 2.5-2.7 or >= 3.2
* Boost python
Note: Python3k is supported, see: https://github.com/mapnik/mapnik/wiki/Python3k
Optional dependencies:
* Cairo - Graphics library for PDF, PS, and SVG formats
* Cairo - Graphics library for output formats like PDF, PS, and SVG
- pkg-config - Required for building with cairo support
- libsigc++ - C++ support for cairomm
- cairomm - C++ bindings for cairo
- pycairo - Python bindings for cairo
* libpq - PostgreSQL libraries (For PostGIS plugin support)
* libgdal - GDAL/OGR input (For gdal and ogr plugin support)
* libsqlite3 - SQLite input (needs RTree support) (sqlite plugin support)
* libsqlite3 - SQLite input (needs RTree support builtin) (sqlite plugin support)
* libocci - Oracle input plugin support
* libcurl - OSM input plugin support
Instructions for installing many of these dependencies on
various platforms can be found at the Mapnik Community Wiki
(http://trac.mapnik.org/wiki/MapnikInstallation).
various platforms can be found at the Mapnik Wiki:
https://github.com/mapnik/mapnik/wiki/Mapnik-Installation
## Building
@ -89,6 +88,10 @@ We provide a simple Makefile wrapper that can be used like:
./configure && make && make install
For help on what options are accepted do:
./configure --help
To interact with the local copy of scons directly you can do:
python scons/scons.py configure
@ -101,9 +104,9 @@ If you want to clean your build do:
make clean
If you experience odd configure errors, try resetting the SCons caches:
If you experience odd configure errors, try cleaning the configure caches:
make reset
make distclean
To install in a custom location do:
@ -121,72 +124,50 @@ To pass custom paths to a dependency, like icu, do:
./configure ICU_INCLUDES=/usr/local/include ICU_LIBS=/usr/local/include
If you want to see configure options do:
For more details on usage see:
./configure --help
For more details on all the options see:
http://trac.mapnik.org/wiki/UsingScons
https://github.com/mapnik/mapnik/wiki/UsingScons
## Testing Installation
First, try importing the Mapnik python module in a python interpreter,
and make sure it does so without errors:
You can run the Mapnik tests locally (without installing) like:
$ python
Python 2.5.1 (r251:54863, Jan 17 2008, 19:35:17)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import mapnik
>>>
make test-local # see the Makefile for how this works
Then, try rendering the demo map, included in the Mapnik source code::
Or you can install and test like:
cd demo/python
python rundemo.py
make install && make test
If the resulting maps look good, this indicates the core components of
Mapnik are installed properly, as well as the Shapefile plugin, Unicode
text support (ICU), and re-projection support using Proj.
Many of the tests are written in python and you can run them individually like:
For further tests see the `tests` folder within the Mapnik source code.
make install
python tests/python_tests/shapefile_test.py
## Learning Mapnik
### Users
### Help
Visit http://trac.mapnik.org/wiki/LearningMapnik for basic tutorials on making maps with Mapnik using the Python bindings.
Mapnik has an active community of talented users and developers making beautiful maps.
### Developers
If you need help or want to participate starting points include:
Visit http://trac.mapnik.org/#DevelopersCorner for resources for getting involved with Mapnik development.
- Sign up and post to the mailing list: http://mapnik.org/contact/
- Join and ask questions on the #mapnik channel on irc://irc.freenode.net/mapnik
- Add your help questions to https://github.com/mapnik/mapnik-support
### Cartographers
## Mapnik Community
TileMill, which uses Mapnik internally, offers great step by step tutorials for
learning advanced map styling: http://mapbox.com/tilemill/docs/crashcourse/introduction/
### Programmers
Mapnik has an active community of talented users and developers making
amazing maps.
Mapnik is great for building your own mapping applications. Visit
https://github.com/mapnik/mapnik/wiki/LearningMapnik for basic
tutorials on how to programmatically use Mapnik.
If you are looking for further help on installation or usage and you can't
find what you are looking for from searching the users list archives
(http://lists.berlios.de/pipermail/mapnik-users/) or the trac wiki
(http://trac.mapnik.org/), feel free to join the Mapnik community and
introduce yourself.
### Contributers
You can get involved by:
* Subscribing to the mapnik-users list:
http://lists.berlios.de/mailman/listinfo/mapnik-users
* Subscribing to the mapnik-developers list:
http://lists.berlios.de/mailman/listinfo/mapnik-devel
* Joining the #mapnik channel on irc://irc.freenode.net/mapnik
* Signing up as a user or contributor at http://www.ohloh.net/p/mapnik/
Read docs/contributing.markdown for resources for getting involved with Mapnik development.

View file

@ -1,31 +1,60 @@
UNAME := $(shell uname)
LINK_FIX=LD_LIBRARY_PATH
ifeq ($(UNAME), Darwin)
LINK_FIX=DYLD_LIBRARY_PATH
else
endif
all: mapnik
install:
python scons/scons.py install
@python scons/scons.py --config=cache --implicit-cache --max-drift=1 install
mapnik:
python scons/scons.py
@python scons/scons.py --config=cache --implicit-cache --max-drift=1
clean:
python scons/scons.py -c
@python scons/scons.py -c --config=cache --implicit-cache --max-drift=1
@if test -e ".sconsign.dblite"; then rm ".sconsign.dblite"; fi
@if test -e "config.log"; then rm "config.log"; fi
@if test -e ".sconf_temp/"; then rm -r ".sconf_temp/"; fi
@find ./ -name "*.os" -exec rm {} \;
@find ./ -name "*.o" -exec rm {} \;
@find ./ -name "*.pyc" -exec rm {} \;
@rm bindings/python/mapnik/paths.py
reset:
if test -e ".sconf_temp/"; then rm -r ".sconf_temp/"; fi
if test -e ".sconsign.dblite"; then rm ".sconsign.dblite"; fi
if test -e "config.cache"; then rm "config.cache"; fi
distclean:
@if test -e "config.cache"; then rm "config.cache"; fi
if test -e "config.py"; then mv "config.py" "config.py.backup"; fi
reset: distclean
rebuild:
make uninstall && make clean && time make && make install
uninstall:
python scons/scons.py uninstall
@python scons/scons.py --config=cache --implicit-cache --max-drift=1 uninstall
test:
@echo "*** Running visual tests…"
@python tests/visual_tests/test.py -q
@echo "*** Running C++ tests..."
@for FILE in tests/cpp_tests/*-bin; do \
$${FILE}; \
done
@echo "*** Running python tests..."
@python tests/run_tests.py -q
@ ./run_tests
test-local:
@echo "*** Boostrapping local test environment..."
@export ${LINK_FIX}=`pwd`/src:${${LINK_FIX}} && \
export PYTHONPATH=`pwd`/bindings/python/:${PYTHONPATH} && \
export MAPNIK_FONT_DIRECTORY=`pwd`/fonts/dejavu-fonts-ttf-2.33/ttf/ && \
export MAPNIK_INPUT_PLUGINS_DIRECTORY=`pwd`/plugins/input/ && \
make test
bench:
@export ${LINK_FIX}=`pwd`/src:${${LINK_FIX}} && \
./benchmark/run
check: test-local
demo:
@echo "*** Running rundemo.cpp…"
cd demo/c++; ./rundemo `mapnik-config --prefix`/lib/mapnik
pep8:
# https://gist.github.com/1903033
@ -38,4 +67,9 @@ grind:
valgrind --leak-check=full --log-fd=1 $${FILE} | grep definitely; \
done
.PHONY: clean reset uninstall test install
render:
@for FILE in tests/data/good_maps/*xml; do \
nik2img.py $${FILE} /tmp/$$(basename $${FILE}).png; \
done
.PHONY: install mapnik clean distclean reset uninstall test demo pep8 grind render

View file

@ -1,12 +1,14 @@
```
_/ _/ _/ _/
_/_/ _/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
_/ _/ _/_/_/ _/_/_/ _/ _/ _/ _/ _/
_/
_/
```
_/ _/ _/ _/
_/_/ _/_/ _/_/_/ _/_/_/ _/_/_/ _/ _/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/_/
_/ _/ _/ _/ _/ _/ _/ _/ _/ _/ _/
_/ _/ _/_/_/ _/_/_/ _/ _/ _/ _/ _/
_/
_/
```
[![Build Status](https://secure.travis-ci.org/mapnik/mapnik.png)](http://travis-ci.org/mapnik/mapnik)
# What is Mapnik?

File diff suppressed because it is too large Load diff

49
benchmark/allocation.cpp Normal file
View file

@ -0,0 +1,49 @@
#include <mapnik/geometry.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/process_cpu_clocks.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::chrono;
using namespace mapnik;
void threaded_benchmark(void test(),std::string const& name, unsigned threads) {
using namespace boost::chrono;
typedef process_cpu_clock clock_type;
process_real_cpu_clock::time_point start = process_real_cpu_clock::now();
boost::thread_group threads;
for (unsigned i=0;i<threads;++i)
{
threads.create_thread(test);
}
threads.join_all();
clock_type::duration elapsed = process_real_cpu_clock::now() - start;
std::clog << boost::chrono::duration_cast<milliseconds>(elapsed)
<< " (" << boost::chrono::duration_cast<seconds>(elapsed) << ")"
<< " <-- " << name << "\n";
}
void test_wkt_creation()
{
boost::ptr_vector<mapnik::geometry_type> paths;
mapnik::wkt_parser parse_wkt;
std::string value("GEOMETRYCOLLECTION(MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20))),POINT(2 3),LINESTRING(2 3,3 4),MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20))),MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20))),MULTIPOLYGON (((40 40, 20 45, 45 30, 40 40)),((20 35, 45 20, 30 5, 10 10, 10 30, 20 35),(30 20, 20 25, 20 15, 30 20))))");
if (!parse_wkt.parse(value, paths)) std::clog << "failed to parse\n";
int iterations = 10000;
typedef process_cpu_clock clock_type;
process_real_cpu_clock::time_point start = process_real_cpu_clock::now();
for (int i=0;i<iterations;++i) {
parse_wkt.parse(value, paths);
}
clock_type::duration elapsed = process_real_cpu_clock::now() - start;
std::clog << "elapsed: " << boost::chrono::duration_cast<milliseconds>(elapsed) << "\n";
}
int main( int, char*[] )
{
return 0;
}

22
benchmark/build.py Normal file
View file

@ -0,0 +1,22 @@
import os
import glob
from copy import copy
Import ('env')
test_env = env.Clone()
test_env['LIBS'] = copy(env['LIBMAPNIK_LIBS'])
test_env.AppendUnique(LIBS='mapnik')
#test_env.AppendUnique(LIBS='sqlite3')
test_env.AppendUnique(CXXFLAGS='-g')
for cpp_test in glob.glob('run*.cpp'):
name = cpp_test.replace('.cpp','')
source_files = [cpp_test]
test_env_local = test_env.Clone()
test_program = test_env_local.Program(name, source=source_files, LINKFLAGS=env['CUSTOM_LDFLAGS'])
Depends(test_program, env.subst('../src/%s' % env['MAPNIK_LIB_NAME']))
# build locally if installing
if 'install' in COMMAND_LINE_TARGETS:
env.Alias('install',test_program)

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

315
benchmark/run.cpp Normal file
View file

@ -0,0 +1,315 @@
#include <mapnik/graphics.hpp>
#include <mapnik/image_data.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/image_reader.hpp>
#include <mapnik/util/conversions.hpp>
// stl
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdio>
// boost
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/bind.hpp>
#define BOOST_CHRONO_HEADER_ONLY
#include <boost/chrono/process_cpu_clocks.hpp>
#include <boost/chrono.hpp>
#include <boost/thread/thread.hpp>
using namespace boost::chrono;
using namespace mapnik;
typedef process_cpu_clock clock_type;
typedef clock_type::duration dur;
template <typename T>
void benchmark(T test, std::string const& name)
{
if (!test.validate()) throw std::runtime_error(std::string("test did not validate: ") + name);
process_cpu_clock::time_point start;
dur elapsed;
if (test.threads_ > 0) {
boost::thread_group tg;
for (unsigned i=0;i<test.threads_;++i)
{
tg.create_thread(test);
}
start = process_cpu_clock::now();
tg.join_all();
elapsed = process_cpu_clock::now() - start;
} else {
start = process_cpu_clock::now();
test();
elapsed = process_cpu_clock::now() - start;
}
std::clog << (test.threads_ ? "threaded -> ": "")
<< name << ": "
<< boost::chrono::duration_cast<milliseconds>(elapsed) << "\n";
}
bool compare_images(std::string const& src_fn,std::string const& dest_fn)
{
std::auto_ptr<mapnik::image_reader> reader1(mapnik::get_image_reader(dest_fn,"png"));
if (!reader1.get())
{
throw mapnik::image_reader_exception("Failed to load: " + dest_fn);
}
boost::shared_ptr<image_32> image_ptr1 = boost::make_shared<image_32>(reader1->width(),reader1->height());
reader1->read(0,0,image_ptr1->data());
std::auto_ptr<mapnik::image_reader> reader2(mapnik::get_image_reader(src_fn,"png"));
if (!reader2.get())
{
throw mapnik::image_reader_exception("Failed to load: " + src_fn);
}
boost::shared_ptr<image_32> image_ptr2 = boost::make_shared<image_32>(reader2->width(),reader2->height());
reader2->read(0,0,image_ptr2->data());
image_data_32 const& dest = image_ptr1->data();
image_data_32 const& src = image_ptr2->data();
unsigned int width = src.width();
unsigned int height = src.height();
if ((width != dest.width()) || height != dest.height()) return false;
for (unsigned int y = 0; y < height; ++y)
{
const unsigned int* row_from = src.getRow(y);
const unsigned int* row_to = dest.getRow(y);
for (unsigned int x = 0; x < width; ++x)
{
if (row_from[x] != row_to[x]) return false;
}
}
return true;
}
struct test1
{
unsigned iter_;
unsigned threads_;
explicit test1(unsigned iterations, unsigned threads=0) :
iter_(iterations),
threads_(threads)
{}
bool validate()
{
return true;
}
void operator()()
{
mapnik::image_data_32 im(256,256);
std::string out;
for (unsigned i=0;i<iter_;++i) {
out.clear();
out = mapnik::save_to_string(im,"png");
}
}
};
struct test2
{
unsigned iter_;
unsigned threads_;
boost::shared_ptr<image_32> im_;
explicit test2(unsigned iterations, unsigned threads=0) :
iter_(iterations),
threads_(threads),
im_()
{
std::string filename("./benchmark/data/multicolor.png");
std::auto_ptr<mapnik::image_reader> reader(mapnik::get_image_reader(filename,"png"));
if (!reader.get())
{
throw mapnik::image_reader_exception("Failed to load: " + filename);
}
im_ = boost::make_shared<image_32>(reader->width(),reader->height());
reader->read(0,0,im_->data());
}
bool validate()
{
std::string expected("./benchmark/data/multicolor-hextree-expected.png");
std::string actual("./benchmark/data/multicolor-hextree-actual.png");
mapnik::save_to_file(im_->data(),actual, "png8:m=h");
return compare_images(actual,expected);
}
void operator()()
{
std::string out;
for (unsigned i=0;i<iter_;++i) {
out.clear();
out = mapnik::save_to_string(im_->data(),"png8:m=h");
}
}
};
struct test3
{
unsigned iter_;
unsigned threads_;
double val_;
explicit test3(unsigned iterations, unsigned threads=0) :
iter_(iterations),
threads_(threads),
val_(-0.123) {}
bool validate()
{
std::ostringstream s;
s << val_;
return (s.str() == "-0.123");
}
void operator()()
{
std::string out;
for (unsigned i=0;i<iter_;++i) {
std::ostringstream s;
s << val_;
out = s.str();
}
}
};
struct test4
{
unsigned iter_;
unsigned threads_;
double val_;
explicit test4(unsigned iterations, unsigned threads=0) :
iter_(iterations),
threads_(threads),
val_(-0.123) {}
bool validate()
{
std::string s;
mapnik::util::to_string(s,val_);
return (s == "-0.123");
}
void operator()()
{
std::string out;
for (unsigned i=0;i<iter_;++i) {
out.clear();
mapnik::util::to_string(out,val_);
}
}
};
struct test5
{
unsigned iter_;
unsigned threads_;
double val_;
explicit test5(unsigned iterations, unsigned threads=0) :
iter_(iterations),
threads_(threads),
val_(-0.123) {}
bool validate()
{
std::string s;
to_string_impl(s,val_);
return (s == "-0.123");
}
bool to_string_impl(std::string &s , double val)
{
s.resize(s.capacity());
while (true)
{
size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val_));
if (n2 <= s.size())
{
s.resize(n2);
break;
}
s.resize(n2);
}
return true;
}
void operator()()
{
std::string out;
for (unsigned i=0;i<iter_;++i)
{
out.clear();
to_string_impl(out , val_);
}
}
};
int main( int, char*[] )
{
try
{
std::cout << "starting benchmark…\n";
{
test1 runner(100);
benchmark(runner,"encoding blank image as png");
}
{
test2 runner(100);
benchmark(runner,"encoding multicolor image as png8:m=h");
}
{
test1 runner(10,10);
benchmark(runner,"encoding blank image as png");
}
{
test2 runner(10,10);
benchmark(runner,"encoding multicolor image as png8:m=h");
}
{
test3 runner(1000000);
benchmark(runner,"double to string conversion with std::ostringstream");
}
{
test4 runner(1000000);
benchmark(runner,"double to string conversion with mapnik::util_to_string");
}
{
test5 runner(1000000);
benchmark(runner,"double to string conversion with snprintf");
}
{
test3 runner(1000000,10);
benchmark(runner,"double to string conversion with std::ostringstream");
}
{
test4 runner(1000000,10);
benchmark(runner,"double to string conversion with mapnik::util_to_string");
}
{
test5 runner(1000000,10);
benchmark(runner,"double to string conversion with snprintf");
}
std::cout << "...benchmark done\n";
return 0;
}
catch (std::exception const& ex)
{
std::clog << "test error: " << ex.what() << "\n";
return -1;
}
}

View file

@ -0,0 +1,15 @@
import mapnik
import random
im = mapnik.Image(256,256)
for x in xrange(0,im.width()):
for y in xrange(0,im.height()):
r = int(random.random() * 255)
g = random.random() * 255
b = random.random() * 255
a = random.random()
color = mapnik.Color('rgba(%i,%i,%i,%f)' % (r,g,b,a))
im.set_pixel(x,y,color)
im.save('./benchmark/data/multicolor.png')

View file

@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
#
import os, re, sys, glob
from subprocess import Popen, PIPE
@ -43,15 +43,7 @@ prefix = env['PREFIX']
target_path = os.path.normpath(env['PYTHON_INSTALL_LOCATION'] + os.path.sep + 'mapnik')
target_path_deprecated = os.path.normpath(env['PYTHON_INSTALL_LOCATION'] + os.path.sep + 'mapnik2')
libraries = ['mapnik']
if env['BOOST_PYTHON_LIB']:
libraries.append(env['BOOST_PYTHON_LIB'])
else:
if is_py3():
libraries.append('boost_python3%s' % env['BOOST_APPEND'])
else:
libraries.append('boost_python%s' % env['BOOST_APPEND'])
libraries = ['mapnik',env['BOOST_PYTHON_LIB']]
# TODO - do solaris/fedora need direct linking too?
if env['PLATFORM'] == 'Darwin':
@ -73,7 +65,7 @@ if env['PLATFORM'] == 'Darwin':
# 3) the below will directly link _mapnik.so to a python version
# 4) _mapnik.so must link to the same python lib as boost_python.dylib otherwise
# python will Abort with a Version Mismatch error.
# See http://trac.mapnik.org/ticket/453 for the seeds of a better approach
# See https://github.com/mapnik/mapnik/issues/453 for the seeds of a better approach
# for now we offer control over method of direct linking...
# The default below is to link against the python dylib in the form of
#/path/to/Python.framework/Python instead of -lpython
@ -94,7 +86,7 @@ if env['PLATFORM'] == 'Darwin':
# /System/Library/Frameworks/Python.framework/Python/Versions/
# or
# /Library/Frameworks/Python.framework/Python/Versions/
# See: http://trac.mapnik.org/ticket/380
# See: https://github.com/mapnik/mapnik/issues/380
link_prefix = env['PYTHON_SYS_PREFIX']
if '.framework' in link_prefix:
python_link_flag = '-F%s -framework Python -Z' % os.path.dirname(link_prefix.split('.')[0])
@ -143,10 +135,7 @@ paths += "__all__ = [mapniklibpath,inputpluginspath,fontscollectionpath]\n"
if not os.path.exists('mapnik'):
os.mkdir('mapnik')
if hasattr(os.path,'relpath'): # python 2.6 and above
file('mapnik/paths.py','w').write(paths % (os.path.relpath(env['MAPNIK_LIB_DIR'],target_path)))
else:
file('mapnik/paths.py','w').write(paths % (env['MAPNIK_LIB_DIR']))
file('mapnik/paths.py','w').write(paths % (env['MAPNIK_LIB_DIR']))
# force open perms temporarily so that `sudo scons install`
# does not later break simple non-install non-sudo rebuild
@ -154,8 +143,14 @@ try:
os.chmod('mapnik/paths.py',0666)
except: pass
# install the core mapnik python files, including '__init__.py'
# install the shared object beside the module directory
sources = glob.glob('*.cpp')
py_env = env.Clone()
py_env.Append(CPPPATH = env['PYTHON_INCLUDES'])
if 'install' in COMMAND_LINE_TARGETS:
# install the core mapnik python files, including '__init__.py'
init_files = glob.glob('mapnik/*.py')
if 'mapnik/paths.py' in init_files:
init_files.remove('mapnik/paths.py')
@ -165,8 +160,7 @@ if 'install' in COMMAND_LINE_TARGETS:
init_mapnik2 = env.Install(target_path_deprecated, 'mapnik2/__init__.py')
env.Alias(target='install', source=init_mapnik2)
# fix perms and install the custom generated 'paths.py'
if 'install' in COMMAND_LINE_TARGETS:
# fix perms and install the custom generated 'paths.py'
targetp = os.path.join(target_path,'paths.py')
env.Alias("install", targetp)
# use env.Command rather than env.Install
@ -177,22 +171,16 @@ if 'install' in COMMAND_LINE_TARGETS:
Chmod("$TARGET", 0644),
])
if 'uninstall' not in COMMAND_LINE_TARGETS:
if env['HAS_CAIRO']:
py_env.Append(CPPPATH = env['CAIRO_CPPPATHS'])
py_env.Append(CXXFLAGS = '-DHAVE_CAIRO')
if env['PLATFORM'] == 'Darwin':
py_env.Append(LIBS=env['CAIRO_LINKFLAGS'])
# install the shared object beside the module directory
sources = glob.glob('*.cpp')
py_env = env.Clone()
py_env.Append(CPPPATH = env['PYTHON_INCLUDES'])
if env['HAS_CAIRO']:
py_env.Append(CPPPATH = env['CAIROMM_CPPPATHS'])
py_env.Append(CXXFLAGS = '-DHAVE_CAIRO')
if env['PLATFORM'] == 'Darwin':
py_env.Append(LIBS=env['CAIROMM_LINKFLAGS'])
if env['HAS_PYCAIRO']:
py_env.ParseConfig('pkg-config --cflags pycairo')
py_env.Append(CXXFLAGS = '-DHAVE_PYCAIRO')
if env['HAS_PYCAIRO']:
py_env.ParseConfig('pkg-config --cflags pycairo')
py_env.Append(CXXFLAGS = '-DHAVE_PYCAIRO')
libraries.append('boost_thread%s' % env['BOOST_APPEND'])
_mapnik = py_env.LoadableModule('mapnik/_mapnik', sources, LIBS=libraries, LDMODULEPREFIX='', LDMODULESUFFIX='.so',LINKFLAGS=linkflags)
@ -204,7 +192,7 @@ if env['PLATFORM'] == 'SunOS' and env['PYTHON_IS_64BIT']:
cxx_module_path = os.path.join(target_path,'64')
else:
cxx_module_path = target_path
if 'uninstall' not in COMMAND_LINE_TARGETS:
pymapniklib = env.Install(cxx_module_path,_mapnik)
py_env.Alias(target='install',source=pymapniklib)

View file

@ -39,12 +39,36 @@ Several things happen when you do:
"""
import itertools
import os
import sys
import warnings
def bootstrap_env():
"""
If an optional settings file exists, inherit its
environment settings before loading the mapnik library.
This feature is intended for customized packages of mapnik.
The settings file should be a python file with an 'env' variable
that declares a dictionary of key:value pairs to push into the
global process environment, if not already set, like:
env = {'ICU_DATA':'/usr/local/share/icu/'}
"""
if os.path.exists(os.path.join(os.path.dirname(__file__),'mapnik_settings.py')):
from mapnik_settings import env
process_keys = os.environ.keys()
for key, value in env.items():
if key not in process_keys:
os.environ[key] = value
bootstrap_env()
from _mapnik import *
from paths import inputpluginspath, fontscollectionpath
from paths import inputpluginspath
from paths import fontscollectionpath
import printing
printing.renderer = render
@ -67,13 +91,13 @@ class _MapnikMetaclass(BoostPythonMetaclass):
_injector = _MapnikMetaclass('_injector', (object, ), {})
def Filter(*args,**kwargs):
warnings.warn("'Filter' is deprecated and will be removed in Mapnik 2.0.1, use 'Expression' instead",
warnings.warn("'Filter' is deprecated and will be removed in Mapnik 3.x, 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 2.0.1, use 'Box2d' instead",
warnings.warn("'Envelope' is deprecated and will be removed in Mapnik 3.x, use 'Box2d' instead",
DeprecationWarning, 2)
Box2d.__init__(self, *args, **kwargs)
@ -113,18 +137,18 @@ class _Coord(Coord,_injector):
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
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
Returns the easting (x) and northing (y) as a
coordinate pair.
Example: Project the geographic coordinates of the
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')
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)
"""
@ -132,19 +156,19 @@ class _Coord(Coord,_injector):
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
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
Returns the longitude (x) and latitude (y) as a
coordinate pair.
Example: Project the cartesian coordinates of the
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')
>>> p = Projection('+init=epsg:31467')
>>> Coord(3507360.12813,5395719.2749).inverse(p)
Coord(9.1, 48.7)
"""
@ -152,13 +176,13 @@ class _Coord(Coord,_injector):
class _Box2d(Box2d,_injector):
"""
Represents a spatial envelope (i.e. bounding box).
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
e1 + e2 is equvalent to e1.expand_to_include(e2) but yields
a new envelope instead of modifying e1
Subtraction:
@ -168,7 +192,7 @@ class _Box2d(Box2d,_injector):
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:
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.
@ -184,8 +208,8 @@ class _Box2d(Box2d,_injector):
def forward(self, projection):
"""
Projects the envelope from the geographic space
into the cartesian space by projecting its corner
Projects the envelope from the geographic space
into the cartesian space by projecting its corner
points.
See also:
@ -195,8 +219,8 @@ class _Box2d(Box2d,_injector):
def inverse(self, projection):
"""
Projects the envelope from the cartesian space
into the geographic space by projecting its corner
Projects the envelope from the cartesian space
into the geographic space by projecting its corner
points.
See also:
@ -211,7 +235,7 @@ class _Projection(Projection,_injector):
def forward(self,obj):
"""
Projects the given object (Box2d or Coord)
Projects the given object (Box2d or Coord)
from the geographic space into the cartesian space.
See also:
@ -222,7 +246,7 @@ class _Projection(Projection,_injector):
def inverse(self,obj):
"""
Projects the given object (Box2d or Coord)
Projects the given object (Box2d or Coord)
from the cartesian space into the geographic space.
See also:
@ -308,7 +332,7 @@ def Shapefile(**keywords):
encoding -- file encoding (default 'utf-8')
>>> from mapnik import Shapefile, Layer
>>> shp = Shapefile(base='/home/mapnik/data',file='world_borders')
>>> shp = Shapefile(base='/home/mapnik/data',file='world_borders')
>>> lyr = Layer('Shapefile Layer')
>>> lyr.datasource = shp
@ -323,7 +347,7 @@ def PostGIS(**keywords):
dbname -- database name to connect to
table -- table name or subselect query
*Note: if using subselects for the 'table' value consider also
*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.
@ -382,7 +406,7 @@ def Raster(**keywords):
tile_stride -- if an image is in tiles, what's the increment between rows/cols (default 1)
>>> 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)
>>> 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
@ -456,7 +480,7 @@ def Ogr(**keywords):
encoding -- file encoding (default 'utf-8')
>>> from mapnik import Ogr, Layer
>>> datasource = Ogr(base='/home/mapnik/data',file='rivers.geojson',layer='OGRGeoJSON')
>>> datasource = Ogr(base='/home/mapnik/data',file='rivers.geojson',layer='OGRGeoJSON')
>>> lyr = Layer('OGR Layer from GeoJSON file')
>>> lyr.datasource = datasource
@ -484,7 +508,7 @@ def SQLite(**keywords):
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')
>>> sqlite = SQLite(base='/home/mapnik/data',file='osm.db',table='osm',extent='-20037508,-19929239,20037508,19929239')
>>> lyr = Layer('SQLite Layer')
>>> lyr.datasource = sqlite
@ -504,7 +528,7 @@ def Rasterlite(**keywords):
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')
>>> rasterlite = Rasterlite(base='/home/mapnik/data',file='osm.db',table='osm',extent='-20037508,-19929239,20037508,19929239')
>>> lyr = Layer('Rasterlite Layer')
>>> lyr.datasource = rasterlite
@ -524,7 +548,7 @@ def Osm(**keywords):
bbox -- data bounding box for fetching data (default None)
>>> from mapnik import Osm, Layer
>>> datasource = Osm(file='test.osm')
>>> datasource = Osm(file='test.osm')
>>> lyr = Layer('Osm Layer')
>>> lyr.datasource = datasource
@ -546,7 +570,7 @@ def Kismet(**keywords):
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')
>>> datasource = Kismet(host='localhost',port=2501,extent='-179,-85,179,85')
>>> lyr = Layer('Kismet Server Layer')
>>> lyr.datasource = datasource
@ -564,7 +588,7 @@ def Geos(**keywords):
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)')
>>> datasource = Geos(wkt='MULTIPOINT(100 100, 50 50, 0 0)')
>>> lyr = Layer('GEOS Layer from WKT string')
>>> lyr.datasource = datasource
@ -572,18 +596,423 @@ def Geos(**keywords):
keywords['type'] = 'geos'
return CreateDatasource(keywords)
def Python(**keywords):
"""Create a Python Datasource.
>>> from mapnik import Python, PythonDatasource
>>> datasource = Python('PythonDataSource')
>>> lyr = Layer('Python datasource')
>>> lyr.datasource = datasource
"""
keywords['type'] = 'python'
return CreateDatasource(keywords)
class PythonDatasource(object):
"""A base class for a Python data source.
Optional arguments:
envelope -- a mapnik.Box2d (minx, miny, maxx, maxy) envelope of the data source, default (-180,-90,180,90)
geometry_type -- one of the DataGeometryType enumeration values, default Point
data_type -- one of the DataType enumerations, default Vector
"""
def __init__(self, envelope=None, geometry_type=None, data_type=None):
self.envelope = envelope or Box2d(-180, -90, 180, 90)
self.geometry_type = geometry_type or DataGeometryType.Point
self.data_type = data_type or DataType.Vector
def features(self, query):
"""Return an iterable which yields instances of Feature for features within the passed query.
Required arguments:
query -- a Query instance specifying the region for which features should be returned
"""
return None
def features_at_point(self, point):
"""Rarely uses. Return an iterable which yields instances of Feature for the specified point."""
return None
@classmethod
def wkb_features(cls, keys, features):
"""A convenience function to wrap an iterator yielding pairs of WKB format geometry and dictionaries of
key-value pairs into mapnik features. Return this from PythonDatasource.features() passing it a sequence of keys
to appear in the output and an iterator yielding features.
For example. One might have a features() method in a derived class like the following:
def features(self, query):
# ... create WKB features feat1 and feat2
return mapnik.PythonDatasource.wkb_features(
keys = ( 'name', 'author' ),
features = [
(feat1, { 'name': 'feat1', 'author': 'alice' }),
(feat2, { 'name': 'feat2', 'author': 'bob' }),
]
)
"""
ctx = Context()
[ctx.push(x) for x in keys]
def make_it(feat, idx):
f = Feature(ctx, idx)
geom, attrs = feat
f.add_geometries_from_wkb(geom)
for k, v in attrs.iteritems():
f[k] = v
return f
return itertools.imap(make_it, features, itertools.count(1))
@classmethod
def wkt_features(cls, keys, features):
"""A convenience function to wrap an iterator yielding pairs of WKT format geometry and dictionaries of
key-value pairs into mapnik features. Return this from PythonDatasource.features() passing it a sequence of keys
to appear in the output and an iterator yielding features.
For example. One might have a features() method in a derived class like the following:
def features(self, query):
# ... create WKT features feat1 and feat2
return mapnik.PythonDatasource.wkt_features(
keys = ( 'name', 'author' ),
features = [
(feat1, { 'name': 'feat1', 'author': 'alice' }),
(feat2, { 'name': 'feat2', 'author': 'bob' }),
]
)
"""
ctx = Context()
[ctx.push(x) for x in keys]
def make_it(feat, idx):
f = Feature(ctx, idx)
geom, attrs = feat
f.add_geometries_from_wkt(geom)
for k, v in attrs.iteritems():
f[k] = v
return f
return itertools.imap(make_it, features, itertools.count(1))
class _TextSymbolizer(TextSymbolizer,_injector):
@property
def name(self):
if isinstance(self.properties.format_tree, FormattingText):
return self.properties.format_tree.text
else:
# There is no single expression which could be returned as name
raise RuntimeError("TextSymbolizer uses complex formatting features, but old compatibility interface is used to access it. Use self.properties.format_tree instead.")
@name.setter
def name(self, name):
self.properties.format_tree = FormattingText(name)
@property
def text_size(self):
return self.format.text_size
@text_size.setter
def text_size(self, text_size):
self.format.text_size = text_size
@property
def face_name(self):
return self.format.face_name
@face_name.setter
def face_name(self, face_name):
self.format.face_name = face_name
@property
def fontset(self):
return self.format.fontset
@fontset.setter
def fontset(self, fontset):
self.format.fontset = fontset
@property
def character_spacing(self):
return self.format.character_spacing
@character_spacing.setter
def character_spacing(self, character_spacing):
self.format.character_spacing = character_spacing
@property
def line_spacing(self):
return self.format.line_spacing
@line_spacing.setter
def line_spacing(self, line_spacing):
self.format.line_spacing = line_spacing
@property
def text_opacity(self):
return self.format.text_opacity
@text_opacity.setter
def text_opacity(self, text_opacity):
self.format.text_opacity = text_opacity
@property
def wrap_char(self):
return self.format.wrap_char
@wrap_char.setter
def wrap_char(self, wrap_char):
self.format.wrap_char = wrap_char
@property
def wrap_character(self):
return self.format.wrap_character
@wrap_char.setter
def wrap_character(self, wrap_character):
self.format.wrap_character = wrap_character
@property
def wrap_before(self):
return self.format.wrap_before
@wrap_before.setter
def wrap_before(self, wrap_before):
self.format.wrap_before = wrap_before
@property
def text_transform(self):
return self.format.text_transform
@text_transform.setter
def text_transform(self, text_transform):
self.format.text_transform = text_transform
@property
def fill(self):
return self.format.fill
@fill.setter
def fill(self, fill):
self.format.fill = fill
@property
def halo_fill(self):
return self.format.halo_fill
@halo_fill.setter
def halo_fill(self, halo_fill):
self.format.halo_fill = halo_fill
@property
def halo_radius(self):
return self.format.halo_radius
@halo_radius.setter
def halo_radius(self, halo_radius):
self.format.halo_radius = halo_radius
@property
def label_placement(self):
return self.properties.label_placement
@label_placement.setter
def label_placement(self, label_placement):
self.properties.label_placement = label_placement
@property
def horizontal_alignment(self):
return self.properties.horizontal_alignment
@horizontal_alignment.setter
def horizontal_alignment(self, horizontal_alignment):
self.properties.horizontal_alignment = horizontal_alignment
@property
def justify_alignment(self):
return self.properties.justify_alignment
@justify_alignment.setter
def justify_alignment(self, justify_alignment):
self.properties.justify_alignment = justify_alignment
@property
def vertical_alignment(self):
return self.properties.vertical_alignment
@vertical_alignment.setter
def vertical_alignment(self, vertical_alignment):
self.properties.vertical_alignment = vertical_alignment
@property
def orientation(self):
return self.properties.orientation
@orientation.setter
def orientation(self, orientation):
self.properties.orientation = orientation
@property
def displacement(self):
return self.properties.displacement
@displacement.setter
def displacement(self, displacement):
self.properties.displacement = displacement
@property
def label_spacing(self):
return self.properties.label_spacing
@label_spacing.setter
def label_spacing(self, label_spacing):
self.properties.label_spacing = label_spacing
@property
def label_position_tolerance(self):
return self.properties.label_position_tolerance
@label_position_tolerance.setter
def label_position_tolerance(self, label_position_tolerance):
self.properties.label_position_tolerance = label_position_tolerance
@property
def avoid_edges(self):
return self.properties.avoid_edges
@avoid_edges.setter
def avoid_edges(self, avoid_edges):
self.properties.avoid_edges = avoid_edges
@property
def minimum_distance(self):
return self.properties.minimum_distance
@minimum_distance.setter
def minimum_distance(self, minimum_distance):
self.properties.minimum_distance = minimum_distance
@property
def minimum_padding(self):
return self.properties.minimum_padding
@minimum_padding.setter
def minimum_padding(self, minimum_padding):
self.properties.minimum_padding = minimum_padding
@property
def minimum_path_length(self):
return self.properties.minimum_path_length
@minimum_path_length.setter
def minimum_path_length(self, minimum_path_length):
self.properties.minimum_path_length = minimum_path_length
@property
def maximum_angle_char_delta(self):
return self.properties.maximum_angle_char_delta
@maximum_angle_char_delta.setter
def maximum_angle_char_delta(self, maximum_angle_char_delta):
self.properties.maximum_angle_char_delta = maximum_angle_char_delta
@property
def force_odd_labels(self):
return self.properties.force_odd_labels
@force_odd_labels.setter
def force_odd_labels(self, force_odd_labels):
self.properties.force_odd_labels = force_odd_labels
@property
def allow_overlap(self):
return self.properties.allow_overlap
@allow_overlap.setter
def allow_overlap(self, allow_overlap):
self.properties.allow_overlap = allow_overlap
@property
def text_ratio(self):
return self.properties.text_ratio
@text_ratio.setter
def text_ratio(self, text_ratio):
self.properties.text_ratio = text_ratio
@property
def wrap_width(self):
return self.properties.wrap_width
@wrap_width.setter
def wrap_width(self, wrap_width):
self.properties.wrap_width = wrap_width
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):
def register_plugins(path=None):
"""Register plugins located by specified path"""
DatasourceCache.instance().register_datasources(path)
# TODO - recurse
def register_fonts(path=fontscollectionpath,valid_extensions=['.ttf','.otf','.ttc','.pfa','.pfb','.ttc','.dfont']):
def register_fonts(path=None,valid_extensions=['.ttf','.otf','.ttc','.pfa','.pfb','.ttc','.dfont']):
"""Recursively register fonts using path argument as base directory"""
if not path:
if os.environ.has_key('MAPNIK_FONT_DIRECTORY'):
path = os.environ.get('MAPNIK_FONT_DIRECTORY')
else:
path = fontscollectionpath
for dirpath, _, filenames in os.walk(path):
for filename in filenames:
if os.path.splitext(filename.lower())[1] in valid_extensions:

View file

@ -0,0 +1,52 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <boost/python.hpp>
#include <mapnik/value.hpp>
#include <mapnik/attribute.hpp>
#include <mapnik/building_symbolizer.hpp>
using namespace mapnik;
using mapnik::building_symbolizer;
using mapnik::color;
void export_building_symbolizer()
{
using namespace boost::python;
class_<building_symbolizer>("BuildingSymbolizer",
init<>("Default BuildingSymbolizer"))
.add_property("fill",make_function
(&building_symbolizer::get_fill,
return_value_policy<copy_const_reference>()),
&building_symbolizer::set_fill)
.add_property("fill_opacity",
&building_symbolizer::get_opacity,
&building_symbolizer::set_opacity)
.add_property("height",
make_function(&building_symbolizer::height,
return_value_policy<copy_const_reference>()),
&building_symbolizer::set_height,
"Set/get the building height")
;
}

View file

@ -23,6 +23,7 @@
// boost
#include <boost/python.hpp>
#include <boost/python/detail/api_placeholder.hpp>
#include <boost/noncopyable.hpp>
// stl
#include <sstream>
@ -30,6 +31,8 @@
// mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/coord.hpp>
#include <mapnik/query.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/feature_layer_desc.hpp>
@ -47,24 +50,15 @@ namespace
using namespace boost::python;
boost::shared_ptr<mapnik::datasource> create_datasource(const dict& d)
{
bool bind=true;
mapnik::parameters params;
boost::python::list keys=d.keys();
for (int i=0; i<len(keys); ++i)
{
std::string key = extract<std::string>(keys[i]);
object obj = d[key];
if (key == "bind")
{
bind = extract<bool>(obj)();
continue;
}
extract<std::string> ex0(obj);
extract<int> ex1(obj);
extract<mapnik::value_integer> ex1(obj);
extract<double> ex2(obj);
if (ex0.check())
{
params[key] = ex0();
@ -79,7 +73,7 @@ boost::shared_ptr<mapnik::datasource> create_datasource(const dict& d)
}
}
return mapnik::datasource_cache::create(params, bind);
return mapnik::datasource_cache::instance().create(params);
}
boost::python::dict describe(boost::shared_ptr<mapnik::datasource> const& ds)
@ -167,10 +161,9 @@ void export_datasource()
.def("describe",&describe)
.def("envelope",&datasource::envelope)
.def("features",&datasource::features)
.def("bind",&datasource::bind)
.def("fields",&fields)
.def("field_types",&field_types)
.def("features_at_point",&datasource::features_at_point)
.def("features_at_point",&datasource::features_at_point, (arg("coord"),arg("tolerance")=0))
.def("params",&datasource::params,return_value_policy<copy_const_reference>(),
"The configuration parameters of the data source. "
"These vary depending on the type of data source.")

View file

@ -21,27 +21,72 @@
*****************************************************************************/
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
#include <mapnik/datasource_cache.hpp>
namespace {
using namespace boost::python;
boost::shared_ptr<mapnik::datasource> create_datasource(const dict& d)
{
mapnik::parameters params;
boost::python::list keys=d.keys();
for (int i=0; i<len(keys); ++i)
{
std::string key = extract<std::string>(keys[i]);
object obj = d[key];
extract<std::string> ex0(obj);
extract<mapnik::value_integer> ex1(obj);
extract<double> ex2(obj);
if (ex0.check())
{
params[key] = ex0();
}
else if (ex1.check())
{
params[key] = ex1();
}
else if (ex2.check())
{
params[key] = ex2();
}
}
return mapnik::datasource_cache::instance().create(params);
}
void register_datasources(std::string const& path)
{
mapnik::datasource_cache::instance().register_datasources(path);
}
std::vector<std::string> plugin_names()
{
return mapnik::datasource_cache::instance().plugin_names();
}
std::string plugin_directories()
{
return mapnik::datasource_cache::instance().plugin_directories();
}
}
void export_datasource_cache()
{
using mapnik::datasource_cache;
using mapnik::singleton;
using mapnik::CreateStatic;
using namespace boost::python;
class_<singleton<datasource_cache,CreateStatic>,boost::noncopyable>("Singleton",no_init)
.def("instance",&singleton<datasource_cache,CreateStatic>::instance,
return_value_policy<reference_existing_object>())
.staticmethod("instance")
;
class_<datasource_cache,bases<singleton<datasource_cache,CreateStatic> >,
boost::noncopyable>("DatasourceCache",no_init)
.def("create",&datasource_cache::create)
class_<datasource_cache,
boost::noncopyable>("DatasourceCache",no_init)
.def("create",&create_datasource)
.staticmethod("create")
.def("register_datasources",&datasource_cache::register_datasources)
.def("register_datasources",&register_datasources)
.staticmethod("register_datasources")
.def("plugin_names",&datasource_cache::plugin_names)
.def("plugin_names",&plugin_names)
.staticmethod("plugin_names")
.def("plugin_directories",&plugin_directories)
.staticmethod("plugin_directories")
;
}

View file

@ -86,6 +86,9 @@ void (box2d<double>::*re_center_p2)(coord<double,2> const& ) = &box2d<double>::r
// clip
void (box2d<double>::*clip)(box2d<double> const&) = &box2d<double>::clip;
// pad
void (box2d<double>::*pad)(double) = &box2d<double>::pad;
// deepcopy
box2d<double> box2d_deepcopy(box2d<double> & obj, boost::python::dict memo)
{
@ -160,13 +163,20 @@ void export_envelope()
"\n "
"Example:\n"
">>> e = Box2d(0, 0, 100, 100)\n"
">>> e.center(Coord60, 60)\n"
">>> e.center()\n"
"Coord(60.0,60.0)\n"
">>> (e.width(), e.height())\n"
"(100.0, 100.0)\n"
">>> c = Box2d(-50, -50, 50, 50)\n"
">>> e.clip(c)\n"
">>> e\n"
"Box2d(10.0, 10.0, 110.0, 110.0)\n"
"Box2d(0.0,0.0,50.0,50.0\n"
)
.def("pad", pad,
(arg("padding")),
"Pad the envelope based on a padding value.\n"
"\n "
"Example:\n"
">>> e = Box2d(0, 0, 100, 100)\n"
">>> e.pad(10)\n"
">>> e\n"
"Box2d(-10.0,-10.0,110.0,110.0\n"
)
.def("width", width_p1,
(arg("new_width")),

View file

@ -23,6 +23,8 @@
// boost
#include <boost/python.hpp>
#include <boost/variant.hpp>
#include <boost/noncopyable.hpp>
// mapnik
#include <mapnik/feature.hpp>
@ -46,12 +48,22 @@ expression_ptr parse_expression_(std::string const& wkt)
return parse_expression(wkt,"utf8");
}
std::string expression_to_string_(mapnik::expr_node const& expr)
{
return mapnik::to_expression_string(expr);
}
mapnik::value expression_evaluate_(mapnik::expr_node const& expr, mapnik::Feature const& f)
{
// will be auto-converted to proper python type by `mapnik_value_to_python`
return boost::apply_visitor(mapnik::evaluate<mapnik::Feature,mapnik::value>(f),expr);
}
bool expression_evaluate_to_bool_(mapnik::expr_node const& expr, mapnik::Feature const& f)
{
return boost::apply_visitor(mapnik::evaluate<mapnik::Feature,mapnik::value>(f),expr).to_bool();
}
// path expression
path_expression_ptr parse_path_(std::string const& path)
{
@ -75,7 +87,8 @@ void export_expression()
"TODO"
"",no_init)
.def("evaluate", &expression_evaluate_)
.def("__str__",&to_expression_string);
.def("to_bool", &expression_evaluate_to_bool_)
.def("__str__",&expression_to_string_);
;
def("Expression",&parse_expression_,(arg("expr")),"Expression string");

View file

@ -28,6 +28,8 @@
#include <boost/python/tuple.hpp>
#include <boost/python/to_python_converter.hpp>
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
// mapnik
#include <mapnik/feature.hpp>
@ -156,18 +158,53 @@ struct UnicodeString_from_python_str
}
};
struct value_null_from_python
{
value_null_from_python()
{
boost::python::converter::registry::push_back(
&convertible,
&construct,
boost::python::type_id<mapnik::value_null>());
}
static void* convertible(PyObject* obj_ptr)
{
if (obj_ptr == Py_None) return obj_ptr;
return 0;
}
static void construct(
PyObject* obj_ptr,
boost::python::converter::rvalue_from_python_stage1_data* data)
{
if (obj_ptr != Py_None) boost::python::throw_error_already_set();
void* storage = (
(boost::python::converter::rvalue_from_python_storage<mapnik::value_null>*)
data)->storage.bytes;
new (storage) mapnik::value_null();
data->convertible = storage;
}
};
void export_feature()
{
using namespace boost::python;
using mapnik::Feature;
// Python to mapnik::value converters
implicitly_convertible<int,mapnik::value>();
implicitly_convertible<double,mapnik::value>();
implicitly_convertible<UnicodeString,mapnik::value>();
implicitly_convertible<bool,mapnik::value>();
// NOTE: order matters here. For example value_null must be listed before
// bool otherwise Py_None will be interpreted as bool (false)
implicitly_convertible<mapnik::value_unicode_string,mapnik::value>();
implicitly_convertible<mapnik::value_null,mapnik::value>();
implicitly_convertible<mapnik::value_integer,mapnik::value>();
implicitly_convertible<mapnik::value_double,mapnik::value>();
implicitly_convertible<mapnik::value_bool,mapnik::value>();
// http://misspent.wordpress.com/2009/09/27/how-to-write-boost-python-converters/
UnicodeString_from_python_str();
value_null_from_python();
class_<context_type,context_ptr,boost::noncopyable>
("Context",init<>("Default ctor."))
@ -175,7 +212,7 @@ void export_feature()
;
class_<Feature,boost::shared_ptr<Feature>,
boost::noncopyable>("Feature",init<context_ptr,int>("Default ctor."))
boost::noncopyable>("Feature",init<context_ptr,mapnik::value_integer>("Default ctor."))
.def("id",&Feature::id)
.def("__str__",&Feature::to_string)
.def("add_geometries_from_wkb", &feature_add_geometries_from_wkb)
@ -188,6 +225,7 @@ void export_feature()
.def("has_key", &Feature::has_key)
.add_property("attributes",&attributes)
.def("__setitem__",&__setitem__)
.def("__contains__",&__getitem__)
.def("__getitem__",&__getitem__)
.def("__getitem__",&__getitem2__)
.def("__len__", &Feature::size)

View file

@ -22,6 +22,7 @@
// boost
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
// mapnik
#include <mapnik/feature.hpp>
@ -49,13 +50,14 @@ inline object pass_through(object const& o) { return o; }
inline mapnik::feature_ptr next(mapnik::featureset_ptr const& itr)
{
if (!itr)
mapnik::feature_ptr f = itr->next();
if (!f)
{
PyErr_SetString(PyExc_StopIteration, "No more features.");
boost::python::throw_error_already_set();
}
return itr->next();
return f;
}
}

View file

@ -21,6 +21,8 @@
*****************************************************************************/
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
#include <mapnik/font_engine_freetype.hpp>
void export_font_engine()

View file

@ -30,21 +30,16 @@
using mapnik::font_set;
struct fontset_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const font_set& fs)
{
return boost::python::make_tuple(fs.get_name());
}
};
void export_fontset ()
{
using namespace boost::python;
class_<font_set>("FontSet", init<>("default fontset constructor")
class_<font_set>("FontSet", init<std::string const&>("default fontset constructor")
)
.def_pickle(fontset_pickle_suite())
.add_property("name",
make_function(&font_set::get_name,return_value_policy<copy_const_reference>()),
&font_set::set_name,
"Get/Set the name of the FontSet.\n"
)
.def("add_face_name",&font_set::add_face_name,
(arg("name")),
"Add a face-name to the fontset.\n"

View file

@ -19,7 +19,6 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
//$Id$
#include <boost/python.hpp>

View file

@ -26,17 +26,21 @@
#include <boost/python/iterator.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
// mapnik
#include <mapnik/geometry.hpp>
#include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/wkb.hpp>
#include <mapnik/json/geometry_parser.hpp>
#include <mapnik/json/geojson_generator.hpp>
#include <boost/version.hpp>
#if BOOST_VERSION >= 104700
#include <mapnik/util/geometry_to_wkb.hpp>
#include <mapnik/util/geometry_to_wkt.hpp>
#include <mapnik/util/geometry_to_svg.hpp>
#endif
namespace {
@ -48,7 +52,7 @@ typedef boost::ptr_vector<geometry_type> path_type;
geometry_type const& getitem_impl(path_type & p, int key)
{
if (key >=0 && key < p.size())
if (key >=0 && key < static_cast<int>(p.size()))
return p[key];
PyErr_SetString(PyExc_IndexError, "Index is out of range");
throw boost::python::error_already_set();
@ -56,30 +60,65 @@ geometry_type const& getitem_impl(path_type & p, int key)
void add_wkt_impl(path_type& p, std::string const& wkt)
{
bool result = mapnik::from_wkt(wkt , p);
if (!result) throw std::runtime_error("Failed to parse WKT");
if (!mapnik::from_wkt(wkt , p))
throw std::runtime_error("Failed to parse WKT");
}
void add_wkb_impl(path_type& p, std::string const& wkb)
{
mapnik::geometry_utils::from_wkb(p, wkb.c_str(), wkb.size());
if (!mapnik::geometry_utils::from_wkb(p, wkb.c_str(), wkb.size()))
throw std::runtime_error("Failed to parse WKB");
}
void add_geojson_impl(path_type& p, std::string const& json)
{
if (!mapnik::json::from_geojson(json, p))
throw std::runtime_error("Failed to parse geojson geometry");
}
boost::shared_ptr<path_type> from_wkt_impl(std::string const& wkt)
{
boost::shared_ptr<path_type> paths = boost::make_shared<path_type>();
bool result = mapnik::from_wkt(wkt, *paths);
if (!result) throw std::runtime_error("Failed to parse WKT");
if (!mapnik::from_wkt(wkt, *paths))
throw std::runtime_error("Failed to parse WKT");
return paths;
}
boost::shared_ptr<path_type> from_wkb_impl(std::string const& wkb)
{
boost::shared_ptr<path_type> paths = boost::make_shared<path_type>();
mapnik::geometry_utils::from_wkb(*paths, wkb.c_str(), wkb.size());
if (!mapnik::geometry_utils::from_wkb(*paths, wkb.c_str(), wkb.size()))
throw std::runtime_error("Failed to parse WKB");
return paths;
}
boost::shared_ptr<path_type> from_geojson_impl(std::string const& json)
{
boost::shared_ptr<path_type> paths = boost::make_shared<path_type>();
if (! mapnik::json::from_geojson(json, *paths))
throw std::runtime_error("Failed to parse geojson geometry");
return paths;
}
mapnik::box2d<double> envelope_impl(path_type & p)
{
mapnik::box2d<double> b;
bool first = true;
BOOST_FOREACH(mapnik::geometry_type const& geom, p)
{
if (first)
{
b = geom.envelope();
first=false;
}
else
{
b.expand_to_include(geom.envelope());
}
}
return b;
}
}
inline std::string boost_version()
@ -93,13 +132,20 @@ inline std::string boost_version()
PyObject* to_wkb( geometry_type const& geom, mapnik::util::wkbByteOrder byte_order)
{
mapnik::util::wkb_buffer_ptr wkb = mapnik::util::to_wkb(geom,byte_order);
return
if (wkb)
{
return
#if PY_VERSION_HEX >= 0x03000000
::PyBytes_FromStringAndSize
::PyBytes_FromStringAndSize
#else
::PyString_FromStringAndSize
::PyString_FromStringAndSize
#endif
((const char*)wkb->buffer(),wkb->size());
((const char*)wkb->buffer(),wkb->size());
}
else
{
Py_RETURN_NONE;
}
}
#else
PyObject* to_wkb( geometry_type const& geom)
@ -114,13 +160,20 @@ PyObject* to_wkb( geometry_type const& geom)
PyObject* to_wkb2( path_type const& p, mapnik::util::wkbByteOrder byte_order)
{
mapnik::util::wkb_buffer_ptr wkb = mapnik::util::to_wkb(p,byte_order);
return
if (wkb)
{
return
#if PY_VERSION_HEX >= 0x03000000
::PyBytes_FromStringAndSize
::PyBytes_FromStringAndSize
#else
::PyString_FromStringAndSize
::PyString_FromStringAndSize
#endif
((const char*)wkb->buffer(),wkb->size());
((const char*)wkb->buffer(),wkb->size());
}
else
{
Py_RETURN_NONE;
}
}
#else
PyObject* to_wkb2( path_type const& p)
@ -174,6 +227,41 @@ std::string to_geojson( path_type const& geom)
return json;
}
std::string to_svg( geometry_type const& geom)
{
#if BOOST_VERSION >= 104700
std::string svg; // Use Python String directly ?
bool result = mapnik::util::to_svg(svg,geom);
if (!result)
{
throw std::runtime_error("Generate WKT failed");
}
return svg;
#else
throw std::runtime_error("mapnik::to_wkt() requires at least boost 1.47 while your build was compiled against boost "
+ boost_version());
#endif
}
/*
// https://github.com/mapnik/mapnik/issues/1437
std::string to_svg2( path_type const& geom)
{
#if BOOST_VERSION >= 104700
std::string svg; // Use Python String directly ?
bool result = mapnik::util::to_svg(svg,geom);
if (!result)
{
throw std::runtime_error("Generate WKT failed");
}
return svg;
#else
throw std::runtime_error("mapnik::to_svg() requires at least boost 1.47 while your build was compiled against boost "
+ boost_version());
#endif
}*/
void export_geometry()
{
using namespace boost::python;
@ -198,21 +286,27 @@ void export_geometry()
.def("type",&geometry_type::type)
.def("to_wkb",&to_wkb)
.def("to_wkt",&to_wkt)
.def("to_svg",&to_svg)
// TODO add other geometry_type methods
;
class_<path_type, boost::shared_ptr<path_type>, boost::noncopyable>("Path")
.def("__getitem__", getitem_impl,return_value_policy<reference_existing_object>())
.def("__len__", &path_type::size)
.def("envelope",envelope_impl)
.def("add_wkt",add_wkt_impl)
.def("add_wkb",add_wkb_impl)
.def("add_geojson",add_geojson_impl)
.def("to_wkt",&to_wkt2)
//.def("to_svg",&to_svg2)
.def("to_wkb",&to_wkb2)
.def("from_wkt",from_wkt_impl)
.def("from_wkb",from_wkb_impl)
.def("from_geojson",from_geojson_impl)
.def("to_geojson",to_geojson)
.staticmethod("from_wkt")
.staticmethod("from_wkb")
.staticmethod("from_geojson")
;
}

View file

@ -32,13 +32,25 @@
using namespace boost::python;
// help compiler see template definitions
static dict (*encode)( mapnik::grid const&, std::string, bool, unsigned int) = mapnik::grid_encode;
static dict (*encode)( mapnik::grid const&, std::string const& , bool, unsigned int) = mapnik::grid_encode;
bool painted(mapnik::grid const& grid)
{
return grid.painted();
}
mapnik::grid::value_type get_pixel(mapnik::grid const& grid, int x, int y)
{
if (x < static_cast<int>(grid.width()) && y < static_cast<int>(grid.height()))
{
mapnik::grid::data_type const & data = grid.data();
return data(x,y);
}
PyErr_SetString(PyExc_IndexError, "invalid x,y for grid dimensions");
boost::python::throw_error_already_set();
return 0;
}
void export_grid()
{
class_<mapnik::grid,boost::shared_ptr<mapnik::grid> >(
@ -52,6 +64,8 @@ void export_grid()
.def("width",&mapnik::grid::width)
.def("height",&mapnik::grid::height)
.def("view",&mapnik::grid::get_view)
.def("get_pixel",&get_pixel)
.def("clear",&mapnik::grid::clear)
.def("encode",encode,
( boost::python::arg("encoding")="utf", boost::python::arg("features")=true,boost::python::arg("resolution")=4 ),
"Encode the grid as as optimized json\n"

View file

@ -34,7 +34,7 @@
using namespace boost::python;
// help compiler see template definitions
static dict (*encode)( mapnik::grid_view const&, std::string, bool, unsigned int) = mapnik::grid_encode;
static dict (*encode)( mapnik::grid_view const&, std::string const& , bool, unsigned int) = mapnik::grid_encode;
void export_grid_view()
{

View file

@ -39,9 +39,6 @@ extern "C"
#include <mapnik/image_reader.hpp>
#include <mapnik/image_compositing.hpp>
// stl
#include <sstream>
// jpeg
#if defined(HAVE_JPEG)
#include <mapnik/jpeg_io.hpp>
@ -49,7 +46,7 @@ extern "C"
// cairo
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
#include <cairomm/surface.h>
#include <mapnik/cairo_context.hpp>
#include <pycairo.h>
#endif
@ -121,6 +118,18 @@ bool painted(mapnik::image_32 const& im)
return im.painted();
}
unsigned get_pixel(mapnik::image_32 const& im, int x, int y)
{
if (x < static_cast<int>(im.width()) && y < static_cast<int>(im.height()))
{
mapnik::image_data_32 const & data = im.data();
return data(x,y);
}
PyErr_SetString(PyExc_IndexError, "invalid x,y for image dimensions");
boost::python::throw_error_already_set();
return 0;
}
void set_pixel(mapnik::image_32 & im, unsigned x, unsigned y, mapnik::color const& c)
{
im.setPixel(x, y, c.rgba());
@ -149,17 +158,16 @@ void blend (image_32 & im, unsigned x, unsigned y, image_32 const& im2, float op
im.set_rectangle_alpha2(im2.data(),x,y,opacity);
}
void composite(image_32 & im, image_32 & im2, mapnik::composite_mode_e mode)
void composite(image_32 & dst, image_32 & src, mapnik::composite_mode_e mode, float opacity)
{
mapnik::composite(im.data(),im2.data(),mode);
mapnik::composite(dst.data(),src.data(),mode,opacity,0,0,false);
}
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
boost::shared_ptr<image_32> from_cairo(PycairoSurface* surface)
boost::shared_ptr<image_32> from_cairo(PycairoSurface* py_surface)
{
Cairo::RefPtr<Cairo::ImageSurface> s(new Cairo::ImageSurface(surface->surface));
boost::shared_ptr<image_32> image_ptr = boost::make_shared<image_32>(s);
mapnik::cairo_surface_ptr surface(py_surface->surface, mapnik::cairo_surface_closer());
boost::shared_ptr<image_32> image_ptr = boost::make_shared<image_32>(surface);
return image_ptr;
}
#endif
@ -167,6 +175,7 @@ boost::shared_ptr<image_32> from_cairo(PycairoSurface* surface)
void export_image()
{
using namespace boost::python;
// NOTE: must match list in include/mapnik/image_compositing.hpp
enum_<mapnik::composite_mode_e>("CompositeOp")
.value("clear", mapnik::clear)
.value("src", mapnik::src)
@ -195,7 +204,12 @@ void export_image()
.value("exclusion", mapnik::exclusion)
.value("contrast", mapnik::contrast)
.value("invert", mapnik::invert)
.value("invert_rgb", mapnik::invert_rgb)
.value("grain_merge", mapnik::grain_merge)
.value("grain_extract", mapnik::grain_extract)
.value("hue", mapnik::hue)
.value("saturation", mapnik::saturation)
.value("color", mapnik::_color)
.value("value", mapnik::_value)
;
class_<image_32,boost::shared_ptr<image_32> >("Image","This class represents a 32 bit RGBA image.",init<int,int>())
@ -210,8 +224,17 @@ void export_image()
.def("set_color_to_alpha",&image_32::set_color_to_alpha, "Set a given color to the alpha channel of the Image")
.def("set_alpha",&image_32::set_alpha, "Set the overall alpha channel of the Image")
.def("blend",&blend)
.def("composite",&composite)
.def("composite",&composite,
( arg("self"),
arg("image"),
arg("mode")=mapnik::src_over,
arg("opacity")=1.0f
))
.def("premultiply",&image_32::premultiply)
.def("demultiply",&image_32::demultiply)
.def("set_pixel",&set_pixel)
.def("get_pixel",&get_pixel)
.def("clear",&image_32::clear)
//TODO(haoyu) The method name 'tostring' might be confusing since they actually return bytes in Python 3
.def("tostring",&tostring1)

View file

@ -1,67 +0,0 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// boost
#include <boost/python.hpp>
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
// mapnik
#include <mapnik/metawriter_inmem.hpp>
using mapnik::metawriter_inmem;
using mapnik::metawriter_inmem_ptr;
namespace {
std::map<std::string, mapnik::value>::const_iterator
mapnik_value_map_begin(const std::map<std::string, mapnik::value> &m) {
return m.begin();
}
std::map<std::string, mapnik::value>::const_iterator
mapnik_value_map_end(const std::map<std::string, mapnik::value> &m) {
return m.end();
}
}
void export_inmem_metawriter() {
using namespace boost::python;
class_<std::map<std::string, mapnik::value> >
("MapnikProperties", "Retarded.", init<>())
.def("__iter__", range(&mapnik_value_map_begin, &mapnik_value_map_end))
;
class_<metawriter_inmem::meta_instance>
("MetaInstance", "Single rendered instance of meta-information.", no_init)
.def_readonly("box", &metawriter_inmem::meta_instance::box)
.def_readonly("properties", &metawriter_inmem::meta_instance::properties)
;
class_<metawriter_inmem, metawriter_inmem_ptr, boost::noncopyable>
("MetaWriterInMem",
"Collects meta-information about elements rendered.",
no_init)
.def("__iter__", range(&metawriter_inmem::inst_begin,
&metawriter_inmem::inst_end))
;
}

View file

@ -24,6 +24,8 @@
#include <boost/python/module.hpp>
#include <boost/python/def.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
#include <mapnik/label_collision_detector.hpp>
#include <mapnik/map.hpp>

View file

@ -77,7 +77,7 @@ struct layer_pickle_suite : boost::python::pickle_suite
l.set_queryable(extract<bool>(state[3]));
mapnik::parameters params = extract<parameters>(state[4]);
l.set_datasource(datasource_cache::instance()->create(params));
l.set_datasource(datasource_cache::instance().create(params));
boost::python::list s = extract<boost::python::list>(state[5]);
for (int i=0;i<len(s);++i)
@ -91,6 +91,43 @@ struct layer_pickle_suite : boost::python::pickle_suite
std::vector<std::string> & (mapnik::layer::*_styles_)() = &mapnik::layer::styles;
void set_maximum_extent(mapnik::layer & l, boost::optional<mapnik::box2d<double> > const& box)
{
if (box)
{
l.set_maximum_extent(*box);
}
else
{
l.reset_maximum_extent();
}
}
void set_buffer_size(mapnik::layer & l, boost::optional<int> const& buffer_size)
{
if (buffer_size)
{
l.set_buffer_size(*buffer_size);
}
else
{
l.reset_buffer_size();
}
}
PyObject * get_buffer_size(mapnik::layer & l)
{
boost::optional<int> buffer_size = l.buffer_size();
if (buffer_size)
{
return PyInt_FromLong(*buffer_size);
}
else
{
Py_RETURN_NONE;
}
}
void export_layer()
{
using namespace boost::python;
@ -149,7 +186,7 @@ void export_layer()
.add_property("active",
&layer::active,
&layer::set_active,
"Get/Set whether this layer is active and will be rendered.\n"
"Get/Set whether this layer is active and will be rendered (same as status property).\n"
"\n"
"Usage:\n"
">>> from mapnik import Layer\n"
@ -161,6 +198,21 @@ void export_layer()
"False\n"
)
.add_property("status",
&layer::active,
&layer::set_active,
"Get/Set whether this layer is active and will be rendered.\n"
"\n"
"Usage:\n"
">>> from mapnik import Layer\n"
">>> lyr = Layer('My Layer','+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs')\n"
">>> lyr.status\n"
"True # Active by default\n"
">>> lyr.status = False # set False to disable layer rendering\n"
">>> lyr.status\n"
"False\n"
)
.add_property("clear_label_cache",
&layer::clear_label_cache,
&layer::set_clear_label_cache,
@ -196,6 +248,28 @@ void export_layer()
"<mapnik.Datasource object at 0x65470>\n"
)
.add_property("buffer_size",
&get_buffer_size,
&set_buffer_size,
"Get/Set the size of buffer around layer in pixels.\n"
"\n"
"Usage:\n"
">>> print(l.buffer_size)\n"
"None # None by default\n"
">>> l.buffer_size = 2\n"
">>> l.buffer_size\n"
"2\n"
)
.add_property("maximum_extent",make_function
(&layer::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent,
"The maximum extent of the map.\n"
"\n"
"Usage:\n"
">>> m.maximum_extent = Box2d(-180,-90,180,90)\n"
)
.add_property("maxzoom",
&layer::max_zoom,
&layer::set_max_zoom,
@ -271,6 +345,14 @@ void export_layer()
">>> lyr.srs = '+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs +over'\n"
)
.add_property("group_by",
make_function(&layer::group_by,return_value_policy<copy_const_reference>()),
&layer::set_group_by,
"Get/Set the optional layer group name.\n"
"\n"
"More details at https://github.com/mapnik/mapnik/wiki/Grouped-rendering:\n"
)
.add_property("styles",
make_function(_styles_,return_value_policy<reference_existing_object>()),
"The styles list attached to this layer.\n"

View file

@ -37,7 +37,7 @@ using mapnik::parse_path;
namespace {
using namespace boost::python;
const std::string get_filename(line_pattern_symbolizer const& t)
std::string get_filename(line_pattern_symbolizer const& t)
{
return path_processor_type::to_string(*t.get_filename());
}
@ -49,17 +49,6 @@ void set_filename(line_pattern_symbolizer & t, std::string const& file_expr)
}
struct line_pattern_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const line_pattern_symbolizer& l)
{
std::string filename = path_processor_type::to_string(*l.get_filename());
// FIXME : Do we need "type" parameter at all ?
return boost::python::make_tuple(filename, guess_type(filename));
}
};
void export_line_pattern_symbolizer()
{
using namespace boost::python;
@ -67,12 +56,23 @@ void export_line_pattern_symbolizer()
class_<line_pattern_symbolizer>("LinePatternSymbolizer",
init<path_expression_ptr>
("<image file expression>"))
//.def_pickle(line_pattern_symbolizer_pickle_suite())
.add_property("transform",
mapnik::get_svg_transform<line_pattern_symbolizer>,
mapnik::set_svg_transform<line_pattern_symbolizer>)
.add_property("filename",
&get_filename,
&set_filename)
.add_property("comp_op",
&line_pattern_symbolizer::comp_op,
&line_pattern_symbolizer::set_comp_op,
"Set/get the comp-op")
.add_property("clip",
&line_pattern_symbolizer::clip,
&line_pattern_symbolizer::set_clip,
"Set/get the line pattern geometry's clipping status")
.add_property("smooth",
&line_pattern_symbolizer::smooth,
&line_pattern_symbolizer::set_smooth,
"smooth value (0..1.0)")
;
}

View file

@ -29,38 +29,41 @@ using mapnik::line_symbolizer;
using mapnik::stroke;
using mapnik::color;
struct line_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const line_symbolizer& l)
{
return boost::python::make_tuple(l.get_stroke());
}
};
void export_line_symbolizer()
{
using namespace boost::python;
enumeration_<line_rasterizer_e>("line_rasterizer")
.value("FULL",RASTERIZER_FULL)
.value("FAST",RASTERIZER_FAST)
;
class_<line_symbolizer>("LineSymbolizer",
init<>("Default LineSymbolizer - 1px solid black"))
.def(init<stroke const&>("TODO"))
.def(init<color const& ,float>())
.def_pickle(line_symbolizer_pickle_suite())
.add_property("rasterizer",
&line_symbolizer::get_rasterizer,
&line_symbolizer::set_rasterizer,
"Set/get the rasterization method of the line of the point")
.add_property("stroke",make_function
(&line_symbolizer::get_stroke,
return_value_policy<copy_const_reference>()),
return_value_policy<reference_existing_object>()),
&line_symbolizer::set_stroke)
.add_property("simplify_tolerance",
&line_symbolizer::simplify_tolerance,
&line_symbolizer::set_simplify_tolerance,
"simplification tolerance measure")
.add_property("offset",
&line_symbolizer::offset,
&line_symbolizer::set_offset,
"offset value")
.add_property("comp_op",
&line_symbolizer::comp_op,
&line_symbolizer::set_comp_op,
"Set/get the comp-op")
.add_property("clip",
&line_symbolizer::clip,
&line_symbolizer::set_clip,
"Set/get the line geometry's clipping status")
.add_property("smooth",
&line_symbolizer::smooth,
&line_symbolizer::set_smooth,

View file

@ -21,113 +21,53 @@
*****************************************************************************/
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
#include <mapnik/debug.hpp>
using mapnik::logger::severity;
using mapnik::logger::format;
using mapnik::logger::output;
void set_severity(const severity::type& s)
{
severity::set(s);
}
severity::type get_severity()
{
return severity::get();
}
void set_object_severity(const std::string& object_name, const severity::type& s)
{
severity::set_object(object_name, s);
}
severity::type get_object_severity(const std::string& object_name)
{
return severity::get_object(object_name);
}
#include <mapnik/utils.hpp>
#include "mapnik_enumeration.hpp"
void export_logger()
{
using namespace boost::python;
enum_<mapnik::logger::severity::type>("SeverityType")
.value("Info", severity::info)
.value("Debug", severity::debug)
.value("Warn", severity::warn)
.value("Error", severity::error)
.value("Fatal", severity::fatal)
.value("None", severity::none)
;
/*
using mapnik::logger;
using mapnik::singleton;
using mapnik::CreateStatic;
using namespace boost::python;
class_<singleton<severity,CreateStatic>,boost::noncopyable>("Singleton",no_init)
.def("instance",&singleton<severity,CreateStatic>::instance,
class_<singleton<logger,CreateStatic>,boost::noncopyable>("Singleton",no_init)
.def("instance",&singleton<logger,CreateStatic>::instance,
return_value_policy<reference_existing_object>())
.staticmethod("instance")
;
class_<severity,bases<singleton<severity,CreateStatic> >,
boost::noncopyable>("Severity",no_init)
.def("get",&severity::get)
.def("set",&severity::set)
.def("get_object",&severity::get_object)
.def("set_object",&severity::set_object)
.staticmethod("get")
.staticmethod("set")
.staticmethod("get_object")
.staticmethod("set_object")
enum_<mapnik::logger::severity_type>("severity_type")
.value("Debug", logger::debug)
.value("Warn", logger::warn)
.value("Error", logger::error)
.value("None", logger::none)
;
*/
def("set_severity", &set_severity,
"\n"
"Set global logger severity.\n"
"\n"
"Usage:\n"
">>> from mapnik import SeverityType, set_severity\n"
">>> set_severity(SeverityType.None)\n"
">>> set_severity(SeverityType.Info)\n"
">>> set_severity(SeverityType.Debug)\n"
">>> set_severity(SeverityType.Warn)\n"
">>> set_severity(SeverityType.Error)\n"
">>> set_severity(SeverityType.Fatal)\n"
);
def("get_severity", &get_severity,
"\n"
"Get global logger severity.\n"
"\n"
"Usage:\n"
">>> from mapnik import get_severity\n"
">>> get_severity()\n"
);
def("set_object_severity", &set_object_severity,
"\n"
"Set logger severity for a single object.\n"
"\n"
"Usage:\n"
">>> from mapnik import SeverityType, set_object_severity\n"
">>> set_object_severity('ogr', SeverityType.None)\n"
">>> set_object_severity('gdal', SeverityType.Info)\n"
">>> set_object_severity('cairo_renderer', SeverityType.Debug)\n"
">>> set_object_severity('agg_renderer', SeverityType.Warn)\n"
">>> set_object_severity('bindings', SeverityType.Error)\n"
);
def("get_object_severity", &get_object_severity,
"\n"
"Get logger severity for a single object.\n"
"\n"
"Usage:\n"
">>> from mapnik import get_object_severity"
">>> get_object_severity('ogr')\n"
);
class_<logger,bases<singleton<logger,CreateStatic> >,
boost::noncopyable>("logger",no_init)
.def("get_severity", &logger::get_severity)
.def("set_severity", &logger::set_severity)
.def("get_object_severity", &logger::get_object_severity)
.def("set_object_severity", &logger::set_object_severity)
.def("clear_object_severity", &logger::clear_object_severity)
.def("get_format", &logger::get_format)
.def("set_format", &logger::set_format)
.def("str", &logger::str)
.def("use_file", &logger::use_file)
.def("use_console", &logger::use_console)
.staticmethod("get_severity")
.staticmethod("set_severity")
.staticmethod("get_object_severity")
.staticmethod("set_object_severity")
.staticmethod("clear_object_severity")
.staticmethod("get_format")
.staticmethod("set_format")
.staticmethod("str")
.staticmethod("use_file")
.staticmethod("use_console")
;
}

View file

@ -26,11 +26,13 @@
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
// mapnik
#include <mapnik/rule.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/ctrans.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/metawriter_inmem.hpp>
#include <mapnik/util/deepcopy.hpp>
//#include <mapnik/util/deepcopy.hpp>
#include "mapnik_enumeration.hpp"
using mapnik::color;
@ -39,85 +41,9 @@ using mapnik::box2d;
using mapnik::layer;
using mapnik::Map;
struct map_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const Map& m)
{
return boost::python::make_tuple(m.width(),m.height(),m.srs());
}
static boost::python::tuple
getstate(const Map& m)
{
boost::python::list l;
for (unsigned i=0;i<m.layer_count();++i)
{
l.append(m.getLayer(i));
}
boost::python::list s;
Map::const_style_iterator it = m.styles().begin();
Map::const_style_iterator end = m.styles().end();
for (; it != end; ++it)
{
std::string const& name = it->first;
const mapnik::feature_type_style & style = it->second;
boost::python::tuple style_pair = boost::python::make_tuple(name,style);
s.append(style_pair);
}
return boost::python::make_tuple(m.get_current_extent(),m.background(),l,s,m.base_path());
}
static void
setstate (Map& m, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 5)
{
PyErr_SetObject(PyExc_ValueError,
("expected 5-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
box2d<double> ext = extract<box2d<double> >(state[0]);
m.zoom_to_box(ext);
if (state[1])
{
color bg = extract<color>(state[1]);
m.set_background(bg);
}
boost::python::list l=extract<boost::python::list>(state[2]);
for (int i=0;i<len(l);++i)
{
m.addLayer(extract<layer>(l[i]));
}
boost::python::list s=extract<boost::python::list>(state[3]);
for (int i=0;i<len(s);++i)
{
boost::python::tuple style_pair=extract<boost::python::tuple>(s[i]);
std::string name = extract<std::string>(style_pair[0]);
mapnik::feature_type_style style = extract<mapnik::feature_type_style>(style_pair[1]);
m.insert_style(name, style);
}
if (state[4])
{
std::string base_path = extract<std::string>(state[4]);
m.set_base_path(base_path);
}
}
};
std::vector<layer>& (Map::*layers_nonconst)() = &Map::layers;
std::vector<layer> const& (Map::*layers_const)() const = &Map::layers;
mapnik::parameters& (Map::*params_nonconst)() = &Map::get_extra_parameters;
boost::optional<mapnik::box2d<double> > const& (Map::*maximum_extent_const)() const = &Map::maximum_extent;
mapnik::feature_type_style find_style(mapnik::Map const& m, std::string const& name)
{
@ -141,26 +67,6 @@ mapnik::font_set find_fontset(mapnik::Map const& m, std::string const& name)
return *fontset;
}
bool has_metawriter(mapnik::Map const& m)
{
if (m.metawriters().size() >=1)
return true;
return false;
}
// returns empty shared_ptr when the metawriter isn't found, or is
// of the wrong type. empty pointers make it back to Python as a None.
mapnik::metawriter_inmem_ptr find_inmem_metawriter(const mapnik::Map & m, std::string const& name) {
mapnik::metawriter_ptr metawriter = m.find_metawriter(name);
mapnik::metawriter_inmem_ptr inmem;
if (metawriter) {
inmem = boost::dynamic_pointer_cast<mapnik::metawriter_inmem>(metawriter);
}
return inmem;
}
// TODO - we likely should allow indexing by negative number from python
// for now, protect against negative values and kindly throw
mapnik::featureset_ptr query_point(mapnik::Map const& m, int index, double x, double y)
@ -184,6 +90,7 @@ mapnik::featureset_ptr query_map_point(mapnik::Map const& m, int index, double x
}
// deepcopy
/*
mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo)
{
// FIXME: ignore memo for now
@ -191,8 +98,8 @@ mapnik::Map map_deepcopy(mapnik::Map & m, boost::python::dict memo)
mapnik::util::deepcopy(m, result);
return result;
}
*/
// TODO - find a simplier way to set optional to uninitialized
void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> > const& box)
{
if (box)
@ -201,7 +108,7 @@ void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> >
}
else
{
m.maximum_extent().reset();
m.reset_maximum_extent();
}
}
@ -241,9 +148,6 @@ void export_map()
"'+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'\n"
))
.def_pickle(map_pickle_suite()
)
.def("append_style",&Map::insert_style,
(arg("style_name"),arg("style_object")),
"Insert a Mapnik Style onto the map by appending it.\n"
@ -311,15 +215,6 @@ void export_map()
"<mapnik._mapnik.Style object at 0x654f0>\n"
)
.def("has_metawriter",
has_metawriter,
"Check if the Map has any active metawriters\n"
"\n"
"Usage:\n"
">>> m.has_metawriter()\n"
"False\n"
)
.def("pan",&Map::pan,
(arg("x"),arg("y")),
"Set the Map center at a given x,y location\n"
@ -458,39 +353,8 @@ void export_map()
">>> extext = Box2d(-180.0, -90.0, 180.0, 90.0)\n"
">>> m.zoom_to_box(extent)\n"
)
.def("get_metawriter_property", &Map::get_metawriter_property,
(arg("name")),
"Reads a metawriter property.\n"
"These properties are completely user-defined and can be used to"
"create filenames, etc.\n"
"\n"
"Usage:\n"
">>> map.set_metawriter_property(\"x\", \"10\")\n"
">>> map.get_metawriter_property(\"x\")\n"
"10\n"
)
.def("set_metawriter_property", &Map::set_metawriter_property,
(arg("name"),arg("value")),
"Sets a metawriter property.\n"
"These properties are completely user-defined and can be used to"
"create filenames, etc.\n"
"\n"
"Usage:\n"
">>> map.set_metawriter_property(\"x\", str(x))\n"
">>> map.set_metawriter_property(\"y\", str(y))\n"
">>> map.set_metawriter_property(\"z\", str(z))\n"
"\n"
"Use a path like \"[z]/[x]/[y].json\" to create filenames.\n"
)
.def("find_inmem_metawriter", find_inmem_metawriter,
(arg("name")),
"Gets an inmem metawriter, or None if no such metawriter "
"exists.\n"
"Use this after the map has been rendered to retrieve information "
"about the hit areas rendered on the map.\n"
)
.def("__deepcopy__",&map_deepcopy)
//.def("__deepcopy__",&map_deepcopy)
.add_property("parameters",make_function(params_nonconst,return_value_policy<reference_existing_object>()),"TODO")
.add_property("aspect_fix_mode",
@ -507,12 +371,30 @@ void export_map()
.add_property("background",make_function
(&Map::background,return_value_policy<copy_const_reference>()),
&Map::set_background,
"The background color of the map.\n"
"The background color of the map (same as background_color property).\n"
"\n"
"Usage:\n"
">>> m.background = Color('steelblue')\n"
)
.add_property("background_color",make_function
(&Map::background,return_value_policy<copy_const_reference>()),
&Map::set_background,
"The background color of the map.\n"
"\n"
"Usage:\n"
">>> m.background_color = Color('steelblue')\n"
)
.add_property("background_image",make_function
(&Map::background_image,return_value_policy<copy_const_reference>()),
&Map::set_background_image,
"The optional background image of the map.\n"
"\n"
"Usage:\n"
">>> m.background_image = '/path/to/image.png'\n"
)
.add_property("base",
make_function(&Map::base_path,return_value_policy<copy_const_reference>()),
&Map::set_base_path,
@ -562,7 +444,7 @@ void export_map()
)
.add_property("maximum_extent",make_function
(maximum_extent_const,return_value_policy<copy_const_reference>()),
(&Map::maximum_extent,return_value_policy<copy_const_reference>()),
&set_maximum_extent,
"The maximum extent of the map.\n"
"\n"

View file

@ -23,10 +23,14 @@
#include <boost/python.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/expression_node.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/markers_symbolizer.hpp>
#include <mapnik/parse_path.hpp>
#include "mapnik_svg.hpp"
#include "mapnik_enumeration.hpp"
#include <mapnik/marker_cache.hpp> // for known_svg_prefix_
using mapnik::markers_symbolizer;
using mapnik::symbolizer_with_image;
@ -46,57 +50,61 @@ void set_filename(mapnik::markers_symbolizer & symbolizer, std::string const& fi
symbolizer.set_filename(parse_path(file_expr));
}
void set_marker_type(mapnik::markers_symbolizer & symbolizer, std::string const& marker_type)
{
std::string filename;
if (marker_type == "ellipse")
{
filename = mapnik::marker_cache::instance().known_svg_prefix_ + "ellipse";
}
else if (marker_type == "arrow")
{
filename = mapnik::marker_cache::instance().known_svg_prefix_ + "arrow";
}
else
{
throw mapnik::value_error("Unknown marker-type: '" + marker_type + "'");
}
symbolizer.set_filename(parse_path(filename));
}
struct markers_symbolizer_pickle_suite : boost::python::pickle_suite
}
// https://github.com/mapnik/mapnik/issues/1367
PyObject* get_fill_opacity_impl(markers_symbolizer & sym)
{
static boost::python::tuple
getinitargs(markers_symbolizer const& p)
{
std::string filename = path_processor_type::to_string(*p.get_filename());
return boost::python::make_tuple(filename,mapnik::guess_type(filename));
}
static boost::python::tuple
getstate(markers_symbolizer const& p)
{
return boost::python::make_tuple(p.get_allow_overlap(),
p.get_ignore_placement());//,p.get_opacity());
}
static void
setstate (markers_symbolizer& p, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 2)
{
PyErr_SetObject(PyExc_ValueError,
("expected 2-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
p.set_allow_overlap(extract<bool>(state[0]));
p.set_ignore_placement(extract<bool>(state[1]));
//p.set_opacity(extract<float>(state[2]));
}
};
boost::optional<float> fill_opacity = sym.get_fill_opacity();
if (fill_opacity)
return ::PyFloat_FromDouble(*fill_opacity);
Py_RETURN_NONE;
}
void export_markers_symbolizer()
{
using namespace boost::python;
mapnik::enumeration_<mapnik::marker_placement_e>("marker_placement")
.value("POINT_PLACEMENT",mapnik::MARKER_POINT_PLACEMENT)
.value("INTERIOR_PLACEMENT",mapnik::MARKER_INTERIOR_PLACEMENT)
.value("LINE_PLACEMENT",mapnik::MARKER_LINE_PLACEMENT)
;
mapnik::enumeration_<mapnik::marker_multi_policy_e>("marker_multi_policy")
.value("EACH",mapnik::MARKER_EACH_MULTI)
.value("WHOLE",mapnik::MARKER_WHOLE_MULTI)
.value("LARGEST",mapnik::MARKER_LARGEST_MULTI)
;
class_<markers_symbolizer>("MarkersSymbolizer",
init<>("Default Markers Symbolizer - blue arrow"))
init<>("Default Markers Symbolizer - circle"))
.def (init<mapnik::path_expression_ptr>("<path expression ptr>"))
//.def_pickle(markers_symbolizer_pickle_suite())
.add_property("filename",
&get_filename,
&set_filename)
.add_property("marker_type",
&get_filename,
&set_marker_type)
.add_property("allow_overlap",
&markers_symbolizer::get_allow_overlap,
&markers_symbolizer::set_allow_overlap)
@ -109,7 +117,11 @@ void export_markers_symbolizer()
.add_property("opacity",
&markers_symbolizer::get_opacity,
&markers_symbolizer::set_opacity,
"Set/get the text opacity")
"Set/get the overall opacity")
.add_property("fill_opacity",
&get_fill_opacity_impl,
&markers_symbolizer::set_fill_opacity,
"Set/get the fill opacity")
.add_property("ignore_placement",
&markers_symbolizer::get_ignore_placement,
&markers_symbolizer::set_ignore_placement)
@ -117,12 +129,42 @@ void export_markers_symbolizer()
&mapnik::get_svg_transform<markers_symbolizer>,
&mapnik::set_svg_transform<markers_symbolizer>)
.add_property("width",
&markers_symbolizer::get_width,
make_function(&markers_symbolizer::get_width,
return_value_policy<copy_const_reference>()),
&markers_symbolizer::set_width,
"Set/get the marker width")
.add_property("height",
&markers_symbolizer::get_height,
make_function(&markers_symbolizer::get_height,
return_value_policy<copy_const_reference>()),
&markers_symbolizer::set_height,
"Set/get the marker height")
.add_property("fill",
&markers_symbolizer::get_fill,
&markers_symbolizer::set_fill,
"Set/get the marker fill color")
.add_property("stroke",
&markers_symbolizer::get_stroke,
&markers_symbolizer::set_stroke,
"Set/get the marker stroke (outline)")
.add_property("placement",
&markers_symbolizer::get_marker_placement,
&markers_symbolizer::set_marker_placement,
"Set/get the marker placement")
.add_property("multi_policy",
&markers_symbolizer::get_marker_multi_policy,
&markers_symbolizer::set_marker_multi_policy,
"Set/get the marker multi geometry rendering policy")
.add_property("comp_op",
&markers_symbolizer::comp_op,
&markers_symbolizer::set_comp_op,
"Set/get the marker comp-op")
.add_property("clip",
&markers_symbolizer::clip,
&markers_symbolizer::set_clip,
"Set/get the marker geometry's clipping status")
.add_property("smooth",
&markers_symbolizer::smooth,
&markers_symbolizer::set_smooth,
"Set/get the marker geometry's smooth value")
;
}

View file

@ -23,11 +23,12 @@
// boost
#include <boost/python.hpp>
#include <boost/make_shared.hpp>
#include <boost/noncopyable.hpp>
//mapnik
#include <mapnik/palette.hpp>
static boost::shared_ptr<mapnik::rgba_palette> make_palette( const std::string& palette, const std::string& format )
static boost::shared_ptr<mapnik::rgba_palette> make_palette( std::string const& palette, std::string const& format )
{
mapnik::rgba_palette::palette_type type = mapnik::rgba_palette::PALETTE_RGBA;
if (format == "rgb")
@ -51,5 +52,8 @@ void export_palette ()
// "Creates a new color palette from a file\n"
// )
.def( "__init__", boost::python::make_constructor(make_palette))
.def("to_string", &mapnik::rgba_palette::to_string,
"Returns the palette as a string.\n"
)
;
}

View file

@ -29,6 +29,8 @@
#include <mapnik/params.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/value.hpp>
// stl
#include <iterator>
using mapnik::parameter;
using mapnik::parameters;
@ -78,7 +80,7 @@ struct parameters_pickle_suite : boost::python::pickle_suite
std::string key = extract<std::string>(keys[i]);
object obj = d[key];
extract<std::string> ex0(obj);
extract<int> ex1(obj);
extract<mapnik::value_integer> ex1(obj);
extract<double> ex2(obj);
extract<UnicodeString> ex3(obj);
@ -142,17 +144,10 @@ mapnik::parameter get_params_by_index(mapnik::parameters const& p, int index)
}
parameters::const_iterator itr = p.begin();
parameters::const_iterator end = p.end();
int idx = 0;
while (itr != end)
std::advance(itr, index);
if (itr != p.end())
{
if (idx == index)
{
return *itr;
}
++idx;
++itr;
return *itr;
}
PyErr_SetString(PyExc_IndexError, "Index is out of range");
throw boost::python::error_already_set();
@ -180,22 +175,24 @@ mapnik::value_holder get_param(mapnik::parameter const& p, int index)
}
}
boost::shared_ptr<mapnik::parameter> create_parameter_from_string(std::string const& key, std::string const& value)
boost::shared_ptr<mapnik::parameter> create_parameter(UnicodeString const& key, mapnik::value_holder const& value)
{
return boost::make_shared<mapnik::parameter>(key,mapnik::value_holder(value));
std::string key_utf8;
mapnik::to_utf8(key, key_utf8);
return boost::make_shared<mapnik::parameter>(key_utf8,value);
}
boost::shared_ptr<mapnik::parameter> create_parameter_from_int(std::string const& key, int value)
{
return boost::make_shared<mapnik::parameter>(key,mapnik::value_holder(value));
}
// needed for Python_Unicode to std::string (utf8) conversion
boost::shared_ptr<mapnik::parameter> create_parameter_from_float(std::string const& key, double value)
boost::shared_ptr<mapnik::parameter> create_parameter_from_string(UnicodeString const& key, UnicodeString const& ustr)
{
return boost::make_shared<mapnik::parameter>(key,mapnik::value_holder(value));
std::string key_utf8;
std::string ustr_utf8;
mapnik::to_utf8(key, key_utf8);
mapnik::to_utf8(ustr,ustr_utf8);
return boost::make_shared<mapnik::parameter>(key_utf8, ustr_utf8);
}
boost::python::dict to_json(const parameters& p)
{
using namespace boost::python;
@ -212,16 +209,19 @@ boost::python::dict to_json(const parameters& p)
void export_parameters()
{
using namespace boost::python;
implicitly_convertible<std::string,mapnik::value_holder>();
implicitly_convertible<mapnik::value_null,mapnik::value_holder>();
implicitly_convertible<mapnik::value_integer,mapnik::value_holder>();
implicitly_convertible<mapnik::value_double,mapnik::value_holder>();
class_<parameter,boost::shared_ptr<parameter> >("Parameter",no_init)
.def("__init__", make_constructor(create_parameter),
"Create a mapnik.Parameter from a pair of values, the first being a string\n"
"and the second being either a string, and integer, or a float")
.def("__init__", make_constructor(create_parameter_from_string),
"Create a mapnik.Parameter from a pair of values, the first being a string\n"
"and the second being either a string, and integer, or a float")
.def("__init__", make_constructor(create_parameter_from_int),
"Create a mapnik.Parameter from a pair of values, the first being a string\n"
"and the second being either a string, and integer, or a float")
.def("__init__", make_constructor(create_parameter_from_float),
"Create a mapnik.Parameter from a pair of values, the first being a string\n"
"and the second being either a string, and integer, or a float")
.def_pickle(parameter_pickle_suite())
.def("__getitem__",get_param)
;

View file

@ -38,7 +38,7 @@ using mapnik::parse_path;
namespace {
using namespace boost::python;
const std::string get_filename(point_symbolizer const& t)
std::string get_filename(point_symbolizer const& t)
{
return path_processor_type::to_string(*t.get_filename());
}
@ -50,47 +50,6 @@ void set_filename(point_symbolizer & t, std::string const& file_expr)
}
struct point_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const point_symbolizer& p)
{
std::string filename = path_processor_type::to_string(*p.get_filename());
return boost::python::make_tuple(filename,mapnik::guess_type(filename));
}
static boost::python::tuple
getstate(const point_symbolizer& p)
{
return boost::python::make_tuple(p.get_allow_overlap(),
p.get_opacity(),
p.get_ignore_placement(),
p.get_point_placement());
}
static void
setstate (point_symbolizer& p, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 4)
{
PyErr_SetObject(PyExc_ValueError,
("expected 4-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
p.set_allow_overlap(extract<bool>(state[0]));
p.set_opacity(extract<float>(state[1]));
p.set_ignore_placement(extract<bool>(state[2]));
p.set_point_placement(extract<point_placement_e>(state[3]));
}
};
void export_point_symbolizer()
{
using namespace boost::python;
@ -103,7 +62,6 @@ void export_point_symbolizer()
class_<point_symbolizer>("PointSymbolizer",
init<>("Default Point Symbolizer - 4x4 black square"))
.def (init<mapnik::path_expression_ptr>("<path expression ptr>"))
.def_pickle(point_symbolizer_pickle_suite())
.add_property("filename",
&get_filename,
&set_filename)
@ -123,5 +81,9 @@ void export_point_symbolizer()
.add_property("transform",
mapnik::get_svg_transform<point_symbolizer>,
mapnik::set_svg_transform<point_symbolizer>)
.add_property("comp_op",
&point_symbolizer::comp_op,
&point_symbolizer::set_comp_op,
"Set/get the comp-op")
;
}

View file

@ -37,7 +37,7 @@ using mapnik::guess_type;
namespace {
using namespace boost::python;
const std::string get_filename(polygon_pattern_symbolizer const& t)
std::string get_filename(polygon_pattern_symbolizer const& t)
{
return path_processor_type::to_string(*t.get_filename());
}
@ -49,41 +49,6 @@ void set_filename(polygon_pattern_symbolizer & t, std::string const& file_expr)
}
struct polygon_pattern_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const polygon_pattern_symbolizer& p)
{
std::string filename = path_processor_type::to_string(*p.get_filename());
return boost::python::make_tuple(filename,guess_type(filename));
}
static boost::python::tuple
getstate(const polygon_pattern_symbolizer& p)
{
return boost::python::make_tuple(p.get_alignment(),p.get_gamma(),p.get_gamma_method());
}
static void
setstate (polygon_pattern_symbolizer& p, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 3)
{
PyErr_SetObject(PyExc_ValueError,
("expected 3-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
p.set_alignment(extract<pattern_alignment_e>(state[0]));
p.set_gamma(extract<float>(state[1]));
p.set_gamma_method(extract<gamma_method_e>(state[2]));
}
};
void export_polygon_pattern_symbolizer()
{
using namespace boost::python;
@ -95,7 +60,6 @@ void export_polygon_pattern_symbolizer()
class_<polygon_pattern_symbolizer>("PolygonPatternSymbolizer",
init<path_expression_ptr>("<path_expression_ptr>"))
.def_pickle(polygon_pattern_symbolizer_pickle_suite())
.add_property("alignment",
&polygon_pattern_symbolizer::get_alignment,
&polygon_pattern_symbolizer::set_alignment,
@ -106,6 +70,9 @@ void export_polygon_pattern_symbolizer()
.add_property("filename",
&get_filename,
&set_filename)
.add_property("opacity",
&polygon_pattern_symbolizer::get_opacity,
&polygon_pattern_symbolizer::set_opacity)
.add_property("gamma",
&polygon_pattern_symbolizer::get_gamma,
&polygon_pattern_symbolizer::set_gamma)
@ -113,5 +80,17 @@ void export_polygon_pattern_symbolizer()
&polygon_pattern_symbolizer::get_gamma_method,
&polygon_pattern_symbolizer::set_gamma_method,
"Set/get the gamma correction method of the polygon")
.add_property("comp_op",
&polygon_pattern_symbolizer::comp_op,
&polygon_pattern_symbolizer::set_comp_op,
"Set/get the pattern comp-op")
.add_property("clip",
&polygon_pattern_symbolizer::clip,
&polygon_pattern_symbolizer::set_clip,
"Set/get the pattern geometry's clipping status")
.add_property("smooth",
&polygon_pattern_symbolizer::smooth,
&polygon_pattern_symbolizer::set_smooth,
"Set/get the pattern geometry's smooth value")
;
}

View file

@ -21,47 +21,12 @@
*****************************************************************************/
#include <boost/python.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/polygon_symbolizer.hpp>
using namespace mapnik;
using mapnik::polygon_symbolizer;
using mapnik::color;
struct polygon_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const polygon_symbolizer& p)
{
return boost::python::make_tuple(p.get_fill());
}
static boost::python::tuple
getstate(const polygon_symbolizer& p)
{
return boost::python::make_tuple(p.get_opacity(),p.get_gamma(),p.get_gamma_method());
}
static void
setstate (polygon_symbolizer& p, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 3)
{
PyErr_SetObject(PyExc_ValueError,
("expected 3-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
p.set_opacity(extract<float>(state[0]));
p.set_gamma(extract<float>(state[1]));
p.set_gamma_method(extract<gamma_method_e>(state[2]));
}
};
void export_polygon_symbolizer()
{
using namespace boost::python;
@ -69,7 +34,6 @@ void export_polygon_symbolizer()
class_<polygon_symbolizer>("PolygonSymbolizer",
init<>("Default PolygonSymbolizer - solid fill grey"))
.def(init<color const&>("TODO"))
.def_pickle(polygon_symbolizer_pickle_suite())
.add_property("fill",make_function
(&polygon_symbolizer::get_fill,
return_value_policy<copy_const_reference>()),
@ -84,10 +48,22 @@ void export_polygon_symbolizer()
&polygon_symbolizer::get_gamma_method,
&polygon_symbolizer::set_gamma_method,
"gamma correction method")
.add_property("comp_op",
&polygon_symbolizer::comp_op,
&polygon_symbolizer::set_comp_op,
"Set/get the polygon comp-op")
.add_property("clip",
&polygon_symbolizer::clip,
&polygon_symbolizer::set_clip,
"Set/get the polygon geometry's clipping status")
.add_property("smooth",
&polygon_symbolizer::smooth,
&polygon_symbolizer::set_smooth,
"smooth value (0..1.0)")
"Set/get the polygon geometry's smooth value")
.add_property("simplify_tolerance",
&polygon_symbolizer::simplify_tolerance,
&polygon_symbolizer::set_simplify_tolerance,
"simplfication tolerance measure")
;
}

View file

@ -22,6 +22,7 @@
// mapnik
#include <mapnik/proj_transform.hpp>
#include <boost/noncopyable.hpp>
// boost
#include <boost/python.hpp>

View file

@ -37,6 +37,7 @@ void export_palette();
void export_image();
void export_image_view();
void export_gamma_method();
void export_scaling_method();
void export_grid();
void export_grid_view();
void export_map();
@ -56,6 +57,7 @@ void export_point_symbolizer();
void export_line_symbolizer();
void export_line_pattern_symbolizer();
void export_polygon_symbolizer();
void export_building_symbolizer();
void export_polygon_pattern_symbolizer();
void export_raster_symbolizer();
void export_text_placement();
@ -65,18 +67,21 @@ void export_projection();
void export_proj_transform();
void export_view_transform();
void export_raster_colorizer();
void export_inmem_metawriter();
void export_label_collision_detector();
void export_logger();
#include <mapnik/version.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/agg_renderer.hpp>
#ifdef HAVE_CAIRO
#include <mapnik/cairo_renderer.hpp>
#endif
#include <mapnik/graphics.hpp>
#include <mapnik/stroke.hpp>
#include <mapnik/font_set.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/config_error.hpp>
@ -88,6 +93,15 @@ void export_logger();
#include "mapnik_value_converter.hpp"
#include "mapnik_threads.hpp"
#include "python_optional.hpp"
#include <mapnik/marker_cache.hpp>
#include <mapnik/mapped_memory_cache.hpp>
void clear_cache()
{
mapnik::marker_cache::instance().clear();
mapnik::mapped_memory_cache::instance().clear();
}
#include <mapnik/stats_processor.hpp>
@ -159,40 +173,92 @@ void render_layer2(const mapnik::Map& map,
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
void render3(const mapnik::Map& map,
PycairoSurface* surface,
PycairoSurface* py_surface,
double scale_factor = 1.0,
unsigned offset_x = 0,
unsigned offset_y = 0)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
mapnik::cairo_renderer<Cairo::Surface> ren(map,s,offset_x, offset_y);
mapnik::cairo_surface_ptr surface(cairo_surface_reference(py_surface->surface), mapnik::cairo_surface_closer());
mapnik::cairo_renderer<mapnik::cairo_surface_ptr> ren(map,surface,scale_factor,offset_x,offset_y);
ren.apply();
}
void render4(const mapnik::Map& map, PycairoSurface* surface)
void render4(const mapnik::Map& map, PycairoSurface* py_surface)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Surface> s(new Cairo::Surface(surface->surface));
mapnik::cairo_renderer<Cairo::Surface> ren(map,s);
mapnik::cairo_surface_ptr surface(cairo_surface_reference(py_surface->surface), mapnik::cairo_surface_closer());
mapnik::cairo_renderer<mapnik::cairo_surface_ptr> ren(map,surface);
ren.apply();
}
void render5(const mapnik::Map& map,
PycairoContext* context,
PycairoContext* py_context,
double scale_factor = 1.0,
unsigned offset_x = 0,
unsigned offset_y = 0)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
mapnik::cairo_renderer<Cairo::Context> ren(map,c,offset_x, offset_y);
mapnik::cairo_ptr context(py_context->ctx, mapnik::cairo_closer());
mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map,context,scale_factor,offset_x, offset_y);
ren.apply();
}
void render6(const mapnik::Map& map, PycairoContext* context)
void render6(const mapnik::Map& map, PycairoContext* py_context)
{
python_unblock_auto_block b;
Cairo::RefPtr<Cairo::Context> c(new Cairo::Context(context->ctx));
mapnik::cairo_renderer<Cairo::Context> ren(map,c);
mapnik::cairo_ptr context(py_context->ctx, mapnik::cairo_closer());
mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map,context);
ren.apply();
}
void render_with_detector2(
const mapnik::Map& map,
PycairoContext* py_context,
boost::shared_ptr<mapnik::label_collision_detector4> detector)
{
python_unblock_auto_block b;
mapnik::cairo_ptr context(py_context->ctx, mapnik::cairo_closer());
mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map,context,detector);
ren.apply();
}
void render_with_detector3(
const mapnik::Map& map,
PycairoContext* py_context,
boost::shared_ptr<mapnik::label_collision_detector4> detector,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
python_unblock_auto_block b;
mapnik::cairo_ptr context(py_context->ctx, mapnik::cairo_closer());
mapnik::cairo_renderer<mapnik::cairo_ptr> ren(map,context,detector,scale_factor,offset_x,offset_y);
ren.apply();
}
void render_with_detector4(
const mapnik::Map& map,
PycairoSurface* py_surface,
boost::shared_ptr<mapnik::label_collision_detector4> detector)
{
python_unblock_auto_block b;
mapnik::cairo_surface_ptr surface(cairo_surface_reference(py_surface->surface), mapnik::cairo_surface_closer());
mapnik::cairo_renderer<mapnik::cairo_surface_ptr> ren(map, surface, detector);
ren.apply();
}
void render_with_detector5(
const mapnik::Map& map,
PycairoSurface* py_surface,
boost::shared_ptr<mapnik::label_collision_detector4> detector,
double scale_factor = 1.0,
unsigned offset_x = 0u,
unsigned offset_y = 0u)
{
python_unblock_auto_block b;
mapnik::cairo_surface_ptr surface(cairo_surface_reference(py_surface->surface), mapnik::cairo_surface_closer());
mapnik::cairo_renderer<mapnik::cairo_surface_ptr> ren(map, surface, detector, scale_factor, offset_x, offset_y);
ren.apply();
}
@ -202,8 +268,8 @@ void render6(const mapnik::Map& map, PycairoContext* context)
void render_tile_to_file(const mapnik::Map& map,
unsigned offset_x, unsigned offset_y,
unsigned width, unsigned height,
const std::string& file,
const std::string& format)
std::string const& file,
std::string const& format)
{
mapnik::image_32 image(width,height);
render(map,image,1.0,offset_x, offset_y);
@ -211,13 +277,13 @@ void render_tile_to_file(const mapnik::Map& map,
}
void render_to_file1(const mapnik::Map& map,
const std::string& filename,
const std::string& format)
std::string const& filename,
std::string const& format)
{
if (format == "pdf" || format == "svg" || format =="ps" || format == "ARGB32" || format == "RGB24")
{
#if defined(HAVE_CAIRO)
mapnik::save_to_cairo_file(map,filename,format);
mapnik::save_to_cairo_file(map,filename,format,1.0);
#else
throw mapnik::ImageWriterException("Cairo backend not available, cannot write to format: " + format);
#endif
@ -230,13 +296,13 @@ void render_to_file1(const mapnik::Map& map,
}
}
void render_to_file2(const mapnik::Map& map,const std::string& filename)
void render_to_file2(const mapnik::Map& map,std::string const& filename)
{
std::string format = mapnik::guess_type(filename);
if (format == "pdf" || format == "svg" || format =="ps")
{
#if defined(HAVE_CAIRO)
mapnik::save_to_cairo_file(map,filename,format);
mapnik::save_to_cairo_file(map,filename,format,1.0);
#else
throw mapnik::ImageWriterException("Cairo backend not available, cannot write to format: " + format);
#endif
@ -250,15 +316,15 @@ void render_to_file2(const mapnik::Map& map,const std::string& filename)
}
void render_to_file3(const mapnik::Map& map,
const std::string& filename,
const std::string& format,
std::string const& filename,
std::string const& format,
double scale_factor = 1.0
)
{
if (format == "pdf" || format == "svg" || format =="ps" || format == "ARGB32" || format == "RGB24")
{
#if defined(HAVE_CAIRO)
mapnik::save_to_cairo_file(map,filename,format);
mapnik::save_to_cairo_file(map,filename,format,scale_factor);
#else
throw mapnik::ImageWriterException("Cairo backend not available, cannot write to format: " + format);
#endif
@ -277,14 +343,21 @@ double scale_denominator(mapnik::Map const &map, bool geographic)
}
// http://docs.python.org/c-api/exceptions.html#standard-exceptions
void config_error_translator(mapnik::config_error const & ex) {
void config_error_translator(mapnik::config_error const & ex)
{
PyErr_SetString(PyExc_RuntimeError, ex.what());
}
void value_error_translator(mapnik::value_error const & ex) {
void value_error_translator(mapnik::value_error const & ex)
{
PyErr_SetString(PyExc_ValueError, ex.what());
}
void runtime_error_translator(std::runtime_error const & ex)
{
PyErr_SetString(PyExc_RuntimeError, ex.what());
}
unsigned mapnik_version()
{
return MAPNIK_VERSION;
@ -319,7 +392,11 @@ bool has_cairo()
bool has_pycairo()
{
#if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO)
#if PY_MAJOR_VERSION >= 3
Pycairo_CAPI = (Pycairo_CAPI_t*) PyCapsule_Import(const_cast<char *>("cairo.CAPI"), 0);
#else
Pycairo_CAPI = (Pycairo_CAPI_t*) PyCObject_Import(const_cast<char *>("cairo"), const_cast<char *>("CAPI"));
#endif
if (Pycairo_CAPI == NULL){
/*
Case where pycairo support has been compiled into
@ -356,6 +433,7 @@ BOOST_PYTHON_MODULE(_mapnik)
register_exception_translator<mapnik::config_error>(&config_error_translator);
register_exception_translator<mapnik::value_error>(&value_error_translator);
register_exception_translator<std::runtime_error>(&runtime_error_translator);
register_cairo();
export_query();
export_geometry();
@ -370,6 +448,7 @@ BOOST_PYTHON_MODULE(_mapnik)
export_image();
export_image_view();
export_gamma_method();
export_scaling_method();
export_grid();
export_grid_view();
export_expression();
@ -384,6 +463,7 @@ BOOST_PYTHON_MODULE(_mapnik)
export_line_symbolizer();
export_line_pattern_symbolizer();
export_polygon_symbolizer();
export_building_symbolizer();
export_polygon_pattern_symbolizer();
export_raster_symbolizer();
export_text_placement();
@ -395,10 +475,18 @@ BOOST_PYTHON_MODULE(_mapnik)
export_coord();
export_map();
export_raster_colorizer();
export_inmem_metawriter();
export_label_collision_detector();
export_logger();
def("clear_cache", &clear_cache,
"\n"
"Clear all global caches of markers and mapped memory regions.\n"
"\n"
"Usage:\n"
">>> from mapnik import clear_cache\n"
">>> clear_cache()\n"
);
def("render_grid",&render_grid,
( arg("map"),
arg("layer"),
@ -551,6 +639,65 @@ BOOST_PYTHON_MODULE(_mapnik)
">>> render(m,context)\n"
"\n"
);
def("render_with_detector", &render_with_detector2,
"\n"
"Render Map to Cairo Context using a pre-constructed detector.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> ctx = Context(surface)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, ctx, detector)\n"
);
def("render_with_detector", &render_with_detector3,
"\n"
"Render Map to Cairo Context using a pre-constructed detector, scale and offsets.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> ctx = Context(surface)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, ctx, detector, 1, 1, 1)\n"
);
def("render_with_detector", &render_with_detector4,
"\n"
"Render Map to Cairo Surface using a pre-constructed detector.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, surface, detector)\n"
);
def("render_with_detector", &render_with_detector5,
"\n"
"Render Map to Cairo Surface using a pre-constructed detector, scale and offsets.\n"
"\n"
"Usage:\n"
">>> from mapnik import Map, LabelCollisionDetector, render_with_detector, load_map\n"
">>> from cairo import SVGSurface, Context\n"
">>> surface = SVGSurface('image.svg', m.width, m.height)\n"
">>> m = Map(256,256)\n"
">>> load_map(m,'mapfile.xml')\n"
">>> detector = LabelCollisionDetector(m)\n"
">>> render_with_detector(m, surface, detector, 1, 1, 1)\n"
);
#endif
def("scale_denominator", &scale_denominator,
@ -596,14 +743,18 @@ BOOST_PYTHON_MODULE(_mapnik)
def("has_pycairo", &has_pycairo, "Get pycairo module status");
def("render_stats", &render_stats);
python_optional<mapnik::stroke>();
python_optional<mapnik::font_set>();
python_optional<mapnik::color>();
python_optional<mapnik::box2d<double> >();
python_optional<mapnik::composite_mode_e>();
python_optional<mapnik::datasource::geometry_t>();
python_optional<std::string>();
python_optional<unsigned>();
python_optional<double>();
python_optional<float>();
python_optional<bool>();
python_optional<int>();
python_optional<mapnik::text_transform_e>();
register_ptr_to_python<mapnik::expression_ptr>();
register_ptr_to_python<mapnik::path_expression_ptr>();

View file

@ -30,12 +30,19 @@
using mapnik::query;
using mapnik::box2d;
struct query_pickle_suite : boost::python::pickle_suite
namespace python = boost::python;
struct resolution_to_tuple
{
static boost::python::tuple
getinitargs(query const& q)
static PyObject* convert(query::resolution_type const& x)
{
return boost::python::make_tuple(q.get_bbox(),q.resolution());
python::object tuple(python::make_tuple(x.get<0>(), x.get<1>()));
return python::incref(tuple.ptr());
}
static PyTypeObject const* get_pytype()
{
return &PyTuple_Type;
}
};
@ -43,10 +50,11 @@ void export_query()
{
using namespace boost::python;
to_python_converter<query::resolution_type, resolution_to_tuple> ();
class_<query>("Query", "a spatial query data object",
init<box2d<double>,query::resolution_type const&,double>() )
.def(init<box2d<double> >())
.def_pickle(query_pickle_suite())
.add_property("resolution",make_function(&query::resolution,
return_value_policy<copy_const_reference>()))
.add_property("bbox", make_function(&query::get_bbox,

View file

@ -25,46 +25,22 @@
// mapnik
#include <mapnik/raster_symbolizer.hpp>
#include <mapnik/raster_colorizer.hpp>
#include <mapnik/image_scaling.hpp>
using mapnik::raster_symbolizer;
namespace {
struct raster_symbolizer_pickle_suite : boost::python::pickle_suite
// https://github.com/mapnik/mapnik/issues/1367
PyObject* get_premultiplied_impl(mapnik::raster_symbolizer & sym)
{
/*
static boost::python::tuple
getinitargs(const raster_symbolizer& r)
{
return boost::python::make_tuple();
}
*/
boost::optional<bool> premultiplied = sym.premultiplied();
if (premultiplied)
return ::PyBool_FromLong(*premultiplied);
Py_RETURN_NONE;
}
static boost::python::tuple
getstate(const raster_symbolizer& r)
{
return boost::python::make_tuple(r.get_mode(),r.get_scaling(),r.get_opacity(),r.get_filter_factor(),r.get_mesh_size());
}
static void
setstate (raster_symbolizer& r, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 5)
{
PyErr_SetObject(PyExc_ValueError,
("expected 5-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
r.set_mode(extract<std::string>(state[0]));
r.set_scaling(extract<std::string>(state[1]));
r.set_opacity(extract<float>(state[2]));
r.set_filter_factor(extract<float>(state[3]));
r.set_mesh_size(extract<unsigned>(state[4]));
}
};
}
using mapnik::raster_symbolizer;
void export_raster_symbolizer()
{
@ -73,35 +49,26 @@ void export_raster_symbolizer()
class_<raster_symbolizer>("RasterSymbolizer",
init<>("Default ctor"))
.def_pickle(raster_symbolizer_pickle_suite())
.add_property("mode",
make_function(&raster_symbolizer::get_mode,return_value_policy<copy_const_reference>()),
&raster_symbolizer::set_mode,
"Get/Set merging mode.\n"
"Possible values are:\n"
"normal, grain_merge, grain_merge2, multiply,\n"
"multiply2, divide, divide2, screen, and hard_light\n"
"\n"
"Usage:\n"
"\n"
">>> from mapnik import RasterSymbolizer\n"
">>> r = RasterSymbolizer()\n"
">>> r.mode = 'grain_merge2'\n"
"Get/Set merging mode. (deprecated, use comp_op instead)\n"
)
.add_property("comp_op",
&raster_symbolizer::comp_op,
&raster_symbolizer::set_comp_op,
"Set/get the raster comp-op"
)
.add_property("scaling",
make_function(&raster_symbolizer::get_scaling,return_value_policy<copy_const_reference>()),
&raster_symbolizer::set_scaling,
&raster_symbolizer::get_scaling_method,
&raster_symbolizer::set_scaling_method,
"Get/Set scaling algorithm.\n"
"Possible values are:\n"
"fast, bilinear, and bilinear8\n"
"\n"
"Usage:\n"
"\n"
">>> from mapnik import RasterSymbolizer\n"
">>> r = RasterSymbolizer()\n"
">>> r.scaling = 'bilinear8'\n"
">>> r.scaling = 'mapnik.scaling_method.GAUSSIAN'\n"
)
.add_property("opacity",
@ -164,5 +131,17 @@ void export_raster_symbolizer()
">>> r = RasterSymbolizer()\n"
">>> r.mesh_size = 32\n"
)
.add_property("premultiplied",
&get_premultiplied_impl,
&raster_symbolizer::set_premultiplied,
"Get/Set premultiplied status of the source image.\n"
"Can be used to override what the source data reports (when in error)\n"
"\n"
"Usage:\n"
"\n"
">>> from mapnik import RasterSymbolizer\n"
">>> r = RasterSymbolizer()\n"
">>> r.premultiplied = False\n"
)
;
}

View file

@ -48,108 +48,6 @@ using mapnik::markers_symbolizer;
using mapnik::symbolizer;
using mapnik::to_expression_string;
struct pickle_symbolizer : public boost::static_visitor<>
{
public:
pickle_symbolizer( boost::python::list syms):
syms_(syms) {}
template <typename T>
void operator () ( T const& sym )
{
syms_.append(sym);
}
private:
boost::python::list syms_;
};
struct extract_symbolizer : public boost::static_visitor<>
{
public:
extract_symbolizer( rule& r):
r_(r) {}
template <typename T>
void operator () ( T const& sym )
{
r_.append(sym);
}
private:
rule& r_;
};
struct rule_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const rule& r)
{
return boost::python::make_tuple(r.get_name(),r.get_min_scale(),r.get_max_scale());
}
static boost::python::tuple
getstate(const rule& r)
{
boost::python::list syms;
rule::symbolizers::const_iterator begin = r.get_symbolizers().begin();
rule::symbolizers::const_iterator end = r.get_symbolizers().end();
pickle_symbolizer serializer( syms );
std::for_each( begin, end , boost::apply_visitor( serializer ));
// We serialize filter expressions AST as strings
std::string filter_expr = to_expression_string(*r.get_filter());
return boost::python::make_tuple(filter_expr,r.has_else_filter(),r.has_also_filter(),syms);
}
static void
setstate (rule& r, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 4)
{
PyErr_SetObject(PyExc_ValueError,
("expected 4-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
if (state[0])
{
rule dfl;
std::string filter = extract<std::string>(state[1]);
std::string default_filter = "<TODO>";//dfl.get_filter()->to_string();
if ( filter != default_filter)
{
r.set_filter(mapnik::parse_expression(filter,"utf8"));
}
}
if (state[1])
{
r.set_else(true);
}
if (state[2])
{
r.set_also(true);
}
boost::python::list syms=extract<boost::python::list>(state[4]);
extract_symbolizer serializer( r );
for (int i=0;i<len(syms);++i)
{
//symbolizer symbol = extract<symbolizer>(syms[i]);
//boost::apply_visitor( serializer, symbol );
}
}
};
void export_rule()
{
using namespace boost::python;
@ -171,7 +69,6 @@ void export_rule()
class_<rule>("Rule",init<>("default constructor"))
.def(init<std::string const&,
boost::python::optional<double,double> >())
.def_pickle(rule_pickle_suite())
.add_property("name",make_function
(&rule::get_name,
return_value_policy<copy_const_reference>()),

View file

@ -0,0 +1,52 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2012 Artem Pavlenko, Jean-Francois Doyon
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
#include <boost/python.hpp>
#include <mapnik/image_scaling.hpp>
#include "mapnik_enumeration.hpp"
void export_scaling_method()
{
using namespace boost::python;
enum_<mapnik::scaling_method_e>("scaling_method")
.value("NEAR", mapnik::SCALING_NEAR)
.value("BILINEAR", mapnik::SCALING_BILINEAR)
.value("BICUBIC", mapnik::SCALING_BICUBIC)
.value("SPLINE16", mapnik::SCALING_SPLINE16)
.value("SPLINE36", mapnik::SCALING_SPLINE36)
.value("HANNING", mapnik::SCALING_HANNING)
.value("HAMMING", mapnik::SCALING_HAMMING)
.value("HERMITE", mapnik::SCALING_HERMITE)
.value("KAISER", mapnik::SCALING_KAISER)
.value("QUADRIC", mapnik::SCALING_QUADRIC)
.value("CATROM", mapnik::SCALING_CATROM)
.value("GAUSSIAN", mapnik::SCALING_GAUSSIAN)
.value("BESSEL", mapnik::SCALING_BESSEL)
.value("MITCHELL", mapnik::SCALING_MITCHELL)
.value("SINC", mapnik::SCALING_SINC)
.value("LANCZOS", mapnik::SCALING_LANCZOS)
.value("BLACKMAN", mapnik::SCALING_BLACKMAN)
.value("BILINEAR8", mapnik::SCALING_BILINEAR8)
;
}

View file

@ -21,13 +21,21 @@
*
*****************************************************************************/
/* The functions in this file produce deprecation warnings.
* But as shield symbolizer doesn't fully support more than one
* placement from python yet these functions are actually the
* correct ones.
*/
#define NO_DEPRECATION_WARNINGS
// boost
#include <boost/python.hpp>
// mapnik
#include <mapnik/shield_symbolizer.hpp>
#include <mapnik/image_util.hpp>
#include <mapnik/path_expression_grammar.hpp>
#include <mapnik/parse_path.hpp>
#include <mapnik/path_expression.hpp>
#include "mapnik_svg.hpp"
using mapnik::color;
@ -53,12 +61,13 @@ tuple get_shield_displacement(const shield_symbolizer& s)
void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg)
{
s.set_shield_displacement(extract<double>(arg[0]),extract<double>(arg[1]));
s.get_placement_options()->defaults.displacement.first = extract<double>(arg[0]);
s.get_placement_options()->defaults.displacement.second = extract<double>(arg[1]);
}
tuple get_text_displacement(const shield_symbolizer& t)
{
position const& pos = t.get_displacement();
position const& pos = t.get_placement_options()->defaults.displacement;
return boost::python::make_tuple(pos.first, pos.second);
}
@ -67,7 +76,7 @@ void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg)
t.set_displacement(extract<double>(arg[0]),extract<double>(arg[1]));
}
const std::string get_filename(shield_symbolizer const& t)
std::string get_filename(shield_symbolizer const& t)
{
return path_processor_type::to_string(*t.get_filename());
}
@ -79,46 +88,6 @@ void set_filename(shield_symbolizer & t, std::string const& file_expr)
}
struct shield_symbolizer_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const shield_symbolizer& s)
{
std::string filename = path_processor_type::to_string(*s.get_filename());
//(name, font name, font size, font color, image file, image type, width, height)
return boost::python::make_tuple( "TODO",//s.get_name(),
s.get_face_name(),s.get_text_size(),s.get_fill(),filename,guess_type(filename));
}
static boost::python::tuple
getstate(const shield_symbolizer& s)
{
return boost::python::make_tuple(s.get_halo_fill(),s.get_halo_radius());
}
// TODO add lots more...
static void
setstate (shield_symbolizer& s, boost::python::tuple state)
{
using namespace boost::python;
/*if (len(state) != 1)
{
PyErr_SetObject(PyExc_ValueError,
("expected 1-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}*/
s.set_halo_fill(extract<color>(state[0]));
s.set_halo_radius(extract<float>(state[1]));
}
};
void export_shield_symbolizer()
{
using namespace boost::python;
@ -126,9 +95,8 @@ void export_shield_symbolizer()
init<expression_ptr,
std::string const&,
unsigned, mapnik::color const&,
path_expression_ptr>("TODO")
path_expression_ptr>()
)
//.def_pickle(shield_symbolizer_pickle_suite())
.add_property("allow_overlap",
&shield_symbolizer::get_allow_overlap,
&shield_symbolizer::set_allow_overlap,
@ -238,5 +206,13 @@ void export_shield_symbolizer()
.add_property("transform",
mapnik::get_svg_transform<shield_symbolizer>,
mapnik::set_svg_transform<shield_symbolizer>)
.add_property("comp_op",
&shield_symbolizer::comp_op,
&shield_symbolizer::set_comp_op,
"Set/get the comp-op")
.add_property("clip",
&shield_symbolizer::clip,
&shield_symbolizer::set_clip,
"Set/get the shield geometry's clipping status")
;
}

View file

@ -32,10 +32,9 @@ using namespace mapnik;
namespace {
using namespace boost::python;
list get_dashes_list(const stroke& stroke)
list get_dashes_list(stroke const& stroke)
{
list l;
if (stroke.has_dash()) {
mapnik::dash_array const& dash = stroke.get_dash_array();
mapnik::dash_array::const_iterator iter = dash.begin();
@ -44,67 +43,24 @@ list get_dashes_list(const stroke& stroke)
l.append(make_tuple(iter->first, iter->second));
}
}
return l;
}
void set_dasharray(stroke & stroke, list const& l)
{
for (int i=0; i<len(l); ++i)
{
boost::python::tuple dash = extract<boost::python::tuple>(l[i]);
if (len(dash) == 2)
{
double d1 = extract<double>(dash[0]);
double d2 = extract<double>(dash[1]);
stroke.add_dash(d1,d2);
}
}
}
struct stroke_pickle_suite : boost::python::pickle_suite
{
static boost::python::tuple
getinitargs(const stroke& s)
{
return boost::python::make_tuple(s.get_color(),s.get_width());
}
static boost::python::tuple
getstate(const stroke& s)
{
boost::python::list dashes = get_dashes_list(s);
return boost::python::make_tuple(s.get_opacity(),
dashes,
s.get_line_cap(),
s.get_line_join(),
s.get_gamma(),
s.get_gamma_method());
}
static void
setstate (stroke& s, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 6)
{
PyErr_SetObject(PyExc_ValueError,
("expected 6-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
s.set_opacity(extract<float>(state[0]));
if (state[1])
{
list dashes = extract<list>(state[1]);
for(boost::python::ssize_t i=0; i<len(dashes); i++) {
double ds1 = extract<double>(dashes[i][0]);
double ds2 = extract<double>(dashes[i][1]);
s.add_dash(ds1,ds2);
}
}
s.set_line_cap(extract<line_cap_e>(state[2]));
s.set_line_join(extract<line_join_e>(state[3]));
s.set_gamma(extract<double>(state[4]));
s.set_gamma_method(extract<gamma_method_e>(state[5]));
}
};
}
void export_stroke ()
{
@ -132,7 +88,6 @@ void export_stroke ()
(arg("color"),arg("width")),
"Creates a new stroke object with a specified color and width.\n")
)
.def_pickle(stroke_pickle_suite())
.add_property("color",make_function
(&stroke::get_color,return_value_policy<copy_const_reference>()),
&stroke::set_color,
@ -159,18 +114,37 @@ void export_stroke ()
.add_property("line_cap",
&stroke::get_line_cap,
&stroke::set_line_cap,
"Gets or sets the line cap of this stroke.\n")
"Gets or sets the line cap of this stroke. (alias of linecap)\n")
.add_property("linecap",
&stroke::get_line_cap,
&stroke::set_line_cap,
"Gets or sets the linecap of this stroke.\n")
.add_property("line_join",
&stroke::get_line_join,
&stroke::set_line_join,
"Returns the line join mode of this stroke.\n")
// todo consider providing a single get/set property
"Returns the line join mode of this stroke. (alias of linejoin)\n")
.add_property("linejoin",
&stroke::get_line_join,
&stroke::set_line_join,
"Returns the linejoin mode of this stroke.\n")
.add_property("miterlimit",
&stroke::get_miterlimit,
&stroke::set_miterlimit,
"Returns the miterlimit mode of this stroke.\n")
.def("add_dash",&stroke::add_dash,
(arg("length"),arg("gap")),
"Adds a dash segment to the dash patterns of this stroke.\n")
.def("get_dashes", get_dashes_list,
"Returns the list of dash segments for this stroke.\n")
.add_property("dasharray",
get_dashes_list,
set_dasharray,
"Gets or sets dasharray string of this stroke. (alternate property to add_dash/get_dashes)\n")
.add_property("dash_offset",
&stroke::dash_offset,
&stroke::set_dash_offset,
"Gets or sets dash offset of this stroke. (alias of dashoffet)\n")
.add_property("dashoffset",
&stroke::dash_offset,
&stroke::set_dash_offset,
"Gets or sets dash offset of this stroke.\n")

View file

@ -25,51 +25,41 @@
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
// mapnik
#include <mapnik/value_error.hpp>
#include "mapnik_enumeration.hpp"
#include <mapnik/feature_type_style.hpp>
#include <mapnik/image_filter_grammar.hpp> // image_filter_grammar
#include <mapnik/image_filter_types.hpp> // generate_image_filters
using mapnik::feature_type_style;
using mapnik::rules;
using mapnik::rule;
struct style_pickle_suite : boost::python::pickle_suite
std::string get_image_filters(feature_type_style & style)
{
static boost::python::tuple
getstate(const feature_type_style& s)
std::string filters_str;
std::back_insert_iterator<std::string> sink(filters_str);
generate_image_filters(sink, style.image_filters());
return filters_str;
}
void set_image_filters(feature_type_style & style, std::string const& filters)
{
std::string::const_iterator itr = filters.begin();
std::string::const_iterator end = filters.end();
mapnik::image_filter_grammar<std::string::const_iterator,
std::vector<mapnik::filter::filter_type> > filter_grammar;
std::vector<mapnik::filter::filter_type> new_filters;
bool result = boost::spirit::qi::phrase_parse(itr,end,
filter_grammar,
boost::spirit::qi::ascii::space,
new_filters);
if (!result || itr!=end)
{
boost::python::list rule_list;
rules::const_iterator it = s.get_rules().begin();
rules::const_iterator end = s.get_rules().end();
for (; it != end; ++it)
{
rule_list.append( *it );
}
return boost::python::make_tuple(rule_list);
throw mapnik::value_error("failed to parse image-filters: '" + std::string(itr,end) + "'");
}
static void
setstate (feature_type_style& s, boost::python::tuple state)
{
using namespace boost::python;
if (len(state) != 1)
{
PyErr_SetObject(PyExc_ValueError,
("expected 1-item tuple in call to __setstate__; got %s"
% state).ptr()
);
throw_error_already_set();
}
boost::python::list rules = extract<boost::python::list>(state[0]);
for (int i=0; i<len(rules); ++i)
{
s.add_rule(extract<rule>(rules[i]));
}
}
};
style.image_filters().swap(new_filters);
}
void export_style()
{
@ -85,9 +75,6 @@ void export_style()
;
class_<feature_type_style>("Style",init<>("default style constructor"))
.def_pickle(style_pickle_suite()
)
.add_property("rules",make_function
(&feature_type_style::get_rules,
return_value_policy<reference_existing_object>()),
@ -102,7 +89,19 @@ void export_style()
.add_property("filter_mode",
&feature_type_style::get_filter_mode,
&feature_type_style::set_filter_mode,
"Set/get the placement of the label")
"Set/get the filter mode of the style")
.add_property("opacity",
&feature_type_style::get_opacity,
&feature_type_style::set_opacity,
"Set/get the opacity of the style")
.add_property("comp_op",
&feature_type_style::comp_op,
&feature_type_style::set_comp_op,
"Set/get the comp-op (composite operation) of the style")
.add_property("image_filters",
get_image_filters,
set_image_filters,
"Set/get the comp-op (composite operation) of the style")
;
}

View file

@ -23,35 +23,32 @@
#define MAPNIK_PYTHON_BINDING_SVG_INCLUDED
// mapnik
#include <mapnik/parse_transform.hpp>
#include <mapnik/symbolizer.hpp>
#include <mapnik/svg/svg_path_parser.hpp>
#include <mapnik/value_error.hpp>
// agg
#include "agg_trans_affine.h"
namespace mapnik {
using namespace boost::python;
template <class T>
const std::string get_svg_transform(T& symbolizer)
std::string get_svg_transform(T& symbolizer)
{
return symbolizer.get_transform_string();
return symbolizer.get_image_transform_string();
}
template <class T>
void set_svg_transform(T& symbolizer, std::string const& transform_wkt)
{
agg::trans_affine tr;
if (!mapnik::svg::parse_transform(transform_wkt.c_str(), tr))
transform_list_ptr trans_expr = mapnik::parse_transform(transform_wkt);
if (!trans_expr)
{
std::stringstream ss;
ss << "Could not parse transform from '" << transform_wkt << "', expected string like: 'matrix(1, 0, 0, 1, 0, 0)'";
ss << "Could not parse transform from '"
<< transform_wkt
<< "', expected SVG transform attribute";
throw mapnik::value_error(ss.str());
}
mapnik::transform_type matrix;
tr.store_to(&matrix[0]);
symbolizer.set_transform(matrix);
symbolizer.set_image_transform(trans_expr);
}
} // end of namespace mapnik

View file

@ -22,6 +22,7 @@
// boost
#include <boost/python.hpp>
#include <boost/variant.hpp>
// mapnik
//symbolizer typdef here rather than mapnik/symbolizer.hpp
@ -95,6 +96,13 @@ public:
{
return "markers";
}
template <typename Symbolizer>
std::string operator() ( Symbolizer const& sym)
{
boost::ignore_unused_variable_warning(sym);
return "unknown";
}
};
std::string get_symbol_type(const symbolizer& symbol)

View file

@ -21,6 +21,7 @@
*****************************************************************************/
#include <boost/python.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/noncopyable.hpp>
#include <mapnik/text_properties.hpp>
#include <mapnik/text_placements/simple.hpp>
@ -28,7 +29,7 @@
#include <mapnik/formatting/text.hpp>
#include <mapnik/formatting/list.hpp>
#include <mapnik/formatting/format.hpp>
#include <mapnik/formatting/expression.hpp>
#include <mapnik/formatting/expression_format.hpp>
#include <mapnik/processed_text.hpp>
#include <mapnik/expression_string.hpp>
#include <mapnik/text_symbolizer.hpp>
@ -230,16 +231,16 @@ struct ListNodeWrap: formatting::list_node, wrapper<formatting::list_node>
formatting::node_ptr get_item(int i)
{
if (i<0) i+= children_.size();
if (i<children_.size()) return children_[i];
if (i < 0) i+= children_.size();
if (i < static_cast<int>(children_.size())) return children_[i];
IndexError();
return formatting::node_ptr(); //Avoid compiler warning
}
void set_item(int i, formatting::node_ptr ptr)
{
if (i<0) i+= children_.size();
if (i<children_.size()) children_[i] = ptr;
if (i < 0) i+= children_.size();
if (i < static_cast<int>(children_.size())) children_[i] = ptr;
IndexError();
}
@ -354,6 +355,14 @@ void export_text_placement()
make_function(&get_properties, return_value_policy<reference_existing_object>()),
&set_properties,
"Shortcut for placements.defaults")
.add_property("comp_op",
&text_symbolizer::comp_op,
&text_symbolizer::set_comp_op,
"Set/get the comp-op")
.add_property("clip",
&text_symbolizer::clip,
&text_symbolizer::set_clip,
"Set/get the text geometry's clipping status")
;
@ -376,6 +385,7 @@ void export_text_placement()
.def_readwrite("maximum_angle_char_delta", &text_symbolizer_properties::max_char_angle_delta)
.def_readwrite("force_odd_labels", &text_symbolizer_properties::force_odd_labels)
.def_readwrite("allow_overlap", &text_symbolizer_properties::allow_overlap)
.def_readwrite("largest_bbox_only", &text_symbolizer_properties::largest_bbox_only)
.def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio)
.def_readwrite("wrap_width", &text_symbolizer_properties::wrap_width)
.def_readwrite("format", &text_symbolizer_properties::format)
@ -389,18 +399,20 @@ void export_text_placement()
set_old_style expression is just a compatibility wrapper and doesn't need to be exposed in python. */
;
class_<char_properties>
class_with_converter<char_properties>
("CharProperties")
.def_readwrite_convert("text_transform", &char_properties::text_transform)
.def_readwrite_convert("fontset", &char_properties::fontset)
.def(init<char_properties const&>()) //Copy constructor
.def_readwrite("face_name", &char_properties::face_name)
.def_readwrite("fontset", &char_properties::fontset)
.def_readwrite("text_size", &char_properties::text_size)
.def_readwrite("character_spacing", &char_properties::character_spacing)
.def_readwrite("line_spacing", &char_properties::line_spacing)
.def_readwrite("text_opacity", &char_properties::text_opacity)
.def_readwrite("wrap_char", &char_properties::wrap_char)
.def_readwrite("wrap_character", &char_properties::wrap_char)
.def_readwrite("wrap_before", &char_properties::wrap_before)
.def_readwrite("text_transform", &char_properties::text_transform)
.def_readwrite("fill", &char_properties::fill)
.def_readwrite("halo_fill", &char_properties::halo_fill)
.def_readwrite("halo_radius", &char_properties::halo_radius)
@ -487,6 +499,7 @@ void export_text_placement()
.def_readwrite_convert("line_spacing", &formatting::format_node::line_spacing)
.def_readwrite_convert("text_opacity", &formatting::format_node::text_opacity)
.def_readwrite_convert("wrap_char", &formatting::format_node::wrap_char)
.def_readwrite_convert("wrap_character", &formatting::format_node::wrap_char)
.def_readwrite_convert("wrap_before", &formatting::format_node::wrap_before)
.def_readwrite_convert("text_transform", &formatting::format_node::text_transform)
.def_readwrite_convert("fill", &formatting::format_node::fill)
@ -526,6 +539,7 @@ void export_text_placement()
.def_readwrite("line_spacing", &formatting::expression_format::line_spacing)
.def_readwrite("text_opacity", &formatting::expression_format::text_opacity)
.def_readwrite("wrap_char", &formatting::expression_format::wrap_char)
.def_readwrite("wrap_character", &formatting::expression_format::wrap_char)
.def_readwrite("wrap_before", &formatting::expression_format::wrap_before)
.def_readwrite("fill", &formatting::expression_format::fill)
.def_readwrite("halo_fill", &formatting::expression_format::halo_fill)

View file

@ -22,6 +22,9 @@
#ifndef MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
#define MAPNIK_PYTHON_BINDING_VALUE_CONVERTER_INCLUDED
// mapnik
#include <mapnik/value.hpp>
// boost
#include <boost/python.hpp>
#include <boost/implicit_cast.hpp>
@ -30,7 +33,7 @@ namespace boost { namespace python {
struct value_converter : public boost::static_visitor<PyObject*>
{
PyObject * operator() (int val) const
PyObject * operator() (mapnik::value_integer val) const
{
#if PY_VERSION_HEX >= 0x03000000
return ::PyLong_FromLong(val);
@ -51,23 +54,19 @@ namespace boost { namespace python {
PyObject * operator() (std::string const& s) const
{
PyObject *obj = Py_None;
obj = ::PyUnicode_DecodeUTF8(s.c_str(),implicit_cast<ssize_t>(s.length()),0);
return obj;
return ::PyUnicode_DecodeUTF8(s.c_str(),implicit_cast<ssize_t>(s.length()),0);
}
PyObject * operator() (UnicodeString const& s) const
PyObject * operator() (mapnik::value_unicode_string const& s) const
{
std::string buffer;
mapnik::to_utf8(s,buffer);
PyObject *obj = Py_None;
obj = ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
return obj;
return ::PyUnicode_DecodeUTF8(buffer.c_str(),implicit_cast<ssize_t>(buffer.length()),0);
}
PyObject * operator() (mapnik::value_null const& /*s*/) const
{
return Py_None;
Py_RETURN_NONE;
}
};

View file

@ -31,6 +31,7 @@ static Pycairo_CAPI_t *Pycairo_CAPI;
static void *extract_surface(PyObject* op)
{
if (PyObject_TypeCheck(op, const_cast<PyTypeObject*>(Pycairo_CAPI->Surface_Type)))
{
return op;
@ -55,7 +56,11 @@ static void *extract_context(PyObject* op)
void register_cairo()
{
#if PY_MAJOR_VERSION >= 3
Pycairo_CAPI = (Pycairo_CAPI_t*) PyCapsule_Import(const_cast<char *>("cairo.CAPI"), 0);
#else
Pycairo_CAPI = (Pycairo_CAPI_t*) PyCObject_Import(const_cast<char *>("cairo"), const_cast<char *>("CAPI"));
#endif
if (Pycairo_CAPI == NULL) return;
boost::python::converter::registry::insert(&extract_surface, boost::python::type_id<PycairoSurface>());

View file

@ -0,0 +1,471 @@
/*****************************************************************************
*
* This file is part of Mapnik (c++ mapping toolkit)
*
* Copyright (C) 2011 Artem Pavlenko
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*****************************************************************************/
// boost
#include <boost/python.hpp>
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>
// mapnik
#include <mapnik/map.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/grid/grid_util.hpp>
#include <mapnik/grid/grid_view.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_kv_iterator.hpp>
#include "mapnik_value_converter.hpp"
#include "python_grid_utils.hpp"
namespace mapnik {
template <typename T>
void grid2utf(T const& grid_type,
boost::python::list& l,
std::vector<grid::lookup_type>& key_order)
{
typedef std::map< typename T::lookup_type, typename T::value_type> keys_type;
typedef typename keys_type::const_iterator keys_iterator;
typename T::data_type const& data = grid_type.data();
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::feature_key_type::const_iterator feature_pos;
keys_type keys;
// start counting at utf8 codepoint 32, aka space character
boost::uint16_t codepoint = 32;
unsigned array_size = data.width();
for (unsigned y = 0; y < data.height(); ++y)
{
boost::uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
typename T::value_type const* row = data.getRow(y);
for (unsigned x = 0; x < data.width(); ++x)
{
typename T::value_type feature_id = row[x];
feature_pos = feature_keys.find(feature_id);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
keys_iterator key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
if (feature_id == mapnik::grid::base_mask)
{
keys[""] = codepoint;
key_order.push_back("");
}
else
{
keys[val] = codepoint;
key_order.push_back(val);
}
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
template <typename T>
void grid2utf(T const& grid_type,
boost::python::list& l,
std::vector<typename T::lookup_type>& key_order,
unsigned int resolution)
{
typedef std::map< typename T::lookup_type, typename T::value_type> keys_type;
typedef typename keys_type::const_iterator keys_iterator;
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::feature_key_type::const_iterator feature_pos;
keys_type keys;
// start counting at utf8 codepoint 32, aka space character
boost::uint16_t codepoint = 32;
unsigned array_size = std::ceil(grid_type.width()/static_cast<float>(resolution));
for (unsigned y = 0; y < grid_type.height(); y=y+resolution)
{
boost::uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
mapnik::grid::value_type const* row = grid_type.getRow(y);
for (unsigned x = 0; x < grid_type.width(); x=x+resolution)
{
typename T::value_type feature_id = row[x];
feature_pos = feature_keys.find(feature_id);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
keys_iterator key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
if (feature_id == mapnik::grid::base_mask)
{
keys[""] = codepoint;
key_order.push_back("");
}
else
{
keys[val] = codepoint;
key_order.push_back(val);
}
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
template <typename T>
void grid2utf2(T const& grid_type,
boost::python::list& l,
std::vector<typename T::lookup_type>& key_order,
unsigned int resolution)
{
typedef std::map< typename T::lookup_type, typename T::value_type> keys_type;
typedef typename keys_type::const_iterator keys_iterator;
typename T::data_type const& data = grid_type.data();
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::feature_key_type::const_iterator feature_pos;
keys_type keys;
// start counting at utf8 codepoint 32, aka space character
uint16_t codepoint = 32;
mapnik::grid::data_type target(data.width()/resolution,data.height()/resolution);
mapnik::scale_grid(target,grid_type.data(),0.0,0.0);
unsigned array_size = target.width();
for (unsigned y = 0; y < target.height(); ++y)
{
uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
mapnik::grid::value_type * row = target.getRow(y);
unsigned x;
for (x = 0; x < target.width(); ++x)
{
feature_pos = feature_keys.find(row[x]);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
keys_iterator key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
keys[val] = codepoint;
key_order.push_back(val);
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
template <typename T>
void write_features(T const& grid_type,
boost::python::dict& feature_data,
std::vector<typename T::lookup_type> const& key_order)
{
typename T::feature_type const& g_features = grid_type.get_grid_features();
if (g_features.size() <= 0)
{
return;
}
std::set<std::string> const& attributes = grid_type.property_names();
typename T::feature_type::const_iterator feat_end = g_features.end();
BOOST_FOREACH ( std::string const& key_item, key_order )
{
if (key_item.empty())
{
continue;
}
typename T::feature_type::const_iterator feat_itr = g_features.find(key_item);
if (feat_itr == feat_end)
{
continue;
}
bool found = false;
boost::python::dict feat;
mapnik::feature_ptr feature = feat_itr->second;
BOOST_FOREACH ( std::string const& attr, attributes )
{
if (attr == "__id__")
{
feat[attr.c_str()] = feature->id();
}
else if (feature->has_key(attr))
{
found = true;
feat[attr.c_str()] = feature->get(attr);
}
}
if (found)
{
feature_data[feat_itr->first] = feat;
}
}
}
template <typename T>
void grid_encode_utf(T const& grid_type,
boost::python::dict & json,
bool add_features,
unsigned int resolution)
{
// convert buffer to utf and gather key order
boost::python::list l;
std::vector<typename T::lookup_type> key_order;
if (resolution != 1) {
// resample on the fly - faster, less accurate
mapnik::grid2utf<T>(grid_type,l,key_order,resolution);
// resample first - slower, more accurate
//mapnik::grid2utf2<T>(grid_type,l,key_order,resolution);
}
else
{
mapnik::grid2utf<T>(grid_type,l,key_order);
}
// convert key order to proper python list
boost::python::list keys_a;
BOOST_FOREACH ( typename T::lookup_type const& key_id, key_order )
{
keys_a.append(key_id);
}
// gather feature data
boost::python::dict feature_data;
if (add_features) {
mapnik::write_features<T>(grid_type,feature_data,key_order);
}
json["grid"] = l;
json["keys"] = keys_a;
json["data"] = feature_data;
}
template <typename T>
boost::python::dict grid_encode( T const& grid, std::string const& format, bool add_features, unsigned int resolution)
{
if (format == "utf") {
boost::python::dict json;
grid_encode_utf<T>(grid,json,add_features,resolution);
return json;
}
else
{
std::stringstream s;
s << "'utf' is currently the only supported encoding format.";
throw mapnik::value_error(s.str());
}
}
template boost::python::dict grid_encode( mapnik::grid const& grid, std::string const& format, bool add_features, unsigned int resolution);
template boost::python::dict grid_encode( mapnik::grid_view const& grid, std::string const& format, bool add_features, unsigned int resolution);
/* new approach: key comes from grid object
* grid size should be same as the map
* encoding, resizing handled as method on grid object
* whether features are dumped is determined by argument not 'fields'
*/
void render_layer_for_grid(mapnik::Map const& map,
mapnik::grid & grid,
unsigned layer_idx, // TODO - layer by name or index
boost::python::list const& fields)
{
std::vector<mapnik::layer> const& layers = map.layers();
std::size_t layer_num = layers.size();
if (layer_idx >= layer_num) {
std::ostringstream s;
s << "Zero-based layer index '" << layer_idx << "' not valid, only '"
<< layer_num << "' layers are in map\n";
throw std::runtime_error(s.str());
}
// convert python list to std::set
boost::python::ssize_t num_fields = boost::python::len(fields);
for(boost::python::ssize_t i=0; i<num_fields; i++) {
boost::python::extract<std::string> name(fields[i]);
if (name.check())
{
grid.add_property_name(name());
}
else
{
std::stringstream s;
s << "list of field names must be strings";
throw mapnik::value_error(s.str());
}
}
// copy property names
std::set<std::string> attributes = grid.property_names();
// todo - make this a static constant
std::string known_id_key = "__id__";
if (attributes.find(known_id_key) != attributes.end())
{
attributes.erase(known_id_key);
}
std::string join_field = grid.get_key();
if (known_id_key != join_field &&
attributes.find(join_field) == attributes.end())
{
attributes.insert(join_field);
}
mapnik::grid_renderer<mapnik::grid> ren(map,grid,1.0,0,0);
mapnik::layer const& layer = layers[layer_idx];
ren.apply(layer,attributes);
}
/* old, original impl - to be removed after further testing
* grid object is created on the fly at potentially reduced size
*/
boost::python::dict render_grid(mapnik::Map const& map,
unsigned layer_idx, // layer
std::string const& key, // key_name
unsigned int step, // resolution
boost::python::list const& fields)
{
std::vector<mapnik::layer> const& layers = map.layers();
std::size_t layer_num = layers.size();
if (layer_idx >= layer_num) {
std::ostringstream s;
s << "Zero-based layer index '" << layer_idx << "' not valid, only '"
<< layer_num << "' layers are in map\n";
throw std::runtime_error(s.str());
}
unsigned int grid_width = map.width()/step;
unsigned int grid_height = map.height()/step;
// TODO - no need to pass step here
mapnik::grid grid(grid_width,grid_height,key,step);
// convert python list to std::set
boost::python::ssize_t num_fields = boost::python::len(fields);
for(boost::python::ssize_t i=0; i<num_fields; i++) {
boost::python::extract<std::string> name(fields[i]);
if (name.check()) {
grid.add_property_name(name());
}
else
{
std::stringstream s;
s << "list of field names must be strings";
throw mapnik::value_error(s.str());
}
}
// copy property names
std::set<std::string> attributes = grid.property_names();
// todo - make this a static constant
std::string known_id_key = "__id__";
if (attributes.find(known_id_key) != attributes.end())
{
attributes.erase(known_id_key);
}
std::string join_field = grid.get_key();
if (known_id_key != join_field &&
attributes.find(join_field) == attributes.end())
{
attributes.insert(join_field);
}
try
{
mapnik::grid_renderer<mapnik::grid> ren(map,grid,1.0,0,0);
mapnik::layer const& layer = layers[layer_idx];
ren.apply(layer,attributes);
}
catch (...)
{
throw;
}
bool add_features = false;
if (num_fields > 0)
add_features = true;
// build dictionary and return to python
boost::python::dict json;
grid_encode_utf(grid,json,add_features,1);
return json;
}
}

View file

@ -24,460 +24,66 @@
// boost
#include <boost/python.hpp>
#include <boost/scoped_array.hpp>
#include <boost/foreach.hpp>
// mapnik
#include <mapnik/debug.hpp>
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/grid/grid.hpp>
#include <mapnik/grid/grid_util.hpp>
#include <mapnik/grid/grid_view.hpp>
#include <mapnik/value_error.hpp>
#include <mapnik/feature.hpp>
#include <mapnik/feature_kv_iterator.hpp>
#include "mapnik_value_converter.hpp"
namespace mapnik {
template <typename T>
static void grid2utf(T const& grid_type,
void grid2utf(T const& grid_type,
boost::python::list& l,
std::vector<grid::lookup_type>& key_order)
{
typename T::data_type const& data = grid_type.data();
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::key_type keys;
typename T::key_type::const_iterator key_pos;
typename T::feature_key_type::const_iterator feature_pos;
// start counting at utf8 codepoint 32, aka space character
boost::uint16_t codepoint = 32;
unsigned array_size = data.width();
for (unsigned y = 0; y < data.height(); ++y)
{
boost::uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
typename T::value_type const* row = data.getRow(y);
for (unsigned x = 0; x < data.width(); ++x)
{
feature_pos = feature_keys.find(row[x]);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
keys[val] = codepoint;
key_order.push_back(val);
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
std::vector<typename T::lookup_type>& key_order);
template <typename T>
static void grid2utf(T const& grid_type,
void grid2utf(T const& grid_type,
boost::python::list& l,
std::vector<typename T::lookup_type>& key_order,
unsigned int resolution)
{
//typename T::data_type const& data = grid_type.data();
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::key_type keys;
typename T::key_type::const_iterator key_pos;
typename T::feature_key_type::const_iterator feature_pos;
// start counting at utf8 codepoint 32, aka space character
boost::uint16_t codepoint = 32;
// TODO - use double?
unsigned array_size = static_cast<unsigned int>(grid_type.width()/resolution);
for (unsigned y = 0; y < grid_type.height(); y=y+resolution)
{
boost::uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
mapnik::grid::value_type const* row = grid_type.getRow(y);
for (unsigned x = 0; x < grid_type.width(); x=x+resolution)
{
feature_pos = feature_keys.find(row[x]);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
keys[val] = codepoint;
key_order.push_back(val);
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
unsigned int resolution);
template <typename T>
static void grid2utf2(T const& grid_type,
void grid2utf2(T const& grid_type,
boost::python::list& l,
std::vector<typename T::lookup_type>& key_order,
unsigned int resolution)
{
typename T::data_type const& data = grid_type.data();
typename T::feature_key_type const& feature_keys = grid_type.get_feature_keys();
typename T::key_type keys;
typename T::key_type::const_iterator key_pos;
typename T::feature_key_type::const_iterator feature_pos;
// start counting at utf8 codepoint 32, aka space character
uint16_t codepoint = 32;
mapnik::grid::data_type target(data.width()/resolution,data.height()/resolution);
mapnik::scale_grid(target,grid_type.data(),0.0,0.0);
unsigned array_size = target.width();
for (unsigned y = 0; y < target.height(); ++y)
{
uint16_t idx = 0;
boost::scoped_array<Py_UNICODE> line(new Py_UNICODE[array_size]);
mapnik::grid::value_type * row = target.getRow(y);
unsigned x;
for (x = 0; x < target.width(); ++x)
{
feature_pos = feature_keys.find(row[x]);
if (feature_pos != feature_keys.end())
{
mapnik::grid::lookup_type val = feature_pos->second;
key_pos = keys.find(val);
if (key_pos == keys.end())
{
// Create a new entry for this key. Skip the codepoints that
// can't be encoded directly in JSON.
if (codepoint == 34) ++codepoint; // Skip "
else if (codepoint == 92) ++codepoint; // Skip backslash
keys[val] = codepoint;
key_order.push_back(val);
line[idx++] = static_cast<Py_UNICODE>(codepoint);
++codepoint;
}
else
{
line[idx++] = static_cast<Py_UNICODE>(key_pos->second);
}
}
// else, shouldn't get here...
}
l.append(boost::python::object(
boost::python::handle<>(
PyUnicode_FromUnicode(line.get(), array_size))));
}
}
unsigned int resolution);
template <typename T>
static void write_features(T const& grid_type,
void write_features(T const& grid_type,
boost::python::dict& feature_data,
std::vector<typename T::lookup_type> const& key_order)
{
std::string const& key = grid_type.get_key();
std::set<std::string> const& attributes = grid_type.property_names();
typename T::feature_type const& g_features = grid_type.get_grid_features();
typename T::feature_type::const_iterator feat_itr = g_features.begin();
typename T::feature_type::const_iterator feat_end = g_features.end();
bool include_key = (attributes.find(key) != attributes.end());
for (; feat_itr != feat_end; ++feat_itr)
{
mapnik::feature_ptr feature = feat_itr->second;
boost::optional<std::string> join_value;
if (key == grid_type.key_name())
{
std::stringstream s;
s << feature->id();
join_value = s.str();
}
else if (feature->has_key(key))
{
join_value = feature->get(key).to_string();
}
if (join_value)
{
// only serialize features visible in the grid
if(std::find(key_order.begin(), key_order.end(), *join_value) != key_order.end()) {
boost::python::dict feat;
bool found = false;
if (key == grid_type.key_name())
{
// drop key unless requested
if (include_key) {
found = true;
//TODO - add __id__ as data key?
//feat[key] = *join_value;
}
}
feature_kv_iterator itr = feature->begin();
feature_kv_iterator end = feature->end();
for ( ;itr!=end; ++itr)
{
std::string const& key_name = boost::get<0>(*itr);
if (key_name == key) {
// drop key unless requested
if (include_key) {
found = true;
feat[key_name] = boost::get<1>(*itr);
}
}
else if ( (attributes.find(key_name) != attributes.end()) )
{
found = true;
feat[key_name] = boost::get<1>(*itr);
}
}
if (found)
{
feature_data[feat_itr->first] = feat;
}
}
}
else
{
MAPNIK_LOG_DEBUG(bindings) << "write_features: Should not get here: key " << key << " not found in grid feature properties";
}
}
}
std::vector<typename T::lookup_type> const& key_order);
template <typename T>
static void grid_encode_utf(T const& grid_type,
void grid_encode_utf(T const& grid_type,
boost::python::dict & json,
bool add_features,
unsigned int resolution)
{
// convert buffer to utf and gather key order
boost::python::list l;
std::vector<typename T::lookup_type> key_order;
if (resolution != 1) {
// resample on the fly - faster, less accurate
mapnik::grid2utf<T>(grid_type,l,key_order,resolution);
// resample first - slower, more accurate
//mapnik::grid2utf2<T>(grid_type,l,key_order,resolution);
}
else
{
mapnik::grid2utf<T>(grid_type,l,key_order);
}
// convert key order to proper python list
boost::python::list keys_a;
BOOST_FOREACH ( typename T::lookup_type const& key_id, key_order )
{
keys_a.append(key_id);
}
// gather feature data
boost::python::dict feature_data;
if (add_features) {
mapnik::write_features<T>(grid_type,feature_data,key_order);
}
json["grid"] = l;
json["keys"] = keys_a;
json["data"] = feature_data;
}
unsigned int resolution);
template <typename T>
static boost::python::dict grid_encode( T const& grid, std::string format, bool add_features, unsigned int resolution)
{
if (format == "utf") {
boost::python::dict json;
grid_encode_utf<T>(grid,json,add_features,resolution);
return json;
}
else
{
std::stringstream s;
s << "'utf' is currently the only supported encoding format.";
throw mapnik::value_error(s.str());
}
}
boost::python::dict grid_encode( T const& grid, std::string const& format, bool add_features, unsigned int resolution);
/* new approach: key comes from grid object
* grid size should be same as the map
* encoding, resizing handled as method on grid object
* whether features are dumped is determined by argument not 'fields'
*/
static void render_layer_for_grid(const mapnik::Map& map,
void render_layer_for_grid(const mapnik::Map& map,
mapnik::grid& grid,
unsigned layer_idx, // TODO - layer by name or index
boost::python::list const& fields)
{
std::vector<mapnik::layer> const& layers = map.layers();
std::size_t layer_num = layers.size();
if (layer_idx >= layer_num) {
std::ostringstream s;
s << "Zero-based layer index '" << layer_idx << "' not valid, only '"
<< layer_num << "' layers are in map\n";
throw std::runtime_error(s.str());
}
// convert python list to std::vector
boost::python::ssize_t num_fields = boost::python::len(fields);
for(boost::python::ssize_t i=0; i<num_fields; i++) {
boost::python::extract<std::string> name(fields[i]);
if (name.check()) {
grid.add_property_name(name());
}
else
{
std::stringstream s;
s << "list of field names must be strings";
throw mapnik::value_error(s.str());
}
}
// copy property names
std::set<std::string> attributes = grid.property_names();
std::string const& key = grid.get_key();
// if key is special __id__ keyword
if (key == grid.key_name())
{
// TODO - should feature.id() be a first class attribute?
// if __id__ is requested to be dumped out
// remove it so that datasource queries will not break
if (attributes.find(key) != attributes.end())
{
attributes.erase(key);
}
}
// if key is not the special __id__ keyword
else if (attributes.find(key) == attributes.end())
{
// them make sure the datasource query includes this field
attributes.insert(key);
}
mapnik::grid_renderer<mapnik::grid> ren(map,grid,1.0,0,0);
mapnik::layer const& layer = layers[layer_idx];
ren.apply(layer,attributes);
}
boost::python::list const& fields);
/* old, original impl - to be removed after further testing
* grid object is created on the fly at potentially reduced size
*/
static boost::python::dict render_grid(const mapnik::Map& map,
boost::python::dict render_grid(const mapnik::Map& map,
unsigned layer_idx, // layer
std::string const& key, // key_name
unsigned int step, // resolution
boost::python::list const& fields)
{
std::vector<mapnik::layer> const& layers = map.layers();
std::size_t layer_num = layers.size();
if (layer_idx >= layer_num) {
std::ostringstream s;
s << "Zero-based layer index '" << layer_idx << "' not valid, only '"
<< layer_num << "' layers are in map\n";
throw std::runtime_error(s.str());
}
unsigned int grid_width = map.width()/step;
unsigned int grid_height = map.height()/step;
// TODO - no need to pass step here
mapnik::grid grid(grid_width,grid_height,key,step);
// convert python list to std::vector
boost::python::ssize_t num_fields = boost::python::len(fields);
for(boost::python::ssize_t i=0; i<num_fields; i++) {
boost::python::extract<std::string> name(fields[i]);
if (name.check()) {
grid.add_property_name(name());
}
else
{
std::stringstream s;
s << "list of field names must be strings";
throw mapnik::value_error(s.str());
}
}
// copy property names
std::set<std::string> attributes = grid.property_names();
// if key is special __id__ keyword
if (key == grid.key_name())
{
// TODO - should feature.id() be a first class attribute?
// if __id__ is requested to be dumped out
// remove it so that datasource queries will not break
if (attributes.find(key) != attributes.end())
{
attributes.erase(key);
}
}
// if key is not the special __id__ keyword
else if (attributes.find(key) == attributes.end())
{
// them make sure the datasource query includes this field
attributes.insert(key);
}
try
{
mapnik::grid_renderer<mapnik::grid> ren(map,grid,1.0,0,0);
mapnik::layer const& layer = layers[layer_idx];
ren.apply(layer,attributes);
}
catch (...)
{
throw;
}
bool add_features = false;
if (num_fields > 0)
add_features = true;
// build dictionary and return to python
boost::python::dict json;
grid_encode_utf(grid,json,add_features,1);
return json;
}
boost::python::list const& fields);
}
#endif // MAPNIK_PYTHON_BINDING_GRID_UTILS_INCLUDED

View file

@ -22,6 +22,7 @@
#include <boost/optional/optional.hpp>
#include <boost/python.hpp>
#include <boost/noncopyable.hpp>
// boost::optional<T> to/from converter from John Wiegley

View file

@ -0,0 +1,68 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Boost.SpatialIndex - geometry helper functions
//
// Copyright 2008 Federico J. Fernandez.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_GGL_INDEX_RTREE_HELPERS_HPP
#define BOOST_GEOMETRY_GGL_INDEX_RTREE_HELPERS_HPP
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/disjoint.hpp>
#include <boost/geometry/core/point_type.hpp>
namespace boost { namespace geometry { namespace index {
/**
* \brief Given two boxes, returns the minimal box that contains them
*/
// TODO: use geometry::expand
template <typename Box>
inline Box enlarge_box(Box const& b1, Box const& b2)
{
// TODO: mloskot - Refactor to readable form. Fix VC++8.0 min/max warnings:
// warning C4002: too many actual parameters for macro 'min
typedef typename geometry::point_type<Box>::type point_type;
point_type pmin(
geometry::get<min_corner, 0>(b1) < geometry::get<min_corner, 0>(b2)
? geometry::get<min_corner, 0>(b1) : geometry::get<min_corner, 0>(b2),
geometry::get<min_corner, 1>(b1) < geometry::get<min_corner, 1>(b2)
? geometry::get<min_corner, 1>(b1) : geometry::get<min_corner, 1>(b2));
point_type pmax(
geometry::get<max_corner, 0>(b1) > geometry::get<max_corner, 0>(b2)
? geometry::get<max_corner, 0>(b1) : geometry::get<max_corner, 0>(b2),
geometry::get<max_corner, 1>(b1) > geometry::get<max_corner, 1>(b2)
? geometry::get<max_corner, 1>(b1) : geometry::get<max_corner, 1>(b2));
return Box(pmin, pmax);
}
/**
* \brief Compute the area of the union of b1 and b2
*/
template <typename Box>
inline typename default_area_result<Box>::type compute_union_area(Box const& b1, Box const& b2)
{
Box enlarged_box = enlarge_box(b1, b2);
return geometry::area(enlarged_box);
}
/**
* \brief Checks if boxes intersects
*/
// TODO: move to geometry::intersects
template <typename Box>
inline bool is_overlapping(Box const& b1, Box const& b2)
{
return ! geometry::disjoint(b1, b2);
}
}}} // namespace boost::geometry::index
#endif // BOOST_GEOMETRY_GGL_INDEX_RTREE_HELPERS_HPP

View file

@ -0,0 +1,774 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Boost.SpatialIndex - rtree implementation
//
// Copyright 2008 Federico J. Fernandez.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_HPP
#define BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_HPP
#include <cstddef>
#include <iostream> // TODO: Remove if print() is removed
#include <stdexcept>
#include <utility>
#include <vector>
#include <boost/concept_check.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/extensions/index/rtree/rtree_node.hpp>
#include <boost/geometry/extensions/index/rtree/rtree_leaf.hpp>
namespace boost { namespace geometry { namespace index
{
template <typename Box, typename Value >
class rtree
{
public:
typedef boost::shared_ptr<rtree_node<Box, Value> > node_pointer;
typedef boost::shared_ptr<rtree_leaf<Box, Value> > leaf_pointer;
/**
* \brief Creates a rtree with 'maximum' elements per node and 'minimum'.
*/
rtree(unsigned int const& maximum, unsigned int const& minimum)
: m_count(0)
, m_min_elems_per_node(minimum)
, m_max_elems_per_node(maximum)
, m_root(new rtree_node<Box, Value>(node_pointer(), 1))
{
}
/**
* \brief Creates a rtree with maximum elements per node
* and minimum (box is ignored).
*/
rtree(Box const& box, unsigned int const& maximum, unsigned int const& minimum)
: m_count(0)
, m_min_elems_per_node(minimum)
, m_max_elems_per_node(maximum)
, m_root(new rtree_node<Box, Value>(node_pointer(), 1))
{
boost::ignore_unused_variable_warning(box);
}
/**
* \brief destructor (virtual because we have virtual functions)
*/
virtual ~rtree() {}
/**
* \brief Remove elements inside the 'box'
*/
inline void remove(Box const& box)
{
try
{
node_pointer leaf(choose_exact_leaf(box));
typename rtree_leaf<Box, Value>::leaf_map q_leaves;
leaf->remove(box);
if (leaf->elements() < m_min_elems_per_node && elements() > m_min_elems_per_node)
{
q_leaves = leaf->get_leaves();
// we remove the leaf_node in the parent node because now it's empty
leaf->get_parent()->remove(leaf->get_parent()->get_box(leaf));
}
typename rtree_node<Box, Value>::node_map q_nodes;
condense_tree(leaf, q_nodes);
std::vector<std::pair<Box, Value> > s;
for (typename rtree_node<Box, Value>::node_map::const_iterator it = q_nodes.begin();
it != q_nodes.end(); ++it)
{
typename rtree_leaf<Box, Value>::leaf_map leaves = it->second->get_leaves();
// reinserting leaves from nodes
for (typename rtree_leaf<Box, Value>::leaf_map::const_iterator itl = leaves.begin();
itl != leaves.end(); ++itl)
{
s.push_back(*itl);
}
}
for (typename std::vector<std::pair<Box, Value> >::const_iterator it = s.begin(); it != s.end(); ++it)
{
m_count--;
insert(it->first, it->second);
}
// if the root has only one child and the child is not a leaf,
// make it the root
if (m_root->elements() == 1)
{
if (!m_root->first_element()->is_leaf())
{
m_root = m_root->first_element();
}
}
// reinserting leaves
for (typename rtree_leaf<Box, Value>::leaf_map::const_iterator it = q_leaves.begin();
it != q_leaves.end(); ++it)
{
m_count--;
insert(it->first, it->second);
}
m_count--;
}
catch(std::logic_error & e)
{
// TODO: mloskot - replace with Boost.Geometry exception
// not found
std::cerr << e.what() << std::endl;
return;
}
}
/**
* \brief Remove element inside the box with value
*/
void remove(Box const& box, Value const& value)
{
try
{
node_pointer leaf;
// find possible leaves
typedef typename std::vector<node_pointer > node_type;
node_type nodes;
m_root->find_leaves(box, nodes);
// refine the result
for (typename node_type::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
{
leaf = *it;
try
{
leaf->remove(value);
break;
} catch (...)
{
leaf = node_pointer();
}
}
if (!leaf)
return;
typename rtree_leaf < Box, Value >::leaf_map q_leaves;
if (leaf->elements() < m_min_elems_per_node && elements() > m_min_elems_per_node)
{
q_leaves = leaf->get_leaves();
// we remove the leaf_node in the parent node because now it's empty
leaf->get_parent()->remove(leaf->get_parent()->get_box(leaf));
}
typename rtree_node<Box, Value>::node_map q_nodes;
condense_tree(leaf, q_nodes);
std::vector<std::pair<Box, Value> > s;
for (typename rtree_node<Box, Value>::node_map::const_iterator it = q_nodes.begin();
it != q_nodes.end(); ++it)
{
typename rtree_leaf<Box, Value>::leaf_map leaves = it->second->get_leaves();
// reinserting leaves from nodes
for (typename rtree_leaf<Box, Value>::leaf_map::const_iterator itl = leaves.begin();
itl != leaves.end(); ++itl)
{
s.push_back(*itl);
}
}
for (typename std::vector<std::pair<Box, Value> >::const_iterator it = s.begin(); it != s.end(); ++it)
{
m_count--;
insert(it->first, it->second);
}
// if the root has only one child and the child is not a leaf,
// make it the root
if (m_root->elements() == 1)
{
if (!m_root->first_element()->is_leaf())
{
m_root = m_root->first_element();
}
}
// reinserting leaves
for (typename rtree_leaf<Box, Value>::leaf_map::const_iterator it = q_leaves.begin();
it != q_leaves.end(); ++it)
{
m_count--;
insert(it->first, it->second);
}
m_count--;
}
catch(std::logic_error & e)
{
// TODO: mloskot - ggl exception
// not found
std::cerr << e.what() << std::endl;
return;
}
}
/**
* \brief Returns the number of elements.
*/
inline unsigned int elements() const
{
return m_count;
}
/**
* \brief Inserts an element with 'box' as key with value.
*/
inline void insert(Box const& box, Value const& value)
{
m_count++;
node_pointer leaf(choose_corresponding_leaf(box));
// check if the selected leaf is full to do the split if necessary
if (leaf->elements() >= m_max_elems_per_node)
{
leaf->insert(box, value);
// split!
node_pointer n1(new rtree_leaf<Box, Value>(leaf->get_parent()));
node_pointer n2(new rtree_leaf<Box, Value>(leaf->get_parent()));
split_node(leaf, n1, n2);
adjust_tree(leaf, n1, n2);
}
else
{
leaf->insert(box, value);
adjust_tree(leaf);
}
}
/**
* \brief Returns all the values inside 'box'
*/
inline std::deque<Value> find(Box const& box) const
{
std::deque<Value> result;
m_root->find(box, result, false);
return result;
}
/**
* \brief Print Rtree (mainly for debug)
*/
inline void print()
{
std::cerr << "===================================" << std::endl;
std::cerr << " Min/Max: " << m_min_elems_per_node << " / " << m_max_elems_per_node << std::endl;
std::cerr << "Leaves: " << m_root->get_leaves().size() << std::endl;
m_root->print();
std::cerr << "===================================" << std::endl;
}
private:
/// number of elements
unsigned int m_count;
/// minimum number of elements per node
unsigned int m_min_elems_per_node;
/// maximum number of elements per node
unsigned int m_max_elems_per_node;
/// tree root
node_pointer m_root;
/**
* \brief Reorganize the tree after a removal. It tries to
* join nodes with less elements than m.
*/
void condense_tree(node_pointer const& leaf,
typename rtree_node<Box, Value>::node_map& q_nodes)
{
if (leaf.get() == m_root.get())
{
// if it's the root we are done
return;
}
node_pointer parent = leaf->get_parent();
parent->adjust_box(leaf);
if (parent->elements() < m_min_elems_per_node)
{
if (parent.get() == m_root.get())
{
// if the parent is underfull and it's the root we just exit
return;
}
// get the nodes that we should reinsert
typename rtree_node<Box, Value>::node_map this_nodes = parent->get_nodes();
for(typename rtree_node<Box, Value>::node_map::const_iterator it = this_nodes.begin();
it != this_nodes.end(); ++it)
{
q_nodes.push_back(*it);
}
// we remove the node in the parent node because now it should be
// re inserted
parent->get_parent()->remove(parent->get_parent()->get_box(parent));
}
condense_tree(parent, q_nodes);
}
/**
* \brief After an insertion splits nodes with more than 'maximum' elements.
*/
inline void adjust_tree(node_pointer& node)
{
if (node.get() == m_root.get())
{
// we finished the adjust
return;
}
// as there are no splits just adjust the box of the parent and go on
node_pointer parent = node->get_parent();
parent->adjust_box(node);
adjust_tree(parent);
}
/**
* \brief After an insertion splits nodes with more than maximum elements
* (recursive step with subtrees 'n1' and 'n2' to be joined).
*/
void adjust_tree(node_pointer& leaf, node_pointer& n1, node_pointer& n2)
{
// check if we are in the root and do the split
if (leaf.get() == m_root.get())
{
node_pointer new_root(new rtree_node<Box,Value>(node_pointer (), leaf->get_level() + 1));
new_root->add_node(n1->compute_box(), n1);
new_root->add_node(n2->compute_box(), n2);
n1->set_parent(new_root);
n2->set_parent(new_root);
n1->update_parent(n1);
n2->update_parent(n2);
m_root = new_root;
return;
}
node_pointer parent = leaf->get_parent();
parent->replace_node(leaf, n1);
parent->add_node(n2->compute_box(), n2);
// if parent is full, split and readjust
if (parent->elements() > m_max_elems_per_node)
{
node_pointer p1(new rtree_node<Box, Value>(parent->get_parent(), parent->get_level()));
node_pointer p2(new rtree_node<Box, Value>(parent->get_parent(), parent->get_level()));
split_node(parent, p1, p2);
adjust_tree(parent, p1, p2);
}
else
{
adjust_tree(parent);
}
}
/**
* \brief Splits 'n' in 'n1' and 'n2'
*/
void split_node(node_pointer const& n, node_pointer& n1, node_pointer& n2) const
{
unsigned int seed1 = 0;
unsigned int seed2 = 0;
std::vector<Box> boxes = n->get_boxes();
n1->set_parent(n->get_parent());
n2->set_parent(n->get_parent());
linear_pick_seeds(n, seed1, seed2);
if (n->is_leaf())
{
n1->add_value(boxes[seed1], n->get_value(seed1));
n2->add_value(boxes[seed2], n->get_value(seed2));
}
else
{
n1->add_node(boxes[seed1], n->get_node(seed1));
n2->add_node(boxes[seed2], n->get_node(seed2));
}
unsigned int index = 0;
if (n->is_leaf())
{
// TODO: mloskot - add assert(node.size() >= 2); or similar
typename rtree_leaf<Box, Value>::leaf_map nodes = n->get_leaves();
unsigned int remaining = nodes.size() - 2;
for (typename rtree_leaf<Box, Value>::leaf_map::const_iterator it = nodes.begin();
it != nodes.end(); ++it, index++)
{
if (index != seed1 && index != seed2)
{
if (n1->elements() + remaining == m_min_elems_per_node)
{
n1->add_value(it->first, it->second);
continue;
}
if (n2->elements() + remaining == m_min_elems_per_node)
{
n2->add_value(it->first, it->second);
continue;
}
remaining--;
/// current boxes of each group
Box b1, b2;
/// enlarged boxes of each group
Box eb1, eb2;
b1 = n1->compute_box();
b2 = n2->compute_box();
/// areas
typedef typename coordinate_type<Box>::type coordinate_type;
coordinate_type b1_area, b2_area;
coordinate_type eb1_area, eb2_area;
b1_area = geometry::area(b1);
b2_area = geometry::area(b2);
eb1_area = compute_union_area(b1, it->first);
eb2_area = compute_union_area(b2, it->first);
if (eb1_area - b1_area > eb2_area - b2_area)
{
n2->add_value(it->first, it->second);
}
if (eb1_area - b1_area < eb2_area - b2_area)
{
n1->add_value(it->first, it->second);
}
if (eb1_area - b1_area == eb2_area - b2_area)
{
if (b1_area < b2_area)
{
n1->add_value(it->first, it->second);
}
if (b1_area > b2_area)
{
n2->add_value(it->first, it->second);
}
if (b1_area == b2_area)
{
if (n1->elements() > n2->elements())
{
n2->add_value(it->first, it->second);
}
else
{
n1->add_value(it->first, it->second);
}
}
}
}
}
}
else
{
// TODO: mloskot - add assert(node.size() >= 2); or similar
typename rtree_node<Box, Value>::node_map nodes = n->get_nodes();
unsigned int remaining = nodes.size() - 2;
for(typename rtree_node<Box, Value>::node_map::const_iterator it = nodes.begin();
it != nodes.end(); ++it, index++)
{
if (index != seed1 && index != seed2)
{
if (n1->elements() + remaining == m_min_elems_per_node)
{
n1->add_node(it->first, it->second);
continue;
}
if (n2->elements() + remaining == m_min_elems_per_node)
{
n2->add_node(it->first, it->second);
continue;
}
remaining--;
/// current boxes of each group
Box b1, b2;
/// enlarged boxes of each group
Box eb1, eb2;
b1 = n1->compute_box();
b2 = n2->compute_box();
/// areas
typedef typename coordinate_type<Box>::type coordinate_type;
coordinate_type b1_area, b2_area;
coordinate_type eb1_area, eb2_area;
b1_area = geometry::area(b1);
b2_area = geometry::area(b2);
eb1_area = compute_union_area(b1, it->first);
eb2_area = compute_union_area(b2, it->first);
if (eb1_area - b1_area > eb2_area - b2_area)
{
n2->add_node(it->first, it->second);
}
if (eb1_area - b1_area < eb2_area - b2_area)
{
n1->add_node(it->first, it->second);
}
if (eb1_area - b1_area == eb2_area - b2_area)
{
if (b1_area < b2_area)
{
n1->add_node(it->first, it->second);
}
if (b1_area > b2_area)
{
n2->add_node(it->first, it->second);
}
if (b1_area == b2_area)
{
if (n1->elements() > n2->elements())
{
n2->add_node(it->first, it->second);
}
else
{
n1->add_node(it->first, it->second);
}
}
}
}
}
}
}
/**
* \brief Choose initial values for the split algorithm (linear version)
*/
void linear_pick_seeds(node_pointer const& n, unsigned int &seed1, unsigned int &seed2) const
{
// get boxes from the node
std::vector<Box>boxes = n->get_boxes();
if (boxes.size() == 0)
{
// TODO: mloskot - throw ggl exception
throw std::logic_error("Empty Node trying to Pick Seeds");
}
// only two dim for now
// unsigned int dimensions =
// geometry::point_traits<Point>::coordinate_count;
// find the first two elements
typedef typename coordinate_type<Box>::type coordinate_type;
coordinate_type separation_x, separation_y;
unsigned int first_x, second_x;
unsigned int first_y, second_y;
find_normalized_separations<0u>(boxes, separation_x, first_x, second_x);
find_normalized_separations<1u>(boxes, separation_y, first_y, second_y);
if (separation_x > separation_y)
{
seed1 = first_x;
seed2 = second_x;
}
else
{
seed1 = first_y;
seed2 = second_y;
}
}
/**
* \brief Find distances between possible initial values for the
* pick_seeds algorithm.
*/
template <std::size_t D, typename T>
void find_normalized_separations(std::vector<Box> const& boxes, T& separation,
unsigned int& first, unsigned int& second) const
{
if (boxes.size() < 2)
{
throw std::logic_error("At least two boxes needed to split");
}
// find the lowest high
typename std::vector<Box>::const_iterator it = boxes.begin();
typedef typename coordinate_type<Box>::type coordinate_type;
coordinate_type lowest_high = geometry::get<max_corner, D>(*it);
unsigned int lowest_high_index = 0;
unsigned int index = 1;
++it;
for(; it != boxes.end(); ++it)
{
if (geometry::get<max_corner, D>(*it) < lowest_high)
{
lowest_high = geometry::get<max_corner, D>(*it);
lowest_high_index = index;
}
index++;
}
// find the highest low
coordinate_type highest_low = 0;
unsigned int highest_low_index = 0;
if (lowest_high_index == 0)
{
highest_low = geometry::get<min_corner, D>(boxes[1]);
highest_low_index = 1;
}
else
{
highest_low = geometry::get<min_corner, D>(boxes[0]);
highest_low_index = 0;
}
index = 0;
for (it = boxes.begin();
it != boxes.end(); ++it, index++)
{
if (geometry::get<min_corner, D>(*it) >= highest_low && index != lowest_high_index)
{
highest_low = geometry::get<min_corner, D>(*it);
highest_low_index = index;
}
}
// find the lowest low
it = boxes.begin();
coordinate_type lowest_low = geometry::get<min_corner, D>(*it);
++it;
for(; it != boxes.end(); ++it)
{
if (geometry::get<min_corner, D>(*it) < lowest_low)
{
lowest_low = geometry::get<min_corner, D>(*it);
}
}
// find the highest high
it = boxes.begin();
coordinate_type highest_high = geometry::get<max_corner, D>(*it);
++it;
for(; it != boxes.end(); ++it)
{
if (geometry::get<max_corner, D>(*it) > highest_high)
{
highest_high = geometry::get<max_corner, D>(*it);
}
}
coordinate_type const width = highest_high - lowest_low;
separation = (highest_low - lowest_high) / width;
first = highest_low_index;
second = lowest_high_index;
}
/**
* \brief Choose one of the possible leaves to make an insertion
*/
inline node_pointer choose_corresponding_leaf(Box const& e)
{
node_pointer node = m_root;
// if the tree is empty add an initial leaf
if (m_root->elements() == 0)
{
leaf_pointer new_leaf(new rtree_leaf<Box, Value>(m_root));
m_root->add_leaf_node(Box (), new_leaf);
return new_leaf;
}
while (!node->is_leaf())
{
/// traverse node's map to see which node we should select
node = node->choose_node(e);
}
return node;
}
/**
* \brief Choose the exact leaf where an insertion should be done
*/
node_pointer choose_exact_leaf(Box const&e) const
{
// find possible leaves
typedef typename std::vector<node_pointer> node_type;
node_type nodes;
m_root->find_leaves(e, nodes);
// refine the result
for (typename node_type::const_iterator it = nodes.begin(); it != nodes.end(); ++it)
{
typedef std::vector<std::pair<Box, Value> > leaves_type;
leaves_type leaves = (*it)->get_leaves();
for (typename leaves_type::const_iterator itl = leaves.begin();
itl != leaves.end(); ++itl)
{
if (itl->first.max_corner() == e.max_corner()
&& itl->first.min_corner() == e.min_corner())
{
return *it;
}
}
}
// TODO: mloskot - ggl exception
throw std::logic_error("Leaf not found");
}
};
}}} // namespace boost::geometry::index
#endif // BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_HPP

View file

@ -0,0 +1,253 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Boost.SpatialIndex - rtree leaf implementation
//
// Copyright 2008 Federico J. Fernandez.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_LEAF_HPP
#define BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_LEAF_HPP
#include <deque>
#include <iostream> // TODO: Remove if print() is removed
#include <stdexcept>
#include <utility>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/extensions/index/rtree/rtree_node.hpp>
namespace boost { namespace geometry { namespace index
{
template <typename Box, typename Value >
class rtree_leaf : public rtree_node<Box, Value>
{
public:
/// container type for the leaves
typedef boost::shared_ptr<rtree_node<Box, Value> > node_pointer;
typedef std::vector<std::pair<Box, Value> > leaf_map;
/**
* \brief Creates an empty leaf
*/
inline rtree_leaf()
{
}
/**
* \brief Creates a new leaf, with 'parent' as parent
*/
inline rtree_leaf(node_pointer const& parent)
: rtree_node<Box, Value> (parent, 0)
{
}
/**
* \brief Search for elements in 'box' in the Rtree. Add them to 'result'.
* If exact_match is true only return the elements having as
* key the 'box'. Otherwise return everything inside 'box'.
*/
virtual void find(Box const& box, std::deque<Value>& result, bool const exact_match)
{
for (typename leaf_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (exact_match)
{
if (geometry::equals(it->first, box))
{
result.push_back(it->second);
}
}
else
{
if (is_overlapping(it->first, box))
{
result.push_back(it->second);
}
}
}
}
/**
* \brief Compute bounding box for this leaf
*/
virtual Box compute_box() const
{
if (m_nodes.empty())
{
return Box ();
}
Box r;
geometry::assign_inverse(r);
for(typename leaf_map::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
geometry::expand(r, it->first);
}
return r;
}
/**
* \brief True if we are a leaf
*/
virtual bool is_leaf() const
{
return true;
}
/**
* \brief Number of elements in the tree
*/
virtual unsigned int elements() const
{
return m_nodes.size();
}
/**
* \brief Insert a new element, with key 'box' and value 'v'
*/
virtual void insert(Box const& box, Value const& v)
{
m_nodes.push_back(std::make_pair(box, v));
}
/**
* \brief Proyect leaves of this node.
*/
virtual std::vector< std::pair<Box, Value> > get_leaves() const
{
return m_nodes;
}
/**
* \brief Add a new child (node) to this node
*/
virtual void add_node(Box const&, node_pointer const&)
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Can't add node to leaf node.");
}
/**
* \brief Add a new leaf to this node
*/
virtual void add_value(Box const& box, Value const& v)
{
m_nodes.push_back(std::make_pair(box, v));
}
/**
* \brief Proyect value in position 'index' in the nodes container
*/
virtual Value get_value(unsigned int index) const
{
return m_nodes[index].second;
}
/**
* \brief Box projector for leaf
*/
virtual Box get_box(unsigned int index) const
{
return m_nodes[index].first;
}
/**
* \brief Remove value with key 'box' in this leaf
*/
virtual void remove(Box const& box)
{
for (typename leaf_map::iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (geometry::equals(it->first, box))
{
m_nodes.erase(it);
return;
}
}
// TODO: mloskot - use GGL exception
throw std::logic_error("Node not found.");
}
/**
* \brief Remove value in this leaf
*/
virtual void remove(Value const& v)
{
for (typename leaf_map::iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (it->second == v)
{
m_nodes.erase(it);
return;
}
}
// TODO: mloskot - use GGL exception
throw std::logic_error("Node not found.");
}
/**
* \brief Proyect boxes from this node
*/
virtual std::vector<Box> get_boxes() const
{
std::vector<Box> result;
for (typename leaf_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
result.push_back(it->first);
}
return result;
}
/**
* \brief Print leaf (mainly for debug)
*/
virtual void print() const
{
std::cerr << "\t" << " --> Leaf --------" << std::endl;
std::cerr << "\t" << " Size: " << m_nodes.size() << std::endl;
for (typename leaf_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
std::cerr << "\t" << " | ";
std::cerr << "( " << geometry::get<min_corner, 0>
(it->first) << " , " << geometry::get<min_corner, 1>
(it->first) << " ) x ";
std::cerr << "( " << geometry::get<max_corner, 0>
(it->first) << " , " << geometry::get<max_corner, 1>
(it->first) << " )";
std::cerr << " -> ";
std::cerr << it->second;
std::cerr << " | " << std::endl;;
}
std::cerr << "\t" << " --< Leaf --------" << std::endl;
}
private:
/// leaves of this node
leaf_map m_nodes;
};
}}} // namespace boost::geometry::index
#endif // BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_LEAF_HPP

View file

@ -0,0 +1,493 @@
// Boost.Geometry (aka GGL, Generic Geometry Library)
// Boost.SpatialIndex - rtree node implementation
//
// Copyright 2008 Federico J. Fernandez.
// Use, modification and distribution is subject to the Boost Software License,
// Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
#ifndef BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_NODE_HPP
#define BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_NODE_HPP
#include <deque>
#include <iostream> // TODO: Remove if print() is removed
#include <stdexcept>
#include <utility>
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/geometry/algorithms/area.hpp>
#include <boost/geometry/algorithms/assign.hpp>
#include <boost/geometry/algorithms/equals.hpp>
#include <boost/geometry/algorithms/expand.hpp>
#include <boost/geometry/extensions/index/rtree/helpers.hpp>
namespace boost { namespace geometry { namespace index
{
/// forward declaration
template <typename Box, typename Value>
class rtree_leaf;
template <typename Box, typename Value>
class rtree_node
{
public:
typedef boost::shared_ptr<rtree_node<Box, Value> > node_pointer;
typedef boost::shared_ptr<rtree_leaf<Box, Value> > leaf_pointer;
/// type for the node map
typedef std::vector<std::pair<Box, node_pointer > > node_map;
/**
* \brief Creates a default node (needed for the containers)
*/
rtree_node()
{
}
/**
* \brief Creates a node with 'parent' as parent and 'level' as its level
*/
rtree_node(node_pointer const& parent, unsigned int const& level)
: m_parent(parent), m_level(level)
{
}
/**
* \brief destructor (virtual because we have virtual functions)
*/
virtual ~rtree_node()
{
}
/**
* \brief Level projector
*/
virtual unsigned int get_level() const
{
return m_level;
}
/**
* \brief Number of elements in the subtree
*/
virtual unsigned int elements() const
{
return m_nodes.size();
}
/**
* \brief Project first element, to replace root in case of condensing
*/
inline node_pointer first_element() const
{
if (0 == m_nodes.size())
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("first_element in empty node");
}
return m_nodes.begin()->second;
}
/**
* \brief True if it is a leaf node
*/
virtual bool is_leaf() const
{
return false;
}
/**
* \brief Proyector for the 'i' node
*/
node_pointer get_node(unsigned int index)
{
return m_nodes[index].second;
}
/**
* \brief Search for elements in 'box' in the Rtree. Add them to 'result'.
* If exact_match is true only return the elements having as
* key the box 'box'. Otherwise return everything inside 'box'.
*/
virtual void find(Box const& box, std::deque<Value>& result, bool const exact_match)
{
for (typename node_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (is_overlapping(it->first, box))
{
it->second->find(box, result, exact_match);
}
}
}
/**
* \brief Return in 'result' all the leaves inside 'box'
*/
void find_leaves(Box const& box, typename std::vector<node_pointer>& result) const
{
for (typename node_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (is_overlapping(it->first, box))
{
if (it->second->is_leaf())
{
result.push_back(it->second);
}
else
{
it->second->find_leaves(box, result);
}
}
}
}
/**
* \brief Compute bounding box for this node
*/
virtual Box compute_box() const
{
if (m_nodes.empty())
{
return Box();
}
Box result;
geometry::assign_inverse(result);
for(typename node_map::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
geometry::expand(result, it->first);
}
return result;
}
/**
* \brief Insert a value (not allowed for a node, only on leaves)
*/
virtual void insert(Box const&, Value const&)
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Insert in node!");
}
/**
* \brief Get the envelopes of a node
*/
virtual std::vector<Box> get_boxes() const
{
std::vector<Box> result;
for(typename node_map::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
result.push_back(it->first);
}
return result;
}
/**
* \brief Recompute the bounding box
*/
void adjust_box(node_pointer const& node)
{
unsigned int index = 0;
for (typename node_map::iterator it = m_nodes.begin();
it != m_nodes.end(); ++it, index++)
{
if (it->second.get() == node.get())
{
m_nodes[index] = std::make_pair(node->compute_box(), node);
return;
}
}
}
/**
* \brief Remove elements inside the 'box'
*/
virtual void remove(Box const& box)
{
for (typename node_map::iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (geometry::equals(it->first, box))
{
m_nodes.erase(it);
return;
}
}
}
/**
* \brief Remove value in this leaf
*/
virtual void remove(Value const&)
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Can't remove a non-leaf node by value.");
}
/**
* \brief Replace the node in the m_nodes vector and recompute the box
*/
void replace_node(node_pointer const& leaf, node_pointer& new_leaf)
{
unsigned int index = 0;
for(typename node_map::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it, index++)
{
if (it->second.get() == leaf.get())
{
m_nodes[index] = std::make_pair(new_leaf->compute_box(), new_leaf);
new_leaf->update_parent(new_leaf);
return;
}
}
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Node not found.");
}
/**
* \brief Add a child to this node
*/
virtual void add_node(Box const& box, node_pointer const& node)
{
m_nodes.push_back(std::make_pair(box, node));
node->update_parent(node);
}
/**
* \brief add a value (not allowed in nodes, only on leaves)
*/
virtual void add_value(Box const&, Value const&)
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Can't add value to non-leaf node.");
}
/**
* \brief Add a child leaf to this node
*/
inline void add_leaf_node(Box const& box, leaf_pointer const& leaf)
{
m_nodes.push_back(std::make_pair(box, leaf));
}
/**
* \brief Choose a node suitable for adding 'box'
*/
node_pointer choose_node(Box const& box)
{
if (m_nodes.size() == 0)
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Empty node trying to choose the least enlargement node.");
}
typedef typename coordinate_type<Box>::type coordinate_type;
bool first = true;
coordinate_type min_area = 0;
coordinate_type min_diff_area = 0;
node_pointer chosen_node;
// check for the least enlargement
for (typename node_map::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
coordinate_type const
diff_area = coordinate_type(compute_union_area(box, it->first))
- geometry::area(it->first);
if (first)
{
// it's the first time, we keep the first
min_diff_area = diff_area;
min_area = geometry::area(it->first);
chosen_node = it->second;
first = false;
}
else
{
if (diff_area < min_diff_area)
{
min_diff_area = diff_area;
min_area = geometry::area(it->first);
chosen_node = it->second;
}
else
{
if (diff_area == min_diff_area)
{
if (geometry::area(it->first) < min_area)
{
min_diff_area = diff_area;
min_area = geometry::area(it->first);
chosen_node = it->second;
}
}
}
}
}
return chosen_node;
}
/**
* \brief Empty the node
*/
virtual void empty_nodes()
{
m_nodes.clear();
}
/**
* \brief Projector for parent
*/
inline node_pointer get_parent() const
{
return m_parent;
}
/**
* \brief Update the parent of all the childs
*/
void update_parent(node_pointer const& node)
{
for (typename node_map::iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
it->second->set_parent(node);
}
}
/**
* \brief Set parent
*/
void set_parent(node_pointer const& node)
{
m_parent = node;
}
/**
* \brief Value projector for leaf_node (not allowed for non-leaf nodes)
*/
virtual Value get_value(unsigned int) const
{
// TODO: mloskot - define & use GGL exception
throw std::logic_error("No values in a non-leaf node.");
}
/**
* \brief Box projector for node 'index'
*/
virtual Box get_box(unsigned int index) const
{
return m_nodes[index].first;
}
/**
* \brief Box projector for node pointed by 'leaf'
*/
virtual Box get_box(node_pointer const& leaf) const
{
for (typename node_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
if (it->second.get() == leaf.get())
{
return it->first;
}
}
// TODO: mloskot - define & use GGL exception
throw std::logic_error("Node not found");
}
/**
* \brief Children projector
*/
node_map get_nodes() const
{
return m_nodes;
}
/**
* \brief Get leaves for a node
*/
virtual std::vector<std::pair<Box, Value> > get_leaves() const
{
typedef std::vector<std::pair<Box, Value> > leaf_type;
leaf_type leaf;
for (typename node_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
leaf_type this_leaves = it->second->get_leaves();
for (typename leaf_type::iterator it_leaf = this_leaves.begin();
it_leaf != this_leaves.end(); ++it_leaf)
{
leaf.push_back(*it_leaf);
}
}
return leaf;
}
/**
* \brief Print Rtree subtree (mainly for debug)
*/
virtual void print() const
{
std::cerr << " --> Node --------" << std::endl;
std::cerr << " Address: " << this << std::endl;
std::cerr << " Level: " << m_level << std::endl;
std::cerr << " Size: " << m_nodes.size() << std::endl;
std::cerr << " | ";
for(typename node_map::const_iterator it = m_nodes.begin(); it != m_nodes.end(); ++it)
{
if (this != it->second->get_parent().get())
{
std::cerr << "ERROR - " << this << " is not " << it->second->get_parent().get() << " ";
}
std::cerr << "( " << geometry::get<min_corner, 0>(it->first) << " , "
<< geometry::get<min_corner, 1>(it->first) << " ) x ";
std::cerr << "( " << geometry::get<max_corner, 0>(it->first) << " , "
<< geometry::get<max_corner, 1>(it->first) << " )";
std::cerr << " | ";
}
std::cerr << std::endl;
std::cerr << " --< Node --------" << std::endl;
// print child nodes
std::cerr << " Children: " << std::endl;
for (typename node_map::const_iterator it = m_nodes.begin();
it != m_nodes.end(); ++it)
{
it->second->print();
}
}
private:
/// parent node
node_pointer m_parent;
/// level of this node
// TODO: mloskot - Why not std::size_t or node_map::size_type, same with member functions?
unsigned int m_level;
/// child nodes
node_map m_nodes;
};
}}} // namespace boost::geometry::index
#endif // BOOST_GEOMETRY_EXTENSIONS_INDEX_RTREE_RTREE_NODE_HPP

View file

@ -0,0 +1,272 @@
// Copyright 2007 Christian Henning.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/*************************************************************************************************/
#ifndef GIL_HSL_H
#define GIL_HSL_H
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Support for HSL color space
/// \author Christian Henning \n
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/gil/gil_all.hpp>
namespace boost { namespace gil {
/// \addtogroup ColorNameModel
/// \{
namespace hsl_color_space
{
/// \brief Hue
struct hue_t {};
/// \brief Saturation
struct saturation_t {};
/// \brief Lightness
struct lightness_t {};
}
/// \}
/// \ingroup ColorSpaceModel
typedef mpl::vector3< hsl_color_space::hue_t
, hsl_color_space::saturation_t
, hsl_color_space::lightness_t
> hsl_t;
/// \ingroup LayoutModel
typedef layout<hsl_t> hsl_layout_t;
GIL_DEFINE_ALL_TYPEDEFS( 32f, hsl );
/// \ingroup ColorConvert
/// \brief RGB to HSL
template <>
struct default_color_converter_impl< rgb_t, hsl_t >
{
template <typename P1, typename P2>
void operator()( const P1& src, P2& dst ) const
{
using namespace hsl_color_space;
// only bits32f for hsl is supported
bits32f temp_red = channel_convert<bits32f>( get_color( src, red_t() ));
bits32f temp_green = channel_convert<bits32f>( get_color( src, green_t() ));
bits32f temp_blue = channel_convert<bits32f>( get_color( src, blue_t() ));
bits32f hue, saturation, lightness;
bits32f min_color, max_color;
if( temp_red < temp_green )
{
min_color = std::min( temp_blue, temp_red );
max_color = std::max( temp_blue, temp_green );
}
else
{
min_color = std::min( temp_blue, temp_green );
max_color = std::max( temp_blue, temp_red );
}
if ( max_color - min_color < 0.001 )
{
// rgb color is gray
hue = 0.f;
saturation = 0.f;
// doesn't matter which rgb channel we use.
lightness = temp_red;
}
else
{
bits32f diff = max_color - min_color;
// lightness calculation
lightness = ( min_color + max_color ) / 2.f;
// saturation calculation
if( lightness < 0.5f )
{
saturation = diff
/ ( min_color + max_color );
}
else
{
saturation = ( max_color - min_color )
/ ( 2.f - diff );
}
// hue calculation
if( std::abs( max_color - temp_red ) < 0.0001f )
{
// max_color is red
hue = ( temp_green - temp_blue )
/ diff;
}
else if( std::abs( max_color - temp_green) < 0.0001f )
{
// max_color is green
// 2.0 + (b - r) / (maxColor - minColor)
hue = 2.f
+ ( temp_blue - temp_red )
/ diff;
}
else
{
// max_color is blue
// 4.0 + (r - g) / (maxColor - minColor)
hue = 4.f
+ ( temp_red - temp_green )
/ diff;
}
hue /= 6.f;
if( hue < 0.f )
{
hue += 1.f;
}
}
get_color( dst,hue_t() ) = hue;
get_color( dst,saturation_t() ) = saturation;
get_color( dst,lightness_t() ) = lightness;
}
};
/// \ingroup ColorConvert
/// \brief HSL to RGB
template <>
struct default_color_converter_impl<hsl_t,rgb_t>
{
template <typename P1, typename P2>
void operator()( const P1& src, P2& dst) const
{
using namespace hsl_color_space;
bits32f red, green, blue;
if( std::abs( get_color( src, saturation_t() )) < 0.0001 )
{
// If saturation is 0, the color is a shade of gray
red = get_color( src, lightness_t() );
green = get_color( src, lightness_t() );
blue = get_color( src, lightness_t() );
}
else
{
float temp1, temp2;
float tempr, tempg, tempb;
//Set the temporary values
if( get_color( src, lightness_t() ) < 0.5 )
{
temp2 = get_color( src, lightness_t() )
* ( 1.f + get_color( src, saturation_t() ) );
}
else
{
temp2 = ( get_color( src, lightness_t() ) + get_color( src, saturation_t() ))
- ( get_color( src, lightness_t() ) * get_color( src, saturation_t() ));
}
temp1 = 2.f
* get_color( src, lightness_t() )
- temp2;
tempr = get_color( src, hue_t() ) + 1.f / 3.f;
if( tempr > 1.f )
{
tempr--;
}
tempg = get_color( src, hue_t() );
tempb = get_color( src, hue_t() ) - 1.f / 3.f;
if( tempb < 0.f )
{
tempb++;
}
//Red
if( tempr < 1.f / 6.f )
{
red = temp1 + ( temp2 - temp1 ) * 6.f * tempr;
}
else if( tempr < 0.5f )
{
red = temp2;
}
else if( tempr < 2.f / 3.f )
{
red = temp1 + (temp2 - temp1)
* (( 2.f / 3.f ) - tempr) * 6.f;
}
else
{
red = temp1;
}
//Green
if( tempg < 1.f / 6.f )
{
green = temp1 + ( temp2 - temp1 ) * 6.f * tempg;
}
else if( tempg < 0.5f )
{
green = temp2;
}
else if( tempg < 2.f / 3.f )
{
green = temp1 + ( temp2 - temp1 )
* (( 2.f / 3.f ) - tempg) * 6.f;
}
else
{
green = temp1;
}
//Blue
if( tempb < 1.f / 6.f )
{
blue = temp1 + (temp2 - temp1) * 6.f * tempb;
}
else if( tempb < 0.5f )
{
blue = temp2;
}
else if( tempb < 2.f / 3.f )
{
blue = temp1 + (temp2 - temp1)
* (( 2.f / 3.f ) - tempb) * 6.f;
}
else
{
blue = temp1;
}
}
get_color(dst,red_t()) =
channel_convert<typename color_element_type< P2, red_t >::type>( red );
get_color(dst,green_t())=
channel_convert<typename color_element_type< P2, green_t >::type>( green );
get_color(dst,blue_t()) =
channel_convert<typename color_element_type< P2, blue_t >::type>( blue );
}
};
} } // namespace boost::gil
#endif // GIL_HSL_H

View file

@ -0,0 +1,245 @@
// Copyright 2004 Christian Henning.
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
/*************************************************************************************************/
#ifndef GIL_HSV_H
#define GIL_HSV_H
////////////////////////////////////////////////////////////////////////////////////////
/// \file
/// \brief Support for HSV color space
/// \author Christian Henning \n
////////////////////////////////////////////////////////////////////////////////////////
#include <boost/cast.hpp>
#include <boost/gil/gil_all.hpp>
namespace boost { namespace gil {
/// \addtogroup ColorNameModel
/// \{
namespace hsv_color_space
{
/// \brief Hue
struct hue_t {};
/// \brief Saturation
struct saturation_t{};
/// \brief Value
struct value_t {};
}
/// \}
/// \ingroup ColorSpaceModel
typedef mpl::vector3< hsv_color_space::hue_t
, hsv_color_space::saturation_t
, hsv_color_space::value_t
> hsv_t;
/// \ingroup LayoutModel
typedef layout<hsv_t> hsv_layout_t;
GIL_DEFINE_ALL_TYPEDEFS( 32f, hsv )
/// \ingroup ColorConvert
/// \brief RGB to HSV
template <>
struct default_color_converter_impl< rgb_t, hsv_t >
{
template <typename P1, typename P2>
void operator()( const P1& src, P2& dst ) const
{
using namespace hsv_color_space;
// only bits32f for hsv is supported
bits32f temp_red = channel_convert<bits32f>( get_color( src, red_t() ));
bits32f temp_green = channel_convert<bits32f>( get_color( src, green_t() ));
bits32f temp_blue = channel_convert<bits32f>( get_color( src, blue_t() ));
bits32f hue, saturation, value;
bits32f min_color, max_color;
if( temp_red < temp_green )
{
min_color = std::min( temp_blue, temp_red );
max_color = std::max( temp_blue, temp_green );
}
else
{
min_color = std::min( temp_blue, temp_green );
max_color = std::max( temp_blue, temp_red );
}
value = max_color;
bits32f diff = max_color - min_color;
if( max_color < 0.0001f )
{
saturation = 0.f;
}
else
{
saturation = diff / max_color;
}
if( saturation < 0.0001f )
{
//it doesn't matter what value it has
hue = 0.f;
}
else
{
if( temp_red == max_color )
{
hue = ( temp_green - temp_blue )
/ diff;
}
else if( temp_green == max_color )
{
hue = 2.f + ( temp_blue - temp_red )
/ diff;
}
else
{
hue = 4.f + ( temp_red - temp_green )
/ diff;
}
//to bring it to a number between 0 and 1
hue /= 6.f;
if( hue < 0.f )
{
hue++;
}
}
get_color( dst, hue_t() ) = hue;
get_color( dst, saturation_t() ) = saturation;
get_color( dst, value_t() ) = value;
}
};
/// \ingroup ColorConvert
/// \brief HSV to RGB
template <>
struct default_color_converter_impl<hsv_t,rgb_t>
{
template <typename P1, typename P2>
void operator()( const P1& src, P2& dst) const
{
using namespace hsv_color_space;
bits32f red, green, blue;
//If saturation is 0, the color is a shade of gray
if( std::abs( get_color( src, saturation_t() )) < 0.0001f )
{
// If saturation is 0, the color is a shade of gray
red = get_color( src, value_t() );
green = get_color( src, value_t() );
blue = get_color( src, value_t() );
}
else
{
bits32f frac, p, q, t, h;
bits32 i;
//to bring hue to a number between 0 and 6, better for the calculations
h = get_color( src, hue_t() );
h *= 6.f;
i = static_cast<bits32>( floor( h ));
frac = h - i;
// p = value * (1 - saturation)
p = get_color( src, value_t() )
* ( 1.f - get_color( src, saturation_t() ));
// q = value * (1 - saturation * hue_frac)
// it drops with increasing distance from floor(hue)
q = get_color( src, value_t() )
* ( 1.f - ( get_color( src, saturation_t() ) * frac ));
// t = value * (1 - (saturation * (1 - hue_frac))
// it grows with increasing distance from floor(hue)
t = get_color( src, value_t() )
* ( 1.f - ( get_color( src, saturation_t() ) * ( 1.f - frac )));
switch( i % 6 )
{
case 0: // red to yellow
{
red = get_color( src, value_t() );
green = t;
blue = p;
break;
}
case 1: // yellow to green
{
red = q;
green = get_color( src, value_t() );
blue = p;
break;
}
case 2: // green to cyan
{
red = p;
green = get_color( src, value_t() );
blue = t;
break;
}
case 3: // cyan to blue
{
red = p;
green = q;
blue = get_color( src, value_t() );
break;
}
case 4: // blue to magenta
{
red = t;
green = p;
blue = get_color( src, value_t() );
break;
}
case 5: // magenta to red
{
red = get_color( src, value_t() );
green = p;
blue = q;
break;
}
}
}
get_color(dst,red_t()) =
channel_convert<typename color_element_type< P2, red_t >::type>( red );
get_color(dst,green_t())=
channel_convert<typename color_element_type< P2, green_t >::type>( green );
get_color(dst,blue_t()) =
channel_convert<typename color_element_type< P2, blue_t >::type>( blue );
}
};
} } // namespace boost::gil
#endif // GIL_HSV_H

2
configure vendored
View file

@ -1,3 +1,3 @@
#!/bin/sh
python scons/scons.py configure $@
python scons/scons.py --implicit-deps-changed configure "$@"

View file

@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
#
import os
from copy import copy
@ -36,12 +36,11 @@ demo_env = env.Clone()
demo_env['CXXFLAGS'] = copy(env['LIBMAPNIK_CXXFLAGS'])
if env['HAS_CAIRO']:
demo_env.PrependUnique(CPPPATH=env['CAIROMM_CPPPATHS'])
demo_env.PrependUnique(CPPPATH=env['CAIRO_CPPPATHS'])
demo_env.Append(CXXFLAGS = '-DHAVE_CAIRO')
libraries = copy(env['LIBMAPNIK_LIBS'])
boost_program_options = 'boost_program_options%s' % env['BOOST_APPEND']
libraries.extend([boost_program_options,'mapnik'])
libraries.append('mapnik')
rundemo = demo_env.Program('rundemo', source, LIBS=libraries, LINKFLAGS=env["CUSTOM_LDFLAGS"])

View file

@ -21,6 +21,13 @@
*****************************************************************************/
#include <mapnik/map.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/line_symbolizer.hpp>
#include <mapnik/polygon_symbolizer.hpp>
#include <mapnik/text_symbolizer.hpp>
#include <mapnik/feature_type_style.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/datasource_cache.hpp>
#include <mapnik/font_engine_freetype.hpp>
#include <mapnik/agg_renderer.hpp>
@ -30,9 +37,8 @@
#include <mapnik/config_error.hpp>
#if defined(HAVE_CAIRO)
// cairo
#include <mapnik/cairo_renderer.hpp>
#include <cairomm/surface.h>
#include <mapnik/cairo_context.hpp>
#endif
#include <iostream>
@ -42,23 +48,28 @@ int main ( int argc , char** argv)
{
if (argc != 2)
{
std::cout << "usage: ./rundemo <mapnik_install_dir>\nUsually /usr/local/lib/mapnik\n";
std::cout << "usage: ./rundemo <mapnik_install_dir>\nUsually /usr/local\n";
std::cout << "Warning: ./rundemo looks for data in ../data/,\nTherefore must be run from within the demo/c++ folder.\n";
return EXIT_SUCCESS;
}
using namespace mapnik;
const std::string srs_lcc="+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 \
+datum=NAD83 +units=m +no_defs";
const std::string srs_merc="+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 \
+lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs";
try {
std::cout << " running demo ... \n";
std::string mapnik_dir(argv[1]);
std::cout << " looking for 'shape.input' plugin in... " << mapnik_dir << "/input/" << "\n";
datasource_cache::instance()->register_datasources(mapnik_dir + "/input/");
std::cout << " looking for DejaVuSans font in... " << mapnik_dir << "/fonts/DejaVuSans.ttf" << "\n";
freetype_engine::register_font(mapnik_dir + "/fonts/DejaVuSans.ttf");
std::cout << " looking for 'shape.input' plugin in... " << mapnik_dir << "/lib/mapnik/input/" << "\n";
datasource_cache::instance().register_datasources(mapnik_dir + "/lib/mapnik/input/");
std::cout << " looking for DejaVuSans font in... " << mapnik_dir << "/lib/mapnik/fonts/DejaVuSans.ttf" << "\n";
freetype_engine::register_font(mapnik_dir + "/lib/mapnik/fonts/DejaVuSans.ttf");
Map m(800,600);
m.set_background(color_factory::from_string("white"));
m.set_background(parse_color("white"));
m.set_srs(srs_merc);
// create styles
// Provinces (polygon)
@ -178,8 +189,9 @@ int main ( int argc , char** argv)
p["encoding"]="latin1";
layer lyr("Provinces");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.add_style("provinces");
lyr.set_srs(srs_lcc);
m.addLayer(lyr);
}
@ -189,7 +201,8 @@ int main ( int argc , char** argv)
p["type"]="shape";
p["file"]="../data/qcdrainage";
layer lyr("Quebec Hydrography");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.set_srs(srs_lcc);
lyr.add_style("drainage");
m.addLayer(lyr);
}
@ -198,9 +211,9 @@ int main ( int argc , char** argv)
parameters p;
p["type"]="shape";
p["file"]="../data/ontdrainage";
layer lyr("Ontario Hydrography");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.set_srs(srs_lcc);
lyr.add_style("drainage");
m.addLayer(lyr);
}
@ -211,7 +224,8 @@ int main ( int argc , char** argv)
p["type"]="shape";
p["file"]="../data/boundaries_l";
layer lyr("Provincial borders");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_srs(srs_lcc);
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.add_style("provlines");
m.addLayer(lyr);
}
@ -222,7 +236,8 @@ int main ( int argc , char** argv)
p["type"]="shape";
p["file"]="../data/roads";
layer lyr("Roads");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_srs(srs_lcc);
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.add_style("smallroads");
lyr.add_style("road-border");
lyr.add_style("road-fill");
@ -238,22 +253,22 @@ int main ( int argc , char** argv)
p["file"]="../data/popplaces";
p["encoding"] = "latin1";
layer lyr("Populated Places");
lyr.set_datasource(datasource_cache::instance()->create(p));
lyr.set_srs(srs_lcc);
lyr.set_datasource(datasource_cache::instance().create(p));
lyr.add_style("popplaces");
m.addLayer(lyr);
}
m.zoom_to_box(box2d<double>(1405120.04127408,-247003.813399447,
1706357.31328276,-25098.593149577));
m.zoom_to_box(box2d<double>(-8024477.28459,5445190.38849,-7381388.20071,5662941.44855));
image_32 buf(m.width(),m.height());
agg_renderer<image_32> ren(m,buf);
ren.apply();
save_to_file<image_data_32>(buf.data(),"demo.jpg","jpeg");
save_to_file<image_data_32>(buf.data(),"demo.png","png");
save_to_file<image_data_32>(buf.data(),"demo256.png","png256");
save_to_file<image_data_32>(buf.data(),"demo.tif","tiff");
save_to_file(buf,"demo.jpg","jpeg");
save_to_file(buf,"demo.png","png");
save_to_file(buf,"demo256.png","png8");
save_to_file(buf,"demo.tif","tiff");
std::cout << "Three maps have been rendered using AGG in the current directory:\n"
"- demo.jpg\n"
@ -263,24 +278,31 @@ int main ( int argc , char** argv)
"Have a look!\n";
#if defined(HAVE_CAIRO)
Cairo::RefPtr<Cairo::ImageSurface> image_surface;
// save to pdf/svg files
save_to_cairo_file(m,"cairo-demo.pdf");
save_to_cairo_file(m,"cairo-demo.svg");
image_surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, m.width(),m.height());
cairo_renderer<Cairo::Surface> png_render(m, image_surface);
/* we could also do:
save_to_cairo_file(m,"cairo-demo.png");
but instead let's build up a surface for more flexibility
*/
cairo_surface_ptr image_surface(
cairo_image_surface_create(CAIRO_FORMAT_ARGB32,m.width(),m.height()),
cairo_surface_closer());
double scale_factor = 1.0;
cairo_ptr image_context = (create_context(image_surface));
mapnik::cairo_renderer<cairo_ptr> png_render(m,image_context,scale_factor);
png_render.apply();
image_surface->write_to_png("cairo-demo.png");
// we can now write to png with cairo functionality
cairo_surface_write_to_png(&*image_surface, "cairo-demo.png");
// but we can also benefit from quantization by converting
// to a mapnik image object and then saving that
image_32 im(image_surface);
save_to_file(im, "cairo-demo256.png","png256");
Cairo::RefPtr<Cairo::Surface> surface;
surface = Cairo::PdfSurface::create("cairo-demo.pdf", m.width(),m.height());
cairo_renderer<Cairo::Surface> pdf_render(m, surface);
pdf_render.apply();
surface = Cairo::SvgSurface::create("cairo-demo.svg", m.width(),m.height());
cairo_renderer<Cairo::Surface> svg_render(m, surface);
svg_render.apply();
save_to_file(im, "cairo-demo256.png","png8");
cairo_surface_finish(&*image_surface);
std::cout << "Three maps have been rendered using Cairo in the current directory:\n"
"- cairo-demo.png\n"

View file

@ -1,5 +1,3 @@
# $Id$
This directory contains a sample python script implementing the Mapnik API.
The script is thoroughly commented and also acts as a mini tutorial. Reading

View file

@ -1,7 +1,7 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# $Id$
#
#
# This file is part of Mapnik (c++ mapping toolkit)
# Copyright (C) 2005 Jean-Francois Doyon
@ -186,7 +186,7 @@ m.layers.append(provlines_lyr)
roads34_lyr = mapnik.Layer('Roads')
roads34_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs"
# create roads datasource (we're going to re-use it later)
# create roads datasource (we're going to re-use it later)
roads34_lyr.datasource = mapnik.Shapefile(file='../data/roads')
@ -221,7 +221,7 @@ m.layers.append(roads34_lyr)
roads2_lyr = mapnik.Layer('Roads')
roads2_lyr.srs = "+proj=lcc +ellps=GRS80 +lat_0=49 +lon_0=-95 +lat+1=49 +lat_2=77 +datum=NAD83 +units=m +no_defs"
# Just get a copy from roads34_lyr
roads2_lyr.datasource = roads34_lyr.datasource
roads2_lyr.datasource = roads34_lyr.datasource
roads2_style_1 = mapnik.Style()
roads2_rule_1 = mapnik.Rule()
@ -306,7 +306,7 @@ popplaces_text_symbolizer = mapnik.TextSymbolizer(mapnik.Expression("[GEONAME]")
# We set a "halo" around the text, which looks like an outline if thin enough,
# or an outright background if large enough.
popplaces_text_symbolizer.label_placement= mapnik.label_placement.POINT_PLACEMENT
popplaces_text_symbolizer.halo_fill = mapnik.Color('white')
popplaces_text_symbolizer.halo_fill = mapnik.Color(255,255,200)
popplaces_text_symbolizer.halo_radius = 1
popplaces_text_symbolizer.avoid_edges = True
#popplaces_text_symbolizer.minimum_padding = 30
@ -322,7 +322,7 @@ m.layers.append(popplaces_lyr)
# Draw map
# Set the initial extent of the map in 'master' spherical Mercator projection
m.zoom_to_box(mapnik.Box2d(-8024477.28459,5445190.38849,-7381388.20071,5662941.44855))
m.zoom_to_box(mapnik.Box2d(-8024477.28459,5445190.38849,-7381388.20071,5662941.44855))
# Render map
im = mapnik.Image(m.width,m.height)
@ -354,7 +354,7 @@ images_.append('demo.tif')
# Render cairo examples
if HAS_PYCAIRO_MODULE and mapnik.has_pycairo():
svg_surface = cairo.SVGSurface('demo.svg', m.width,m.height)
mapnik.render(m, svg_surface)
svg_surface.finish()
@ -368,7 +368,7 @@ if HAS_PYCAIRO_MODULE and mapnik.has_pycairo():
postscript_surface = cairo.PSSurface('demo.ps', m.width,m.height)
mapnik.render(m, postscript_surface)
images_.append('demo.ps')
postscript_surface.finish()
postscript_surface.finish()
image_surface = cairo.ImageSurface(cairo.FORMAT_RGB24, m.width, m.height)
mapnik.render(m, image_surface)
@ -381,12 +381,12 @@ if HAS_PYCAIRO_MODULE and mapnik.has_pycairo():
image_surface.write_to_png('demo_cairo_argb32.png')
images_.append('demo_cairo_argb32.png')
image_surface.finish()
else:
print '\n\nPycairo not available...',
if mapnik.has_cairo():
print ' will render Cairo formats using alternative method'
mapnik.render_to_file(m,'demo.pdf')
images_.append('demo.pdf')
mapnik.render_to_file(m,'demo.ps')
@ -399,7 +399,7 @@ else:
images_.append('demo_cairo_argb.png')
print "\n\n", len(images_), "maps have been rendered in the current directory:"
for im_ in images_:
print "-", im_

23
demo/simple-renderer/render.py Executable file
View file

@ -0,0 +1,23 @@
#!/usr/bin/env python
import sys
import mapnik
def render(input_file, output_file, width=800, height=800, bbox=None):
m = mapnik.Map(width, height)
mapnik.load_map(m, input_file, False)
if bbox is not None:
m.zoom_to_box(bbox)
else:
m.zoom_all()
mapnik.render_to_file(m, output_file)
if len(sys.argv) == 2:
render(sys.argv[1], "output.png")
elif len(sys.argv) == 3:
render(sys.argv[1], sys.argv[2])
elif len(sys.argv) == 5:
render(sys.argv[1], sys.argv[2], int(sys.argv[3]), int(sys.argv[4]))
else:
print "usage: %s style_file [output_file] [width height]" % sys.argv[0]
sys.exit(1)

View file

@ -42,7 +42,7 @@ road_rule.symbols.append(LineSymbolizer(road_stroke))
road_style.rules.append(road_rule);
#Road text
text_symbolizer = TextSymbolizer('NAME', 'DejaVu Sans Book', 20, Color('black'))
text_symbolizer = TextSymbolizer(Expression('[NAME]'), 'DejaVu Sans Book', 20, Color('black'))
text_symbolizer.label_placement=label_placement.LINE_PLACEMENT
text_symbolizer.minimum_distance = 0
#text_symbolizer.max_char_angle_delta = 40
@ -77,6 +77,6 @@ im = Image(m.width,m.height)
render(m, im)
# Save image to file
save_to_file('output.png', 'png',im) # true-colour RGBA
im.save('output.png') # true-colour RGBA
print "Done\n"

View file

@ -43,7 +43,7 @@ road_rule.symbols.append(LineSymbolizer(road_stroke))
road_style.rules.append(road_rule);
#Road text
text_symbolizer = TextSymbolizer('NAME', 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer = TextSymbolizer(Expression('[NAME]'), 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer.label_placement=label_placement.LINE_PLACEMENT
text_symbolizer.minimum_distance = 0
#text_symbolizer.max_char_angle_delta = 40
@ -78,6 +78,6 @@ im = Image(m.width,m.height)
render(m, im)
# Save image to file
save_to_file('output.png', 'png',im) # true-colour RGBA
im.save('output.png') # true-colour RGBA
print "Done\n"

View file

@ -44,7 +44,7 @@ road_rule.symbols.append(LineSymbolizer(road_stroke))
road_style.rules.append(road_rule);
#Road text
text_symbolizer = TextSymbolizer('NAME', 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer = TextSymbolizer(Expression('[NAME]'), 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer.label_placement=label_placement.LINE_PLACEMENT
text_symbolizer.minimum_distance = 0
#text_symbolizer.max_char_angle_delta = 40
@ -79,6 +79,6 @@ im = Image(m.width,m.height)
render(m, im)
# Save image to file
save_to_file('output.png', 'png',im) # true-colour RGBA
im.save('output.png') # true-colour RGBA
print "Done\n"

View file

@ -43,7 +43,7 @@ road_rule.symbols.append(LineSymbolizer(road_stroke))
road_style.rules.append(road_rule);
#Road text
text_symbolizer = TextSymbolizer('NAME', 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer = TextSymbolizer(Expression('[NAME]'), 'DejaVu Sans Book', 10, Color('black'))
text_symbolizer.label_placement=label_placement.LINE_PLACEMENT
text_symbolizer.minimum_distance = 0
#text_symbolizer.max_char_angle_delta = 40
@ -77,6 +77,6 @@ im = Image(m.width,m.height)
render(m, im)
# Save image to file
save_to_file('output.png', 'png',im) # true-colour RGBA
im.save('output.png') # true-colour RGBA
print "Done\n"

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "about_dialog.hpp"

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#if !defined ABOUT_DIALOG_HPP

View file

@ -17,7 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#
# $Id$
#
Import ('env')
import os

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "info_dialog.hpp"

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef INFO_DIALOG_HPP

View file

@ -17,11 +17,12 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "layer_info_dialog.hpp"
// mapnik
#include <mapnik/params.hpp>
#include <mapnik/params_impl.hpp>
#include <mapnik/layer.hpp>

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef LAYER_INFO_DIALOG_HPP

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include <QtGui>
#include "layerdelegate.hpp"

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef LAYER_DELEGATE_HPP
#define LAYER_DELEGATE_HPP

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "layerlistmodel.hpp"

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef LAYER_LIST_MODEL_HPP
@ -26,7 +25,9 @@
#include <QAbstractListModel>
#include <QModelIndex>
#include <QVariant>
#ifndef Q_MOC_RUN
#include <mapnik/map.hpp>
#endif
#include <boost/optional/optional.hpp>
class LayerListModel : public QAbstractListModel

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "layerwidget.hpp"
#include <qabstractitemdelegate.h>

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef LAYERWIDGET_HPP
#define LAYERWIDGET_HPP

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
// qt
#include <QApplication>
@ -36,52 +35,59 @@ int main( int argc, char **argv )
using mapnik::datasource_cache;
using mapnik::freetype_engine;
QCoreApplication::setOrganizationName("Mapnik");
QCoreApplication::setOrganizationDomain("mapnik.org");
QCoreApplication::setApplicationName("Viewer");
QSettings settings("viewer.ini",QSettings::IniFormat);
// register input plug-ins
QString plugins_dir = settings.value("mapnik/plugins_dir",
QVariant("/usr/local/lib/mapnik/input/")).toString();
datasource_cache::instance()->register_datasources(plugins_dir.toStdString());
// register fonts
int count = settings.beginReadArray("mapnik/fonts");
for (int index=0; index < count; ++index)
try
{
settings.setArrayIndex(index);
QString font_dir = settings.value("dir").toString();
freetype_engine::register_fonts(font_dir.toStdString());
}
settings.endArray();
QCoreApplication::setOrganizationName("Mapnik");
QCoreApplication::setOrganizationDomain("mapnik.org");
QCoreApplication::setApplicationName("Viewer");
QSettings settings("viewer.ini",QSettings::IniFormat);
QApplication app( argc, argv );
MainWindow window;
window.show();
if (argc > 1) window.open(argv[1]);
if (argc >= 3)
{
QStringList list = QString(argv[2]).split(",");
if (list.size()==4)
// register input plug-ins
QString plugins_dir = settings.value("mapnik/plugins_dir",
QVariant("/usr/local/lib/mapnik/input/")).toString();
datasource_cache::instance().register_datasources(plugins_dir.toStdString());
// register fonts
int count = settings.beginReadArray("mapnik/fonts");
for (int index=0; index < count; ++index)
{
settings.setArrayIndex(index);
QString font_dir = settings.value("dir").toString();
freetype_engine::register_fonts(font_dir.toStdString());
}
settings.endArray();
QApplication app( argc, argv );
MainWindow window;
window.show();
if (argc > 1) window.open(argv[1]);
if (argc >= 3)
{
QStringList list = QString(argv[2]).split(",");
if (list.size()==4)
{
bool ok;
double x0 = list[0].toDouble(&ok);
double y0 = list[1].toDouble(&ok);
double x1 = list[2].toDouble(&ok);
double y1 = list[3].toDouble(&ok);
if (ok) window.set_default_extent(x0,y0,x1,y1);
}
}
else
{
window.zoom_all();
}
if (argc == 4)
{
bool ok;
double x0 = list[0].toDouble(&ok);
double y0 = list[1].toDouble(&ok);
double x1 = list[2].toDouble(&ok);
double y1 = list[3].toDouble(&ok);
if (ok) window.set_default_extent(x0,y0,x1,y1);
double scaling_factor = QString(argv[3]).toDouble(&ok);
if (ok) window.set_scaling_factor(scaling_factor);
}
return app.exec();
}
else
catch (std::exception const& ex)
{
window.zoom_all();
std::cerr << "Could not start viewer: '" << ex.what() << "'\n";
return 1;
}
if (argc == 4)
{
bool ok;
double scaling_factor = QString(argv[3]).toDouble(&ok);
if (ok) window.set_scaling_factor(scaling_factor);
}
return app.exec();
}

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
// stl
#include <iostream>
@ -31,11 +30,17 @@
#include <QList>
#include <QItemDelegate>
#include <QSlider>
#include <QComboBox>
#include <QDoubleSpinBox>
// mapnik
#ifndef Q_MOC_RUN // QT moc chokes on BOOST_JOIN
#include <mapnik/config_error.hpp>
#include <mapnik/load_map.hpp>
#include <mapnik/save_map.hpp>
#include <mapnik/projection.hpp>
#endif
// qt
#include "mainwindow.hpp"
@ -94,6 +99,13 @@ MainWindow::MainWindow()
connect(mapWidget_, SIGNAL(mapViewChanged()),layerTab_, SLOT(update()));
// slider
connect(slider_,SIGNAL(valueChanged(int)),mapWidget_,SLOT(zoomToLevel(int)));
// renderer selector
connect(renderer_selector_,SIGNAL(currentIndexChanged(QString const&)),
mapWidget_, SLOT(updateRenderer(QString const&)));
// scale factor
connect(scale_factor_,SIGNAL(valueChanged(double)),
mapWidget_, SLOT(updateScaleFactor(double)));
//
connect(layerTab_,SIGNAL(update_mapwidget()),mapWidget_,SLOT(updateMap()));
connect(layerTab_,SIGNAL(layerSelected(int)),
@ -365,6 +377,23 @@ void MainWindow::createToolBars()
fileToolBar->addAction(infoAct);
fileToolBar->addAction(reloadAct);
fileToolBar->addAction(printAct);
renderer_selector_ = new QComboBox(fileToolBar);
renderer_selector_->setFocusPolicy(Qt::NoFocus);
renderer_selector_->addItem("AGG");
#ifdef HAVE_CAIRO
renderer_selector_->addItem("Cairo");
#endif
renderer_selector_->addItem("Grid");
fileToolBar->addWidget(renderer_selector_);
scale_factor_ = new QDoubleSpinBox(fileToolBar);
scale_factor_->setMinimum(0.1);
scale_factor_->setMaximum(5.0);
scale_factor_->setSingleStep(0.1);
scale_factor_->setValue(1.0);
fileToolBar->addWidget(scale_factor_);
slider_ = new QSlider(Qt::Horizontal,fileToolBar);
slider_->setRange(1,18);
slider_->setTickPosition(QSlider::TicksBelow);

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
@ -28,6 +27,7 @@
#include <QActionGroup>
#include <QStatusBar>
#include <QAbstractItemModel>
#include <QDoubleSpinBox>
#include "mapwidget.hpp"
@ -36,6 +36,8 @@
class LayerTab;
class StyleTab;
class QSlider;
class QComboBox;
class QDoubleSpinBox;
class MainWindow : public QMainWindow
{
@ -106,6 +108,8 @@ private:
//status bar
QStatusBar *status;
QSlider * slider_;
QComboBox * renderer_selector_;
QDoubleSpinBox * scale_factor_;
mapnik::box2d<double> default_extent_;
};

View file

@ -17,13 +17,14 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include <QtGui>
#include <boost/bind.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/graphics.hpp>
#include <mapnik/grid/grid_renderer.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/scale_denominator.hpp>
@ -31,6 +32,13 @@
#include <mapnik/memory_datasource.hpp>
#include <mapnik/feature_kv_iterator.hpp>
#include <mapnik/config_error.hpp>
#include <mapnik/image_util.hpp>
#ifdef HAVE_CAIRO
// cairo
#include <mapnik/cairo_renderer.hpp>
#endif
#include "mapwidget.hpp"
#include "info_dialog.hpp"
@ -81,7 +89,8 @@ MapWidget::MapWidget(QWidget *parent)
first_(true),
pen_(QColor(0,0,255,96)),
selectedLayer_(-1),
scaling_factor_(1.0)
scaling_factor_(1.0),
cur_renderer_(AGG)
{
pen_.setWidth(3);
pen_.setCapStyle(Qt::RoundCap);
@ -173,29 +182,29 @@ void MapWidget::mousePressEvent(QMouseEvent* e)
feature_ptr feat = fs->next();
if (feat)
{
feature_kv_iterator itr(*feat,true);
feature_kv_iterator end(*feat);
for ( ;itr!=end; ++itr)
{
info.push_back(QPair<QString,QString>(QString(boost::get<0>(*itr).c_str()),
boost::get<1>(*itr).to_string().c_str()));
}
typedef mapnik::coord_transform2<mapnik::CoordTransform,mapnik::geometry_type> path_type;
typedef mapnik::coord_transform<mapnik::CoordTransform,mapnik::geometry_type> path_type;
for (unsigned i=0; i<feat->num_geometries();++i)
{
mapnik::geometry_type & geom = feat->get_geometry(i);
path_type path(t,geom,prj_trans);
if (geom.num_points() > 0)
if (geom.size() > 0)
{
QPainterPath qpath;
double x,y;
path.vertex(&x,&y);
qpath.moveTo(x,y);
for (unsigned j = 1; j < geom.num_points(); ++j)
for (unsigned j = 1; j < geom.size(); ++j)
{
path.vertex(&x,&y);
qpath.lineTo(x,y);
@ -278,6 +287,32 @@ void MapWidget::mouseReleaseEvent(QMouseEvent* e)
}
}
void MapWidget::wheelEvent(QWheelEvent* e)
{
if (!map_)
{
return;
}
QPoint corner(map_->width(), map_->height());
QPoint zoomCoords;
double zoom;
if (e->delta() > 0)
{
zoom = 0.5;
QPoint center = corner / 2;
QPoint delta = e->pos() - center;
zoomCoords = zoom * delta + center;
}
else
{
zoom = 2.0;
zoomCoords = corner - e->pos();
}
map_->pan_and_zoom(zoomCoords.x(), zoomCoords.y(), zoom);
updateMap();
}
void MapWidget::keyPressEvent(QKeyEvent *e)
{
@ -449,6 +484,7 @@ void MapWidget::export_to_file(unsigned ,unsigned ,std::string const&,std::strin
//agg_renderer renderer(map,image);
//renderer.apply();
//image.saveToFile(filename,type);
std::cout << "Export to file .." << std::endl;
}
void MapWidget::set_scaling_factor(double scaling_factor)
@ -456,24 +492,129 @@ void MapWidget::set_scaling_factor(double scaling_factor)
scaling_factor_ = scaling_factor;
}
void render_agg(mapnik::Map const& map, double scaling_factor, QPixmap & pix)
{
unsigned width=map.width();
unsigned height=map.height();
image_32 buf(width,height);
mapnik::agg_renderer<image_32> ren(map,buf,scaling_factor);
try
{
ren.apply();
QImage image((uchar*)buf.raw_data(),width,height,QImage::Format_ARGB32);
pix = QPixmap::fromImage(image.rgbSwapped());
}
catch (mapnik::config_error & ex)
{
std::cerr << ex.what() << std::endl;
}
catch (const std::exception & ex)
{
std::cerr << "exception: " << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception caught!\n";
}
}
void render_grid(mapnik::Map const& map, double scaling_factor, QPixmap & pix)
{
unsigned width=map.width();
unsigned height=map.height();
mapnik::grid buf(width,height,"F_CODE", 1);
mapnik::grid_renderer<mapnik::grid> ren(map,buf,scaling_factor);
try
{
ren.apply();
mapnik::value_integer * imdata = static_cast<mapnik::value_integer*>(buf.raw_data());
// Not sure how to display long long values ??
//QImage image(width,height,QImage::Format_RGB32);
//for (unsigned i = 0 ; i < height ; ++i)
//{
// for (unsigned j = 0 ; j < width ; ++j)
// {
// image.setPixel(j,i,qRgb((uint8_t)(imdata[i*width+j]>>8),
// (uint8_t)(imdata[i*width+j+1]>>8),
// (uint8_t)(imdata[i*width+j+2]>>8)));
// }
//}
//pix = QPixmap::fromImage(image);
}
catch (mapnik::config_error & ex)
{
std::cerr << ex.what() << std::endl;
}
catch (const std::exception & ex)
{
std::cerr << "exception: " << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception caught!\n";
}
}
void render_cairo(mapnik::Map const& map, double scaling_factor, QPixmap & pix)
{
#ifdef HAVE_CAIRO
mapnik::cairo_surface_ptr image_surface(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,map.width(),map.height()),
mapnik::cairo_surface_closer());
mapnik::cairo_renderer<mapnik::cairo_surface_ptr> renderer(map, image_surface, scaling_factor);
renderer.apply();
image_32 buf(image_surface);
QImage image((uchar*)buf.raw_data(),buf.width(),buf.height(),QImage::Format_ARGB32);
pix = QPixmap::fromImage(image.rgbSwapped());
#endif
}
void MapWidget::updateRenderer(QString const& txt)
{
if (txt == "AGG") cur_renderer_ = AGG;
else if (txt == "Cairo") cur_renderer_ = Cairo;
else if (txt == "Grid") cur_renderer_ = Grid;
std::cerr << "Update renderer called" << std::endl;
updateMap();
}
void MapWidget::updateScaleFactor(double scale_factor)
{
set_scaling_factor(scale_factor);
updateMap();
}
void MapWidget::updateMap()
{
if (map_)
{
unsigned width=map_->width();
unsigned height=map_->height();
if (cur_renderer_== AGG)
{
render_agg(*map_, scaling_factor_, pix_);
}
else if (cur_renderer_ == Cairo)
{
render_cairo(*map_, scaling_factor_, pix_);
}
else if (cur_renderer_ == Grid)
{
render_grid(*map_, scaling_factor_, pix_);
}
else
{
std::cerr << "Unknown renderer..." << std::endl;
}
image_32 buf(width,height);
try
{
mapnik::agg_renderer<image_32> ren(*map_,buf,scaling_factor_);
ren.apply();
QImage image((uchar*)buf.raw_data(),width,height,QImage::Format_ARGB32);
pix_=QPixmap::fromImage(image.rgbSwapped());
try
{
projection prj(map_->srs()); // map projection
box2d<double> ext = map_->get_current_extent();
double x0 = ext.minx();
double y0 = ext.miny();
@ -486,10 +627,6 @@ void MapWidget::updateMap()
// emit signal to interested widgets
emit mapViewChanged();
}
catch (mapnik::config_error & ex)
{
std::cerr << ex.what() << std::endl;
}
catch (...)
{
std::cerr << "Unknown exception caught!\n";

View file

@ -17,7 +17,6 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef MAP_WIDGET_HPP
#define MAP_WIDGET_HPP
@ -31,13 +30,16 @@
#include <string>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#ifndef Q_MOC_RUN
#include <mapnik/map.hpp>
#endif
class MapWidget : public QWidget
{
Q_OBJECT
public:
public:
enum eTool
{
ZoomToBox = 1,
@ -45,6 +47,13 @@ class MapWidget : public QWidget
Info,
};
enum eRenderer
{
AGG,
Cairo,
Grid
};
private:
boost::shared_ptr<mapnik::Map> map_;
int selected_;
@ -60,6 +69,7 @@ private:
QPen pen_;
int selectedLayer_;
double scaling_factor_;
eRenderer cur_renderer_;
public:
MapWidget(QWidget *parent=0);
void setTool(eTool tool);
@ -79,6 +89,8 @@ public slots:
void zoomToLevel(int level);
void updateMap();
void layerSelected(int);
void updateRenderer(QString const& txt);
void updateScaleFactor(double scale_factor);
signals:
void mapViewChanged();
protected:
@ -87,6 +99,7 @@ protected:
void mousePressEvent(QMouseEvent* e);
void mouseMoveEvent(QMouseEvent* e);
void mouseReleaseEvent(QMouseEvent* e);
void wheelEvent(QWheelEvent* e);
void keyPressEvent(QKeyEvent *e);
void export_to_file(unsigned width,
unsigned height,

View file

@ -17,21 +17,23 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#include "styles_model.hpp"
#include <mapnik/expression_string.hpp>
#include <mapnik/noncopyable.hpp>
#include <mapnik/rule.hpp>
#include <mapnik/feature_type_style.hpp>
// boost
#include <boost/concept_check.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
// qt
#include <QList>
#include <QIcon>
#include <QPainter>
#include <QPixmap>
class node : private boost::noncopyable
class node : private mapnik::noncopyable
{
struct node_base
{
@ -162,6 +164,18 @@ struct symbolizer_info : public boost::static_visitor<QString>
return QString("ShieldSymbolizer");
}
QString operator() (mapnik::markers_symbolizer const& sym) const
{
boost::ignore_unused_variable_warning(sym);
return QString("MarkersSymbolizer");
}
QString operator() (mapnik::building_symbolizer const& sym) const
{
boost::ignore_unused_variable_warning(sym);
return QString("BuildingSymbolizer");
}
template <typename T>
QString operator() (T const& ) const
{
@ -223,7 +237,7 @@ class symbolizer_node
{
public:
symbolizer_node(mapnik::symbolizer const & sym)
: sym_(sym) {}
: sym_(sym) {}
~symbolizer_node(){}
QString name() const

View file

@ -17,13 +17,16 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
//$Id$
#ifndef STYLE_MODEL_HPP
#define STYLE_MODEL_HPP
#include <QAbstractItemModel>
#ifndef Q_MOC_RUN
#include <mapnik/map.hpp>
#endif
#include <boost/scoped_ptr.hpp>
class node;

View file

@ -1,18 +1,11 @@
######################################################################
# Mapnik viewer - Copyright (C) 2007 Artem Pavlenko
######################################################################
CC = g++
TEMPLATE = app
INCLUDEPATH += /usr/local/include/
INCLUDEPATH += /usr/boost/include/
INCLUDEPATH += /usr/X11/include/
INCLUDEPATH += /usr/X11/include/freetype2
INCLUDEPATH += .
QMAKE_CXXFLAGS +=' -DDARWIN -Wno-missing-field-initializers -ansi'
unix:LIBS = -L/usr/local/lib -L/usr/X11/lib -lmapnik -lfreetype
unix:LIBS += -lboost_system -licuuc -lboost_filesystem -lboost_regex
QMAKE_CXX = clang++
QMAKE_CXXFLAGS += $$system(mapnik-config --cflags)
QMAKE_LFLAGS += $$system(mapnik-config --libs)
QMAKE_LFLAGS += $$system(mapnik-config --ldflags --dep-libs)
# Input

14
deps/agg/build.py vendored
View file

@ -15,9 +15,10 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# $Id$
#
import glob
import os
from glob import glob
Import('env')
@ -28,4 +29,11 @@ if env['SUNCC']:
else:
cxxflags = env['CUSTOM_CXXFLAGS'] + ' -O%s -fPIC -DNDEBUG' % env['OPTIMIZATION']
agg_env.StaticLibrary('agg', glob.glob('./src/' + '*.cpp'), LIBS=[], CPPPATH='./include', CXXFLAGS=cxxflags, LINKFLAGS=env['CUSTOM_LDFLAGS'])
agg_env.StaticLibrary('agg', glob('./src/' + '*.cpp'), LIBS=[], CXXFLAGS=cxxflags, LINKFLAGS=env['CUSTOM_LDFLAGS'])
if 'install' in COMMAND_LINE_TARGETS:
inc_target = os.path.normpath(env['INSTALL_PREFIX']+'/include/mapnik/agg')
# TODO - restrict to just agg headers used in mapnik includes?
includes = glob('./include/*.h')
target = env.Install(inc_target, includes)
env.Alias(target='install', source=target)

View file

@ -35,10 +35,12 @@ namespace agg
//------------------------------------------------------------pod_allocator
template<class T> struct pod_allocator
{
//static T* allocate(unsigned num) { return static_cast<T*>(::operator new(sizeof(T)*num));}
//static void deallocate(T* ptr, unsigned) { ::operator delete(ptr) ;}
static T* allocate(unsigned num) { return new T [num]; }
static void deallocate(T* ptr, unsigned) { delete [] ptr; }
static void deallocate(T* ptr, unsigned) { delete [] ptr; }
};
// Single object allocator. It's also can be replaced with your custom
// allocator. The difference is that it can only allocate a single
// object and the constructor and destructor must be called.

View file

@ -29,6 +29,7 @@ namespace agg
void rewind(unsigned) {}
unsigned vertex(double*, double*) { return path_cmd_stop; }
unsigned type() const { return 0; }
};
@ -64,6 +65,7 @@ namespace agg
}
unsigned vertex(double* x, double* y);
unsigned type() const { return m_source->type(); }
private:
// Prohibit copying

Some files were not shown because too many files have changed in this diff Show more