Skip to content

Commit

Permalink
rapidjson io (#11)
Browse files Browse the repository at this point in the history
* test json

* not ready

* rapidjson

* rapidjson io

* not ready

* update

* rapidjson io

* has from to rapidjson

* more bindings

* lint

* why offset not inited?

* fix

* add rapidjson

* update

* add geometry

* good

* indexer

* add indexer

* not ready

* loading geojson

* fix

* fix

* fix

---------

Co-authored-by: TANG ZHIXIONG <[email protected]>
  • Loading branch information
district10 and zhixiong-tang authored Sep 9, 2023
1 parent 0f27c44 commit f42ff04
Show file tree
Hide file tree
Showing 11 changed files with 808 additions and 77 deletions.
57 changes: 57 additions & 0 deletions nano_fmm/converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import os
from typing import Union

from loguru import logger

from nano_fmm import Indexer, rapidjson


def remap_network_with_string_id(
network: Union[str, rapidjson],
*,
export: str = None,
):
"""
network.json normally has:
{
"type": "Feature",
"geometry": {...},
"properties": {
"id": 244,
"nexts": [326, 452],
"prevs": [5241, 563],
...
},
}
if you have:
{
"id": "road1",
"nexts": ["road2", "road3"],
"prevs": ["road4", "road5"],
}
"""
if isinstance(network, str):
path = network
network = rapidjson()
network.load(path)
indexer = Indexer()
features = network["features"]
for i in range(len(features)):
f = features[i]
props = f["properties"]
if "id" not in props or "nexts" not in props and "prevs" not in props:
continue
props["id"] = indexer.id(props["id"]())
props["nexts"] = [indexer.id(n) for n in props["nexts"]()]
props["prevs"] = [indexer.id(n) for n in props["prevs"]()]
if "folds" in props:
props["folds"][0] = [indexer.id(i) for i in props["folds"][0]()]
props["type"] = "road"
if export:
network["index"] = indexer.to_rapidjson()
export = os.path.abspath(export)
os.makedirs(os.path.dirname(export), exist_ok=True)
network.dump(export, indent=True)
logger.info(f"wrote to {export}")
return network, indexer
87 changes: 66 additions & 21 deletions src/bindings/pybind11_network.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <pybind11/stl_bind.h>

#include "nano_fmm/network.hpp"
#include "nano_fmm/indexer.hpp"
#include "spdlog/spdlog.h"

namespace nano_fmm
Expand All @@ -30,19 +31,38 @@ void bind_network(py::module &m)
//
.def_property_readonly(
"position",
[](const ProjectedPoint &self) { return self.position_; })
[](const ProjectedPoint &self) -> const Eigen::Vector3d {
return self.position();
})
.def_property_readonly(
"direction",
[](const ProjectedPoint &self) { return self.direction_; })
[](const ProjectedPoint &self) -> const Eigen::Vector3d {
return self.direction();
})
.def_property_readonly(
"distance",
[](const ProjectedPoint &self) { return self.distance_; })
[](const ProjectedPoint &self) { return self.distance(); })
.def_property_readonly(
"road_id", [](const ProjectedPoint &self) { return self.road_id_; })
"road_id",
[](const ProjectedPoint &self) { return self.road_id(); })
.def_property_readonly(
"offset", [](const ProjectedPoint &self) { return self.offset_; })
"offset", [](const ProjectedPoint &self) { return self.offset(); })
//
;
.def("from_rapidjson", &ProjectedPoint::from_rapidjson, "json"_a)
.def("to_rapidjson",
py::overload_cast<>(&ProjectedPoint::to_rapidjson, py::const_))
//
.def("__repr__", [](const ProjectedPoint &self) {
auto &p = self.position();
auto &d = self.direction();
return fmt::format("ProjectedPoint(pos=[{},{},{}],dir=[{},{},{}],"
"dist={},road={},offset={})",
p[0], p[1], p[2], //
d[0], d[1], d[2], //
self.distance(), self.road_id(), self.offset());
});
//
;

