From 4ec27df5a1aebe5000b935709ce33815635fa9b1 Mon Sep 17 00:00:00 2001 From: James Shiffer Date: Mon, 30 Oct 2023 00:53:00 -0700 Subject: [PATCH] Attempted to implement map static data --- .gitignore | 2 + Makefile | 24 +++++++ README.md | 9 +++ main.cpp | 11 ---- map/MapProvider.h | 8 +-- map/Road.h | 21 +++++++ map/RoadDirection.h | 3 +- map/RoadSensor.h | 20 ++++++ map/SigalertMapProvider.cpp | 121 ++++++++++++++++++++++++++++++++++++ map/SigalertMapProvider.h | 32 +++++++++- map/SigalertRoad.cpp | 29 +++++++++ map/SigalertRoad.h | 37 +++++++---- map/SigalertRoadSection.cpp | 26 ++++++++ map/SigalertRoadSection.h | 12 ++-- map/SigalertRoadSensor.cpp | 21 +++++++ map/SigalertRoadSensor.h | 18 +++++- map/SigalertTypes.cpp | 22 +++++++ map/SigalertTypes.h | 33 ++++++++++ tui/main.cpp | 55 ++++++++++++++++ 19 files changed, 464 insertions(+), 40 deletions(-) delete mode 100644 main.cpp create mode 100644 map/Road.h create mode 100644 map/RoadSensor.h create mode 100644 map/SigalertTypes.cpp create mode 100644 map/SigalertTypes.h create mode 100644 tui/main.cpp diff --git a/.gitignore b/.gitignore index e257658..b0870b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ # ---> C++ +FemMaps + # Prerequisites *.d diff --git a/Makefile b/Makefile index e69de29..a413c1c 100644 --- a/Makefile +++ b/Makefile @@ -0,0 +1,24 @@ +CC = g++ +CFLAGS = -g -Wall -Wextra +LIBS = `pkg-config --cflags --libs caca libcurl` +OUT = obj +SRC = map +OBJS = $(OUT)/SigalertMapProvider.o $(OUT)/SigalertRoad.o $(OUT)/SigalertRoadSection.o $(OUT)/SigalertRoadSensor.o $(OUT)/SigalertTypes.o +PROG = FemMaps +EXECUTABLES = tui/$(PROG) + +.PHONY: all clean tui + +all: $(EXECUTABLES) + +$(OUT)/%.o: $(SRC)/%.cpp + mkdir -p $(OUT) + $(CC) $(CFLAGS) -c $< -o $@ + +tui/$(PROG): $(OBJS) tui/*.cpp + $(CC) $(LIBS) $^ -o $@ + +tui: tui/$(PROG) + +clean: + rm -f $(EXECUTABLES) $(OBJS) diff --git a/README.md b/README.md index 16ac070..3a85ad9 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,15 @@ At FemboyFinancial we love maps! The code in this repo will be used to power a l showing traffic conditions on various Bay Area highways. Until the physical wall display is made, though, a TUI frontend can be used. +## Dependencies + +* IntervalTree (included) +* JSON for Modern C++ (included) + +TUI only: +* libcaca + + ## Building ```sh diff --git a/main.cpp b/main.cpp deleted file mode 100644 index 012a8c8..0000000 --- a/main.cpp +++ /dev/null @@ -1,11 +0,0 @@ -// -// Created by scoliono on 10/14/23. -// - -#include - -int main(int argc, char** argv) -{ - std::cout << "FemMaps" << std::endl; - return 0; -} diff --git a/map/MapProvider.h b/map/MapProvider.h index dec5eab..fea3763 100644 --- a/map/MapProvider.h +++ b/map/MapProvider.h @@ -5,14 +5,14 @@ #ifndef FEMMAPS_MAPPROVIDER_H #define FEMMAPS_MAPPROVIDER_H +#include "Road.h" +#include class MapProvider { public: - virtual void refresh() = 0; - virtual void roadSections() = 0; - virtual void roads() = 0; - + virtual void refreshData() = 0; + virtual const std::map& roads() const = 0; }; diff --git a/map/Road.h b/map/Road.h new file mode 100644 index 0000000..4277d2e --- /dev/null +++ b/map/Road.h @@ -0,0 +1,21 @@ +// +// Created by scoliono on 10/15/23. +// + +#ifndef FEMMAPS_ROAD_H +#define FEMMAPS_ROAD_H + +#include "../include/IntervalTree.h" +#include "RoadSensor.h" + +class Road { + +public: + virtual const char *name() const = 0; + virtual int id() const = 0; + virtual const IntervalTree& speedLimits() const = 0; + virtual const std::vector& sensors() const = 0; + +}; + +#endif //FEMMAPS_ROAD_H diff --git a/map/RoadDirection.h b/map/RoadDirection.h index 4d0212a..986de5e 100644 --- a/map/RoadDirection.h +++ b/map/RoadDirection.h @@ -9,7 +9,8 @@ enum RoadDirection { NORTH, EAST, SOUTH, - WEST + WEST, + UNKNOWN }; #endif //FEMMAPS_ROADDIRECTION_H diff --git a/map/RoadSensor.h b/map/RoadSensor.h new file mode 100644 index 0000000..874b764 --- /dev/null +++ b/map/RoadSensor.h @@ -0,0 +1,20 @@ +// +// Created by scoliono on 10/15/23. +// + +#ifndef FEMMAPS_ROADSENSOR_H +#define FEMMAPS_ROADSENSOR_H + +class Road; + +class RoadSensor { + +public: + virtual const Road* road() = 0; + virtual const int* pos() = 0; + virtual int speed() = 0; + virtual int speedLimit() = 0; + +}; + +#endif //FEMMAPS_ROADSENSOR_H diff --git a/map/SigalertMapProvider.cpp b/map/SigalertMapProvider.cpp index 2f06407..394fa50 100644 --- a/map/SigalertMapProvider.cpp +++ b/map/SigalertMapProvider.cpp @@ -3,3 +3,124 @@ // #include "SigalertMapProvider.h" +#include "SigalertRoadSensor.h" +#include +#include +#include + +using nlohmann::json; + +SigalertMapProvider::SigalertMapProvider() +{ + m_curl = curl_easy_init(); +} + +SigalertMapProvider::~SigalertMapProvider() +{ + curl_easy_cleanup(m_curl); + if (m_static_data.memory) + free(m_static_data.memory); + if (m_current_data.memory) + free(m_current_data.memory); + + for (auto section : m_allsections) + delete section; + + for (auto sensor : m_allsensors) + delete sensor; +} + +size_t SigalertMapProvider::curl_write_callback(void* contents, size_t size, size_t nmemb, void* userp) +{ + size_t realsize = size * nmemb; + struct MemoryStruct* mem = static_cast(userp); + + char* ptr = static_cast(realloc(mem->memory, mem->size + realsize + 1)); + if (!ptr) + { + /* out of memory! */ + std::cerr << "not enough memory (realloc returned NULL)" << std::endl; + return 0; + } + + mem->memory = ptr; + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; +} + +void SigalertMapProvider::refreshData() +{ + curl_easy_setopt(m_curl, CURLOPT_URL, SigalertMapProvider::STATIC_DATA_URL); + curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, &SigalertMapProvider::curl_write_callback); + curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, (void*)&m_static_data); + curl_easy_setopt(m_curl, CURLOPT_USERAGENT, "libcurl-agent/1.0"); + CURLcode res = curl_easy_perform(m_curl); + + if (res != CURLE_OK) + { + std::cerr << "request for static map data failed: " << curl_easy_strerror(res) << std::endl; + return; + } + + try + { + m_static_json = json::parse(m_static_data.memory); + } + catch (json::parse_error& e) + { + std::cerr << "JSON error while parsing static map data: " << e.what() << std::endl; + return; + } + populateRoadSensors(); + populateRoads(); + populateRoadSections(); +} + +/** + * Expects road sensors to be populated first. + */ +void SigalertMapProvider::populateRoads() +{ + auto roads = m_static_json.at("roads"); + for (auto& el : roads.items()) + { + SigalertRoadJson road_json = el.value(); + int i = atoi(el.key().c_str()); + SigalertRoad* road = new SigalertRoad(i, road_json); + m_roads.insert(std::pair(i, static_cast(road))); + road->populateSensors(m_allsensors); + } +} + +/** + * Expects roads to be populated first. + */ +void SigalertMapProvider::populateRoadSections() +{ + auto sections = m_static_json.at("roadSections"); + for (auto& el : sections.items()) + { + SigalertRoadSectionJson section_json = el.value(); + SigalertRoad* road = dynamic_cast(m_roads[section_json.road_id]); + SigalertRoadSection* section = new SigalertRoadSection(section_json, road); + m_allsections.push_back(section); + road->addSection(section); + } +} + +void SigalertMapProvider::populateRoadSensors() +{ + auto names = m_static_json.at("sensorNames"); + auto pos = m_static_json.at("sensorPositions"); + //auto speeds = m_current_json.at("speeds"); + size_t idx = 0; + for (auto& el : names.items()) + { + SigalertRoadSensor* sensor = new SigalertRoadSensor(idx, el.value(), pos[idx], -1); + m_allsensors.push_back(sensor); + idx++; + } +} diff --git a/map/SigalertMapProvider.h b/map/SigalertMapProvider.h index 0872eed..3f0f730 100644 --- a/map/SigalertMapProvider.h +++ b/map/SigalertMapProvider.h @@ -7,19 +7,45 @@ #include "MapProvider.h" #include "SigalertRoad.h" +#include "SigalertRoadSection.h" #include "../include/json.hpp" +#include +#include + class SigalertMapProvider : public MapProvider { public: SigalertMapProvider(); + ~SigalertMapProvider(); void refreshData(); - void roadSections(); - void roads(); + inline const std::vector roadSections() const { return m_allsections; } + inline const std::vector roadSensors() const { return m_allsensors; } + inline const std::map& roads() const { return m_roads; } private: + static constexpr const char* STATIC_DATA_URL = "https://cdn-static.sigalert.com/240/Zip/RegionInfo/NoCalStatic.json"; + static constexpr const char* CURRENT_DATA_URL = "https://www.sigalert.com/Data/NoCal/0~j/NoCalData.json"; + void populateRoads(); - std::map m_roads; + void populateRoadSections(); + void populateRoadSensors(); + + std::map m_roads; + std::vector m_allsections; + std::vector m_allsensors; + CURL* m_curl; + + struct MemoryStruct { + char* memory; + size_t size; + }; + size_t curl_write_callback(void* contents, size_t size, size_t nmemb, void* userp); + + MemoryStruct m_static_data; + nlohmann::json m_static_json; + MemoryStruct m_current_data; + nlohmann::json m_current_json; }; diff --git a/map/SigalertRoad.cpp b/map/SigalertRoad.cpp index b46d2a2..d361412 100644 --- a/map/SigalertRoad.cpp +++ b/map/SigalertRoad.cpp @@ -3,3 +3,32 @@ // #include "SigalertRoad.h" +#include "SigalertRoadSensor.h" +#include "../include/IntervalTree.h" +#include + + +SigalertRoad::SigalertRoad(int id, const SigalertRoadJson& json) + : m_id(id), m_name(json.name), m_section_idx_range{json.section_start_idx, json.section_end_idx} +{ + std::vector> intervals; + for (auto sensor : json.sensors) + { + intervals.push_back(Interval(sensor[0], sensor[1], sensor[2])); + } + m_sensor_speedlimits = IntervalTree(std::move(intervals)); +} + +void SigalertRoad::addSection(SigalertRoadSection* section) +{ + m_sections.push_back(section); +} + +void SigalertRoad::populateSensors(const std::vector& sensors) +{ + for (int i = m_section_idx_range[0]; i <= m_section_idx_range[1]; ++i) + { + m_sensors.push_back(sensors[i]); + sensors[i]->setRoad(this); + } +} diff --git a/map/SigalertRoad.h b/map/SigalertRoad.h index 36b962f..92adf9b 100644 --- a/map/SigalertRoad.h +++ b/map/SigalertRoad.h @@ -5,28 +5,39 @@ #ifndef FEMMAPS_SIGALERTROAD_H #define FEMMAPS_SIGALERTROAD_H -#include "SigalertRoadSection.h" -#include "SigalertRoadSensor.h" +#include "Road.h" +#include "RoadSensor.h" +#include "SigalertTypes.h" #include "../include/IntervalTree.h" +#include "../include/json.hpp" +#include +#include -class SigalertRoad { + +class SigalertRoadSection; +class SigalertRoadSensor; + +class SigalertRoad : public Road { public: - SigalertRoad(); - const std::vector& sections(); - const std::vector& sensors(); - const IntervalTree& speedLimits(); - int id(); + SigalertRoad(int id, const SigalertRoadJson& json); + inline int id() const { return m_id; } + inline const char* name() const { return m_name.c_str(); } + inline const std::vector& sections() const { return m_sections; } + inline const std::vector& sensors() const { return m_sensors; } + inline const IntervalTree& speedLimits() const { return m_sensor_speedlimits; } + + void addSection(SigalertRoadSection* section); + void populateSensors(const std::vector& sensors); private: - void populateRoadSections(); - void populateRoadSensors(); - int m_id; std::string m_name; - std::vector m_sections; - std::vector m_sensors; + std::vector m_sections; + int m_section_idx_range[2]; + std::vector m_sensors; IntervalTree m_sensor_speedlimits; + }; diff --git a/map/SigalertRoadSection.cpp b/map/SigalertRoadSection.cpp index f367428..58bdbf7 100644 --- a/map/SigalertRoadSection.cpp +++ b/map/SigalertRoadSection.cpp @@ -3,3 +3,29 @@ // #include "SigalertRoadSection.h" +#include "SigalertRoad.h" + +SigalertRoadSection::SigalertRoadSection(const SigalertRoadSectionJson& json, const SigalertRoad* road) + : m_road(road), m_sensor_idx_range{json.sensor_start_idx, json.sensor_end_idx} +{ + if (json.dir == "North") + { + m_dir = NORTH; + } + else if (json.dir == "East") + { + m_dir = EAST; + } + else if (json.dir == "South") + { + m_dir = SOUTH; + } + else if (json.dir == "West") + { + m_dir = WEST; + } + else + { + m_dir = UNKNOWN; + } +} diff --git a/map/SigalertRoadSection.h b/map/SigalertRoadSection.h index 03bef9e..0f31f6f 100644 --- a/map/SigalertRoadSection.h +++ b/map/SigalertRoadSection.h @@ -6,17 +6,19 @@ #define FEMMAPS_SIGALERTROADSECTION_H #include "RoadDirection.h" +#include "SigalertRoad.h" +#include "SigalertTypes.h" + class SigalertRoadSection { public: - SigalertRoadSection(); - int speedLimit(int sensorIdx); - RoadDirection direction(); - const SigalertRoadSection& road(); + SigalertRoadSection(const SigalertRoadSectionJson& json, const SigalertRoad* road); + inline RoadDirection direction() { return m_dir; } + inline const SigalertRoad* road() { return m_road; } private: - SigalertRoad* m_road; + const SigalertRoad* m_road; RoadDirection m_dir; int m_sensor_idx_range[2]; }; diff --git a/map/SigalertRoadSensor.cpp b/map/SigalertRoadSensor.cpp index d9873ef..38ffbb2 100644 --- a/map/SigalertRoadSensor.cpp +++ b/map/SigalertRoadSensor.cpp @@ -3,3 +3,24 @@ // #include "SigalertRoadSensor.h" + + +SigalertRoadSensor::SigalertRoadSensor(const int id, const std::string& name, const std::vector& pos, int speed) + : m_id(id), m_name(name), m_pos{pos[0], pos[1]}, m_speed(speed) +{ +} + +SigalertRoadSensor::~SigalertRoadSensor() +{ +} + +int SigalertRoadSensor::speedLimit() +{ + const IntervalTree it = m_road->speedLimits(); + auto iv = it.findContained(m_id, m_id); + if (!iv.size()) + { + return -1; + } + return iv[0].value; +} diff --git a/map/SigalertRoadSensor.h b/map/SigalertRoadSensor.h index b53877d..f4db8e1 100644 --- a/map/SigalertRoadSensor.h +++ b/map/SigalertRoadSensor.h @@ -5,17 +5,29 @@ #ifndef FEMMAPS_SIGALERTROADSENSOR_H #define FEMMAPS_SIGALERTROADSENSOR_H +#include "Road.h" +#include "RoadSensor.h" +#include "SigalertRoad.h" #include -class SigalertRoadSensor { +class SigalertRoadSensor : public RoadSensor { public: - SigalertRoadSensor(const std::string& name, const int pos[]); + SigalertRoadSensor(const int id, const std::string& name, const std::vector& pos, int speed); + virtual ~SigalertRoadSensor(); + inline const Road* road() { return static_cast(m_road); } + inline void setRoad(const SigalertRoad* road) { m_road = road; } + inline const char* name() { return m_name.c_str(); } + inline const int* pos() { return m_pos; } + inline int speed() { return m_speed; } + int speedLimit(); private: + int m_id; + const SigalertRoad* m_road; std::string m_name; - int m_pos[2]; + const int m_pos[2]; int m_speed; }; diff --git a/map/SigalertTypes.cpp b/map/SigalertTypes.cpp new file mode 100644 index 0000000..e8b677d --- /dev/null +++ b/map/SigalertTypes.cpp @@ -0,0 +1,22 @@ +// +// Created by scoliono on 10/30/23. +// + +#include "SigalertTypes.h" + +void from_json(const json& j, SigalertRoadJson& obj) +{ + j.at(0).get_to(obj.name); + j.at(1).get_to(obj.section_start_idx); + j.at(2).get_to(obj.section_end_idx); + j.at(3).get_to(obj.sensors); +} + +void from_json(const json& j, SigalertRoadSectionJson& obj) +{ + j.at(0).get_to(obj.road_id); + j.at(1).get_to(obj.dir); + j.at(2).get_to(obj.name); + j.at(3).get_to(obj.sensor_start_idx); + j.at(4).get_to(obj.sensor_end_idx); +} diff --git a/map/SigalertTypes.h b/map/SigalertTypes.h new file mode 100644 index 0000000..f39cf16 --- /dev/null +++ b/map/SigalertTypes.h @@ -0,0 +1,33 @@ +// +// Created by scoliono on 10/29/23. +// + +#ifndef FEMMAPS_SIGALERTTYPES_H +#define FEMMAPS_SIGALERTTYPES_H + +#include "../include/json.hpp" + +using nlohmann::json; + + +struct SigalertRoadJson { + std::string name; + int section_start_idx; + int section_end_idx; + std::vector> sensors; +}; + +struct SigalertRoadSectionJson { + int road_id; + std::string dir; + std::string name; + int sensor_start_idx; + int sensor_end_idx; +}; + + +void from_json(const json& j, SigalertRoadJson& obj); +void from_json(const json& j, SigalertRoadSectionJson& obj); + + +#endif //FEMMAPS_SIGALERTTYPES_H diff --git a/tui/main.cpp b/tui/main.cpp new file mode 100644 index 0000000..0c72e9a --- /dev/null +++ b/tui/main.cpp @@ -0,0 +1,55 @@ +// +// Created by scoliono on 10/14/23. +// + +#include +#include +#include "../map/SigalertMapProvider.h" +#include "../map/SigalertRoadSensor.h" + +void caca_display_drivers() +{ + const char* const* drivers = caca_get_display_driver_list(); + for (int i = 0; drivers[i] != NULL; i += 2) + { + std::cout << drivers[i] << '\t' << drivers[i + 1] << std::endl; + } +} + +int main(int argc, char** argv) +{ + caca_canvas_t *cv; + caca_display_t *dp; + caca_event_t ev; + dp = caca_create_display_with_driver(NULL, "ncurses"); + if (!dp) { + std::cerr << "Failed to create caca display" << std::endl; + return 1; + } + cv = caca_get_canvas(dp); + caca_set_display_title(dp, "Hello!"); + caca_set_color_ansi(cv, CACA_BLACK, CACA_WHITE); + caca_put_str(cv, 0, 0, "This is a message"); + + SigalertMapProvider smp; + smp.refreshData(); + int i = 0; + while (i < smp.roadSensors().size()) + { + auto sensor = smp.roadSensors()[i]; + if (i < smp.roadSensors().size() - 1) + { + auto sensor2 = smp.roadSensors()[i + 1]; + caca_draw_thin_line(cv, sensor->pos()[0], sensor->pos()[1], sensor2->pos()[0], sensor2->pos()[1]); + } + else + { + caca_draw_thin_line(cv, sensor->pos()[0], sensor->pos()[1], sensor->pos()[0], sensor->pos()[1]); + } + i += 2; + caca_refresh_display(dp); + } + caca_get_event(dp, CACA_EVENT_KEY_PRESS, &ev, -1); + caca_free_display(dp); + return 0; +}