Skip to content

Commit

Permalink
Merge pull request #269 from mapnik/proj6
Browse files Browse the repository at this point in the history
proj6 + pytest
  • Loading branch information
artemp authored Feb 22, 2024
2 parents 6c0e9b5 + 9045155 commit 5ab32f0
Show file tree
Hide file tree
Showing 102 changed files with 3,468 additions and 4,931 deletions.
18 changes: 9 additions & 9 deletions mapnik/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def __init__(self, *args, **kwargs):
Box2d.__init__(self, *args, **kwargs)


class _Coord(Coord, _injector()):
class Coord(_mapnik.Coord, _injector()):
"""
Represents a point with two coordinates (either lon/lat or x/y).
Expand Down Expand Up @@ -183,7 +183,7 @@ def inverse(self, projection):
return inverse_(self, projection)


class _Box2d(Box2d, _injector()):
class Box2d(_mapnik.Box2d, _injector()):
"""
Represents a spatial envelope (i.e. bounding box).
Expand Down Expand Up @@ -238,7 +238,7 @@ def inverse(self, projection):
return inverse_(self, projection)


class _Projection(Projection, _injector()):
class Projection(_mapnik.Projection, _injector()):

def __repr__(self):
return "Projection('%s')" % self.params()
Expand Down Expand Up @@ -266,15 +266,15 @@ def inverse(self, obj):
return inverse_(obj, self)


class _Feature(Feature, _injector()):
class Feature(_mapnik.Feature, _injector()):
__geo_interface__ = property(lambda self: json.loads(self.to_geojson()))


class _Geometry(Geometry, _injector()):
class Geometry(_mapnik.Geometry, _injector()):
__geo_interface__ = property(lambda self: json.loads(self.to_geojson()))


class _Datasource(Datasource, _injector()):
class Datasource(_mapnik.Datasource, _injector()):

def featureset(self, fields = None, variables = {}):
query = Query(self.envelope())
Expand All @@ -291,13 +291,13 @@ def all_features(self, fields=None, variables={}):
return self.__iter__(fields, variables)


class _Color(Color, _injector()):
class Color(_mapnik.Color, _injector()):

def __repr__(self):
return "Color(R=%d,G=%d,B=%d,A=%d)" % (self.r, self.g, self.b, self.a)


class _SymbolizerBase(SymbolizerBase, _injector()):
class SymbolizerBase(_mapnik.SymbolizerBase, _injector()):
# back compatibility

@property
Expand Down Expand Up @@ -804,7 +804,7 @@ def make_it(feat, idx):
return itertools.imap(make_it, features, itertools.count(1))


class _TextSymbolizer(TextSymbolizer, _injector()):
class TextSymbolizer(_mapnik.TextSymbolizer, _injector()):

@property
def name(self):
Expand Down
71 changes: 34 additions & 37 deletions mapnik/printing/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@

"""Mapnik classes to assist in creating printable maps."""

from __future__ import absolute_import, print_function

import logging
import math

from mapnik import Box2d, Coord, Geometry, Layer, Map, Projection, Style, render
from mapnik import Box2d, Coord, Geometry, Layer, Map, Projection, ProjTransform, Style, render
from mapnik.printing.conversions import m2pt, m2px
from mapnik.printing.formats import pagesizes
from mapnik.printing.scales import any_scale, default_scale, deg_min_sec_scale, sequence_scale
Expand All @@ -25,12 +22,12 @@
HAS_PANGOCAIRO_MODULE = False

try:
from PyPDF2 import PdfFileReader, PdfFileWriter
from PyPDF2.generic import (ArrayObject, DecodedStreamObject, DictionaryObject, FloatObject, NameObject,
NumberObject, TextStringObject)
HAS_PYPDF2 = True
from pypdf import PdfReader, PdfWriter
from pypdf.generic import (ArrayObject, DecodedStreamObject, DictionaryObject, FloatObject, NameObject,
NumberObject, TextStringObject)
HAS_PYPDF = True
except ImportError:
HAS_PYPDF2 = False
HAS_PYPDF = False

