rapidjson steppin in

This commit is contained in:
karenzshea 2017-05-25 17:56:42 +02:00
parent f5564c9275
commit aed2c0124a
3 changed files with 98 additions and 52 deletions

View File

@ -478,6 +478,8 @@ if(ENABLE_MASON)
# expat and bzip2 are used from mason rather than the system # expat and bzip2 are used from mason rather than the system
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include) include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/libosmium/include)
include_directories(SYSTEM ${CMAKE_CURRENT_SOURCE_DIR}/third_party/rapidjson/include)
else() else()
find_package(Boost 1.54 REQUIRED COMPONENTS ${BOOST_COMPONENTS}) find_package(Boost 1.54 REQUIRED COMPONENTS ${BOOST_COMPONENTS})

View File

@ -6,6 +6,8 @@
#include <boost/geometry.hpp> #include <boost/geometry.hpp>
#include <boost/geometry/index/rtree.hpp> #include <boost/geometry/index/rtree.hpp>
#include <rapidjson/document.h>
#include <chrono> #include <chrono>
namespace osrm namespace osrm
@ -37,6 +39,7 @@ class Timezoner
private: private:
void LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time); void LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time);
void ValidateFeature(const rapidjson::Value &feature);
struct tm default_time; struct tm default_time;
rtree_t rtree; rtree_t rtree;

View File

