Merge pull request #1539 from mapycz/compositing
Image filters and compositing tests on New Zealand
49
tests/data/good_maps/style_level_comp_op.xml
Normal file
|
@ -0,0 +1,49 @@
|
|||
<Map srs="+proj=lonlat +ellps=WGS84 +datum=WGS84 +no_defs +over"
|
||||
background-color="lightsteelblue">
|
||||
|
||||
<Style name="land">
|
||||
<Rule>
|
||||
<PolygonSymbolizer fill="darkslategrey" geometry-transform="translate(+1,+1)" />
|
||||
<PolygonSymbolizer fill="lightcyan" geometry-transform="translate(-1,-1)" />
|
||||
<PolygonSymbolizer fill="forestgreen" />
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
<Style name="markers" comp-op="src-over">
|
||||
<Rule>
|
||||
<MarkersSymbolizer fill="saddlebrown"
|
||||
fill-opacity="0.5"
|
||||
stroke-width="0"
|
||||
width="45 - 5 * [SCALERANK]"
|
||||
height="45 - 5 * [SCALERANK]"
|
||||
allow-overlap="true"
|
||||
ignore-placement="true"
|
||||
/>
|
||||
<MarkersSymbolizer fill="white"
|
||||
fill-opacity="0.7"
|
||||
stroke-width="0"
|
||||
width="5"
|
||||
height="5"
|
||||
allow-overlap="true"
|
||||
ignore-placement="true"
|
||||
/>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
<Layer name="land">
|
||||
<StyleName>land</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">shape</Parameter>
|
||||
<Parameter name="file">../shp/new_zealand/ne_50m_land.shp</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
<Layer name="cities">
|
||||
<StyleName>markers</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">shape</Parameter>
|
||||
<Parameter name="file">../shp/new_zealand/ne_50m_populated_places_simple.shp</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
</Map>
|
68
tests/data/good_maps/style_level_image_filter.xml
Normal file
|
@ -0,0 +1,68 @@
|
|||
<Map srs="+proj=lonlat +ellps=WGS84 +datum=WGS84 +no_defs +over"
|
||||
background-color="lightsteelblue">
|
||||
|
||||
<Style name="land">
|
||||
<Rule>
|
||||
<PolygonSymbolizer fill="darkslategrey" geometry-transform="translate(+1,+1)" />
|
||||
<PolygonSymbolizer fill="lightcyan" geometry-transform="translate(-1,-1)" />
|
||||
<PolygonSymbolizer fill="forestgreen" />
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
<Style name="markers" comp-op="hard-light">
|
||||
<Rule>
|
||||
<MarkersSymbolizer fill="saddlebrown"
|
||||
fill-opacity="0.5"
|
||||
stroke-width="0"
|
||||
width="45 - 5 * [SCALERANK]"
|
||||
height="45 - 5 * [SCALERANK]"
|
||||
allow-overlap="true"
|
||||
ignore-placement="true"
|
||||
/>
|
||||
<MarkersSymbolizer fill="white"
|
||||
fill-opacity="0.7"
|
||||
stroke-width="0"
|
||||
width="5"
|
||||
height="5"
|
||||
allow-overlap="true"
|
||||
ignore-placement="true"
|
||||
/>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
<Style name="labels" comp-op="overlay">
|
||||
<Rule>
|
||||
<TextSymbolizer face-name="DejaVu Sans Book"
|
||||
size="12"
|
||||
fill="black"
|
||||
halo-fill="white"
|
||||
halo-radius="2"
|
||||
dx="7"
|
||||
dy="1"
|
||||
avoid-edges="true"
|
||||
vertical-alignment="middle"
|
||||
placement-type="simple"
|
||||
placements="NE,NW"
|
||||
allow-overlap="false"
|
||||
>[NAME]</TextSymbolizer>
|
||||
</Rule>
|
||||
</Style>
|
||||
|
||||
<Layer name="land">
|
||||
<StyleName>land</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">shape</Parameter>
|
||||
<Parameter name="file">../shp/new_zealand/ne_50m_land.shp</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
<Layer name="cities">
|
||||
<StyleName>markers</StyleName>
|
||||
<StyleName>labels</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">shape</Parameter>
|
||||
<Parameter name="file">../shp/new_zealand/ne_50m_populated_places_simple.shp</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
|
||||
</Map>
|
BIN
tests/data/shp/new_zealand/ne_50m_land.dbf
Normal file
1
tests/data/shp/new_zealand/ne_50m_land.prj
Normal file
|
@ -0,0 +1 @@
|
|||
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.017453292519943295]]
|
BIN
tests/data/shp/new_zealand/ne_50m_land.shp
Normal file
BIN
tests/data/shp/new_zealand/ne_50m_land.shx
Normal file
BIN
tests/data/shp/new_zealand/ne_50m_populated_places_simple.dbf
Normal file
|
@ -0,0 +1 @@
|
|||
GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.017453292519943295]]
|
BIN
tests/data/shp/new_zealand/ne_50m_populated_places_simple.shp
Normal file
BIN
tests/data/shp/new_zealand/ne_50m_populated_places_simple.shx
Normal file
|
@ -2,7 +2,8 @@
|
|||
|
||||
from nose.tools import *
|
||||
import os,sys
|
||||
from utilities import execution_path, Todo, get_unique_colors, pixel2channels
|
||||
from utilities import execution_path, run_tests, Todo
|
||||
from utilities import get_unique_colors, pixel2channels, side_by_side_image
|
||||
import mapnik
|
||||
|
||||
def setup():
|
||||
|
@ -20,6 +21,10 @@ def debug_image(image,step=2):
|
|||
red,green,blue,alpha = pixel2channels(pixel)
|
||||
print "rgba(%s,%s,%s,%s) at %s,%s" % (red,green,blue,alpha,x,y)
|
||||
|
||||
def replace_style(m, name, style):
|
||||
m.remove_style(name)
|
||||
m.append_style(name, style)
|
||||
|
||||
# note: it is impossible to know for all pixel colors
|
||||
# we can only detect likely cases of non premultiplied colors
|
||||
def validate_pixels_are_not_premultiplied(image):
|
||||
|
@ -97,6 +102,8 @@ def test_compare_images():
|
|||
successes.append(name)
|
||||
else:
|
||||
fails.append('failed comparing actual (%s) and expected(%s)' % (actual,'tests/python_tests/'+ expected))
|
||||
fail_im = side_by_side_image(expected_im, a)
|
||||
fail_im.save('/tmp/mapnik-comp-op-test-' + name + '.fail.png')
|
||||
eq_(len(successes),num_ops,'\n'+'\n'.join(fails))
|
||||
b.demultiply()
|
||||
# b will be slightly modified by pre and then de multiplication rounding errors
|
||||
|
@ -142,6 +149,36 @@ def test_pre_multiply_status_of_map2():
|
|||
mapnik.render(m,im)
|
||||
eq_(validate_pixels_are_not_premultiplied(im),True)
|
||||
|
||||
def test_style_level_comp_op():
|
||||
m = mapnik.Map(256, 256)
|
||||
mapnik.load_map(m, '../data/good_maps/style_level_comp_op.xml')
|
||||
m.zoom_all()
|
||||
successes = []
|
||||
fails = []
|
||||
for name in mapnik.CompositeOp.names:
|
||||
# find_style returns a copy of the style object
|
||||
style_markers = m.find_style("markers")
|
||||
style_markers.comp_op = getattr(mapnik.CompositeOp, name)
|
||||
# replace the original style with the modified one
|
||||
replace_style(m, "markers", style_markers)
|
||||
im = mapnik.Image(m.width, m.height)
|
||||
mapnik.render(m, im)
|
||||
actual = '/tmp/mapnik-style-comp-op-' + name + '.png'
|
||||
expected = 'images/style-comp-op/' + name + '.png'
|
||||
im.save(actual)
|
||||
if not os.path.exists(expected):
|
||||
print 'generating expected test image: %s' % expected
|
||||
im.save(expected)
|
||||
expected_im = mapnik.Image.open(expected)
|
||||
# compare them
|
||||
if im.tostring() == expected_im.tostring():
|
||||
successes.append(name)
|
||||
else:
|
||||
fails.append('failed comparing actual (%s) and expected(%s)' % (actual,'tests/python_tests/'+ expected))
|
||||
fail_im = side_by_side_image(expected_im, im)
|
||||
fail_im.save('/tmp/mapnik-style-comp-op-' + name + '.fail.png')
|
||||
eq_(len(fails), 0, '\n'+'\n'.join(fails))
|
||||
|
||||
def test_style_level_opacity():
|
||||
m = mapnik.Map(512,512)
|
||||
mapnik.load_map(m,'../data/good_maps/style_level_opacity_and_blur.xml')
|
||||
|
@ -221,4 +258,4 @@ def test_background_image_with_alpha_and_background_color_against_composited_con
|
|||
|
||||
if __name__ == "__main__":
|
||||
setup()
|
||||
[eval(run)() for run in dir() if 'test_' in run]
|
||||
run_tests(eval(x) for x in dir() if x.startswith("test_"))
|
||||
|
|
|
@ -1,7 +1,19 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
from nose.tools import *
|
||||
from utilities import execution_path, run_tests
|
||||
from utilities import side_by_side_image
|
||||
import os, mapnik
|
||||
import re
|
||||
|
||||
def setup():
|
||||
# All of the paths used are relative, if we run the tests
|
||||
# from another directory we need to chdir()
|
||||
os.chdir(execution_path('.'))
|
||||
|
||||
def replace_style(m, name, style):
|
||||
m.remove_style(name)
|
||||
m.append_style(name, style)
|
||||
|
||||
def test_append():
|
||||
s = mapnik.Style()
|
||||
|
@ -11,5 +23,45 @@ def test_append():
|
|||
s.image_filters = 'sharpen'
|
||||
eq_(s.image_filters,'sharpen')
|
||||
|
||||
def test_style_level_image_filter():
|
||||
m = mapnik.Map(256, 256)
|
||||
mapnik.load_map(m, '../data/good_maps/style_level_image_filter.xml')
|
||||
m.zoom_all()
|
||||
successes = []
|
||||
fails = []
|
||||
for name in ("", "agg-stack-blur(2,2)", "blur",
|
||||
"edge-detect", "emboss", "gray", "invert",
|
||||
"sharpen", "sobel", "x-gradient", "y-gradient"):
|
||||
if name == "":
|
||||
filename = "none"
|
||||
else:
|
||||
filename = re.sub(r"[^-_a-z.0-9]", "", name)
|
||||
# find_style returns a copy of the style object
|
||||
style_markers = m.find_style("markers")
|
||||
style_markers.image_filters = name
|
||||
style_labels = m.find_style("labels")
|
||||
style_labels.image_filters = name
|
||||
# replace the original style with the modified one
|
||||
replace_style(m, "markers", style_markers)
|
||||
replace_style(m, "labels", style_labels)
|
||||
im = mapnik.Image(m.width, m.height)
|
||||
mapnik.render(m, im)
|
||||
actual = '/tmp/mapnik-style-image-filter-' + filename + '.png'
|
||||
expected = 'images/style-image-filter/' + filename + '.png'
|
||||
im.save(actual)
|
||||
if not os.path.exists(expected):
|
||||
print 'generating expected test image: %s' % expected
|
||||
im.save(expected)
|
||||
expected_im = mapnik.Image.open(expected)
|
||||
# compare them
|
||||
if im.tostring() == expected_im.tostring():
|
||||
successes.append(name)
|
||||
else:
|
||||
fails.append('failed comparing actual (%s) and expected(%s)' % (actual,'tests/python_tests/'+ expected))
|
||||
fail_im = side_by_side_image(expected_im, im)
|
||||
fail_im.save('/tmp/mapnik-style-image-filter-' + filename + '.fail.png')
|
||||
eq_(len(fails), 0, '\n'+'\n'.join(fails))
|
||||
|
||||
if __name__ == "__main__":
|
||||
[eval(run)() for run in dir() if 'test_' in run]
|
||||
setup()
|
||||
run_tests(eval(x) for x in dir() if x.startswith("test_"))
|
||||
|
|
BIN
tests/python_tests/images/style-comp-op/clear.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
tests/python_tests/images/style-comp-op/color.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/color_burn.png
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
tests/python_tests/images/style-comp-op/color_dodge.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/contrast.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/darken.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/difference.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/dst.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
tests/python_tests/images/style-comp-op/dst_atop.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
tests/python_tests/images/style-comp-op/dst_in.png
Normal file
After Width: | Height: | Size: 7.4 KiB |
BIN
tests/python_tests/images/style-comp-op/dst_out.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/dst_over.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
tests/python_tests/images/style-comp-op/exclusion.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/grain_extract.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
BIN
tests/python_tests/images/style-comp-op/grain_merge.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/hard_light.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/hue.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/python_tests/images/style-comp-op/invert.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/python_tests/images/style-comp-op/lighten.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
tests/python_tests/images/style-comp-op/minus.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/multiply.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/overlay.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/plus.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/saturation.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/screen.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/soft_light.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/src.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
tests/python_tests/images/style-comp-op/src_atop.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/src_in.png
Normal file
After Width: | Height: | Size: 4.9 KiB |
BIN
tests/python_tests/images/style-comp-op/src_out.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
tests/python_tests/images/style-comp-op/src_over.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/value.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
tests/python_tests/images/style-comp-op/xor.png
Normal file
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 32 KiB |
BIN
tests/python_tests/images/style-image-filter/blur.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
tests/python_tests/images/style-image-filter/edge-detect.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
tests/python_tests/images/style-image-filter/emboss.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
tests/python_tests/images/style-image-filter/gray.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
tests/python_tests/images/style-image-filter/invert.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
tests/python_tests/images/style-image-filter/none.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
tests/python_tests/images/style-image-filter/sharpen.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
tests/python_tests/images/style-image-filter/sobel.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
tests/python_tests/images/style-image-filter/x-gradient.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
tests/python_tests/images/style-image-filter/y-gradient.png
Normal file
After Width: | Height: | Size: 25 KiB |
|
@ -140,7 +140,7 @@ def test_render_points():
|
|||
p = mapnik.Projection(projs[projdescr])
|
||||
m.zoom_to_box(p.forward(mapnik.Box2d(ul_lonlat,lr_lonlat)))
|
||||
# Render to SVG so that it can be checked how many points are there with string comparison
|
||||
svg_file = os.path.join(tempfile.gettempdir(),'%s.svg')
|
||||
svg_file = os.path.join(tempfile.gettempdir(), 'mapnik-render-points-%s.svg' % projdescr)
|
||||
mapnik.render_to_file(m, svg_file)
|
||||
num_points_present = len(ds.all_features())
|
||||
svg = open(svg_file,'r').read()
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin
|
||||
|
||||
import os, sys, inspect
|
||||
import os, sys, inspect, traceback
|
||||
import mapnik
|
||||
|
||||
def execution_path(filename):
|
||||
return os.path.join(os.path.dirname(sys._getframe(1).f_code.co_filename), filename)
|
||||
|
@ -55,3 +57,31 @@ def get_unique_colors(im):
|
|||
pixels.append(pixel)
|
||||
pixels = sorted(pixels)
|
||||
return map(pixel2rgba,pixels)
|
||||
|
||||
def run_tests(iterable):
|
||||
failed = 0
|
||||
for test in iterable:
|
||||
try:
|
||||
test()
|
||||
sys.stderr.write("\x1b[32m✓ \x1b[m" + test.__name__ + "\x1b[m\n")
|
||||
except:
|
||||
exc_type, exc_value, exc_tb = sys.exc_info()
|
||||
failed += 1
|
||||
sys.stderr.write("\x1b[31m✘ \x1b[m" + test.__name__ + "\x1b[m\n")
|
||||
for mline in traceback.format_exception_only(exc_type, exc_value):
|
||||
for line in mline.rstrip().split("\n"):
|
||||
sys.stderr.write(" \x1b[31m" + line + "\x1b[m\n")
|
||||
sys.stderr.write(" Traceback:\n")
|
||||
for mline in traceback.format_tb(exc_tb):
|
||||
for line in mline.rstrip().split("\n"):
|
||||
sys.stderr.write(" " + line + "\n")
|
||||
sys.stderr.flush()
|
||||
return failed
|
||||
|
||||
def side_by_side_image(left_im, right_im):
|
||||
width = left_im.width() + 1 + right_im.width()
|
||||
height = max(left_im.height(), right_im.height())
|
||||
im = mapnik.Image(width, height)
|
||||
im.blend(0, 0, left_im, 1.0)
|
||||
im.blend(left_im.width() + 1, 0, right_im, 1.0)
|
||||
return im
|
||||
|
|