add a directory for plugin templates - and add a first "hello world" with basic docs"
This commit is contained in:
parent
9281e6dcd8
commit
ea39e6f69e
10 changed files with 387 additions and 1 deletions
|
@ -326,6 +326,7 @@ opts.AddVariables(
|
|||
BoolVariable('DEMO', 'Compile demo c++ application', 'False'),
|
||||
BoolVariable('PGSQL2SQLITE', 'Compile and install a utility to convert postgres tables to sqlite', 'False'),
|
||||
BoolVariable('COLOR_PRINT', 'Print build status information in color', 'True'),
|
||||
BoolVariable('SAMPLE_INPUT_PLUGINS', 'Compile and install sample plugins', 'False'),
|
||||
)
|
||||
|
||||
# variables to pickle after successful configure step
|
||||
|
@ -1318,3 +1319,8 @@ if not HELP_REQUESTED:
|
|||
|
||||
# write the viewer.ini file
|
||||
SConscript('demo/viewer/SConscript')
|
||||
|
||||
# if requested, build the sample input plugins
|
||||
if env['SAMPLE_INPUT_PLUGINS']:
|
||||
SConscript('plugins/input/templates/helloworld/build.py')
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@
|
|||
#include <map>
|
||||
#include <string>
|
||||
|
||||
namespace mapnik {
|
||||
namespace mapnik {
|
||||
|
||||
typedef MAPNIK_DECL boost::shared_ptr<Feature> feature_ptr;
|
||||
|
||||
struct MAPNIK_DECL Featureset
|
||||
|
|
14
plugins/input/templates/README
Normal file
14
plugins/input/templates/README
Normal file
|
@ -0,0 +1,14 @@
|
|||
template plugins
|
||||
----------------
|
||||
|
||||
Directory to hold sample plugin templates.
|
||||
|
||||
These are NOT intended to be used except for testing by developers.
|
||||
|
||||
Build these plugins with:
|
||||
|
||||
$ scons SAMPLE_INPUT_PLUGINS=True
|
||||
|
||||
Only an ultra-simple hello world is available currently,
|
||||
but planned are example plugins templates for file-based
|
||||
and sql-based datasources.
|
51
plugins/input/templates/helloworld/README
Normal file
51
plugins/input/templates/helloworld/README
Normal file
|
@ -0,0 +1,51 @@
|
|||
hello world plugin
|
||||
------------------
|
||||
|
||||
This is a very simple sample plugin. It is designed to help developers
|
||||
see the skeletal basics needed to achieve a functional datasource plugin.
|
||||
|
||||
Code comments attempt to highlight which code is mandatory, which is
|
||||
simply recommended, and which is purely fluff used to get the plugin to
|
||||
actually show some data.
|
||||
|
||||
When added to a map it provides a single point geometry representing
|
||||
the center of any query. This means that it should place a point in
|
||||
the middle of any map tile and display a "hello world!" label if used like:
|
||||
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Map srs="+init=epsg:4326" background-color="white">
|
||||
<Style name="style">
|
||||
<Rule>
|
||||
<PointSymbolizer />
|
||||
<TextSymbolizer name="[key]" face_name="DejaVu Sans Book" size="10" dx="5" dy="5"/>
|
||||
</Rule>
|
||||
</Style>
|
||||
<Layer name="test" srs="+init=epsg:4326">
|
||||
<StyleName>style</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">hello</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
</Map>
|
||||
|
||||
|
||||
Or used in python like:
|
||||
|
||||
from mapnik2 import *
|
||||
m = Map(600,400)
|
||||
m.background = Color('white')
|
||||
s = Style()
|
||||
r = Rule()
|
||||
r.symbols.append(PointSymbolizer())
|
||||
t = TextSymbolizer(Expression("[key]"),"DejaVu Sans Book",10,Color('black'))
|
||||
t.displacement(15,15)
|
||||
r.symbols.append(t)
|
||||
s.rules.append(r)
|
||||
m.append_style('style',s)
|
||||
ds = Datasource(type="hello")
|
||||
l = Layer('test')
|
||||
l.styles.append('style')
|
||||
l.datasource = ds
|
||||
m.layers.append(l)
|
||||
m.zoom_all()
|
||||
render_to_file(m,'test.png')
|
82
plugins/input/templates/helloworld/build.py
Normal file
82
plugins/input/templates/helloworld/build.py
Normal file
|
@ -0,0 +1,82 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# Mapnik uses the build tool SCons.
|
||||
|
||||
# This python file is run to compile a plugin
|
||||
# It must be called from the main 'SConstruct' file like:
|
||||
|
||||
# SConscript('path/to/this/file.py')
|
||||
|
||||
# see docs at: http://www.scons.org/wiki/SConscript()
|
||||
|
||||
import os
|
||||
|
||||
# Give this plugin a name
|
||||
# here this happens to be the same as the directory
|
||||
PLUGIN_NAME = 'hello'
|
||||
|
||||
# Here we pull from the SCons environment exported from the main instance
|
||||
Import ('env')
|
||||
|
||||
# the below install details are also pulled from the
|
||||
# main SConstruct file where configuration happens
|
||||
|
||||
# DESTDIR is default None, PREFIX is default '/usr/local/'
|
||||
install_prefix = os.path.join(env['DESTDIR'],env['PREFIX'])
|
||||
|
||||
# LIBDIR_SCHEMA is likely either 'lib' or 'lib64', LIB_DIR_NAME is default None
|
||||
lib_name = '%s%s' % (env['LIBDIR_SCHEMA'],env['LIB_DIR_NAME'])
|
||||
|
||||
# plugins can go anywhere, and be registered in custom locations by Mapnik
|
||||
# but the standard location is '/usr/local/lib/mapnik2/input'
|
||||
install_dest = os.path.join(install_prefix,lib_name,'input')
|
||||
|
||||
# clone the environment here
|
||||
# so that if we modify the env it in this file
|
||||
# those changes to not pollute other builds later on...
|
||||
plugin_env = env.Clone()
|
||||
|
||||
# Add the cpp files that need to be compiled
|
||||
plugin_sources = Split(
|
||||
"""
|
||||
%(PLUGIN_NAME)s_datasource.cpp
|
||||
%(PLUGIN_NAME)s_featureset.cpp
|
||||
""" % locals()
|
||||
)
|
||||
|
||||
# Add any external libraries this plugin should
|
||||
# directly link to
|
||||
libraries = [ '' ] # eg 'libfoo'
|
||||
|
||||
# on mac os x we need to directly link to mapnik and libicu*
|
||||
if env['PLATFORM'] == 'Darwin':
|
||||
libraries.append('mapnik2')
|
||||
# link libicuuc, but ICU_LIB_NAME is used custom builds of icu can
|
||||
# have different library names like osx which offers /usr/lib/libicucore.dylib
|
||||
libraries.append(env['ICU_LIB_NAME'])
|
||||
|
||||
TARGET = plugin_env.SharedLibrary(
|
||||
# the name of the target to build, eg 'sqlite.input'
|
||||
'../%s' % PLUGIN_NAME,
|
||||
# prefix - normally none used
|
||||
SHLIBPREFIX='',
|
||||
# extension, mapnik expects '.input'
|
||||
SHLIBSUFFIX='.input',
|
||||
# list of source files to compile
|
||||
source=plugin_sources,
|
||||
# libraries to link to
|
||||
LIBS=libraries,
|
||||
# any custom linkflags, eg. LDFLAGS
|
||||
# in this case CUSTOM_LDFLAGS comes
|
||||
# from Mapnik's main SConstruct file
|
||||
# and can be removed here if you do
|
||||
# not need it
|
||||
LINKFLAGS=env.get('CUSTOM_LDFLAGS')
|
||||
)
|
||||
|
||||
# if 'uninstall' is not passed on the command line
|
||||
# then we actually create the install targets that
|
||||
# scons will install if 'install' is passed as an arg
|
||||
if 'uninstall' not in COMMAND_LINE_TARGETS:
|
||||
env.Install(install_dest, TARGET)
|
||||
env.Alias('install', install_dest)
|
86
plugins/input/templates/helloworld/hello_datasource.cpp
Normal file
86
plugins/input/templates/helloworld/hello_datasource.cpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
// file plugin
|
||||
#include "hello_datasource.hpp"
|
||||
#include "hello_featureset.hpp"
|
||||
|
||||
using mapnik::datasource;
|
||||
using mapnik::parameters;
|
||||
|
||||
DATASOURCE_PLUGIN(hello_datasource)
|
||||
|
||||
hello_datasource::hello_datasource(parameters const& params, bool bind)
|
||||
: datasource(params),
|
||||
type_(datasource::Vector),
|
||||
desc_(*params_.get<std::string>("type"), *params_.get<std::string>("encoding","utf-8")),
|
||||
extent_()
|
||||
{
|
||||
if (bind)
|
||||
{
|
||||
this->bind();
|
||||
}
|
||||
}
|
||||
|
||||
void hello_datasource::bind() const
|
||||
{
|
||||
if (is_bound_) return;
|
||||
|
||||
// every datasource must have some way of reporting its extent
|
||||
// in this case we are not actually reading from any data so for fun
|
||||
// let's just create a world extent in Mapnik's default srs:
|
||||
// '+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs' (equivalent to +init=epsg:4326)
|
||||
// see http://spatialreference.org/ref/epsg/4326/ for more details
|
||||
extent_.init(-180,-90,180,90);
|
||||
|
||||
is_bound_ = true;
|
||||
}
|
||||
|
||||
hello_datasource::~hello_datasource() { }
|
||||
|
||||
// This name must match the plugin filename, eg 'hello.input'
|
||||
std::string const hello_datasource::name_="hello";
|
||||
|
||||
std::string hello_datasource::name()
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
int hello_datasource::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
mapnik::box2d<double> hello_datasource::envelope() const
|
||||
{
|
||||
if (!is_bound_) bind();
|
||||
|
||||
return extent_;
|
||||
}
|
||||
|
||||
mapnik::layer_descriptor hello_datasource::get_descriptor() const
|
||||
{
|
||||
if (!is_bound_) bind();
|
||||
|
||||
return desc_;
|
||||
}
|
||||
|
||||
mapnik::featureset_ptr hello_datasource::features(mapnik::query const& q) const
|
||||
{
|
||||
if (!is_bound_) bind();
|
||||
|
||||
// if the query box intersects our world extent then query for features
|
||||
if (extent_.intersects(q.get_bbox()))
|
||||
{
|
||||
return mapnik::featureset_ptr(new hello_featureset(q.get_bbox(),desc_.get_encoding()));
|
||||
}
|
||||
|
||||
// otherwise return an empty featureset pointer
|
||||
return mapnik::featureset_ptr();
|
||||
}
|
||||
|
||||
mapnik::featureset_ptr hello_datasource::features_at_point(mapnik::coord2d const& pt) const
|
||||
{
|
||||
if (!is_bound_) bind();
|
||||
|
||||
// features_at_point is rarely used - only by custom applications,
|
||||
// so for this sample plugin let's do nothing...
|
||||
return mapnik::featureset_ptr();
|
||||
}
|
51
plugins/input/templates/helloworld/hello_datasource.hpp
Normal file
51
plugins/input/templates/helloworld/hello_datasource.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
#ifndef FILE_DATASOURCE_HPP
|
||||
#define FILE_DATASOURCE_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/datasource.hpp>
|
||||
|
||||
class hello_datasource : public mapnik::datasource
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
// arguments must not change
|
||||
hello_datasource(mapnik::parameters const& params, bool bind=true);
|
||||
|
||||
// destructor
|
||||
virtual ~hello_datasource ();
|
||||
|
||||
// mandatory: type of the plugin, used to match at runtime
|
||||
int type() const;
|
||||
|
||||
// mandatory: name of the plugin
|
||||
static std::string name();
|
||||
|
||||
// mandatory: function to query features by box2d
|
||||
// this is called when rendering, specifically in feature_style_processor.hpp
|
||||
mapnik::featureset_ptr features(mapnik::query const& q) const;
|
||||
|
||||
// mandatory: function to query features by point (coord2d)
|
||||
// not used by rendering, but available to calling applications
|
||||
mapnik::featureset_ptr features_at_point(mapnik::coord2d const& pt) const;
|
||||
|
||||
// mandatory: return the box2d of the datasource
|
||||
// called during rendering to determine if the layer should be processed
|
||||
mapnik::box2d<double> envelope() const;
|
||||
|
||||
// mandatory: return the layer descriptor
|
||||
mapnik::layer_descriptor get_descriptor() const;
|
||||
|
||||
// mandatory: whether to bind the datasource or delay
|
||||
void bind() const;
|
||||
|
||||
private:
|
||||
// recommended naming convention of datasource members:
|
||||
// name_, type_, extent_, and desc_
|
||||
static const std::string name_;
|
||||
int type_;
|
||||
mutable mapnik::layer_descriptor desc_;
|
||||
mutable mapnik::box2d<double> extent_;
|
||||
};
|
||||
|
||||
|
||||
#endif // FILE_DATASOURCE_HPP
|
46
plugins/input/templates/helloworld/hello_featureset.cpp
Normal file
46
plugins/input/templates/helloworld/hello_featureset.cpp
Normal file
|
@ -0,0 +1,46 @@
|
|||
#include "hello_featureset.hpp"
|
||||
|
||||
hello_featureset::hello_featureset(mapnik::box2d<double> const& box, std::string const& encoding)
|
||||
: box_(box),
|
||||
count_(0),
|
||||
tr_(new mapnik::transcoder(encoding)) { }
|
||||
|
||||
hello_featureset::~hello_featureset() { }
|
||||
|
||||
mapnik::feature_ptr hello_featureset::next()
|
||||
{
|
||||
if (!count_)
|
||||
{
|
||||
// create a new feature
|
||||
mapnik::feature_ptr feature(new mapnik::Feature(count_));
|
||||
|
||||
// create an attribute pair of key:value
|
||||
UnicodeString ustr = tr_->transcode("hello world!");
|
||||
boost::put(*feature,"key",ustr);
|
||||
|
||||
// we need a geometry to display so just for fun here
|
||||
// we take the center of the bbox that was used to query
|
||||
// since we don't actually have any data to pull from...
|
||||
mapnik::coord2d center = box_.center();
|
||||
|
||||
// create a new geometry
|
||||
mapnik::geometry2d * point = new mapnik::point_impl;
|
||||
|
||||
// we use path type geometries in Mapnik to fit nicely with AGG and Cairo
|
||||
// here we stick an x,y pair into the geometry
|
||||
point->move_to(center.x,center.y);
|
||||
|
||||
// add the geometry to the feature
|
||||
feature->add_geometry(point);
|
||||
|
||||
// increment to count so that we only return on feature
|
||||
++count_;
|
||||
|
||||
// return the feature!
|
||||
return feature;
|
||||
}
|
||||
|
||||
// otherwise return an empty feature_ptr
|
||||
return mapnik::feature_ptr();
|
||||
}
|
||||
|
30
plugins/input/templates/helloworld/hello_featureset.hpp
Normal file
30
plugins/input/templates/helloworld/hello_featureset.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef HELLO_FEATURESET_HPP
|
||||
#define HELLO_FEATURESET_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/datasource.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/scoped_ptr.hpp> // needed for wrapping the transcoder
|
||||
|
||||
// extend the mapnik::Featureset defined in include/mapnik/datasource.hpp
|
||||
class hello_featureset : public mapnik::Featureset
|
||||
{
|
||||
public:
|
||||
// this constructor can have any arguments you need
|
||||
hello_featureset(mapnik::box2d<double> const& box, std::string const& encoding);
|
||||
|
||||
// desctructor
|
||||
virtual ~hello_featureset();
|
||||
|
||||
// mandatory: you must expose a next() method, called when rendering
|
||||
mapnik::feature_ptr next();
|
||||
|
||||
private:
|
||||
// members are up to you, but these are recommended
|
||||
mapnik::box2d<double> const& box_;
|
||||
mutable int count_;
|
||||
boost::scoped_ptr<mapnik::transcoder> tr_;
|
||||
};
|
||||
|
||||
#endif // HELLO_FEATURESET_HPP
|
19
plugins/input/templates/helloworld/test.xml
Normal file
19
plugins/input/templates/helloworld/test.xml
Normal file
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Map srs="+init=epsg:4326" background-color="white">
|
||||
<Style name="style">
|
||||
<Rule>
|
||||
<PointSymbolizer />
|
||||
<!-- the hello world sample hardcodes 'key' as the hypothetical field name -->
|
||||
<TextSymbolizer name="[key]" face_name="DejaVu Sans Book" size="10" dx="5" dy="5"/>
|
||||
</Rule>
|
||||
</Style>
|
||||
<!-- this example only works in EPSG:4326 -->
|
||||
<Layer name="test" srs="+init=epsg:4326">
|
||||
<StyleName>style</StyleName>
|
||||
<Datasource>
|
||||
<!-- here we create a 'hello' type datasource which simply
|
||||
displays a point in the middle of the world's boundin box -->
|
||||
<Parameter name="type">hello</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
</Map>
|
Loading…
Reference in a new issue