Merge commit '1454105b124daccc4d64654e11c442a2a1aa5008' into harfbuzz

Conflicts:
	src/build.py
This commit is contained in:
Hermann Kraus 2013-03-16 17:44:03 +01:00
commit 20ff37eb2d
22 changed files with 468 additions and 313 deletions

View file

@ -8,6 +8,10 @@ For a complete change history, see the git log.
## Future
- Added ability to access style list from map by (name,obj) in python (#1725)
- Added `is_solid` method to python mapnik.Image and mapnik.ImageView classes (#1728)
- Changed scale_denominator C++ interface to take scale as first argument rather than map.
- Added support for `background-image` in cairo_renderer (#1724)

View file

@ -370,6 +370,8 @@ PathVariable.PathAccept),
('JOBS', 'Set the number of parallel compilations', "1", lambda key, value, env: int(value), int),
BoolVariable('DEMO', 'Compile demo c++ application', 'True'),
BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'),
BoolVariable('SHAPEINDEX', 'Compile and install a utility to generate shapefile indexes in the custom format (.index) Mapnik supports', 'True'),
BoolVariable('SVG2PNG', 'Compile and install a utility to generate render an svg file to a png on the command line', 'False'),
BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'),
BoolVariable('SAMPLE_INPUT_PLUGINS', 'Compile and install sample plugins', 'False'),
BoolVariable('BIGINT', 'Compile support for 64-bit integers in mapnik::value', 'True'),
@ -1805,12 +1807,14 @@ if not HELP_REQUESTED:
# Build shapeindex and remove its dependency from the LIBS
if 'boost_program_options%s' % env['BOOST_APPEND'] in env['LIBS']:
if env['SHAPEINDEX']:
SConscript('utils/shapeindex/build.py')
# Build the pgsql2psqlite app if requested
if env['PGSQL2SQLITE']:
SConscript('utils/pgsql2sqlite/build.py')
if env['SVG2PNG']:
SConscript('utils/svg2png/build.py')
# devtools not ready for public

View file

@ -138,6 +138,9 @@ boost::python::list field_types(boost::shared_ptr<mapnik::datasource> const& ds)
return fld_types;
}}
mapnik::parameters const& (mapnik::datasource::*params_const)() const = &mapnik::datasource::params;
void export_datasource()
{
using namespace boost::python;
@ -164,7 +167,7 @@ void export_datasource()
.def("fields",&fields)
.def("field_types",&field_types)
.def("features_at_point",&datasource::features_at_point, (arg("coord"),arg("tolerance")=0))
.def("params",&datasource::params,return_value_policy<copy_const_reference>(),
.def("params",make_function(params_const,return_value_policy<copy_const_reference>()),
"The configuration parameters of the data source. "
"These vary depending on the type of data source.")
;

View file

@ -107,6 +107,28 @@ bool painted(mapnik::image_32 const& im)
return im.painted();
}
bool is_solid(mapnik::image_32 const& im)
{
if (im.width() > 0 && im.height() > 0)
{
mapnik::image_data_32 const & data = im.data();
mapnik::image_data_32::pixel_type const* first_row = data.getRow(0);
mapnik::image_data_32::pixel_type const first_pixel = first_row[0];
for (unsigned y = 0; y < im.height(); ++y)
{
mapnik::image_data_32::pixel_type const * row = data.getRow(y);
for (unsigned x = 0; x < im.width(); ++x)
{
if (first_pixel != row[x])
{
return false;
}
}
}
}
return true;
}
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()))
@ -206,6 +228,7 @@ void export_image()
.def("height",&image_32::height)
.def("view",&image_32::get_view)
.def("painted",&painted)
.def("is_solid",&is_solid)
.add_property("background",make_function
(&image_32::get_background,return_value_policy<copy_const_reference>()),
&image_32::set_background, "The background color of the image.")

View file