py::class_<UbodtRecord>(m, "UbodtRecord", py::module_local()) //
.def(py::init<int64_t, int64_t, int64_t, int64_t, double>(),
Expand All @@ -55,41 +75,43 @@ void bind_network(py::module &m)
//
.def_property_readonly(
"source_road",
[](const UbodtRecord &self) { return self.source_road_; })
[](const UbodtRecord &self) { return self.source_road(); })
.def_property_readonly(
"target_road",
[](const UbodtRecord &self) { return self.target_road_; })
[](const UbodtRecord &self) { return self.target_road(); })
.def_property_readonly(
"source_next",
[](const UbodtRecord &self) { return self.source_next_; })
[](const UbodtRecord &self) { return self.source_next(); })
.def_property_readonly(
"target_prev",
[](const UbodtRecord &self) { return self.target_prev_; })
.def_property_readonly(
"cost", [](const UbodtRecord &self) { return self.cost_; })
[](const UbodtRecord &self) { return self.target_prev(); })
.def_property_readonly(
"next", [](const UbodtRecord &self) { return self.next_; },
rvp::reference_internal)
"cost", [](const UbodtRecord &self) { return self.cost(); })
//
.def(py::self == py::self)
.def(py::self < py::self)
//
.def("from_rapidjson", &UbodtRecord::from_rapidjson, "json"_a)
.def("to_rapidjson",
py::overload_cast<>(&UbodtRecord::to_rapidjson, py::const_))
//
.def("__repr__", [](const UbodtRecord &self) {
return fmt::format(
"UbodtRecord(s->t=[{}->{}], cost:{}, sn:{},tp:{})",
self.source_road_, self.target_road_, //
self.cost_, //
self.source_next_, self.target_prev_);
self.source_road(), self.target_road(), //
self.cost(), //
self.source_next(), self.target_prev());
});
//
;

py::class_<Network>(m, "Network", py::module_local()) //
//
.def(py::init<bool>(), py::kw_only(), "is_wgs84"_a = false)
.def(py::init<bool>(), py::kw_only(), "is_wgs84"_a)
//
.def("add_road", &Network::add_road, "geom"_a, py::kw_only(), "id"_a)
.def("add_link", &Network::add_link, "source_road"_a, "target_road"_a)
.def("add_link", &Network::add_link, "source_road"_a, "target_road"_a,
py::kw_only(), "check_road"_a = false)
.def("remove_road", &Network::remove_road, "id"_a)
.def("remove_link", &Network::remove_link, //
"source_road"_a, "target_road"_a)
Expand Down Expand Up @@ -120,8 +142,8 @@ void bind_network(py::module &m)
py::call_guard<py::gil_scoped_release>())
//
.def_static("load", &Network::load, "path"_a)
.def("dump", &Network::dump, "path"_a, py::kw_only(),
"with_config"_a = true)
.def("dump", &Network::dump, "path"_a, py::kw_only(), "indent"_a = true,
"as_geojson"_a = true)
//
.def("build_ubodt",
py::overload_cast<std::optional<double>>(&Network::build_ubodt,
Expand All @@ -145,6 +167,29 @@ void bind_network(py::module &m)
//
.def("to_2d", &Network::to_2d)
//
.def("from_geojson", &Network::from_geojson, "json"_a)
.def("to_geojson",
py::overload_cast<>(&Network::to_geojson, py::const_))
.def("from_rapidjson", &Network::from_rapidjson, "json"_a)
.def("to_rapidjson",
py::overload_cast<>(&Network::to_rapidjson, py::const_))
//
;

py::class_<Indexer>(m, "Indexer", py::module_local()) //
.def(py::init<>())
.def("id", py::overload_cast<int64_t>(&Indexer::id), "id"_a)
.def("id", py::overload_cast<const std::string &>(&Indexer::id), "id"_a)
.def("index",
py::overload_cast<const std::string &, int64_t>(&Indexer::index),
"str_id"_a, "int_id"_a)
.def("index", py::overload_cast<>(&Indexer::index, py::const_))
//
.def("from_rapidjson", &Indexer::from_rapidjson, "json"_a)
.def("to_rapidjson",
py::overload_cast<>(&Indexer::to_rapidjson, py::const_))
//
//
;
}
} // namespace nano_fmm
15 changes: 14 additions & 1 deletion src/nano_fmm/config.hpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
#pragma once

