Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finish SimpleSets, add chorematic map, and refactoring #48

Open
wants to merge 107 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
2b227b7
Attempt at generalizing the RegionArrangement definition to allow for…
Oct 25, 2022
b399351
Fix compilation of RegionArrangement
Willem3141 Oct 25, 2022
6ffdbd3
added triangle to core
Oct 25, 2022
ed3ae37
some fixes to regionarrangement
Oct 25, 2022
fe2836d
starting a simple VW implementation on arrangements
Oct 25, 2022
dd8010d
Merge branch 'SimplificationBranchTooManyTemplates' of https://github…
Oct 25, 2022
5773f70
fixed some compilation bugs, first VW algorithm implemented w/o topol…
Oct 25, 2022
43e4e57
WIP: VW no templates
Oct 30, 2022
5ffab58
WIP: templated VW implementation
Oct 30, 2022
ecfd29f
WIP: VW simplification
Nov 1, 2022
112d748
WIP: VW simplification
Nov 3, 2022
5fed0d4
WIP: VW simplification
Nov 4, 2022
90a2eeb
WIP: VW Simplification
Nov 4, 2022
06257f9
Merge branch 'HEAD' of https://github.com/tue-alga/cartocrow.git into…
Feb 20, 2023
58b9bc1
added explanatory comments in the simplification package, and separat…
Feb 20, 2023
cdbd20a
more comments, code refactoring to make naming more sensible and conf…
Feb 24, 2023
2d9a82b
finalized comments
Feb 24, 2023
55803e8
Fix compilation with the GNU linker on Linux
Willem3141 May 3, 2023
c73999f
changes to how arrangements work
Aug 11, 2023
9e77ebf
Adding the edge-collapse algorithm by Kronenfeld et al.
Aug 11, 2023
1860525
Merge branch 'SimplificationBranch' of https://github.com/tue-alga/ca…
Aug 14, 2023
711e880
fixed bug in KSBB in updating blocking counters
Aug 15, 2023
c2b17f1
Fix the renderer module not installing its headers
Willem3141 Aug 17, 2023
d9c7376
simplification code: assertions, commenting, removed debugging code, …
Aug 26, 2024
92a943c
working on revising the simplification setup
Aug 29, 2024
35de208
Merge branch 'master' into SimplificationBranch
Aug 29, 2024
da76329
fixed last few issues with the merge and made the VW and KSBB demos c…
Aug 29, 2024
0357fe4
fixed local install, again compatible with master (isoline/fitToBox)
Aug 30, 2024
511633a
Fix multiple pages in IpeRenderer
Yvee1 Oct 19, 2024
ff89de6
Fix bounds check in SimpleSets settings.h
Yvee1 Oct 19, 2024
80485f9
Add methods for drawing a Rectangle and a Box
Yvee1 Oct 19, 2024
4a2a386
Add demo for SimpleSets' partition filtration
Yvee1 Oct 19, 2024
6ab538b
Try CavalierContours for offsetting circular arcs
Yvee1 Oct 20, 2024
f9e3c61
Merge branch 'tue-alga:master' into SimpleSets
Yvee1 Oct 20, 2024
1c45a03
Add CavalierContours wrapper functions
Yvee1 Oct 27, 2024
436cd6b
Implement smoothing
Yvee1 Nov 24, 2024
8f22426
Make isStraight less strict: allow small turns
Yvee1 Nov 24, 2024
e0383f1
Add smoothing settings
Yvee1 Nov 24, 2024
515d8ab
Implement maximum weight disk & set up chorematic map
Yvee1 Nov 29, 2024
245c596
Sample random points in arrangement
Yvee1 Nov 29, 2024
0137667
Refactor, improve UI, add tests
Yvee1 Dec 1, 2024
5087664
Fix smallest maximum weight disk algorithm
Yvee1 Dec 3, 2024
266573f
Add basic parallelism for computing min. weight disk
Yvee1 Dec 3, 2024
4cf78f1
Add performance test ipe file
Yvee1 Dec 3, 2024
b7b3a25
Small improvements and fixes & add new sampling methods
Yvee1 Dec 4, 2024
9d574fb
Add centroidal Voronoi sampling method & refactor
Yvee1 Dec 10, 2024
aa7fa54
Try GDAL; add hex/square sampling; some refactoring
Yvee1 Dec 13, 2024
b78b658
Merge branch 'SimplificationBranch' into fit-disk-choropleth
Yvee1 Dec 13, 2024
63301da
Add sample per region option; make simplification module compile & pa…
Yvee1 Dec 21, 2024
c060102
Fit disks to multiple bins; refactor sampler
Yvee1 Dec 22, 2024
36a979f
Setup experiments; parallelize local Voronoi; compute score
Yvee1 Dec 23, 2024
86131ff
Change 2-class colors; fix score computation; start on perturb disk r…
Yvee1 Dec 24, 2024
68e6aa7
Simplification: add simple workaround to mergeWithNext
Yvee1 Jan 2, 2025
6eef42d
Let ipeToRegionMap join multiple regions with the same name.
Yvee1 Jan 2, 2025
43ff9b1
Add basic clipping functionality to GeometryRenderer
Yvee1 Jan 2, 2025
ab63604
Start on script for auto-generating chorematic choropleth figures
Yvee1 Jan 2, 2025
3f1dbe5
Add line cap and line join rendering settings.
Yvee1 Jan 2, 2025
c60bca4
Add text alignment functionality to renderer.
Yvee1 Jan 2, 2025
bf5590e
Fix line cap and line join in ipe renderer.
Yvee1 Jan 2, 2025
9f3b3f3
Finalize script to auto-generate Dutch chorematic choropleths
Yvee1 Jan 2, 2025
584986b
Add halfplane class and render functions
Yvee1 Jan 12, 2025
4951778
Add back render helper functions for circle-segment objects
Yvee1 Jan 12, 2025
084823d
Optimize function to compute smallest max. weight disk & handle edge …
Yvee1 Jan 12, 2025
2b55548
Improve local and grid sampling & add allow halfplanes
Yvee1 Jan 12, 2025
8d46ec9
Add option to use symmetric difference as objective function
Yvee1 Jan 12, 2025
d0323de
Merge branch 'fit-disk-choropleth' of https://github.com/Yvee1/cartoc…
Yvee1 Jan 12, 2025
2213d7a
Fix minor sampling issues; styling; experiments
Yvee1 Jan 15, 2025
d8bac60
Failed attempt at fixing simplification split function
Yvee1 Jan 23, 2025
e4fc7a2
Fix conflicting include guards
Yvee1 Jan 23, 2025
79ad5fd
Add option to change latex preamble in ipe renderer
Yvee1 Jan 23, 2025
d197314
Implement missing painting_renderer functions
Yvee1 Jan 23, 2025
a04fc15
Fix renderer demo bounding box
Yvee1 Jan 23, 2025
bce330a
Add missing polyline include
Yvee1 Jan 23, 2025
cf6da23
'Fix' clipping being enabled once clipping path is set
Yvee1 Jan 23, 2025
13f9565
Add SimpleSets frontend
Yvee1 Jan 23, 2025
ab5a6a7
Use PaintingRenderer to improve rendering performance in SimpleSets
Yvee1 Jan 23, 2025
4fe6814
Fix todo in copyBoundedFaceData
Yvee1 Jan 23, 2025
a8c52c8
Choropleth: draw labels after drawing all faces
Yvee1 Jan 23, 2025
7294b02
Chorematic map: normalize score
Yvee1 Jan 23, 2025
326e614
Chorematic map: change threshold for collinearity
Yvee1 Jan 23, 2025
dea110a
Chorematic map: update make_figures
Yvee1 Jan 23, 2025
9daec73
Change region arrangement overlay traits such that it can deal with h…
Yvee1 Jan 23, 2025
633db7c
Add debug info when region has no label
Yvee1 Jan 23, 2025
0d5aec7
Add chorematic_map frontend; implement missing svg renderer functions…
Yvee1 Jan 24, 2025
c042d78
Document simplesets and chorematic_map
Yvee1 Jan 24, 2025
e8c0325
Fix compilation errors and warnings
Yvee1 Jan 27, 2025
7a926c3
Fix drawAxes again after change in inverseConvertBox
Yvee1 Jan 27, 2025
180ed35
Draw PolygonWithHoles and PolygonSet with RenderPath
Yvee1 Jan 27, 2025
93d943d
Making escaping of special characters in drawText optional
Yvee1 Jan 27, 2025
163f039
Add pretendExact functions to go from Inexact to Exact representation
Yvee1 Jan 27, 2025
88d8684
Look into todo: joining two PolygonSets seems to just work
Yvee1 Jan 28, 2025
97c4398
Do render_path todo.
Yvee1 Jan 28, 2025
a14f92a
Look into simplesets todos; add default settings values and more info
Yvee1 Jan 28, 2025
595f402
Fix frontend compilation
Yvee1 Feb 1, 2025
23a4557
Move all SimpleSets helpers to core
Yvee1 Feb 1, 2025
bf276bd
Add circle editable
Yvee1 Feb 2, 2025
c9001e2
Move circle tangent functions to their own file and generalize them a…
Yvee1 Feb 2, 2025
476d085
cavc_helpers: add opening/closing operators, rename smoothing operato…
Yvee1 Feb 4, 2025
1cd9172
SimpleSets: set maximum smoothing value of slider to 0.2
Yvee1 Feb 5, 2025
26a86f5
Rename approximate_convex_hull file to clarify it is of disks
Yvee1 Feb 5, 2025
2d2715c
Rename CS types to be more appropriate
Yvee1 Feb 5, 2025
c04ccaa
Document and refactor approximate_convex_hull_of_disks
Yvee1 Feb 5, 2025
b7346eb
Document arrangement_helpers.h
Yvee1 Feb 5, 2025
673c011
If single circle, return it as exact convex hull.
Yvee1 Feb 5, 2025
197469e
Document cs_curve_helpers
Yvee1 Feb 5, 2025
2164c33
Document and refactor cs_polygon_helpers
Yvee1 Feb 5, 2025
2414d58
Document and refactor cs_polyline_helpers
Yvee1 Feb 5, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ include_directories(${CGAL_INCLUDE_DIR})
link_libraries(${CGAL_LIBRARIES})
find_package(GMP REQUIRED)

