Mapnik mapper ============= .. doctest:: :hide: >>> from shelley import Box, Color >>> from shelley.symbolizers import LineSymbolizer >>> from shelley.datasources.simple import Feature, Geometry >>> from shelley.mappers.mapnik import Style, Rule, Layer, Filter >>> import doc_utils Map --- .. currentmodule:: shelley.mappers.mapnik .. class:: Map(layers, background, srs) Defines a map that can be drawn :param layers: list of Layer objects :param background: color of the map's background :type background: string or Color object :param srs: the projection of the map Layer ----- .. class:: Layer(feature_source, styles) Applies styles to the features in the datasource :param feature_source: a FeatureSource object :param styles: a list of FeatureStyle objects Style ----- .. class:: Style(name=None, rules=[]) Collects a list of rules, that can be applied to features, together with a name. .. doctest:: >>> feature = Feature( ... geometry=Geometry(type='LineString', coordinates=[[2, 2], [4, 8], [6, 2], [8, 8]]), ... properties={'road': 'major'} ... ) .. doctest:: >>> style = Style( ... rules=[Rule(symbolizers=[LineSymbolizer(color=Color(0, 255, 255), width=24, join='round')]), ... Rule(symbolizers=[LineSymbolizer(color=Color(0, 0, 255), width=8, join='round')])] ... ) .. doctest:: :hide: >>> bounds = Box(min_x=0, min_y=0, max_x=10, max_y=10) >>> doc_utils.render_style(style, feature, 'style.png', bounds, 100, 50) .. image:: .scratch/style.png Rule ---- .. class:: Rule(symbolizers, filter=None, min_scale=None, max_scale=None) Applies a list of symbolizers to a datasource's features. :param symbolizers: symbolizers to apply to geometries :type symbolizers: list :param filter: Filter object :param min_scale: minimum scale to draw geometries at :type min_scale: float :param max_scale: scale from which geometries will not be drawn :type max_scale: float If the filter is specified then the feature must pass the filter before the symbolizers are applied. If the min or max scale is supplied then the geometries are drawn if the drawing context's scale >= min_scale and < max_scale Rule scale ^^^^^^^^^^ A map scale is the proportion of device units to the map units they represent. For example if a device represents 1000km as 1cm then the scale is said to be 1/100000. In Shelley the scale is defined by the denominator of the scale assuming a numerator of 1. So a scale of 1/1000 is represented as 1000. Because a pixel in an image can be different sizes dependant on the monitor being used we assume a pixel size of 0.28mm square. So for a scale of 1/1000 (denominator=1000) a pixel would represent 0.28mm X 1000 = 2800mm = 28cm = 0.28m >>> style = Style( ... rules=[Rule(symbolizers=[LineSymbolizer(width=1)], max_scale_denom=10), ... Rule(symbolizers=[LineSymbolizer(width=5)], min_scale_denom=10)] ... ) TODO need to show map bounds to illustrate determination of scale .. doctest:: :hide: >>> doc_utils.render_style(style, feature, 'rule_scale.png', bounds, 100, 50) .. image:: .scratch/rule_scale.png Rule filter ^^^^^^^^^^^ .. doctest:: >>> style = Style( ... rules=[Rule(symbolizers=[LineSymbolizer(width=1)], filter=Filter("[road] = 'minor'")), ... Rule(symbolizers=[LineSymbolizer(width=5)], filter=Filter("[road] = 'major'"))] ... ) .. doctest:: :hide: >>> doc_utils.render_style(style, feature, 'rule_filter.png', bounds, 100, 50) .. image:: .scratch/rule_filter.png Filter expressions """""""""""""""""" .. doctest:: >>> feature = Feature(properties={'building': 'factory', 'stories': 5}) .. doctest:: >>> Filter("[building] = 'school'").passes(feature) False >>> Filter("[building] = 'factory'").passes(feature) True >>> Filter("[building] <> 'mosque'").passes(feature) True >>> Filter("[building] = 'factory' or [building] = 'school'").passes(feature) True >>> Filter("[building] = 'factory' and ([stories] <= 2 or [stories] > 4)").passes(feature) True >>> Filter("not ([building] = 'shop') and [stories] < 6").passes(feature) True Mapnik XML files ---------------- Mapnik has an xml map definition format. .. literalinclude:: simple.xml.template :language: xml We can read this map definition with Shelley. .. doctest:: :hide: >>> import shelley >>> from shelley.mappers import mapnik >>> xml_path = doc_utils.scratch_path('simple.xml') >>> map = mapnik.parse_xml(xml_path) >>> view = shelley.Box(-180, -90, 180, 90, srs=map.srs) >>> image_path = doc_utils.scratch_path('mapnik.png') >>> from shelley.renderers import cairorender >>> shelley.render(map, view, cairorender.Image(600, 300, image_path)) .. image:: .scratch/mapnik.png