@ -76,6 +76,27 @@ PyObject* view_tostring3(image_view<image_data_32> const & view, std::string con
(s.data(),s.size());
}
bool is_solid(image_view<image_data_32> const& view)
{
if (view.width() > 0 && view.height() > 0)
{
mapnik::image_view<image_data_32>::pixel_type const* first_row = view.getRow(0);
mapnik::image_view<image_data_32>::pixel_type const first_pixel = first_row[0];
for (unsigned y = 0; y < view.height(); ++y)
{
mapnik::image_view<image_data_32>::pixel_type const * row = view.getRow(y);
for (unsigned x = 0; x < view.width(); ++x)
{
if (first_pixel != row[x])
{
return false;
}
}
}
}
return true;
}
void save_view1(image_view<image_data_32> const& view,
std::string const& filename)
{
@ -104,6 +125,7 @@ void export_image_view()
class_<image_view<image_data_32> >("ImageView","A view into an image.",no_init)
.def("width",&image_view<image_data_32>::width)
.def("height",&image_view<image_data_32>::height)
.def("is_solid",&is_solid)
.def("tostring",&view_tostring1)
.def("tostring",&view_tostring2)
.def("tostring",&view_tostring3)

View file

@ -116,10 +116,10 @@ void set_maximum_extent(mapnik::Map & m, boost::optional<mapnik::box2d<double> >
struct extract_style
{
typedef mapnik::feature_type_style result_type;
typedef boost::python::tuple result_type;
result_type operator() (std::map<std::string, mapnik::feature_type_style>::value_type const& val) const
{
return val.second;
return boost::python::make_tuple(val.first,val.second);
}
};

View file

@ -28,7 +28,6 @@
#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;
@ -45,18 +44,12 @@ std::string get_image_filters(feature_type_style & style)
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)
bool result = parse_image_filters(filters, new_filters);
if (!result)
{
throw mapnik::value_error("failed to parse image-filters: '" + std::string(itr,end) + "'");
throw mapnik::value_error("failed to parse image-filters: '" + filters + "'");
}
style.image_filters().swap(new_filters);
}

View file

