hello world plugin is now standalone at https://github.com/mapnik/hello-world-input-plugin - refs #2790
This commit is contained in:
parent
493e1d8362
commit
41ad16f4d7
11 changed files with 1 additions and 504 deletions
16
SConstruct
16
SConstruct
|
@ -411,7 +411,6 @@ opts.AddVariables(
|
|||
BoolVariable('SVG2PNG', 'Compile and install a utility to generate render an svg file to a png on the command line', 'False'),
|
||||
BoolVariable('NIK2IMG', 'Compile and install a utility to generate render a map to an image', 'True'),
|
||||
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'),
|
||||
)
|
||||
|
||||
|
@ -452,7 +451,6 @@ pickle_store = [# Scons internal variables
|
|||
'HAS_PYCAIRO',
|
||||
'PYCAIRO_PATHS',
|
||||
'HAS_LIBXML2',
|
||||
'SAMPLE_INPUT_PLUGINS',
|
||||
'PKG_CONFIG_PATH',
|
||||
'PATH',
|
||||
'PATH_REMOVE',
|
||||
|
@ -1964,16 +1962,4 @@ if not HELP_REQUESTED:
|
|||
SConscript('utils/mapnik-config/build.py')
|
||||
|
||||
# write the viewer.ini file
|
||||
SConscript('demo/viewer/build.py')
|
||||
|
||||
# if requested, build the sample input plugins
|
||||
if env['SAMPLE_INPUT_PLUGINS']:
|
||||
SConscript('plugins/input/templates/helloworld/build.py')
|
||||
else:
|
||||
if 'install' in COMMAND_LINE_TARGETS:
|
||||
plugin_path = os.path.join(env['MAPNIK_INPUT_PLUGINS_DEST'],'hello.input')
|
||||
if os.path.exists(plugin_path):
|
||||
color_print(4,"Notice: removing out of date plugin: '%s'" % plugin_path)
|
||||
os.unlink(plugin_path)
|
||||
if os.path.exists('plugins/input/templates/hello.input'):
|
||||
os.unlink('plugins/input/templates/hello.input')
|
||||
SConscript('demo/viewer/build.py')
|
|
@ -1,16 +0,0 @@
|
|||
## template plugins
|
||||
|
||||
Directory to hold sample plugin templates.
|
||||
|
||||
These are NOT intended to be used except for testing by developers.
|
||||
|
||||
Build these plugins with the Mapnik build system:
|
||||
|
||||
./configure SAMPLE_INPUT_PLUGINS=True
|
||||
make install
|
||||
|
||||
Or develop them locally using the `Makefile` provided.
|
||||
|
||||
Only an ultra-simple hello world is available currently,
|
||||
but planned are example plugins templates for file-based
|
||||
and sql-based datasources.
|
|
@ -1,30 +0,0 @@
|
|||
# To use clang, run: make CXX=clang++
|
||||
|
||||
CXXFLAGS = $(shell mapnik-config --cflags) -fPIC
|
||||
|
||||
LIBS = $(shell mapnik-config --libs --ldflags --dep-libs)
|
||||
|
||||
SRC = $(wildcard *.cpp)
|
||||
|
||||
OBJ = $(SRC:.cpp=.o)
|
||||
|
||||
BIN = hello.input
|
||||
|
||||
all : $(SRC) $(BIN)
|
||||
|
||||
$(BIN) : $(OBJ)
|
||||
$(CXX) -shared $(OBJ) $(LIBS) -o $@
|
||||
|
||||
.cpp.o :
|
||||
$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
.PHONY : clean
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ)
|
||||
rm -f $(BIN)
|
||||
|
||||
deploy : all
|
||||
cp hello.input $(shell mapnik-config --input-plugins)
|
||||
|
||||
install: all deploy
|
|
@ -1,40 +0,0 @@
|
|||
## 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.
|
||||
|
||||
It is not a model plugin of best practices as much as a model of the bare
|
||||
minimum you need to have a working plugin that returns a single feature.
|
||||
|
||||
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
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Map srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" 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="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
|
||||
<StyleName>style</StyleName>
|
||||
<Datasource>
|
||||
<Parameter name="type">hello</Parameter>
|
||||
</Datasource>
|
||||
</Layer>
|
||||
</Map>
|
||||
```
|
||||
|
||||
Or used in python like:
|
||||
|
||||
```
|
||||
import mapnik
|
||||
ds = mapnik.Datasource(type="hello")
|
||||
```
|
|
@ -1,86 +0,0 @@
|
|||
#!/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
|
||||
|
||||
# Here we pull from the SCons environment exported from the main instance
|
||||
Import ('plugin_base')
|
||||
Import ('env')
|
||||
|
||||
# Give this plugin a name
|
||||
# here this happens to be the same as the directory
|
||||
PLUGIN_NAME = 'hello'
|
||||
|
||||
# the below install details are also pulled from the
|
||||
# main SConstruct file where configuration happens
|
||||
|
||||
# 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 = plugin_base.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'
|
||||
|
||||
libraries.append('boost_system%s' % env['BOOST_APPEND'])
|
||||
# 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'])
|
||||
|
||||
# this is valid if we are building an external plugin as shared library
|
||||
if env['PLUGIN_LINKING'] == 'shared':
|
||||
# plugins can go anywhere, and be registered in custom locations by Mapnik
|
||||
# but the standard location is '/usr/local/lib/mapnik/input'
|
||||
install_dest = env['MAPNIK_INPUT_PLUGINS_DEST']
|
||||
|
||||
# only link mapnik if we are build an external shared object
|
||||
libraries.append(env['MAPNIK_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
|
||||
)
|
||||
|
||||
# if the plugin links to libmapnik ensure it is built first
|
||||
Depends(TARGET, env.subst('../../../../src/%s' % env['MAPNIK_LIB_NAME']))
|
||||
|
||||
# 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)
|
||||
|
||||
# Return the plugin building options to scons
|
||||
# This is used when statically linking the plugin with mapnik)
|
||||
plugin_obj = {
|
||||
'LIBS': libraries,
|
||||
'SOURCES': plugin_sources,
|
||||
}
|
||||
|
||||
Return('plugin_obj')
|
|
@ -1,76 +0,0 @@
|
|||
// file plugin
|
||||
#include "hello_datasource.hpp"
|
||||
#include "hello_featureset.hpp"
|
||||
|
||||
// boost
|
||||
|
||||
|
||||
using mapnik::datasource;
|
||||
using mapnik::parameters;
|
||||
|
||||
DATASOURCE_PLUGIN(hello_datasource)
|
||||
|
||||
hello_datasource::hello_datasource(parameters const& params)
|
||||
: datasource(params),
|
||||
desc_(hello_datasource::name(), *params.get<std::string>("encoding","utf-8")),
|
||||
extent_()
|
||||
{
|
||||
this->init(params);
|
||||
}
|
||||
|
||||
void hello_datasource::init(mapnik::parameters const& params)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
hello_datasource::~hello_datasource() { }
|
||||
|
||||
// This name must match the plugin filename, eg 'hello.input'
|
||||
const char * hello_datasource::name()
|
||||
{
|
||||
return "hello";
|
||||
}
|
||||
|
||||
mapnik::datasource::datasource_t hello_datasource::type() const
|
||||
{
|
||||
return datasource::Vector;
|
||||
}
|
||||
|
||||
mapnik::box2d<double> hello_datasource::envelope() const
|
||||
{
|
||||
return extent_;
|
||||
}
|
||||
|
||||
boost::optional<mapnik::datasource_geometry_t> hello_datasource::get_geometry_type() const
|
||||
{
|
||||
return mapnik::datasource_geometry_t::Point;
|
||||
}
|
||||
|
||||
mapnik::layer_descriptor hello_datasource::get_descriptor() const
|
||||
{
|
||||
return desc_;
|
||||
}
|
||||
|
||||
mapnik::featureset_ptr hello_datasource::features(mapnik::query const& q) const
|
||||
{
|
||||
// if the query box intersects our world extent then query for features
|
||||
if (extent_.intersects(q.get_bbox()))
|
||||
{
|
||||
return std::make_shared<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, double tol) const
|
||||
{
|
||||
// features_at_point is rarely used - only by custom applications,
|
||||
// so for this sample plugin let's do nothing...
|
||||
return mapnik::featureset_ptr();
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
#ifndef FILE_DATASOURCE_HPP
|
||||
#define FILE_DATASOURCE_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/datasource.hpp>
|
||||
#include <mapnik/params.hpp>
|
||||
#include <mapnik/query.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/box2d.hpp>
|
||||
#include <mapnik/coord.hpp>
|
||||
#include <mapnik/feature_layer_desc.hpp>
|
||||
|
||||
// boost
|
||||
#include <boost/optional.hpp>
|
||||
#include <memory>
|
||||
|
||||
// stl
|
||||
#include <string>
|
||||
|
||||
class hello_datasource : public mapnik::datasource
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
// arguments must not change
|
||||
hello_datasource(mapnik::parameters const& params);
|
||||
|
||||
// destructor
|
||||
virtual ~hello_datasource ();
|
||||
|
||||
// mandatory: type of the plugin, used to match at runtime
|
||||
mapnik::datasource::datasource_t type() const;
|
||||
|
||||
// mandatory: name of the plugin
|
||||
static const char * 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, double tol = 0) 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: optionally return the overal geometry type of the datasource
|
||||
boost::optional<mapnik::datasource_geometry_t> get_geometry_type() const;
|
||||
|
||||
// mandatory: return the layer descriptor
|
||||
mapnik::layer_descriptor get_descriptor() const;
|
||||
|
||||
private:
|
||||
// recommended - do intialization in a so-named init function
|
||||
// to reduce code in constructor
|
||||
void init(mapnik::parameters const& params);
|
||||
// recommended naming convention of datasource members:
|
||||
// name_, type_, extent_, and desc_
|
||||
static const std::string name_;
|
||||
mapnik::layer_descriptor desc_;
|
||||
mapnik::box2d<double> extent_;
|
||||
};
|
||||
|
||||
|
||||
#endif // FILE_DATASOURCE_HPP
|
|
@ -1,69 +0,0 @@
|
|||
// mapnik
|
||||
#include <mapnik/feature_factory.hpp>
|
||||
#include <mapnik/value_types.hpp>
|
||||
|
||||
// boost
|
||||
|
||||
#include "hello_featureset.hpp"
|
||||
|
||||
hello_featureset::hello_featureset(mapnik::box2d<double> const& box, std::string const& encoding)
|
||||
: box_(box),
|
||||
feature_id_(1),
|
||||
tr_(new mapnik::transcoder(encoding)),
|
||||
ctx_(std::make_shared<mapnik::context_type>())
|
||||
{
|
||||
// add known field names to attributes schema
|
||||
ctx_->push("key");
|
||||
}
|
||||
|
||||
hello_featureset::~hello_featureset() { }
|
||||
|
||||
mapnik::feature_ptr hello_featureset::next()
|
||||
{
|
||||
if (feature_id_ == 1)
|
||||
{
|
||||
// create a new feature
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
|
||||
|
||||
// increment the count
|
||||
++feature_id_;
|
||||
|
||||
// create an attribute pair of key:value
|
||||
feature->put("key",tr_->transcode("hello world point!"));
|
||||
|
||||
// take the center of the bbox that was used to query
|
||||
// to dynamically generate a fake point
|
||||
mapnik::coord2d center = box_.center();
|
||||
|
||||
// create a new point geometry
|
||||
feature->set_geometry(mapnik::geometry::point<double>(center.x,center.y));
|
||||
|
||||
// return the feature!
|
||||
return feature;
|
||||
}
|
||||
else if (feature_id_ == 2)
|
||||
{
|
||||
// create a second feature
|
||||
mapnik::feature_ptr feature(mapnik::feature_factory::create(ctx_,feature_id_));
|
||||
|
||||
// increment the count
|
||||
++feature_id_;
|
||||
|
||||
// create an attribute pair of key:value
|
||||
feature->put("key",tr_->transcode("hello world line!"));
|
||||
|
||||
// take the outer ring of the bbox that was used to query
|
||||
// to dynamically generate a fake line
|
||||
mapnik::geometry::line_string<double> line;
|
||||
line.reserve(4);
|
||||
line.add_coord(box_.minx(),box_.maxy());
|
||||
line.add_coord(box_.maxx(),box_.maxy());
|
||||
line.add_coord(box_.maxx(),box_.miny());
|
||||
line.add_coord(box_.minx(),box_.miny());
|
||||
feature->set_geometry(std::move(line));
|
||||
return feature;
|
||||
}
|
||||
|
||||
// otherwise return an empty feature
|
||||
return mapnik::feature_ptr();
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
#ifndef HELLO_FEATURESET_HPP
|
||||
#define HELLO_FEATURESET_HPP
|
||||
|
||||
// mapnik
|
||||
#include <mapnik/datasource.hpp>
|
||||
#include <mapnik/feature.hpp>
|
||||
#include <mapnik/unicode.hpp>
|
||||
|
||||
// boost
|
||||
// needed for wrapping the transcoder
|
||||
|
||||
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> box_;
|
||||
mapnik::value_integer feature_id_;
|
||||
const std::unique_ptr<mapnik::transcoder> tr_;
|
||||
mapnik::context_ptr ctx_;
|
||||
};
|
||||
|
||||
#endif // HELLO_FEATURESET_HPP
|
|
@ -1,19 +0,0 @@
|
|||
<Map srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs" background-color="white" minimum-version="0.7.2">
|
||||
<Style name="style">
|
||||
<Rule>
|
||||
<PointSymbolizer/>
|
||||
<!-- the hello world sample hardcodes 'key' as the hypothetical field name -->
|
||||
<TextSymbolizer name="[key]" size="10" dx="5" dy="5" face-name="DejaVu Sans Book"/>
|
||||
<LineSymbolizer stroke="green" stroke-width="3"/>
|
||||
</Rule>
|
||||
</Style>
|
||||
<!-- this example only works in EPSG:4326 -->
|
||||
<Layer name="test" srs="+proj=longlat +ellps=WGS84 +datum=WGS84 +no_defs">
|
||||
<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>
|
|
@ -1,55 +0,0 @@
|
|||
#include "catch.hpp"
|
||||
|
||||
#include <mapnik/datasource_cache.hpp>
|
||||
#include <mapnik/datasource.hpp>
|
||||
#include <mapnik/util/fs.hpp>
|
||||
|
||||
TEST_CASE("datasources") {
|
||||
|
||||
SECTION("hello world") {
|
||||
|
||||
std::string plugin("./plugins/input/templates/hello.input");
|
||||
if (mapnik::util::exists(plugin))
|
||||
{
|
||||
try
|
||||
{
|
||||
mapnik::datasource_cache::instance().register_datasource(plugin);
|
||||
mapnik::parameters p;
|
||||
p["type"]="hello";
|
||||
mapnik::datasource_ptr ds = mapnik::datasource_cache::instance().create(p);
|
||||
mapnik::box2d<double> bbox = ds->envelope();
|
||||
mapnik::query q(bbox);
|
||||
mapnik::featureset_ptr fs = ds->features(q);
|
||||
REQUIRE( fs != mapnik::featureset_ptr() );
|
||||
mapnik::feature_ptr feat1 = fs->next();
|
||||
REQUIRE( feat1 != mapnik::feature_ptr() );
|
||||
mapnik::feature_ptr feat2 = fs->next();
|
||||
REQUIRE( feat2 != mapnik::feature_ptr() );
|
||||
REQUIRE( fs->next() == mapnik::feature_ptr() );
|
||||
REQUIRE( feat1->id() == static_cast<mapnik::value_integer>(1) );
|
||||
REQUIRE( feat2->id() == static_cast<mapnik::value_integer>(2) );
|
||||
auto const& geom1 = feat1->get_geometry();
|
||||
REQUIRE( geom1.is<mapnik::geometry::point<double> >() );
|
||||
auto const& point = mapnik::util::get<mapnik::geometry::point<double> >(geom1);
|
||||
REQUIRE( point.x == bbox.center().x );
|
||||
REQUIRE( point.y == bbox.center().y );
|
||||
auto const& geom2 = feat2->get_geometry();
|
||||
REQUIRE( geom2.is<mapnik::geometry::line_string<double> >() );
|
||||
auto const& line = mapnik::util::get<mapnik::geometry::line_string<double> >(geom2);
|
||||
REQUIRE( line.size() == 4 );
|
||||
REQUIRE( line[0].x == bbox.minx() );
|
||||
REQUIRE( line[0].y == bbox.maxy() );
|
||||
}
|
||||
catch (std::exception const& ex)
|
||||
{
|
||||
FAIL(ex.what());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WARN( std::string("could not register ") + plugin );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue