mirror of
https://github.com/systemed/tilemaker.git
synced 2026-05-06 16:30:00 -04:00
643 lines
23 KiB
C++
643 lines
23 KiB
C++
#include "osm_lua_processing.h"
|
|
#include "helpers.h"
|
|
#include <iostream>
|
|
using namespace std;
|
|
|
|
kaguya::State *g_luaState = nullptr;
|
|
bool supportsRemappingShapefiles = false;
|
|
|
|
int lua_error_handler(int errCode, const char *errMessage)
|
|
{
|
|
cerr << "lua runtime error: " << errMessage << endl;
|
|
std::string traceback = (*g_luaState)["debug"]["traceback"];
|
|
cerr << "traceback: " << traceback << endl;
|
|
exit(0);
|
|
}
|
|
|
|
// ---- initialization routines
|
|
|
|
OsmLuaProcessing::OsmLuaProcessing(
|
|
OSMStore const *indexStore, OSMStore &osmStore,
|
|
const class Config &configIn, class LayerDefinition &layers,
|
|
const string &luaFile,
|
|
const class ShpMemTiles &shpMemTiles,
|
|
class OsmMemTiles &osmMemTiles,
|
|
AttributeStore &attributeStore):
|
|
indexStore(indexStore), osmStore(osmStore),
|
|
shpMemTiles(shpMemTiles),
|
|
osmMemTiles(osmMemTiles),
|
|
attributeStore(attributeStore),
|
|
config(configIn),
|
|
layers(layers) {
|
|
|
|
newWayID = MAX_WAY_ID;
|
|
|
|
// ---- Initialise Lua
|
|
g_luaState = &luaState;
|
|
luaState.setErrorHandler(lua_error_handler);
|
|
luaState.dofile(luaFile.c_str());
|
|
luaState["OSM"].setClass(kaguya::UserdataMetatable<OsmLuaProcessing>()
|
|
.addFunction("Id", &OsmLuaProcessing::Id)
|
|
.addFunction("Holds", &OsmLuaProcessing::Holds)
|
|
.addFunction("Find", &OsmLuaProcessing::Find)
|
|
.addFunction("FindIntersecting", &OsmLuaProcessing::FindIntersecting)
|
|
.addFunction("Intersects", &OsmLuaProcessing::Intersects)
|
|
.addFunction("FindCovering", &OsmLuaProcessing::FindCovering)
|
|
.addFunction("CoveredBy", &OsmLuaProcessing::CoveredBy)
|
|
.addFunction("IsClosed", &OsmLuaProcessing::IsClosed)
|
|
.addFunction("Area", &OsmLuaProcessing::Area)
|
|
.addFunction("AreaIntersecting", &OsmLuaProcessing::AreaIntersecting)
|
|
.addFunction("Length", &OsmLuaProcessing::Length)
|
|
.addFunction("Centroid", &OsmLuaProcessing::Centroid)
|
|
.addFunction("Layer", &OsmLuaProcessing::Layer)
|
|
.addFunction("LayerAsCentroid", &OsmLuaProcessing::LayerAsCentroid)
|
|
.addOverloadedFunctions("Attribute", &OsmLuaProcessing::Attribute, &OsmLuaProcessing::AttributeWithMinZoom)
|
|
.addOverloadedFunctions("AttributeNumeric", &OsmLuaProcessing::AttributeNumeric, &OsmLuaProcessing::AttributeNumericWithMinZoom)
|
|
.addOverloadedFunctions("AttributeBoolean", &OsmLuaProcessing::AttributeBoolean, &OsmLuaProcessing::AttributeBooleanWithMinZoom)
|
|
.addFunction("MinZoom", &OsmLuaProcessing::MinZoom)
|
|
);
|
|
if (luaState["attribute_function"]) {
|
|
supportsRemappingShapefiles = true;
|
|
} else {
|
|
supportsRemappingShapefiles = false;
|
|
}
|
|
|
|
// ---- Call init_function of Lua logic
|
|
|
|
luaState("if init_function~=nil then init_function() end");
|
|
|
|
}
|
|
|
|
OsmLuaProcessing::~OsmLuaProcessing() {
|
|
// Call exit_function of Lua logic
|
|
luaState("if exit_function~=nil then exit_function() end");
|
|
}
|
|
|
|
// ---- Helpers provided for main routine
|
|
|
|
// Has this object been assigned to any layers?
|
|
bool OsmLuaProcessing::empty() {
|
|
return outputs.size()==0;
|
|
}
|
|
|
|
bool OsmLuaProcessing::canRemapShapefiles() {
|
|
return supportsRemappingShapefiles;
|
|
}
|
|
|
|
kaguya::LuaTable OsmLuaProcessing::newTable() {
|
|
return luaState.newTable();//kaguya::LuaTable(luaState);
|
|
}
|
|
|
|
kaguya::LuaTable OsmLuaProcessing::remapAttributes(kaguya::LuaTable& in_table, const std::string &layerName) {
|
|
kaguya::LuaTable out_table = luaState["attribute_function"].call<kaguya::LuaTable>(in_table, layerName);
|
|
return out_table;
|
|
}
|
|
|
|
// ---- Metadata queries called from Lua
|
|
|
|
// Get the ID of the current object
|
|
string OsmLuaProcessing::Id() const {
|
|
return to_string(originalOsmID);
|
|
}
|
|
|
|
// Check if there's a value for a given key
|
|
bool OsmLuaProcessing::Holds(const string& key) const {
|
|
return currentTags.find(key) != currentTags.end();
|
|
}
|
|
|
|
// Get an OSM tag for a given key (or return empty string if none)
|
|
string OsmLuaProcessing::Find(const string& key) const {
|
|
auto it = currentTags.find(key);
|
|
if(it == currentTags.end()) return "";
|
|
return it->second;
|
|
}
|
|
|
|
// ---- Spatial queries called from Lua
|
|
|
|
vector<string> OsmLuaProcessing::FindIntersecting(const string &layerName) {
|
|
if (!isWay ) { return shpMemTiles.namesOfGeometries(intersectsQuery(layerName, false, getPoint())); }
|
|
else if (!isClosed) { return shpMemTiles.namesOfGeometries(intersectsQuery(layerName, false, linestringCached())); }
|
|
else if (isRelation){ return shpMemTiles.namesOfGeometries(intersectsQuery(layerName, false, multiPolygonCached())); }
|
|
else { return shpMemTiles.namesOfGeometries(intersectsQuery(layerName, false, polygonCached())); }
|
|
}
|
|
|
|
bool OsmLuaProcessing::Intersects(const string &layerName) {
|
|
if (!isWay ) { return !intersectsQuery(layerName, true, getPoint()).empty(); }
|
|
else if (!isClosed) { return !intersectsQuery(layerName, true, linestringCached()).empty(); }
|
|
else if (isRelation){ return !intersectsQuery(layerName, true, multiPolygonCached()).empty(); }
|
|
else { return !intersectsQuery(layerName, true, polygonCached()).empty(); }
|
|
}
|
|
|
|
vector<string> OsmLuaProcessing::FindCovering(const string &layerName) {
|
|
if (!isWay ) { return shpMemTiles.namesOfGeometries(coveredQuery(layerName, false, getPoint())); }
|
|
else if (!isClosed) { return shpMemTiles.namesOfGeometries(coveredQuery(layerName, false, linestringCached())); }
|
|
else if (isRelation){ return shpMemTiles.namesOfGeometries(coveredQuery(layerName, false, multiPolygonCached())); }
|
|
else { return shpMemTiles.namesOfGeometries(coveredQuery(layerName, false, polygonCached())); }
|
|
}
|
|
|
|
bool OsmLuaProcessing::CoveredBy(const string &layerName) {
|
|
if (!isWay ) { return !coveredQuery(layerName, true, getPoint()).empty(); }
|
|
else if (!isClosed) { return !coveredQuery(layerName, true, linestringCached()).empty(); }
|
|
else if (isRelation){ return !coveredQuery(layerName, true, multiPolygonCached()).empty(); }
|
|
else { return !coveredQuery(layerName, true, polygonCached()).empty(); }
|
|
}
|
|
|
|
double OsmLuaProcessing::AreaIntersecting(const string &layerName) {
|
|
if (!isWay || !isClosed) { return 0.0; }
|
|
else if (isRelation){ return intersectsArea(layerName, multiPolygonCached()); }
|
|
else { return intersectsArea(layerName, polygonCached()); }
|
|
}
|
|
|
|
|
|
template <typename GeometryT>
|
|
std::vector<uint> OsmLuaProcessing::intersectsQuery(const string &layerName, bool once, GeometryT &geom) const {
|
|
Box box; geom::envelope(geom, box);
|
|
std::vector<uint> ids = shpMemTiles.QueryMatchingGeometries(layerName, once, box,
|
|
[&](const RTree &rtree) { // indexQuery
|
|
vector<IndexValue> results;
|
|
rtree.query(geom::index::intersects(box), back_inserter(results));
|
|
return results;
|
|
},
|
|
[&](OutputObject &oo) { // checkQuery
|
|
return geom::intersects(geom, osmStore.retrieve<mmap::multi_polygon_t>(oo.handle));
|
|
}
|
|
);
|
|
return ids;
|
|
}
|
|
|
|
template <typename GeometryT>
|
|
double OsmLuaProcessing::intersectsArea(const string &layerName, GeometryT &geom) const {
|
|
Box box; geom::envelope(geom, box);
|
|
double area = 0.0;
|
|
std::vector<uint> ids = shpMemTiles.QueryMatchingGeometries(layerName, false, box,
|
|
[&](const RTree &rtree) { // indexQuery
|
|
vector<IndexValue> results;
|
|
rtree.query(geom::index::intersects(box), back_inserter(results));
|
|
return results;
|
|
},
|
|
[&](OutputObject &oo) { // checkQuery
|
|
MultiPolygon tmp;
|
|
geom::intersection(geom, osmStore.retrieve<mmap::multi_polygon_t>(oo.handle), tmp);
|
|
area += multiPolygonArea(tmp);
|
|
return false;
|
|
}
|
|
);
|
|
return area;
|
|
}
|
|
|
|
template <typename GeometryT>
|
|
std::vector<uint> OsmLuaProcessing::coveredQuery(const string &layerName, bool once, GeometryT &geom) const {
|
|
Box box; geom::envelope(geom, box);
|
|
std::vector<uint> ids = shpMemTiles.QueryMatchingGeometries(layerName, once, box,
|
|
[&](const RTree &rtree) { // indexQuery
|
|
vector<IndexValue> results;
|
|
rtree.query(geom::index::intersects(box), back_inserter(results));
|
|
return results;
|
|
},
|
|
[&](OutputObject &oo) { // checkQuery
|
|
if (oo.geomType!=OutputGeometryType::POLYGON) return false; // can only be covered by a polygon!
|
|
return geom::covered_by(geom, osmStore.retrieve<mmap::multi_polygon_t>(oo.handle));
|
|
}
|
|
);
|
|
return ids;
|
|
}
|
|
|
|
// Returns whether it is closed polygon
|
|
bool OsmLuaProcessing::IsClosed() const {
|
|
if (!isWay) return false; // nonsense: it isn't a way
|
|
if (isRelation) return true; // TODO: check it when non-multipolygon are supported
|
|
return isClosed;
|
|
}
|
|
|
|
void reverse_project(DegPoint& p) {
|
|
geom::set<1>(p, latp2lat(geom::get<1>(p)));
|
|
}
|
|
|
|
// Returns area
|
|
double OsmLuaProcessing::Area() {
|
|
if (!IsClosed()) return 0;
|
|
|
|
#if BOOST_VERSION >= 106700
|
|
geom::strategy::area::spherical<> sph_strategy(RadiusMeter);
|
|
if (isRelation) {
|
|
// Boost won't calculate area of a multipolygon, so we just total up the member polygons
|
|
return multiPolygonArea(multiPolygonCached());
|
|
} else if (isWay) {
|
|
// Reproject back into lat/lon and then run Boo
|
|
geom::model::polygon<DegPoint> p;
|
|
geom::assign(p,polygonCached());
|
|
geom::for_each_point(p, reverse_project);
|
|
return geom::area(p, sph_strategy);
|
|
}
|
|
#else
|
|
if (isRelation) {
|
|
return geom::area(multiPolygonCached());
|
|
} else if (isWay) {
|
|
return geom::area(polygonCached());
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
double OsmLuaProcessing::multiPolygonArea(const MultiPolygon &mp) const {
|
|
geom::strategy::area::spherical<> sph_strategy(RadiusMeter);
|
|
double totalArea = 0;
|
|
for (MultiPolygon::const_iterator it = mp.begin(); it != mp.end(); ++it) {
|
|
geom::model::polygon<DegPoint> p;
|
|
geom::assign(p,*it);
|
|
geom::for_each_point(p, reverse_project);
|
|
totalArea += geom::area(p, sph_strategy);
|
|
}
|
|
return totalArea;
|
|
}
|
|
|
|
// Returns length
|
|
double OsmLuaProcessing::Length() {
|
|
if (isWay) {
|
|
geom::model::linestring<DegPoint> l;
|
|
geom::assign(l, linestringCached());
|
|
geom::for_each_point(l, reverse_project);
|
|
return geom::length(l, geom::strategy::distance::haversine<float>(RadiusMeter));
|
|
}
|
|
// multi_polygon would be calculated as zero
|
|
return 0;
|
|
}
|
|
|
|
// Cached geometries creation
|
|
const Linestring &OsmLuaProcessing::linestringCached() {
|
|
if (!linestringInited) {
|
|
linestringInited = true;
|
|
|
|
if (isRelation) {
|
|
//A relation is being treated as a linestring, which might be
|
|
//caused by bug in the Lua script
|
|
auto const &relation = indexStore->retrieve<RelationStore::relation_entry_t>(relationHandle);
|
|
linestringCache = OSMStore::wayListLinestring(indexStore->wayListMultiPolygon(relation.first.cbegin(), relation.first.cend(), relation.second.cbegin(), relation.second.cend()));
|
|
} else if (isWay) {
|
|
auto const &nodeVecPtr = &indexStore->retrieve<WayStore::nodeid_vector_t>(nodeVecHandle);
|
|
linestringCache = indexStore->nodeListLinestring(nodeVecPtr->cbegin(),nodeVecPtr->cend());
|
|
}
|
|
}
|
|
return linestringCache;
|
|
}
|
|
|
|
const Polygon &OsmLuaProcessing::polygonCached() {
|
|
if (!polygonInited) {
|
|
polygonInited = true;
|
|
auto const &nodeVecPtr = &indexStore->retrieve<WayStore::nodeid_vector_t>(nodeVecHandle);
|
|
polygonCache = indexStore->nodeListPolygon(nodeVecPtr->cbegin(), nodeVecPtr->cend());
|
|
}
|
|
return polygonCache;
|
|
}
|
|
|
|
const MultiPolygon &OsmLuaProcessing::multiPolygonCached() {
|
|
if (!multiPolygonInited) {
|
|
multiPolygonInited = true;
|
|
auto const &relation = indexStore->retrieve<RelationStore::relation_entry_t>(relationHandle);
|
|
multiPolygonCache = indexStore->wayListMultiPolygon(relation.first.cbegin(), relation.first.cend(), relation.second.cbegin(), relation.second.cend());
|
|
}
|
|
return multiPolygonCache;
|
|
}
|
|
|
|
// ---- Requests from Lua to write this way/node to a vector tile's Layer
|
|
|
|
// Add object to specified layer from Lua
|
|
void OsmLuaProcessing::Layer(const string &layerName, bool area) {
|
|
if (layers.layerMap.count(layerName) == 0) {
|
|
throw out_of_range("ERROR: Layer(): a layer named as \"" + layerName + "\" doesn't exist.");
|
|
}
|
|
|
|
OutputGeometryType geomType = isWay ? (area ? OutputGeometryType::POLYGON : OutputGeometryType::LINESTRING) : OutputGeometryType::POINT;
|
|
try {
|
|
if (geomType==OutputGeometryType::POINT) {
|
|
LatpLon pt = indexStore->nodes_at(osmID);
|
|
Point p = Point(pt.lon, pt.latp);
|
|
|
|
if(!CorrectGeometry(p)) return;
|
|
|
|
OutputObjectRef oo(new OutputObjectOsmStorePoint(geomType, false,
|
|
layers.layerMap[layerName],
|
|
osmID, osmStore.store_point(osmStore.osm(), p), attributeStore.empty_set()));
|
|
outputs.push_back(std::make_pair(oo, AttributeStore::key_value_set_entry_t()));
|
|
return;
|
|
}
|
|
else if (geomType==OutputGeometryType::POLYGON) {
|
|
// polygon
|
|
|
|
MultiPolygon mp;
|
|
|
|
if (isRelation) {
|
|
try {
|
|
mp = multiPolygonCached();
|
|
} catch(std::out_of_range &err) {
|
|
cout << "In relation " << originalOsmID << ": " << err.what() << endl;
|
|
return;
|
|
}
|
|
}
|
|
else if (isWay) {
|
|
//Is there a more efficient way to do this?
|
|
Linestring ls = linestringCached();
|
|
Polygon p;
|
|
geom::assign_points(p, ls);
|
|
|
|
mp.push_back(p);
|
|
}
|
|
|
|
if(!CorrectGeometry(mp)) return;
|
|
|
|
OutputObjectRef oo(new OutputObjectOsmStoreMultiPolygon(geomType, false,
|
|
layers.layerMap[layerName],
|
|
osmID, osmStore.store_multi_polygon(osmStore.osm(), mp), attributeStore.empty_set()));
|
|
outputs.push_back(std::make_pair(oo, AttributeStore::key_value_set_entry_t()));
|
|
}
|
|
else if (geomType==OutputGeometryType::LINESTRING) {
|
|
// linestring
|
|
Linestring ls = linestringCached();
|
|
|
|
if(!CorrectGeometry(ls)) return;
|
|
|
|
OutputObjectRef oo(new OutputObjectOsmStoreLinestring(geomType, false,
|
|
layers.layerMap[layerName],
|
|
osmID, osmStore.store_linestring(osmStore.osm(), ls), attributeStore.empty_set()));
|
|
outputs.push_back(std::make_pair(oo, AttributeStore::key_value_set_entry_t()));
|
|
}
|
|
} catch (std::invalid_argument &err) {
|
|
cerr << "Error in OutputObjectOsmStore constructor: " << err.what() << endl;
|
|
}
|
|
}
|
|
|
|
void OsmLuaProcessing::LayerAsCentroid(const string &layerName) {
|
|
if (layers.layerMap.count(layerName) == 0) {
|
|
throw out_of_range("ERROR: LayerAsCentroid(): a layer named as \"" + layerName + "\" doesn't exist.");
|
|
}
|
|
|
|
Point geomp;
|
|
try {
|
|
geomp = calculateCentroid();
|
|
if(geom::is_empty(geomp)) {
|
|
cerr << "Geometry is empty in OsmLuaProcessing::LayerAsCentroid (" << (isRelation ? "relation " : isWay ? "way " : "node ") << originalOsmID << ")" << endl;
|
|
return;
|
|
}
|
|
|
|
} catch(std::out_of_range &err) {
|
|
cout << "Couldn't find " << (isRelation ? "relation " : isWay ? "way " : "node " ) << originalOsmID << ": " << err.what() << endl;
|
|
return;
|
|
} catch (geom::centroid_exception &err) {
|
|
cerr << "Problem geometry " << (isRelation ? "relation " : isWay ? "way " : "node " ) << originalOsmID << ": " << err.what() << endl;
|
|
return;
|
|
} catch (std::invalid_argument &err) {
|
|
cerr << "Error in OutputObjectOsmStore constructor for " << (isRelation ? "relation " : isWay ? "way " : "node " ) << originalOsmID << ": " << err.what() << endl;
|
|
return;
|
|
}
|
|
|
|
OutputObjectRef oo(new OutputObjectOsmStorePoint(OutputGeometryType::POINT,
|
|
false, layers.layerMap[layerName],
|
|
osmID, osmStore.store_point(osmStore.osm(), geomp), attributeStore.empty_set()));
|
|
outputs.push_back(std::make_pair(oo, AttributeStore::key_value_set_entry_t()));
|
|
}
|
|
|
|
Point OsmLuaProcessing::calculateCentroid() {
|
|
Point centroid;
|
|
if (isRelation) {
|
|
Geometry tmp;
|
|
auto const &relation = indexStore->retrieve<RelationStore::relation_entry_t>(relationHandle);
|
|
tmp = indexStore->wayListMultiPolygon(relation.first.cbegin(), relation.first.cend(), relation.second.cbegin(), relation.second.cend());
|
|
geom::centroid(tmp, centroid);
|
|
return Point(centroid.x()*10000000.0, centroid.y()*10000000.0);
|
|
} else if (isWay) {
|
|
Polygon p;
|
|
geom::assign_points(p, linestringCached());
|
|
geom::centroid(p, centroid);
|
|
return Point(centroid.x()*10000000.0, centroid.y()*10000000.0);
|
|
} else {
|
|
LatpLon pt = indexStore->nodes_at(osmID);
|
|
return Point(pt.lon, pt.latp);
|
|
}
|
|
}
|
|
|
|
std::vector<double> OsmLuaProcessing::Centroid() {
|
|
Point c = calculateCentroid();
|
|
return std::vector<double> { latp2lat(c.y()/10000000.0), c.x()/10000000.0 };
|
|
}
|
|
|
|
|
|
// Set attributes in a vector tile's Attributes table
|
|
void OsmLuaProcessing::Attribute(const string &key, const string &val) { AttributeWithMinZoom(key,val,0); }
|
|
void OsmLuaProcessing::AttributeWithMinZoom(const string &key, const string &val, const char minzoom) {
|
|
if (val.size()==0) { return; } // don't set empty strings
|
|
if (outputs.size()==0) { cerr << "Can't add Attribute " << key << " if no Layer set" << endl; return; }
|
|
vector_tile::Tile_Value v;
|
|
v.set_string_value(val);
|
|
outputs.back().second.push_back(attributeStore.store_key_value(key, v, minzoom));
|
|
setVectorLayerMetadata(outputs.back().first->layer, key, 0);
|
|
}
|
|
|
|
void OsmLuaProcessing::AttributeNumeric(const string &key, const float val) { AttributeNumericWithMinZoom(key,val,0); }
|
|
void OsmLuaProcessing::AttributeNumericWithMinZoom(const string &key, const float val, const char minzoom) {
|
|
if (outputs.size()==0) { cerr << "Can't add Attribute " << key << " if no Layer set" << endl; return; }
|
|
vector_tile::Tile_Value v;
|
|
v.set_float_value(val);
|
|
outputs.back().second.push_back(attributeStore.store_key_value(key, v, minzoom));
|
|
setVectorLayerMetadata(outputs.back().first->layer, key, 1);
|
|
}
|
|
|
|
void OsmLuaProcessing::AttributeBoolean(const string &key, const bool val) { AttributeBooleanWithMinZoom(key,val,0); }
|
|
void OsmLuaProcessing::AttributeBooleanWithMinZoom(const string &key, const bool val, const char minzoom) {
|
|
if (outputs.size()==0) { cerr << "Can't add Attribute " << key << " if no Layer set" << endl; return; }
|
|
vector_tile::Tile_Value v;
|
|
v.set_bool_value(val);
|
|
outputs.back().second.push_back(attributeStore.store_key_value(key, v, minzoom));
|
|
setVectorLayerMetadata(outputs.back().first->layer, key, 2);
|
|
}
|
|
|
|
// Set minimum zoom
|
|
void OsmLuaProcessing::MinZoom(const unsigned z) {
|
|
if (outputs.size()==0) { cerr << "Can't set minimum zoom if no Layer set" << endl; return; }
|
|
outputs.back().first->setMinZoom(z);
|
|
}
|
|
|
|
// Record attribute name/type for vector_layers table
|
|
void OsmLuaProcessing::setVectorLayerMetadata(const uint_least8_t layer, const string &key, const uint type) {
|
|
layers.layers[layer].attributeMap[key] = type;
|
|
}
|
|
|
|
// We are now processing a node
|
|
void OsmLuaProcessing::setNode(NodeID id, LatpLon node, const tag_map_t &tags) {
|
|
reset();
|
|
osmID = id;
|
|
originalOsmID = id;
|
|
isWay = false;
|
|
isRelation = false;
|
|
lon = node.lon;
|
|
latp= node.latp;
|
|
currentTags = tags;
|
|
|
|
//Start Lua processing for node
|
|
luaState["node_function"](this);
|
|
if (!this->empty()) {
|
|
|
|
TileCoordinates index = latpLon2index(node, this->config.baseZoom);
|
|
for (auto jt = this->outputs.begin(); jt != this->outputs.end(); ++jt) {
|
|
|
|
// Store the attributes of the generated geometry
|
|
jt->first->setAttributeSet(attributeStore.store_set(jt->second));
|
|
|
|
osmMemTiles.AddObject(index, jt->first);
|
|
}
|
|
}
|
|
}
|
|
|
|
// We are now processing a way
|
|
void OsmLuaProcessing::setWay(WayID wayId, OSMStore::handle_t handle, const tag_map_t &tags) {
|
|
reset();
|
|
osmID = wayId;
|
|
originalOsmID = osmID;
|
|
isWay = true;
|
|
isRelation = false;
|
|
nodeVecHandle = handle;
|
|
relationHandle = OSMStore::handle_t();
|
|
linestringInited = polygonInited = multiPolygonInited = false;
|
|
|
|
try {
|
|
auto const &nodeVecPtr = &indexStore->retrieve<WayStore::nodeid_vector_t>(nodeVecHandle);
|
|
isClosed = nodeVecPtr->front()==nodeVecPtr->back();
|
|
|
|
} catch (std::out_of_range &err) {
|
|
std::stringstream ss;
|
|
ss << "Way " << originalOsmID << " is missing a node";
|
|
throw std::out_of_range(ss.str());
|
|
}
|
|
|
|
currentTags = tags;
|
|
/*currentTags.clear();
|
|
for(auto const &i: tags) {
|
|
currentTags.emplace(std::piecewise_construct,
|
|
std::forward_as_tuple(i.first.begin(), i.first.end()),
|
|
std::forward_as_tuple(i.second.begin(), i.second.end()));
|
|
} */
|
|
|
|
|
|
bool ok = true;
|
|
if (ok) {
|
|
luaState.setErrorHandler(kaguya::ErrorHandler::throwDefaultError);
|
|
|
|
//Start Lua processing for way
|
|
kaguya::LuaFunction way_function = luaState["way_function"];
|
|
kaguya::LuaRef ret = way_function(this);
|
|
assert(!ret);
|
|
}
|
|
|
|
if (!this->empty()) {
|
|
// create a list of tiles this way passes through (tileSet)
|
|
unordered_set<TileCoordinates> tileSet;
|
|
try {
|
|
auto const &nodeVecPtr = &indexStore->retrieve<WayStore::nodeid_vector_t>(nodeVecHandle);
|
|
insertIntermediateTiles(indexStore->nodeListLinestring(nodeVecPtr->cbegin(),nodeVecPtr->cend()), this->config.baseZoom, tileSet);
|
|
|
|
// then, for each tile, store the OutputObject for each layer
|
|
bool polygonExists = false;
|
|
for (auto it = tileSet.begin(); it != tileSet.end(); ++it) {
|
|
TileCoordinates index = *it;
|
|
for (auto jt = this->outputs.begin(); jt != this->outputs.end(); ++jt) {
|
|
|
|
// Store the attributes of the generated geometry
|
|
jt->first->setAttributeSet(attributeStore.store_set(jt->second));
|
|
|
|
if (jt->first->geomType == OutputGeometryType::POLYGON) {
|
|
polygonExists = true;
|
|
continue;
|
|
}
|
|
osmMemTiles.AddObject(index, jt->first);
|
|
}
|
|
}
|
|
|
|
// for polygon, fill inner tiles
|
|
if (polygonExists) {
|
|
fillCoveredTiles(tileSet);
|
|
for (auto it = tileSet.begin(); it != tileSet.end(); ++it) {
|
|
TileCoordinates index = *it;
|
|
for (auto jt = this->outputs.begin(); jt != this->outputs.end(); ++jt) {
|
|
|
|
// Store the attributes of the generated geometry
|
|
jt->first->setAttributeSet(attributeStore.store_set(jt->second));
|
|
|
|
if (jt->first->geomType != OutputGeometryType::POLYGON) continue;
|
|
osmMemTiles.AddObject(index, jt->first);
|
|
}
|
|
}
|
|
}
|
|
} catch(std::out_of_range &err) {
|
|
cerr << "Error calculating intermediate tiles: " << err.what() << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We are now processing a relation
|
|
// (note that we store relations as ways with artificial IDs, and that
|
|
// we use decrementing positive IDs to give a bit more space for way IDs)
|
|
void OsmLuaProcessing::setRelation(int64_t relationId, OSMStore::handle_t relationHandle, const tag_map_t &tags) {
|
|
reset();
|
|
osmID = --newWayID;
|
|
originalOsmID = relationId;
|
|
isWay = true;
|
|
isRelation = true;
|
|
isClosed = true; // TODO: change this when we support route relations
|
|
|
|
this->relationHandle = relationHandle;
|
|
|
|
currentTags = tags;
|
|
/* currentTags.clear();
|
|
for(auto const &i: tags) {
|
|
currentTags.emplace(std::piecewise_construct,
|
|
std::forward_as_tuple(i.first.begin(), i.first.end()),
|
|
std::forward_as_tuple(i.second.begin(), i.second.end()));
|
|
} */
|
|
|
|
bool ok = true;
|
|
if (ok) {
|
|
//Start Lua processing for relation
|
|
luaState["way_function"](this);
|
|
}
|
|
|
|
if (!this->empty()) {
|
|
MultiPolygon mp;
|
|
try {
|
|
// for each tile the relation may cover, put the output objects.
|
|
auto const &relation = indexStore->retrieve<RelationStore::relation_entry_t>(relationHandle);
|
|
mp = indexStore->wayListMultiPolygon(relation.first.cbegin(), relation.first.cend(), relation.second.cbegin(), relation.second.cend());
|
|
} catch(std::out_of_range &err) {
|
|
cout << "In relation " << originalOsmID << ": " << err.what() << endl;
|
|
return;
|
|
}
|
|
|
|
unordered_set<TileCoordinates> tileSet;
|
|
if (mp.size() == 1) {
|
|
insertIntermediateTiles(mp[0].outer(), this->config.baseZoom, tileSet);
|
|
fillCoveredTiles(tileSet);
|
|
} else {
|
|
for (Polygon poly: mp) {
|
|
unordered_set<TileCoordinates> tileSetTmp;
|
|
insertIntermediateTiles(poly.outer(), this->config.baseZoom, tileSetTmp);
|
|
fillCoveredTiles(tileSetTmp);
|
|
tileSet.insert(tileSetTmp.begin(), tileSetTmp.end());
|
|
}
|
|
}
|
|
|
|
for (auto it = tileSet.begin(); it != tileSet.end(); ++it) {
|
|
TileCoordinates index = *it;
|
|
for (auto jt = this->outputs.begin(); jt != this->outputs.end(); ++jt) {
|
|
// Store the attributes of the generated geometry
|
|
jt->first->setAttributeSet(attributeStore.store_set(jt->second));
|
|
|
|
osmMemTiles.AddObject(index, jt->first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vector<string> OsmLuaProcessing::GetSignificantNodeKeys() {
|
|
return luaState["node_keys"];
|
|
}
|
|
|