find_package(GDAL CONFIG REQUIRED)
find_package(Qt5Widgets REQUIRED)
set(CMAKE_AUTOMOC ON)
find_package(glog REQUIRED)
Expand All @@ -55,6 +56,7 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR})

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")

add_subdirectory(cartocrow)
add_subdirectory(demos)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ CartoCrow depends on the following build tools:

And it depends on the following libraries:

* CGAL (5.4, 5.5) – for implementations of computational geometry algorithms we need
* CGAL (5.6) – for implementations of computational geometry algorithms we need
* glog (0.5.0, 0.6.0) – for logging
* ipelib (7.2.26) – for [Ipe](https://ipe.otfried.org) input and SVG/Ipe output
* nlohmann-json (3.10.5, 3.11.2) – for JSON parsing
Expand Down
4 changes: 4 additions & 0 deletions cartocrow/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ add_subdirectory(simplification)
add_subdirectory(flow_map)
add_subdirectory(isoline_simplification)
add_subdirectory(simplesets)
add_subdirectory(chorematic_map)

add_library(cartocrow_lib INTERFACE)
target_link_libraries(
Expand All @@ -26,6 +27,9 @@ target_link_libraries(
necklace_map
flow_map
simplification
isoline_simplification
simplesets
chorematic_map
)

install(
Expand Down
29 changes: 29 additions & 0 deletions cartocrow/chorematic_map/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
set(SOURCES
maximum_weight_disk.cpp
sampler.h
parse_points.cpp
disk_area.cpp
choropleth.cpp
natural_breaks_external.cpp
choropleth_disks.cpp
input_parsing.cpp
)
set(HEADERS
maximum_weight_disk.h
parse_points.h
disk_area.h
choropleth.h
natural_breaks.h
weighted_region_sample.h
choropleth_disks.h
input_parsing.h
)

add_library(chorematic_map ${SOURCES})
target_link_libraries(chorematic_map
PUBLIC core
PRIVATE GDAL::GDAL
)

cartocrow_install_module(chorematic_map)
install(FILES ${HEADERS} DESTINATION ${CARTOCROW_INSTALL_DIR}/chorematic_map)
65 changes: 65 additions & 0 deletions cartocrow/chorematic_map/choropleth.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "choropleth.h"
#include "../core/arrangement_helpers.h"
#include "../core/centroid.h"
#include "../core/rectangle_helpers.h"

namespace cartocrow::chorematic_map {
using namespace renderer;

PolygonWithHoles<Inexact> transform(const CGAL::Aff_transformation_2<Inexact>& t, const PolygonWithHoles<Inexact>& pwh) {
Polygon<Inexact> outerT;
if (!pwh.is_unbounded()) {
outerT = transform(t, pwh.outer_boundary());
}
std::vector<Polygon<Inexact>> holesT;
for (const auto& h : pwh.holes()) {
holesT.push_back(transform(t, h));
}
return {outerT, holesT.begin(), holesT.end()};
}

void ChoroplethPainting::paint(GeometryRenderer& renderer) const {
const auto& arr = *m_choropleth.m_arr;
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) {
if (!fit->has_outer_ccb()) continue;
auto region = fit->data();
if (region.empty() || region == "#") {
renderer.setMode(0);
continue;
} else {
renderer.setMode(GeometryRenderer::fill);
}
std::optional<int> bin = m_choropleth.regionToBin(region);
Color color = m_options.noDataColor;
if (bin.has_value()) {
if (m_colors.size() <= *bin) {
std::cerr << "No color specified for bin " << *bin << std::endl;
} else {
color = m_colors.at(*bin);
}
}
renderer.setFill(color);
auto poly = approximate(face_to_polygon_with_holes<Exact>(fit));
renderer.draw(transform(m_options.transformation, poly));
}
if (m_options.drawLabels) {
for (auto fit = arr.faces_begin(); fit != arr.faces_end(); ++fit) {
if (!fit->has_outer_ccb())
continue;
auto region = fit->data();
auto poly = approximate(face_to_polygon_with_holes<Exact>(fit));
auto c = centroid(poly);
renderer.setMode(GeometryRenderer::stroke);
renderer.setStroke(Color{0, 0, 0}, m_options.strokeWidth);
auto label = fit->data().empty() ? "empty" : fit->data();
renderer.drawText(c.transform(m_options.transformation), label);
}
}
renderer.setLineCap(GeometryRenderer::RoundCap);
renderer.setMode(GeometryRenderer::stroke);
renderer.setStroke(m_options.strokeColor, m_options.strokeWidth);
for (auto eit = arr.edges_begin(); eit != arr.edges_end(); ++eit) {
renderer.draw(Segment<Inexact>(approximate(eit->source()->point()), approximate(eit->target()->point())).transform(m_options.transformation));
}
}
}
170 changes: 170 additions & 0 deletions cartocrow/chorematic_map/choropleth.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#ifndef CARTOCROW_CHOROPLETH_H
#define CARTOCROW_CHOROPLETH_H

#include <utility>
#include "../renderer/geometry_painting.h"
#include "../core/region_arrangement.h"
#include "../core/arrangement_helpers.h"
#include "natural_breaks.h"

#include <CGAL/Aff_transformation_2.h>

namespace cartocrow::chorematic_map {
/// A choropleth map. It is a \ref RegionArrangement where double values are associated with each region.
/// Thresholds that define classes can be passed to the constructor.
/// If no thresholds are passed, the Fisher-Jenks natural breaks algorithm is used to compute natural thresholds.
///
/// This class represents only an abstract choropleth; to draw one, see \ref ChoroplethPainting.
class Choropleth {
public:
std::shared_ptr<RegionArrangement> m_arr;
std::shared_ptr<std::unordered_map<std::string, double>> m_data;

void naturalBreaks(int nBins) {
m_thresholds.clear();
std::vector<double> values;
for (auto& [_, value] : *m_data) {
values.push_back(value);
}
natural_breaks(values.begin(), values.end(), std::back_inserter(m_thresholds), nBins);
}

void rebin() {
// Clear
m_bins.clear();
m_regionToBin.clear();

// Initialize bins
for (int i = 0; i < m_thresholds.size() + 1; ++i) {
m_bins.push_back(std::vector<std::string>({}));
}

// Fill bins and regionToBin map
for (auto fit = m_arr->faces_begin(); fit != m_arr->faces_end(); ++fit) {
auto region = fit->data();
if (!m_data->contains(region)) continue;
double value = m_data->at(region);
int bin = std::distance(m_thresholds.begin(), std::upper_bound(m_thresholds.begin(), m_thresholds.end(), value));
m_bins[bin].push_back(region);
m_regionToBin[region] = bin;
}
}

Choropleth(std::shared_ptr<RegionArrangement> arr,
std::shared_ptr<std::unordered_map<std::string, double>> data,
int nBins) :
m_arr(std::move(arr)), m_data(std::move(data)) {
naturalBreaks(nBins);
rebin();
}

template <class InputIterator>
Choropleth(std::shared_ptr<RegionArrangement> arr,
std::shared_ptr<std::unordered_map<std::string, double>> data,
InputIterator thresholdsBegin, InputIterator thresholdsEnd) :
m_arr(std::move(arr)), m_data(std::move(data)), m_thresholds(thresholdsBegin, thresholdsEnd) {
rebin();
}

std::optional<int> regionToBin(const std::string& region) const {
if (!m_regionToBin.contains(region)) return std::nullopt;
return m_regionToBin.at(region);
}

template <class InputIterator>
void setThresholds(InputIterator begin, InputIterator end) {
m_thresholds.clear();
m_thresholds.resize(0);
std::copy(begin, end, std::back_inserter(m_thresholds));
}

std::vector<double>& getThresholds() {
return m_thresholds;
}

std::vector<double> getIntervals() {
std::optional<double> min;
std::optional<double> max;
for (auto& [region, value] : *m_data) {
if (!min.has_value() || value < *min) {
min = value;
}
if (!max.has_value() || value > *max) {
max = value;
}
}
std::vector<double> intervals({*min});
for (const auto& t : getThresholds()) {
intervals.push_back(t);
}
intervals.push_back(*max);
return intervals;
}

int numberOfBins() const {
return m_bins.size();
}

std::vector<Number<Exact>> binAreas() const {
std::vector<Number<Exact>> areas;
for (int i = 0; i < m_bins.size(); ++i) {
areas.emplace_back(0);
}
for (auto fit = m_arr->faces_begin(); fit != m_arr->faces_end(); ++fit) {
auto pwh = face_to_polygon_with_holes<Exact>(fit);
auto& region = fit->data();
if (!m_regionToBin.contains(region)) continue;
auto& bin = m_regionToBin.at(region);
areas[bin] += abs(pwh.outer_boundary().area());
for (auto& hole : pwh.holes()) {
areas[bin] -= abs(hole.area());
}
}
return areas;
}

private:
std::vector<double> m_thresholds;
std::vector<std::vector<std::string>> m_bins;
std::unordered_map<std::string, int> m_regionToBin;
};

/// Draws a \ref Choropleth. One should pass a color for each bin of the choropleth.
/// Drawing style can be configured via \ref ChoroplethPainting::Options.
class ChoroplethPainting : public renderer::GeometryPainting {
public:
Choropleth& m_choropleth;
std::vector<Color> m_colors;

struct Options {
bool drawLabels = false;
Color noDataColor = Color(200, 200, 200);
CGAL::Aff_transformation_2<Inexact> transformation = CGAL::IDENTITY;
double strokeWidth = 1.0;
Color strokeColor = Color(0, 0, 0);
};

Options m_options;

// Needed because bug in clang gcc:
// https://stackoverflow.com/questions/53408962/try-to-understand-compiler-error-message-default-member-initializer-required-be
static Options defaultOptions() {
return {};
}

template <class InputIterator>
ChoroplethPainting(Choropleth& choropleth, InputIterator beginColors, InputIterator endColors, Options options = defaultOptions())
: m_choropleth(choropleth), m_colors(beginColors, endColors), m_options(options) {}

template <class InputIterator>
void setColors(InputIterator begin, InputIterator end) {
m_colors.clear();
m_colors.resize(0);
std::copy(begin, end, std::back_inserter(m_colors));
}

void paint(renderer::GeometryRenderer& renderer) const override;
};
}

#endif //CARTOCROW_CHOROPLETH_H
Loading