"""
Style of centering to use with the map.
Expand Down Expand Up @@ -90,7 +87,7 @@ def __init__(self,
Args:
pagesize: tuple of page size in meters, see predefined sizes in mapnik.formats module
margin: page margin in meters
box: the box to render the map into. Must be within page area, margin excluded.
box: the box to render the map into. Must be within page area, margin excluded.
This should be a Mapnik Box2d object. Default is the full page without margin.
percent_box: similar to box argument but specified as a percent (0->1) of the full page size.
If both box and percent_box are specified percent_box will be used.
Expand All @@ -104,7 +101,7 @@ def __init__(self,
be a value from the mapnik.utils.centering class. The default is to center on the maps constrained
axis. Typically this will be horizontal for portrait pages and vertical for landscape pages.
is_latlon: whether the map is in lat lon degrees or not.
use_ocg_layers: create OCG layers in the PDF, requires PyPDF2
use_ocg_layers: create OCG layers in the PDF, requires pypdf
font_name: the font name used each time text is written (e.g., legend titles, representative fraction, etc.)
"""
self._pagesize = pagesize
Expand Down Expand Up @@ -563,7 +560,7 @@ def render_scale(self, m, ctx=None, width=0.05, num_divisions=3, bar_size=8.0, w
Args:
m: the Map object to render the scale for
ctx: A cairo context to render the scale into. If this is None, we create a context and find out
ctx: A cairo context to render the scale into. If this is None, we create a context and find out
the best location for the scale bar
width: the width of area available for rendering the scale bar (in meters)
num_divisions: the number of divisions for the scale bar
Expand Down Expand Up @@ -737,7 +734,7 @@ def render_graticule_on_map(self, m, dec_degrees=True, grid_layer_name="Graticul

# renders the vertical graticule axes
self._render_graticule_axes_and_text(
m,
m,
p2,
latlon_bounds,
latlon_buffer,
Expand Down Expand Up @@ -1119,7 +1116,7 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
Takes a multi pages PDF as input and converts each page to a layer in a single page PDF.
Note:
requires PyPDF2 to be available
requires pypdf to be available
Args:
layer_names should be a sequence of the user visible names of the layers, if not given
Expand All @@ -1128,17 +1125,17 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
if output_name is not provided a temporary file will be used for the conversion which
will then be copied back over the source file.
"""
if not HAS_PYPDF2:
raise RuntimeError("PyPDF2 not available; PyPDF2 required to convert pdf pages to layers")
if not HAS_PYPDF:
raise RuntimeError("pypdf not available; pypdf required to convert pdf pages to layers")

with open(filename, "rb+") as f:
file_reader = PdfFileReader(f)
file_writer = PdfFileWriter()
file_reader = PdfReader(f)
file_writer = PdfWriter()

template_page_size = file_reader.pages[0].mediaBox
output_pdf = file_writer.addBlankPage(
width=template_page_size.getWidth(),
height=template_page_size.getHeight())
template_page_size = file_reader.pages[0].mediabox
output_pdf = file_writer.add_blank_page(
width=template_page_size.width,
height=template_page_size.height)

content_key = NameObject('/Contents')
output_pdf[content_key] = ArrayObject()
Expand All @@ -1149,15 +1146,15 @@ def convert_pdf_pages_to_layers(self, filename, layer_names=None, reverse_all_bu
(properties, ocgs) = self._make_ocg_layers(file_reader, file_writer, output_pdf, layer_names)

properties_key = NameObject('/Properties')
output_pdf[resource_key][properties_key] = file_writer._addObject(properties)
output_pdf[resource_key][properties_key] = file_writer._add_object(properties)

ocproperties = DictionaryObject()
ocproperties[NameObject('/OCGs')] = ocgs

default_view = self._get_pdf_default_view(ocgs, reverse_all_but_last)
ocproperties[NameObject('/D')] = file_writer._addObject(default_view)
ocproperties[NameObject('/D')] = file_writer._add_object(default_view)

file_writer._root_object[NameObject('/OCProperties')] = file_writer._addObject(ocproperties)
file_writer._root_object[NameObject('/OCProperties')] = file_writer._add_object(ocproperties)

f.seek(0)
file_writer.write(f)
Expand Down Expand Up @@ -1189,7 +1186,7 @@ def _make_ocg_layers(self, file_reader, file_writer, output_pdf, layer_names=Non
page[NameObject(
'/Contents')] = ArrayObject((ocgs_start, page['/Contents'], ocg_end))

output_pdf.mergePage(page)
output_pdf.merge_page(page)

ocg = DictionaryObject()
ocg[NameObject('/Type')] = NameObject('/OCG')
Expand All @@ -1199,7 +1196,7 @@ def _make_ocg_layers(self, file_reader, file_writer, output_pdf, layer_names=Non
else:
ocg[NameObject('/Name')] = TextStringObject('Layer %d' % (idx + 1))

indirect_ocg = file_writer._addObject(ocg)
indirect_ocg = file_writer._add_object(ocg)
properties[ocg_name] = indirect_ocg
ocgs.append(indirect_ocg)

Expand Down Expand Up @@ -1238,20 +1235,20 @@ def add_geospatial_pdf_header(self, m, filename, epsg=None, wkt=None):
The epsg code or the wkt text of the projection must be provided.
Must be called *after* the page has had .finish() called.
"""
if not HAS_PYPDF2:
raise RuntimeError("PyPDF2 not available; PyPDF2 required to add geospatial header to PDF")
if not HAS_PYPDF:
raise RuntimeError("pypdf not available; pypdf required to add geospatial header to PDF")

if not any((epsg,wkt)):
raise RuntimeError("EPSG or WKT required to add geospatial header to PDF")

with open(filename, "rb+") as f:
file_reader = PdfFileReader(f)
file_writer = PdfFileWriter()
file_reader = PdfReader(f)
file_writer = PdfWriter()

# preserve OCProperties at document root if we have one
if file_reader.trailer['/Root'].has_key(NameObject('/OCProperties')):
if NameObject('/OCProperties') in file_reader.trailer['/Root']:
file_writer._root_object[NameObject('/OCProperties')] = file_reader.trailer[
'/Root'].getObject()[NameObject('/OCProperties')]
'/Root'].get_object()[NameObject('/OCProperties')]

for page in file_reader.pages:
gcs = DictionaryObject()
Expand All @@ -1265,7 +1262,7 @@ def add_geospatial_pdf_header(self, m, filename, epsg=None, wkt=None):
measure = self._get_pdf_measure(m, gcs)
page[NameObject('/VP')] = self._get_pdf_vp(measure)

file_writer.addPage(page)
file_writer.add_page(page)

f.seek(0)
file_writer.write(f)
Expand Down Expand Up @@ -1318,11 +1315,11 @@ def _get_pdf_gpts(self, m):
"""
gpts = ArrayObject()

proj = Projection(m.srs)
tr = ProjTransform(Projection(m.srs), Projection("epsg:4326"))
env = m.envelope()
for x in ((env.minx, env.miny), (env.minx, env.maxy),
for p in ((env.minx, env.miny), (env.minx, env.maxy),
(env.maxx, env.maxy), (env.maxx, env.miny)):
latlon_corner = proj.inverse(Coord(*x))
latlon_corner = tr.forward(Coord(*p))
# these are in lat,lon order according to the specification
gpts.append(FloatObject(str(latlon_corner.y)))
gpts.append(FloatObject(str(latlon_corner.x)))
Expand Down
12 changes: 8 additions & 4 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import subprocess
import sys
import glob
import pkg_resources

from distutils import sysconfig
from ctypes.util import find_library

Expand Down Expand Up @@ -230,19 +232,21 @@ def run(self):
if os.environ.get("PYCAIRO", "false") == "true":
try:
extra_comp_args.append('-DHAVE_PYCAIRO')
print("-I%s/include/pycairo".format(sys.exec_prefix))
extra_comp_args.append("-I{0}/include/pycairo".format(sys.exec_prefix))
dist = pkg_resources.get_distribution('pycairo')
print(dist.location)
print("-I%s/cairo/include".format(dist.location))
extra_comp_args.append("-I{0}/cairo/include".format(dist.location))
#extra_comp_args.extend(check_output(["pkg-config", '--cflags', 'pycairo']).strip().split(' '))
#linkflags.extend(check_output(["pkg-config", '--libs', 'pycairo']).strip().split(' '))
except:
raise Exception("Failed to find compiler options for pycairo")

if sys.platform == 'darwin':
extra_comp_args.append('-mmacosx-version-min=10.11')
extra_comp_args.append('-mmacosx-version-min=13.0')
# silence warning coming from boost python macros which
# would is hard to silence via pragma
extra_comp_args.append('-Wno-parentheses-equality')
linkflags.append('-mmacosx-version-min=10.11')
linkflags.append('-mmacosx-version-min=13.0')
else:
linkflags.append('-lrt')
linkflags.append('-Wl,-z,origin')
Expand Down
8 changes: 3 additions & 5 deletions src/mapnik_enumeration.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,17 @@ class enumeration_ :
using namespace boost::python::converter;
return base_type::base::to_python(
registered<native_type>::converters.m_class_object
, static_cast<long>( v ));
, static_cast<long>(native_type(v)));

}
};

void init() {
boost::python::implicitly_convertible<native_type, EnumWrapper>();
boost::python::to_python_converter<EnumWrapper, converter >();

for (unsigned i = 0; i < EnumWrapper::MAX; ++i)
for (auto const& kv : EnumWrapper::lookupMap())
{
// Register the strings already defined for this enum.
base_type::value( EnumWrapper::get_string( i ), native_type( i ) );
base_type::value(kv.second.c_str(), kv.first);
}
}

Expand Down
10 changes: 5 additions & 5 deletions src/mapnik_gamma_method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,11 @@ void export_gamma_method()
using namespace boost::python;

mapnik::enumeration_<mapnik::gamma_method_e>("gamma_method")
.value("POWER", mapnik::GAMMA_POWER)
.value("LINEAR",mapnik::GAMMA_LINEAR)
.value("NONE", mapnik::GAMMA_NONE)
.value("THRESHOLD", mapnik::GAMMA_THRESHOLD)
.value("MULTIPLY", mapnik::GAMMA_MULTIPLY)
.value("POWER", mapnik::gamma_method_enum::GAMMA_POWER)
.value("LINEAR",mapnik::gamma_method_enum::GAMMA_LINEAR)
.value("NONE", mapnik::gamma_method_enum::GAMMA_NONE)
.value("THRESHOLD", mapnik::gamma_method_enum::GAMMA_THRESHOLD)
.value("MULTIPLY", mapnik::gamma_method_enum::GAMMA_MULTIPLY)
;

}
6 changes: 4 additions & 2 deletions src/mapnik_projection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,10 @@ void export_projection ()
.def ("params", make_function(&projection::params,
return_value_policy<copy_const_reference>()),
"Returns the PROJ string for this projection.\n")
.def ("expanded",&projection::expanded,
"normalize PROJ definition by expanding epsg:XXXX syntax\n")
.def ("definition",&projection::definition,
"Return projection definition\n")
.def ("description", &projection::description,
"Returns projection description")
.add_property ("geographic", &projection::is_geographic,
"This property is True if the projection is a geographic projection\n"
"(i.e. it uses lon/lat coordinates)\n")
Expand Down
13 changes: 4 additions & 9 deletions src/mapnik_raster_colorizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ using mapnik::colorizer_stop;
using mapnik::colorizer_stops;
using mapnik::colorizer_mode_enum;
using mapnik::color;
using mapnik::COLORIZER_INHERIT;
using mapnik::COLORIZER_LINEAR;
using mapnik::COLORIZER_DISCRETE;
using mapnik::COLORIZER_EXACT;


namespace {
void add_stop(raster_colorizer_ptr & rc, colorizer_stop & stop)
Expand Down Expand Up @@ -196,10 +191,10 @@ void export_raster_colorizer()
;

enum_<colorizer_mode_enum>("ColorizerMode")
.value("COLORIZER_INHERIT", COLORIZER_INHERIT)
.value("COLORIZER_LINEAR", COLORIZER_LINEAR)
.value("COLORIZER_DISCRETE", COLORIZER_DISCRETE)
.value("COLORIZER_EXACT", COLORIZER_EXACT)
.value("COLORIZER_INHERIT", colorizer_mode_enum::COLORIZER_INHERIT)
.value("COLORIZER_LINEAR", colorizer_mode_enum::COLORIZER_LINEAR)
.value("COLORIZER_DISCRETE", colorizer_mode_enum::COLORIZER_DISCRETE)
.value("COLORIZER_EXACT", colorizer_mode_enum::COLORIZER_EXACT)
.export_values()
;

Expand Down
4 changes: 2 additions & 2 deletions src/mapnik_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ void export_style()
using namespace boost::python;

mapnik::enumeration_<mapnik::filter_mode_e>("filter_mode")
.value("ALL",mapnik::FILTER_ALL)
.value("FIRST",mapnik::FILTER_FIRST)
.value("ALL",mapnik::filter_mode_enum::FILTER_ALL)
.value("FIRST",mapnik::filter_mode_enum::FILTER_FIRST)
;

class_<rules>("Rules",init<>("default ctor"))
Expand Down
Loading

0 comments on commit 5ab32f0

Please sign in to comment.