@ -1,12 +1,11 @@
#include "util/timezones.hpp"
#include "util/exception.hpp" #include "util/exception.hpp"
#include "util/log.hpp" #include "util/log.hpp"
#include "util/timezones.hpp"
#include <boost/scope_exit.hpp> #include <boost/scope_exit.hpp>
#ifdef ENABLE_SHAPEFILE #include "rapidjson/document.h"
#include <shapefil.h> #include "rapidjson/istreamwrapper.h"
#endif
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
@ -36,42 +35,80 @@ Timezoner::Timezoner(std::string tz_filename, std::time_t utc_time_now)
LoadLocalTimesRTree(tz_filename, utc_time_now); LoadLocalTimesRTree(tz_filename, utc_time_now);
} }
void Timezoner::ValidateFeature(const rapidjson::Value &feature)
{
if (!feature.HasMember("type"))
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature is missing type member.");
} else if (!feature["type"].IsString())
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature non-string type member.");
}
if (!feature.HasMember("properties"))
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature is missing properties member.");
}
else if (!features[i].GetObject()["properties"].IsObject())
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature has non-object properties member.");
}
if (!feature["properties"].GetObject().HasMember("TZID"))
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature is missing TZID member in properties.");
}
else if (!feature["properties"].GetObject()["TZID"].IsString())
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature has non-string TZID value.");
}
if (!feature.HasMember("geometry"))
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature is missing geometry member.");
}
else if (!feature.GetObject()["geometry"].IsObject())
{
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature non-object geometry member.");
}
if (!feature["geometry"].GetObject().HasMember("type"))
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature geometry is missing type member.");
if (!feature["geometry"].GetObject().HasMember("coordinates"))
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Feature geometry is missing coordinates member.");
}
void Timezoner::LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time) void Timezoner::LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::time_t utc_time)
{ {
if (tz_shapes_filename.empty()) if (tz_shapes_filename.empty())
return; return;
#ifdef ENABLE_SHAPEFILE std::ifstream file(tz_shapes_filename);
// Load time zones shapes and collect local times of utc_time if (!file.is_open)
auto shphandle = SHPOpen(tz_shapes_filename.c_str(), "rb"); throw osrm::util::exception("failed to open " + tz_shapes_filename);
auto dbfhandle = DBFOpen(tz_shapes_filename.c_str(), "rb");
BOOST_SCOPE_EXIT(&shphandle, &dbfhandle) rapidjson::IStreamWrapper isw(file);
rapidjson::Document geojson;
geojson.ParseStream(isw);
if (geojson.HasParseError)
{ {
DBFClose(dbfhandle); auto error_code = geojson.GetParseError();
SHPClose(shphandle); auto error_offset = geojson.GetErrorOffset();
} throw osrm::util::exception("Failed to parse " + tz_shapes_filename + " with error " +
BOOST_SCOPE_EXIT_END error_code + ". JSON malformed at " + error_offset);
if (!shphandle || !dbfhandle)
{
throw osrm::util::exception("failed to open " + tz_shapes_filename + ".shp or " +
tz_shapes_filename + ".dbf file");
}
int num_entities, shape_type;
SHPGetInfo(shphandle, &num_entities, &shape_type, NULL, NULL);
if (num_entities != DBFGetRecordCount(dbfhandle))
{
throw osrm::util::exception("inconsistent " + tz_shapes_filename + ".shp and " +
tz_shapes_filename + ".dbf files");
}
const auto tzid = DBFGetFieldIndex(dbfhandle, "TZID");
if (tzid == -1)
{
throw osrm::util::exception("did not find field called 'TZID' in the " +
tz_shapes_filename + ".dbf file");
} }
if (!geojson.HasMember("FeatureCollection"))
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Expecting a geojson feature collection.");
if (!geojson["FeatureCollection"].GetObject().HasMember("features"))
throw osrm::util::exception("Failed to parse " + tz_shapes_filename +
". Missing features list.");
// Lambda function that returns local time in the tzname time zone // Lambda function that returns local time in the tzname time zone
// Thread safety: MT-Unsafe const:env // Thread safety: MT-Unsafe const:env
@ -89,39 +126,43 @@ void Timezoner::LoadLocalTimesRTree(const std::string &tz_shapes_filename, std::
return it->second; return it->second;
}; };
BOOST_ASSERT(geojson["features"].IsArray());
// Get all time zone shapes and save local times in a vector rapidjson::Value &features_array = geojson["features"].GetArray();
std::vector<rtree_t::value_type> polygons; std::vector<rtree_t::value_type> polygons;
for (int shape = 0; shape < num_entities; ++shape) for (rapidjson::SizeType i = 0; i < features_array.Size(); i++)
{ {
auto object = SHPReadObject(shphandle, shape); ValidateFeature(features_array[i]);
BOOST_SCOPE_EXIT(&object) { SHPDestroyObject(object); } std::string feat_type = features[i].GetObject()["geometry"].GetObject()["type"].GetString();
BOOST_SCOPE_EXIT_END if (feat_type == "polygon")
if (object && object->nSHPType == SHPT_POLYGON)
{ {
// Find time zone polygon and place its bbox in into R-Tree
polygon_t polygon; polygon_t polygon;
for (int vertex = 0; vertex < object->nVertices; ++vertex) // per geojson spec, the first array of polygon coords is the exterior ring
auto coords_outer_array = features[i]
.GetObject()["geometry"]
.GetObject()["coordinates"]
.GetArray()[0]
.GetArray();
for (rapidjson::SizeType i = 0; i < coords_outer_array.Size(); ++i)
{ {
polygon.outer().emplace_back(object->padfX[vertex], object->padfY[vertex]); // polygon.outer().emplace_back(object->padfX[vertex], object->padfY[vertex]);
rapidjson::Value &coords = coords_outer_array[i].GetArray();
polygon.emplace_back(coords[0].GetDouble(), coords[1].GetDouble());
} }
polygons.emplace_back(boost::geometry::return_envelope<box_t>(polygon), polygons.emplace_back(boost::geometry::return_envelope<box_t>(polygon),
local_times.size()); local_times.size());
// Get time zone name and emplace polygon and local time for the UTC input // Get time zone name and emplace polygon and local time for the UTC input
const auto tzname = DBFReadStringAttribute(dbfhandle, shape, tzid); const auto tzname =
local_times.emplace_back(local_time_t{polygon, get_local_time_in_tz(tzname)}); features[i].GetObject()["properties"].GetObject()["TZID"].GetString();
local_times.push_back(local_time_t{polygon, get_local_time_in_tz(tzname)});
// std::cout << boost::geometry::dsv(boost::geometry::return_envelope<box_t>(polygon)) }
// << " " << tzname << " " << asctime(&local_times.back().second); else
{
util::Log << "Skipping non-polygon shape in timezone file " + tz_shapes_filename;
} }
} }
// Create R-tree for collected shape polygons // Create R-tree for collected shape polygons
rtree = rtree_t(polygons); rtree = rtree_t(polygons);
#endif
} }
struct tm Timezoner::operator()(const point_t &point) const struct tm Timezoner::operator()(const point_t &point) const