#include "nano_fmm/types.hpp"

namespace nano_fmm
{
struct Config
{
double ubodt_thresh = 3000.0;

SETUP_FLUENT_API(Config, double, ubodt_thresh)
Config &from_rapidjson(const RapidjsonValue &json);
RapidjsonValue to_rapidjson(RapidjsonAllocator &allocator) const;
RapidjsonValue to_rapidjson() const
{
RapidjsonAllocator allocator;
return to_rapidjson(allocator);
}

private:
double ubodt_thresh_ = 3000.0;
};
} // namespace nano_fmm
100 changes: 100 additions & 0 deletions src/nano_fmm/indexer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#pragma once

#include <unordered_map>
#include <string>
#include <iostream>
#include "nano_fmm/types.hpp"

namespace nano_fmm
{
struct Indexer
{
// get str id (with auto setup)
std::string id(int64_t id)
{
auto itr = int2str_.find(id);
if (itr != int2str_.end()) {
return itr->second;
}
int round = 0;
auto id_str = std::to_string(id);
auto str_id = id_str;
while (str2int_.count(str_id)) {
++round;
str_id = id_str + "/" + std::to_string(round);
}
index(str_id, id);
return str_id;
}
// get int id (with auto setup)
int64_t id(const std::string &id)
{
auto itr = str2int_.find(id);
if (itr != str2int_.end()) {
return itr->second;
}
try {
// '44324' -> 44324
// 'w44324' -> 44324
int64_t ii =
id[0] == 'w' ? std::stoll(id.substr(1)) : std::stoll(id);
if (index(id, ii)) {
return ii;
}
} catch (...) {
}
while (!index(id, id_cursor_)) {
++id_cursor_;
}
return id_cursor_++;
}
// setup str/int id, returns true (setup) or false (skip)
bool index(const std::string &str_id, int64_t int_id)
{
if (str2int_.count(str_id) || int2str_.count(int_id)) {
return false;
}
str2int_.emplace(str_id, int_id);
int2str_.emplace(int_id, str_id);
return true;
}
std::map<std::string, int64_t> index() const
{
return {str2int_.begin(), str2int_.end()};
}

Indexer &from_rapidjson(const RapidjsonValue &json)
{
// for (auto &m: json.GetMe)
for (auto &m : json.GetObject()) {
index(std::string(m.name.GetString(), m.name.GetStringLength()),
m.value.GetInt64());
}
return *this;
}
RapidjsonValue to_rapidjson(RapidjsonAllocator &allocator) const
{
RapidjsonValue json(rapidjson::kObjectType);
for (auto &pair : str2int_) {
auto &str = pair.first;
json.AddMember(RapidjsonValue(str.data(), str.size(), allocator),
RapidjsonValue(pair.second), allocator);
}
std::sort(
json.MemberBegin(), json.MemberEnd(), [](auto &lhs, auto &rhs) {
return strcmp(lhs.name.GetString(), rhs.name.GetString()) < 0;
});
return json;
}
RapidjsonValue to_rapidjson() const
{
RapidjsonAllocator allocator;
return to_rapidjson(allocator);
}

private:
std::unordered_map<std::string, int64_t> str2int_;
std::unordered_map<int64_t, std::string> int2str_;
int64_t id_cursor_{1000000};
};
} // namespace nano_fmm
Loading

0 comments on commit f42ff04

Please sign in to comment.