@ -45,7 +45,7 @@ namespace mapnik {
template <typename T>
void grid2utf(T const& grid_type,
boost::python::list& l,
std::vector<grid::lookup_type>& key_order)
std::vector<typename T::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;

View file

@ -48,6 +48,8 @@ namespace agg
ClipperLib::Clipper m_clipper;
clipper_PolyFillType m_subjFillType;
clipper_PolyFillType m_clipFillType;
double start_x_;
double start_y_;
int Round(double val)
{
@ -67,7 +69,9 @@ namespace agg
m_contour(-1),
m_operation(op),
m_subjFillType(subjFillType),
m_clipFillType(clipFillType)
m_clipFillType(clipFillType),
start_x_(0),
start_y_(0)
{
m_scaling_factor = std::max(std::min(scaling_factor, 6),0);
m_scaling_factor = Round(std::pow((double)10, m_scaling_factor));
@ -84,7 +88,9 @@ namespace agg
m_contour(-1),
m_operation(op),
m_subjFillType(subjFillType),
m_clipFillType(clipFillType)
m_clipFillType(clipFillType),
start_x_(0),
start_y_(0)
{
m_scaling_factor = std::max(std::min(scaling_factor, 6),0);
m_scaling_factor = Round(std::pow((double)10, m_scaling_factor));
@ -271,6 +277,8 @@ namespace agg
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
template<class VSA, class VSB>
unsigned conv_clipper<VSA, VSB>::vertex(double *x, double *y)
{
@ -281,10 +289,14 @@ namespace agg
if( next_vertex( x, y ) )
{
m_status =status_line_to;
start_x_ = *x;
start_y_ = *y;
return path_cmd_move_to;
}
else
{
*x = start_x_;
*y = start_y_;
m_status = status_stop;
return path_cmd_end_poly | path_flags_close;
}
@ -301,6 +313,8 @@ namespace agg
else
{
m_status = status_move_to;
*x = start_x_;
*y = start_y_;
return path_cmd_end_poly | path_flags_close;
}
}

View file

@ -99,6 +99,11 @@ public:
return params_;
}
parameters & params()
{
return params_;
}
/*!
* @brief Get the type of the datasource
* @return The type of the datasource (Vector or Raster)

View file

@ -320,8 +320,14 @@ operator>>(std::istream & is, mapnik::enumeration<ENUM, THE_MAX> & e)
/** Helper macro. Creates a typedef.
* @relates mapnik::enumeration
*/
#ifdef _MSC_VER
#define DEFINE_ENUM( name, e) \
template enumeration<e, e ## _MAX>; \
typedef enumeration<e, e ## _MAX> name
#else
#define DEFINE_ENUM( name, e) \
typedef enumeration<e, e ## _MAX> name
#endif
/** Helper macro. Runs the verify_mapnik_enum() method during static initialization.
* @relates mapnik::enumeration

View file

@ -23,6 +23,9 @@
#ifndef MAPNIK_IMAGE_FILTER_TYPES_HPP
#define MAPNIK_IMAGE_FILTER_TYPES_HPP
// mapnik
#include <mapnik/config.hpp>
// boost
#include <boost/variant/variant_fwd.hpp>
@ -125,7 +128,9 @@ inline std::ostream& operator<< (std::ostream& os, invert)
inline std::ostream& operator<< (std::ostream& os, filter_type const& filter);
bool generate_image_filters(std::back_insert_iterator<std::string> & sink, std::vector<filter_type> const& v);
MAPNIK_DECL bool generate_image_filters(std::back_insert_iterator<std::string> & sink, std::vector<filter_type> const& v);
MAPNIK_DECL bool parse_image_filters(std::string const& filters, std::vector<filter_type>& image_filters);
}}

View file

@ -24,34 +24,14 @@
#define MAPNIK_GEOMETRY_TO_WKT_HPP
// mapnik
#include <mapnik/global.hpp>
#include <mapnik/config.hpp>
#include <mapnik/geometry.hpp>
#include <mapnik/util/geometry_wkt_generator.hpp>
// boost
#include <boost/spirit/include/karma.hpp>
namespace mapnik { namespace util {
namespace karma = boost::spirit::karma;
MAPNIK_DECL bool to_wkt(std::string & wkt, mapnik::geometry_type const& geom);
bool to_wkt(std::string & wkt, mapnik::geometry_type const& geom)
{
typedef std::back_insert_iterator<std::string> sink_type;
sink_type sink(wkt);
wkt_generator<sink_type, mapnik::geometry_type> generator(true);
bool result = karma::generate(sink, generator, geom);
return result;
}
bool to_wkt(std::string & wkt, mapnik::geometry_container const& geom)
{
typedef std::back_insert_iterator<std::string> sink_type;
sink_type sink(wkt);
wkt_multi_generator<sink_type, mapnik::geometry_container> generator;
bool result = karma::generate(sink, generator, geom);
return result;
}
MAPNIK_DECL bool to_wkt(std::string & wkt, mapnik::geometry_container const& geom);
}}

View file

@ -56,11 +56,20 @@ regex = 'boost_regex%s' % env['BOOST_APPEND']
system = 'boost_system%s' % env['BOOST_APPEND']
# clear out and re-set libs for this env
lib_env['LIBS'] = ['freetype','ltdl','png','tiff','z','proj',env['ICU_LIB_NAME'],filesystem,system,regex,'harfbuzz']
lib_env['LIBS'] = ['freetype','ltdl','z',env['ICU_LIB_NAME'],filesystem,system,regex,'harfbuzz']
if env['PROJ']:
lib_env['LIBS'].append('proj')
if env['PNG']:
lib_env['LIBS'].append('png')
if env['JPEG']:
lib_env['LIBS'].append('jpeg')
if env['TIFF']:
lib_env['LIBS'].append('tiff')
if len(env['EXTRA_FREETYPE_LIBS']):
lib_env['LIBS'].extend(copy(env['EXTRA_FREETYPE_LIBS']))
@ -141,7 +150,6 @@ source = Split(
palette.cpp
path_expression_grammar.cpp
plugin.cpp
png_reader.cpp
point_symbolizer.cpp
polygon_pattern_symbolizer.cpp
polygon_symbolizer.cpp
@ -149,7 +157,6 @@ source = Split(
save_map.cpp
shield_symbolizer.cpp
text_symbolizer.cpp
tiff_reader.cpp
wkb.cpp
projection.cpp
proj_transform.cpp
@ -219,6 +226,18 @@ if env['JPEG']:
jpeg_reader.cpp
""")
if env['TIFF']:
source += Split(
"""
tiff_reader.cpp
""")
if env['PNG']:
source += Split(
"""
png_reader.cpp
""")
# agg backend
source += Split(
"""

View file

@ -34,6 +34,14 @@ static const char * gradient_strings[] = {
IMPLEMENT_ENUM( gradient_e, gradient_strings )
static const char * gradient_unit_strings[] = {
"user-space-on-use",
"user-space-on-use-bounding-box",
"object-bounding-box",
""
};
IMPLEMENT_ENUM( gradient_unit_e, gradient_unit_strings )
gradient::gradient()
: gradient_type_(NO_GRADIENT),

View file

@ -21,6 +21,7 @@
*****************************************************************************/
// mapnik
#include <mapnik/image_filter_types.hpp>
#include <mapnik/image_filter_grammar.hpp> // image_filter_grammar
// boost
#include <boost/spirit/include/karma.hpp>
@ -63,4 +64,17 @@ bool generate_image_filters(std::back_insert_iterator<std::string>& sink, std::v
return r;
}
bool parse_image_filters(std::string const& filters, std::vector<filter_type>& image_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;
bool r = boost::spirit::qi::phrase_parse(itr,end,
filter_grammar,
boost::spirit::qi::ascii::space,
image_filters);
return r && itr==end;
}
}}

View file

@ -425,15 +425,10 @@ void map_parser::parse_style(Map & map, xml_node const& sty)
if (filters)
{
std::string filter_str = *filters;
std::string::const_iterator itr = filter_str.begin();
std::string::const_iterator end = filter_str.end();
bool result = boost::spirit::qi::phrase_parse(itr,end,
sty.get_tree().image_filters_grammar,
boost::spirit::qi::ascii::space,
style.image_filters());
if (!result || itr!=end)
bool result = filter::parse_image_filters(filter_str, style.image_filters());
if (!result)
{
throw config_error("failed to parse image-filters: '" + std::string(itr,end) + "'");
throw config_error("failed to parse image-filters: '" + filter_str + "'");
}
}

View file

@ -26,6 +26,7 @@
#include <mapnik/geometry.hpp>
#include <mapnik/util/geometry_wkt_generator.hpp>
#include <mapnik/util/geometry_to_wkt.hpp>
#include <mapnik/util/path_iterator.hpp>
#include <mapnik/util/container_adapter.hpp>
@ -156,6 +157,23 @@ wkt_multi_generator<OutputIterator, GeometryContainer>::wkt_multi_generator()
template struct mapnik::util::wkt_generator<std::back_insert_iterator<std::string>, mapnik::geometry_type>;
template struct mapnik::util::wkt_multi_generator<std::back_insert_iterator<std::string>, mapnik::geometry_container >;
bool to_wkt(std::string & wkt, mapnik::geometry_type const& geom)
{
typedef std::back_insert_iterator<std::string> sink_type;
sink_type sink(wkt);
wkt_generator<sink_type, mapnik::geometry_type> generator(true);
bool result = karma::generate(sink, generator, geom);
return result;
}
bool to_wkt(std::string & wkt, mapnik::geometry_container const& geom)
{
typedef std::back_insert_iterator<std::string> sink_type;
sink_type sink(wkt);
wkt_multi_generator<sink_type, mapnik::geometry_container> generator;
bool result = karma::generate(sink, generator, geom);
return result;
}
}}

