From 914b4c2e8b3c4d074fbdc6698c5f7d15cc2c26b6 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Mon, 28 Jul 2014 19:26:35 -0700 Subject: [PATCH] support for inline topojson (in memory string) - closes #2058 --- .../input/topojson/topojson_datasource.cpp | 52 +++++++++++++------ .../input/topojson/topojson_datasource.hpp | 5 +- tests/python_tests/topojson_plugin_test.py | 17 ++++++ 3 files changed, 58 insertions(+), 16 deletions(-) diff --git a/plugins/input/topojson/topojson_datasource.cpp b/plugins/input/topojson/topojson_datasource.cpp index 4f7417011..1403373c8 100644 --- a/plugins/input/topojson/topojson_datasource.cpp +++ b/plugins/input/topojson/topojson_datasource.cpp @@ -146,33 +146,55 @@ topojson_datasource::topojson_datasource(parameters const& params) type_(datasource::Vector), desc_(topojson_datasource::name(), *params.get("encoding","utf-8")), - file_(*params.get("file","")), + filename_(), + inline_string_(), extent_(), tr_(new mapnik::transcoder(*params.get("encoding","utf-8"))), tree_(16,1) { - if (file_.empty()) throw mapnik::datasource_exception("TopoJSON Plugin: missing parameter"); - - boost::optional base = params.get("base"); - if (base) + boost::optional inline_string = params.get("inline"); + if (inline_string) { - file_ = *base + "/" + file_; + inline_string_ = *inline_string; } + else + { + boost::optional file = params.get("file"); + if (!file) throw mapnik::datasource_exception("TopoJSON Plugin: missing parameter"); - using base_iterator_type = std::istreambuf_iterator; - + boost::optional base = params.get("base"); + if (base) + filename_ = *base + "/" + *file; + else + filename_ = *file; + } + if (!inline_string_.empty()) + { + std::istringstream in(inline_string_); + parse_topojson(in); + } + else + { #if defined (_WINDOWS) - std::ifstream is(mapnik::utf8_to_utf16(file_),std::ios_base::in | std::ios_base::binary); + std::ifstream in(mapnik::utf8_to_utf16(filename_),std::ios_base::in | std::ios_base::binary); #else - std::ifstream is(file_.c_str(),std::ios_base::in | std::ios_base::binary); + std::ifstream in(filename_.c_str(),std::ios_base::in | std::ios_base::binary); #endif - if (!is.is_open()) - { - throw mapnik::datasource_exception("TopoJSON Plugin: could not open: '" + file_ + "'"); + if (!in.is_open()) + { + throw mapnik::datasource_exception("TopoJSON Plugin: could not open: '" + filename_ + "'"); + } + parse_topojson(in); + in.close(); } +} +template +void topojson_datasource::parse_topojson(T & stream) +{ + using base_iterator_type = std::istreambuf_iterator; boost::spirit::multi_pass begin = - boost::spirit::make_default_multi_pass(base_iterator_type(is)); + boost::spirit::make_default_multi_pass(base_iterator_type(stream)); boost::spirit::multi_pass end = boost::spirit::make_default_multi_pass(base_iterator_type()); @@ -182,7 +204,7 @@ topojson_datasource::topojson_datasource(parameters const& params) bool result = boost::spirit::qi::phrase_parse(begin, end, g, space, topo_); if (!result) { - throw mapnik::datasource_exception("topojson_datasource: Failed parse TopoJSON file '" + file_ + "'"); + throw mapnik::datasource_exception("topojson_datasource: Failed parse TopoJSON file '" + filename_ + "'"); } std::size_t count = 0; diff --git a/plugins/input/topojson/topojson_datasource.hpp b/plugins/input/topojson/topojson_datasource.hpp index 2b83fb4c2..c9d31a74e 100644 --- a/plugins/input/topojson/topojson_datasource.hpp +++ b/plugins/input/topojson/topojson_datasource.hpp @@ -65,11 +65,14 @@ public: mapnik::box2d envelope() const; mapnik::layer_descriptor get_descriptor() const; boost::optional get_geometry_type() const; + template + void parse_topojson(T & stream); private: mapnik::datasource::datasource_t type_; std::map statistics_; mapnik::layer_descriptor desc_; - std::string file_; + std::string filename_; + std::string inline_string_; mapnik::box2d extent_; std::shared_ptr tr_; mapnik::topojson::topology topo_; diff --git a/tests/python_tests/topojson_plugin_test.py b/tests/python_tests/topojson_plugin_test.py index c36c0c50d..ff1f2ed42 100644 --- a/tests/python_tests/topojson_plugin_test.py +++ b/tests/python_tests/topojson_plugin_test.py @@ -54,6 +54,23 @@ if 'topojson' in mapnik.DatasourceCache.plugin_names(): eq_(f['NOM_FR'], u'Qu\xe9bec') eq_(f['NOM_FR'], u'Québec') + def test_geojson_from_in_memory_string(): + ds = mapnik.Datasource(type='topojson',inline=open('../data/json/escaped.topojson','r').read()) + f = ds.all_features()[0] + eq_(len(ds.fields()),7) + + desc = ds.describe() + eq_(desc['geometry_type'],mapnik.DataGeometryType.Point) + + eq_(f['name'], u'Test') + eq_(f['int'], 1) + eq_(f['description'], u'Test: \u005C') + eq_(f['spaces'], u'this has spaces') + eq_(f['double'], 1.1) + eq_(f['boolean'], True) + eq_(f['NOM_FR'], u'Qu\xe9bec') + eq_(f['NOM_FR'], u'Québec') + # @raises(RuntimeError) def test_that_nonexistant_query_field_throws(**kwargs): ds = mapnik.Datasource(type='topojson',file='../data/json/escaped.topojson')