Merge branch 'master' of github.com:mapnik/mapnik

This commit is contained in:
Dane Springmeyer 2011-10-21 14:53:08 -07:00
commit b34eb0664a
10 changed files with 285 additions and 27 deletions

View file

@ -410,6 +410,13 @@ def Raster(**keywords):
Optional keyword arguments:
base -- path prefix (default None)
multi -- whether the image is in tiles on disk (default False)
Multi-tiled keyword arguments:
x_width -- virtual image number of tiles in X direction (required)
y_width -- virtual image number of tiles in Y direction (required)
tile_size -- if an image is in tiles, how large are the tiles (default 256)
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)

View file

@ -65,6 +65,10 @@ raster_datasource::raster_datasource(const parameters& params, bool bind)
else
filename_ = *file;
multi_tiles_ = *params_.get<bool>("multi", false);
tile_size_ = *params_.get<unsigned>("tile_size", 256);
tile_stride_ = *params_.get<unsigned>("tile_stride", 1);
format_=*params_.get<std::string>("format","tiff");
boost::optional<double> lox = params_.get<double>("lox");
@ -96,32 +100,49 @@ void raster_datasource::bind() const
{
if (is_bound_) return;
if (! boost::filesystem::exists(filename_))
throw datasource_exception("Raster Plugin: " + filename_ + " does not exist");
try
if (multi_tiles_)
{
std::auto_ptr<image_reader> reader(mapnik::get_image_reader(filename_, format_));
if (reader.get())
{
width_ = reader->width();
height_ = reader->height();
boost::optional<unsigned> x_width = params_.get<unsigned>("x_width");
boost::optional<unsigned> y_width = params_.get<unsigned>("y_width");
if (!x_width)
throw datasource_exception("Raster Plugin: x-width parameter not supplied for multi-tiled data source.");
if (!y_width)
throw datasource_exception("Raster Plugin: y-width parameter not supplied for multi-tiled data source.");
width_ = x_width.get() * tile_size_;
height_ = y_width.get() * tile_size_;
}
else
{
if (! boost::filesystem::exists(filename_))
throw datasource_exception("Raster Plugin: " + filename_ + " does not exist");
try
{
std::auto_ptr<image_reader> reader(mapnik::get_image_reader(filename_, format_));
if (reader.get())
{
width_ = reader->width();
height_ = reader->height();
}
}
catch (mapnik::image_reader_exception const& ex)
{
std::cerr << "Raster Plugin: image reader exception caught: " << ex.what() << std::endl;
throw;
}
catch (...)
{
std::cerr << "Raster Plugin: exception caught" << std::endl;
throw;
}
}
#ifdef MAPNIK_DEBUG
std::clog << "Raster Plugin: RASTER SIZE(" << width_ << "," << height_ << ")" << std::endl;
std::clog << "Raster Plugin: RASTER SIZE(" << width_ << "," << height_ << ")" << std::endl;
#endif
}
}
catch (mapnik::image_reader_exception const& ex)
{
std::cerr << "Raster Plugin: image reader exception caught: " << ex.what() << std::endl;
throw;
}
catch (...)
{
std::cerr << "Raster Plugin: exception caught" << std::endl;
throw;
}
is_bound_ = true;
}
@ -165,7 +186,16 @@ featureset_ptr raster_datasource::features(query const& q) const
std::clog << "Raster Plugin: BOX SIZE(" << width << " " << height << ")" << std::endl;
#endif
if (width * height > 512*512)
if (multi_tiles_)
{
#ifdef MAPNIK_DEBUG
std::clog << "Raster Plugin: MULTI-TILED policy" << std::endl;
#endif
tiled_multi_file_policy policy(filename_, format_, tile_size_, extent_, q.get_bbox(), width_, height_, tile_stride_);
return boost::make_shared<raster_featureset<tiled_multi_file_policy> >(policy, extent_, q);
}
else if (width * height > 512*512)
{
#ifdef MAPNIK_DEBUG
std::clog << "Raster Plugin: TILED policy" << std::endl;

View file

@ -37,6 +37,9 @@ class raster_datasource : public mapnik::datasource
std::string format_;
mapnik::box2d<double> extent_;
bool extent_initialized_;
bool multi_tiles_;
unsigned tile_size_;
unsigned tile_stride_;
mutable unsigned width_;
mutable unsigned height_;
public:

View file

@ -27,6 +27,7 @@
#include "raster_featureset.hpp"
#include <boost/algorithm/string/replace.hpp>
using mapnik::query;
using mapnik::CoordTransform;
@ -67,16 +68,18 @@ feature_ptr raster_featureset<LookupPolicy>::next()
std::clog << "Raster Plugin: READER = " << curIter_->format() << " " << curIter_->file()
<< " size(" << curIter_->width() << "," << curIter_->height() << ")" << std::endl;
#endif
if (reader.get())
{
int image_width=reader->width();
int image_height=reader->height();
int image_width = policy_.img_width(reader->width());
int image_height = policy_.img_height(reader->height());
if (image_width>0 && image_height>0)
{
CoordTransform t(image_width,image_height,extent_,0,0);
box2d<double> intersect=bbox_.intersect(curIter_->envelope());
box2d<double> ext=t.forward(intersect);
box2d<double> rem=policy_.transform(ext);
if ( ext.width()>0.5 && ext.height()>0.5 )
{
//select minimum raster containing whole ext
@ -96,7 +99,8 @@ feature_ptr raster_featureset<LookupPolicy>::next()
int width = end_x - x_off;
int height = end_y - y_off;
//calculate actual box2d of returned raster
box2d<double> feature_raster_extent(x_off, y_off, x_off+width, y_off+height);
box2d<double> feature_raster_extent(rem.minx() + x_off, rem.miny() + y_off,
rem.maxx() + x_off + width, rem.maxy() + y_off + height);
intersect = t.backward(feature_raster_extent);
image_data_32 image(width,height);
@ -121,5 +125,20 @@ feature_ptr raster_featureset<LookupPolicy>::next()
return feature_ptr();
}
std::string
tiled_multi_file_policy::interpolate(std::string const &pattern, int x, int y) const
{
// TODO: make from some sort of configurable interpolation
int tms_y = tile_stride_ * ((image_height_ / tile_size_) - y - 1);
int tms_x = tile_stride_ * x;
std::string xs = (boost::format("%03d/%03d/%03d") % (tms_x / 1000000) % ((tms_x / 1000) % 1000) % (tms_x % 1000)).str();
std::string ys = (boost::format("%03d/%03d/%03d") % (tms_y / 1000000) % ((tms_y / 1000) % 1000) % (tms_y % 1000)).str();
std::string rv(pattern);
boost::algorithm::replace_all(rv, "${x}", xs);
boost::algorithm::replace_all(rv, "${y}", ys);
return rv;
}
template class raster_featureset<single_file_policy>;
template class raster_featureset<tiled_file_policy>;
template class raster_featureset<tiled_multi_file_policy>;

View file

@ -29,7 +29,7 @@
// boost
#include <boost/utility.hpp>
#include <boost/format.hpp>
class single_file_policy
{
@ -95,6 +95,21 @@ public:
{
return const_iterator();
}
inline int img_width(int reader_width) const
{
return reader_width;
}
inline int img_height(int reader_height) const
{
return reader_height;
}
inline box2d<double> transform(box2d<double> &) const
{
return box2d<double>(0, 0, 0, 0);
}
};
class tiled_file_policy
@ -154,11 +169,125 @@ public:
return infos_.end();
}
inline int img_width(int reader_width) const
{
return reader_width;
}
inline int img_height(int reader_height) const
{
return reader_height;
}
inline box2d<double> transform(box2d<double> &) const
{
return box2d<double>(0, 0, 0, 0);
}
private:
std::vector<raster_info> infos_;
};
class tiled_multi_file_policy
{
public:
typedef std::vector<raster_info>::const_iterator const_iterator;
tiled_multi_file_policy(std::string const& file_pattern, std::string const& format, unsigned tile_size,
box2d<double> extent, box2d<double> bbox,unsigned width, unsigned height,
unsigned tile_stride)
: image_width_(width), image_height_(height), tile_size_(tile_size), tile_stride_(tile_stride)
{
double lox = extent.minx();
double loy = extent.miny();
int max_x = int(std::ceil(double(width)/double(tile_size)));
int max_y = int(std::ceil(double(height)/double(tile_size)));
double pixel_x = extent.width()/double(width);
double pixel_y = extent.height()/double(height);
#ifdef MAPNIK_DEBUG
std::clog << "Raster Plugin: PIXEL SIZE("<< pixel_x << "," << pixel_y << ")" << std::endl;
#endif
// intersection of query with extent => new query
box2d<double> e = bbox.intersect(extent);
const int x_min = int(std::floor((e.minx() - lox) / (tile_size * pixel_x)));
const int y_min = int(std::floor((e.miny() - loy) / (tile_size * pixel_y)));
const int x_max = int(std::ceil((e.maxx() - lox) / (tile_size * pixel_x)));
const int y_max = int(std::ceil((e.maxy() - loy) / (tile_size * pixel_y)));
for (int x = x_min ; x < x_max ; ++x)
{
for (int y = y_min ; y < y_max ; ++y)
{
// x0, y0, x1, y1 => projection-space image coordinates.
double x0 = lox + x*tile_size*pixel_x;
double y0 = loy + y*tile_size*pixel_y;
double x1 = x0 + tile_size*pixel_x;
double y1 = y0 + tile_size*pixel_y;
// check if it intersects the query
if (e.intersects(box2d<double>(x0,y0,x1,y1)))
{
// tile_box => intersection of tile with query in projection-space.
box2d<double> tile_box = e.intersect(box2d<double>(x0,y0,x1,y1));
std::string file = interpolate(file_pattern, x, y);
raster_info info(file,format,tile_box,tile_size,tile_size);
infos_.push_back(info);
}
}
}
#ifdef MAPNIK_DEBUG
std::clog << "Raster Plugin: INFO SIZE=" << infos_.size() << " " << file_pattern << std::endl;
#endif
}
const_iterator begin()
{
return infos_.begin();
}
const_iterator end()
{
return infos_.end();
}
inline int img_width(int) const
{
return image_width_;
}
inline int img_height(int) const
{
return image_height_;
}
inline box2d<double> transform(box2d<double> &box) const
{
int x_offset = int(std::floor(box.minx() / tile_size_));
int y_offset = int(std::floor(box.miny() / tile_size_));
box2d<double> rem(x_offset * tile_size_, y_offset * tile_size_,
x_offset * tile_size_, y_offset * tile_size_);
box.init(box.minx() - rem.minx(),
box.miny() - rem.miny(),
box.maxx() - rem.maxx(),
box.maxy() - rem.maxy());
return rem;
}
private:
std::string interpolate(std::string const &pattern, int x, int y) const;
unsigned int image_width_, image_height_, tile_size_, tile_stride_;
std::vector<raster_info> infos_;
};
template <typename LookupPolicy>
class raster_featureset : public mapnik::Featureset

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python
from nose.tools import *
from utilities import execution_path, save_data, contains_word
import os, mapnik2
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 test_multi_tile_policy():
srs = '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs'
lyr = mapnik2.Layer('raster')
lyr.datasource = mapnik2.Raster(
file = '../data/raster_tiles/${x}/${y}.tif',
lox = -180,
loy = -90,
hix = 180,
hiy = 90,
multi = 1,
tile_size = 256,
x_width = 2,
y_width = 2
)
lyr.srs = srs
_map = mapnik2.Map(256, 256, srs)
style = mapnik2.Style()
rule = mapnik2.Rule()
sym = mapnik2.RasterSymbolizer()
rule.symbols.append(sym)
style.rules.append(rule)
_map.append_style('foo', style)
lyr.styles.append('foo')
_map.layers.append(lyr)
_map.zoom_to_box(lyr.envelope())
im = mapnik2.Image(_map.width, _map.height)
mapnik2.render(_map, im)
save_data('test_multi_tile_policy.png', im.tostring('png'))
# test green chunk
assert im.view(0,64,1,1).tostring() == '\x00\xff\x00\xff'
assert im.view(127,64,1,1).tostring() == '\x00\xff\x00\xff'
assert im.view(0,127,1,1).tostring() == '\x00\xff\x00\xff'
assert im.view(127,127,1,1).tostring() == '\x00\xff\x00\xff'
# test blue chunk
assert im.view(128,64,1,1).tostring() == '\x00\x00\xff\xff'
assert im.view(255,64,1,1).tostring() == '\x00\x00\xff\xff'
assert im.view(128,127,1,1).tostring() == '\x00\x00\xff\xff'
assert im.view(255,127,1,1).tostring() == '\x00\x00\xff\xff'
# test red chunk
assert im.view(0,128,1,1).tostring() == '\xff\x00\x00\xff'
assert im.view(127,128,1,1).tostring() == '\xff\x00\x00\xff'
assert im.view(0,191,1,1).tostring() == '\xff\x00\x00\xff'
assert im.view(127,191,1,1).tostring() == '\xff\x00\x00\xff'
# test magenta chunk
assert im.view(128,128,1,1).tostring() == '\xff\x00\xff\xff'
assert im.view(255,128,1,1).tostring() == '\xff\x00\xff\xff'
assert im.view(128,191,1,1).tostring() == '\xff\x00\xff\xff'
assert im.view(255,191,1,1).tostring() == '\xff\x00\xff\xff'
if __name__ == "__main__":
setup()
[eval(run)() for run in dir() if 'test_' in run]