hello world plugin is now standalone at https://github.com/mapnik/hello-world-input-plugin - refs #2790

This commit is contained in:
Dane Springmeyer 2015-05-11 17:07:33 -07:00
parent 493e1d8362
commit 41ad16f4d7
11 changed files with 1 additions and 504 deletions

View file

@ -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',
@ -1965,15 +1963,3 @@ if not HELP_REQUESTED:
# 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')

View file

@ -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.

View file

@ -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

View file

@ -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")
```

View file

@ -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')

View file

@ -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();
}

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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>

View file

@ -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 );
}
}
}