View file

@ -182,10 +182,14 @@ std::string xml_node::xml_text = "<xmltext>";
std::string const& xml_node::name() const
{
if (!is_text_)
{
return name_;
}
else
{
return xml_text;
}
}
std::string const& xml_node::text() const
{
@ -193,7 +197,8 @@ std::string const& xml_node::text() const
{
processed_ = true;
return name_;
} else
}
else
{
throw config_error("text() called on non-text node", *this);
}
@ -336,7 +341,8 @@ std::string xml_node::get_text() const
if (is_text_)
{
return name_;
} else
}
else
{
return "";
}

View file

@ -335,6 +335,16 @@ def test_map_init():
m = mapnik.Map(256, 256, '+proj=latlong')
eq_(m.srs, '+proj=latlong')
def test_map_style_access():
m = mapnik.Map(256, 256)
sty = mapnik.Style()
m.append_style("style",sty)
styles = list(m.styles)
eq_(len(styles),1)
eq_(styles[0][0],'style')
# returns a copy so let's just check it is the right instance
eq_(isinstance(styles[0][1],mapnik.Style),True)
def test_map_maximum_extent_modification():
m = mapnik.Map(256, 256)
eq_(m.maximum_extent, None)

View file

@ -12,56 +12,76 @@ def setup():
# from another directory we need to chdir()
os.chdir(execution_path('.'))
def test_simplest_render():
m = mapnik.Map(256, 256)
i = mapnik.Image(m.width, m.height)
mapnik.render(m, i)
s = i.tostring()
im = mapnik.Image(m.width, m.height)
eq_(im.painted(),False)
eq_(im.is_solid(),True)
mapnik.render(m, im)
eq_(im.painted(),False)
eq_(im.is_solid(),True)
s = im.tostring()
eq_(s, 256 * 256 * '\x00\x00\x00\x00')
def test_render_image_to_string():
i = mapnik.Image(256, 256)
i.background = mapnik.Color('black')
s = i.tostring()
im = mapnik.Image(256, 256)
im.background = mapnik.Color('black')
eq_(im.painted(),False)
eq_(im.is_solid(),True)
s = im.tostring()
eq_(s, 256 * 256 * '\x00\x00\x00\xff')
s = im.tostring('png')
s = i.tostring('png')
def test_non_solid_image():
im = mapnik.Image(256, 256)
im.background = mapnik.Color('black')
eq_(im.painted(),False)
eq_(im.is_solid(),True)
# set one pixel to a different color
im.set_pixel(0,0,mapnik.Color('white'))
eq_(im.painted(),False)
eq_(im.is_solid(),False)
def test_non_solid_image_view():
im = mapnik.Image(256, 256)
im.background = mapnik.Color('black')
view = im.view(0,0,256,256)
eq_(view.is_solid(),True)
# set one pixel to a different color
im.set_pixel(0,0,mapnik.Color('white'))
eq_(im.is_solid(),False)
# view, since it is the exact dimensions of the image
# should also be non-solid
eq_(view.is_solid(),False)
# but not a view that excludes the single diff pixel
view2 = im.view(1,1,256,256)
eq_(view2.is_solid(),True)
def test_setting_alpha():
w,h = 256,256
im1 = mapnik.Image(w,h)
# white, half transparent
im1.background = mapnik.Color('rgba(255,255,255,.5)')
eq_(im1.painted(),False)
eq_(im1.is_solid(),True)
# pure white
im2 = mapnik.Image(w,h)
im2.background = mapnik.Color('rgba(255,255,255,1)')
im2.set_alpha(.5)
eq_(im2.painted(),False)
eq_(im2.is_solid(),True)
eq_(len(im1.tostring()), len(im2.tostring()))
def test_render_image_to_file():
i = mapnik.Image(256, 256)
i.background = mapnik.Color('black')
im = mapnik.Image(256, 256)
im.background = mapnik.Color('black')
if mapnik.has_jpeg():
i.save('test.jpg')
i.save('test.png', 'png')
im.save('test.jpg')
im.save('test.png', 'png')
if os.path.exists('test.jpg'):
os.remove('test.jpg')
else:
return False
if os.path.exists('test.png'):
os.remove('test.png')
else:
@ -71,34 +91,32 @@ def get_paired_images(w,h,mapfile):
tmp_map = 'tmp_map.xml'
m = mapnik.Map(w,h)
mapnik.load_map(m,mapfile)
i = mapnik.Image(w,h)
im = mapnik.Image(w,h)
m.zoom_all()
mapnik.render(m,i)
mapnik.render(m,im)
mapnik.save_map(m,tmp_map)
m2 = mapnik.Map(w,h)
mapnik.load_map(m2,tmp_map)
i2 = mapnik.Image(w,h)
im2 = mapnik.Image(w,h)
m2.zoom_all()
mapnik.render(m2,i2)
mapnik.render(m2,im2)
os.remove(tmp_map)
return i,i2
return im,im2
def test_render_from_serialization():
try:
i,i2 = get_paired_images(100,100,'../data/good_maps/building_symbolizer.xml')
eq_(i.tostring(),i2.tostring())
im,im2 = get_paired_images(100,100,'../data/good_maps/building_symbolizer.xml')
eq_(im.tostring(),im2.tostring())
i,i2 = get_paired_images(100,100,'../data/good_maps/polygon_symbolizer.xml')
eq_(i.tostring(),i2.tostring())
im,im2 = get_paired_images(100,100,'../data/good_maps/polygon_symbolizer.xml')
eq_(im.tostring(),im2.tostring())
except RuntimeError, e:
# only test datasources that we have installed
if not 'Could not create datasource' in str(e):
raise RuntimeError(e)
def test_render_points():
if not mapnik.has_cairo(): return
# create and populate point datasource (WGS84 lat-lon coordinates)
ds = mapnik.MemoryDatasource()
context = mapnik.Context()
@ -163,6 +181,14 @@ def test_render_with_scale_factor():
for size in sizes:
im = mapnik.Image(256, 256)
mapnik.render(m,im,size)
expected_file = './images/support/marker-text-line-scale-factor-%s.png' % size
actual_file = '/tmp/' + os.path.basename(expected_file)
im.save(actual_file,'png8')
#im.save(expected_file,'png8')
# we save and re-open here so both png8 images are ready as full color png
actual = mapnik.Image.open(expected_file)
expected = mapnik.Image.open(expected_file)
eq_(actual.tostring(),expected.tostring(), 'failed comparing actual (%s) and expected (%s)' % (actual_file,expected_file))
im.save('./images/support/marker-text-line-scale-factor-%s.png' % size,'png8')
if __name__ == "__main__":

View file

@ -73,7 +73,7 @@ def main():
argv.append('-v')
dirname = os.path.dirname(sys.argv[0])
argv.extend(['-w', dirname+'/python_tests'])
argv.extend(['-w', os.path.join(dirname,'python_tests')])
if not nose.run(argv=argv, plugins=[TodoPlugin(), Doctest()]):
sys.exit(1)