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

Fix Issues with Shifting of Insets to Position and String to Decimal Converter #218

Open
wants to merge 46 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
245e901
Shift original insets to position
nihalzp Nov 17, 2024
5b989f0
Calculate bbox on transformed original insets
nihalzp Nov 17, 2024
a3e19b9
Show ERROR output in a single line
nihalzp Nov 17, 2024
e493c85
Calculate inset area on transformed original coordinates
nihalzp Nov 17, 2024
76495a5
Use boost's own cmake.config (Fixes #223)
adisidev Nov 29, 2024
f0536c9
Remove Redundant Negative for Check Unsigned Char
adisidev Nov 29, 2024
d291175
Use "none" as default if no csv is provided
nihalzp Dec 2, 2024
d8d6872
Allow only geojson file for equal area map
nihalzp Dec 2, 2024
08761ee
Avoid potential division by zero when no csv is provided
nihalzp Dec 2, 2024
63dabb7
Add GeoDiv method to update id
nihalzp Dec 2, 2024
073ae2b
Allow update of InsetState GeoDiv ids using id mapping
nihalzp Dec 2, 2024
5e2d047
Use dump() when accesing nlohmann json string object
nihalzp Dec 2, 2024
47a2667
Only output the non simplified geosjon when equal area map is requested
nihalzp Dec 2, 2024
0c5c00c
Do not store csv file name in CartogramInfo
nihalzp Dec 2, 2024
b83a2b9
Parse GeoJSON before the CSV
nihalzp Dec 2, 2024
3f0cd84
Refactor read_geojson to remove dependency of csv
nihalzp Dec 2, 2024
41634e8
Refactor read_csv and handle the given inset position of GeoDivs
nihalzp Dec 2, 2024
5ff5b2b
Allows strip quotes when accessing nlohmann json string object
nihalzp Dec 2, 2024
bb44ca0
Merge branch 'main' into inset-fix
nihalzp Dec 2, 2024
89b589f
Improve and automate `benchmark.sh`
adisidev Dec 3, 2024
0a4a0ac
Merge pull request #224 from mgastner/improve-benchmark-sh
adisidev Dec 3, 2024
6dc2b26
Add instructions to install for WSL users
adisidev Dec 3, 2024
3b1ad37
Add benchmarking instructions
adisidev Dec 3, 2024
ac950c8
Move us_counties data to second column
adisidev Dec 3, 2024
5a240b6
Fix insets appearing too distant from each other
nihalzp Dec 3, 2024
41ce02e
Revamp the logic of parser
nihalzp Dec 3, 2024
c7dd6f3
Integrate new target area parser
nihalzp Dec 3, 2024
3fa6e02
Remove deprecated tests
nihalzp Dec 3, 2024
03507bf
Use visual_variable_file_name
adisidev Dec 8, 2024
527f4c7
Handle empty target area in CSV
nihalzp Dec 9, 2024
409fc33
Write function parameter names in header files
nihalzp Dec 10, 2024
3046fd4
Add vector library
adisidev Dec 17, 2024
f7f9b5a
Calculate densities before quadtree construction
nihalzp Dec 17, 2024
07fc7e6
Merge remote-tracking branch 'origin/inset-fix' into inset-fix
adisidev Dec 18, 2024
7dff687
Add instructions to push changes to production
adisidev Dec 18, 2024
ae2baf5
Change CSV mismatch error to warning
adisidev Dec 18, 2024
64aed88
process_area_strs after checking validity
adisidev Dec 18, 2024
ab67eff
Add functions to scale, move and transform points
adisidev Dec 18, 2024
2009185
Create function to write all insets as an svg
adisidev Dec 18, 2024
6bec777
When creating CSV, base it on GeoJSON name
adisidev Dec 18, 2024
e9de287
Use transform_points function instead of long loop
adisidev Dec 18, 2024
c8920df
long_grid_side_length --> max_n_grid_rows_or_cols
adisidev Dec 23, 2024
118c861
long_grid_side_length --> max_n_grid_rows_or_cols
adisidev Dec 23, 2024
f44f74a
Merge branch 'inset-fix' of https://github.com/mgastner/cartogram-cpp…
adisidev Dec 23, 2024
0166544
Write unblurred density after creating quadtree
adisidev Dec 23, 2024
28b23e4
plot quadtree corners on density image
adisidev Dec 23, 2024
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: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ set(CGAL_DIR ${PROJECT_SOURCE_DIR}/external/cgal)
find_package(CGAL REQUIRED)

# Boost
find_package(Boost REQUIRED COMPONENTS unit_test_framework)
find_package(Boost REQUIRED CONFIG COMPONENTS unit_test_framework)

# PkgConfig, fftw, and cairo
find_package(PkgConfig REQUIRED)
Expand Down
45 changes: 39 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ Have a look through to apt-requirements.txt if you'd like to see what all will b
apt install -y g++-11 build-essential cmake libboost-all-dev nlohmann-json3-dev libomp-dev libfftw3-dev libcairo2-dev
```

### Using WSL (Windows Subsystem for Linux)

For Windows users, we recommend using our program through Windows Subsystem for Linux (WSL).

Please install [Ubuntu](https://apps.microsoft.com/detail/9pdxgncfsczv) from the Microsoft Store, and then follow the same instructions as for Debian-based distributions (found above).

We recommend you to compile anywhere outside the `/mnt` directory, as compiling in the `/mnt` directory may lead to unexpected behavior.

### Installation

Go to the `cartogram-cpp` directory in your preferred terminal and execute the following commands.
Expand Down Expand Up @@ -120,12 +128,6 @@ cartogram sample_data/world_by_country_since_2022/world_by_country_since_2022.ge

You may inspect the resultant SVG to check if everything looks as expected.

### Contributing

Contributions are highly encouraged! Please feel free to take a stab at any at any of the open issues and send in a pull request. If you need help getting setup or more guidance contributing, please @ any of the main contributors (@adisidev, @nihalzp, @mgastner) under any of the open issues (or after creating your own issue), and we'll be happy to guide you!

Maintainers, please make sure to run the "Build and Release" workflow under GitHub Actions before approving the pull request. You may delete the newly created release before merging the pull-request. Another release should be automatically created after merging with main.

### Testing

If you'd like to contribute to the project, please run our tests after you make any changes.
Expand All @@ -144,6 +146,26 @@ Additionally, you may go to the `cartogram-cpp/tests` directory and run the foll
bash stress_test.sh
```

### Benchmarking

To benchmark the program, first install [hyperfine](https://github.com/sharkdp/hyperfine). You can install it using Homebrew on macOS:

```shell script
brew install hyperfine
```

Or using apt on Debian-based distributions:

```shell script
apt install hyperfine
```

Then, go to the `cartogram-cpp/tests` directory and run the following command:

```shell script
bash stress_test.sh
```

### Uninstallation

Go to the `cartogram-cpp` directory in your preferred terminal and execute the following command:
Expand All @@ -157,3 +179,14 @@ Upon successful uninstallation, the following will be outputted:
> Built target uninstall

Further, running `cartogram` should no longer work.

### Pushing changes to [go-cart.io](https://go-cart.io)

To push changes to production, please follow the the instructions on [go-cart-io/carotgram-docker](https://github.com/go-cart-io/cartogram-docker).


### Contributing

Contributions are highly encouraged! Please feel free to take a stab at any at any of the open issues and send in a pull request. If you need help getting setup or more guidance contributing, please @ any of the main contributors (@adisidev, @nihalzp, @mgastner) under any of the open issues (or after creating your own issue), and we'll be happy to guide you!

Maintainers, please make sure to run the "Build and Release" workflow under GitHub Actions before approving the pull request. You may delete the newly created release before merging the pull-request. Another release should be automatically created after merging with main.
23 changes: 18 additions & 5 deletions include/cartogram_info.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,24 @@ class CartogramInfo
std::map<std::string, std::string> gd_to_inset_;
std::string id_header_;
std::set<std::string> ids_in_visual_variables_file_;
std::vector<std::string> initial_id_order_;
std::map<std::string, InsetState> inset_states_;
bool is_world_map_;
std::string map_name_;
std::map<std::string, std::map<std::string, std::string>> properties_map_;
std::vector<std::string> unique_properties_;

// TODO: We assume that either all external rings are counterclockwise or
// all are clockwise. This dichotomy covers most geospatial boundary
// files in the wild, but it would still be sensible to allow cases
// where there are external rings with opposite winding directions.
bool original_ext_ring_is_clockwise_{};
std::string visual_variable_file_;
nlohmann::json cgal_to_json(bool = false);

public:
explicit CartogramInfo(bool, const std::string &);
explicit CartogramInfo(bool);
[[nodiscard]] double cart_initial_total_target_area() const;
void construct_inset_state_from_geodivs(const nlohmann::json &);
[[nodiscard]] double area() const;
[[nodiscard]] bool is_world_map() const;
void json_to_geojson(
Expand All @@ -36,11 +39,21 @@ class CartogramInfo
void read_csv(const argparse::ArgumentParser &);
void read_geojson(const std::string &, bool, std::string &);
std::map<std::string, InsetState> &ref_to_inset_states();
void relocate_geodivs_based_on_inset_pos(
const std::map<std::string, std::map<std::string, std::string>> &);
void replace_missing_and_zero_target_areas();
std::string set_map_name(const std::string &);
void shift_insets_to_target_position();
void set_id_header(const std::string &);
void shift_insets_to_target_position(bool output_to_stdout = false);
void update_id_header_info(const std::string &);
void write_csv(const std::string &csv_file_name);
void write_geojson(const std::string &, const std::string &, bool = false);
void write_geojson(
const std::string &,
const std::string &,
bool = false,
bool = false);
InsetState convert_to_inset_state();
void write_svg(const std::string &suffix = "");
};

#endif // CARTOGRAM_INFO_HPP_
#endif // CARTOGRAM_INFO_HPP_
3 changes: 2 additions & 1 deletion include/geo_div.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class GeoDiv
void push_back(const Polygon_with_holes &);
std::vector<Polygon_with_holes> &ref_to_polygons_with_holes();
void sort_pwh_descending_by_area();
void update_id(const std::string &);
};

#endif // GEO_DIV_HPP_
#endif // GEO_DIV_HPP_
29 changes: 19 additions & 10 deletions include/inset_state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,6 @@ class InsetState
const double total_inset_area);
Bbox get_bbox_bar(const double bar_width, const double bar_height);

GeoDiv &get_geo_div(const std::string &);

std::pair<double, unsigned int> get_km_legend_length();
std::pair<double, unsigned int> get_visual_variable_legend_length();

Expand All @@ -182,7 +180,10 @@ class InsetState
void insert_color(const std::string &, const Color &);
void insert_color(const std::string &, std::string &);
bool insert_constraint_safely(const Point &p1, const Point &p2);
bool insert_constraint_safely_to_dt(Delaunay &dt, const Point &p1, const Point &p2);
bool insert_constraint_safely_to_dt(
Delaunay &dt,
const Point &p1,
const Point &p2);
void insert_label(const std::string &, const std::string &);
void insert_target_area(const std::string &, double);
void insert_whether_input_target_area_is_missing(const std::string &, bool);
Expand All @@ -193,7 +194,7 @@ class InsetState
char,
unsigned int) const;
bool is_input_target_area_missing(const std::string &) const;
void is_simple(const char* caller_func) const;
void is_simple(const char *caller_func) const;
std::string label_at(const std::string &) const;
double latt_const() const;
unsigned int lx() const;
Expand All @@ -211,7 +212,10 @@ class InsetState
unsigned int n_geo_divs() const;
unsigned long n_points() const;
unsigned int n_rings() const;
void normalize_inset_area(double total_cart_target_area, bool = false);
void normalize_inset_area(
double total_cart_target_area,
bool equal_area = false,
bool normalize_original = false);
void normalize_target_area();
std::string pos() const;
void project();
Expand All @@ -235,12 +239,12 @@ class InsetState
void set_geo_divs(std::vector<GeoDiv> new_geo_divs);
void set_inset_name(const std::string &);
void store_initial_area();
void store_initial_target_area();
void store_initial_target_area(const double override = 0.0);
void simplify(unsigned int);
void store_original_geo_divs();
double target_area_at(const std::string &) const;
bool target_area_is_missing(const std::string &) const;
double total_inset_area() const;
double total_inset_area(bool = false) const;
double total_target_area() const;
Polygon transform_to_equal_area_projection_coor(Polygon edge_points);
std::array<Point, 3> transformed_triangle(
Expand All @@ -249,16 +253,21 @@ class InsetState

// Apply given function to all points
void transform_points(const std::function<Point(Point)> &, bool = false);
void transform_polygons(const std::function<Polygon(Polygon)> &, bool = false);
void scale_points(double scale_factor, bool project_original = false);
void move_points(double dx, double dy, bool project_original = false);
std::array<Point, 3> untransformed_triangle(const Point &, bool = false)
const;
void trim_grid_heatmap(cairo_t *cr, double padding);
void update_delaunay_t();
void update_gd_ids(const std::map<std::string, std::string> &);

// Cairo functions
void write_cairo_map(
const std::string &,
bool,
const std::unordered_map<Point, Vector> = std::unordered_map<Point, Vector>()) const;
const std::unordered_map<Point, Vector> =
std::unordered_map<Point, Vector>()) const;
void write_cairo_polygons_to_svg(
const std::string &,
bool,
Expand All @@ -281,11 +290,11 @@ class InsetState
cairo_t *cr,
const bool fill_polygons,
const bool colors,
const bool plot_equal_area_map) const;
const bool plot_equal_area_map,
const double line_width = 0.0) const;
void write_labels_on_surface(cairo_t *cr);
void write_density_image(
const std::string filename,
const double *density,
const bool plot_pycnophylactic);
void write_intersections_image(unsigned int res);
void write_legend_on_surface(cairo_t *cr, bool equal_area_map);
Expand Down
2 changes: 1 addition & 1 deletion include/parse_arguments.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ argparse::ArgumentParser parsed_arguments(
bool &plot_intersections,
bool &plot_polygons,
bool &remove_tiny_polygons,
double &minimum_polygon_area,
double &min_polygon_area,
bool &plot_quadtree,
bool &rays,
bool &output_preprocessed);
Expand Down
5 changes: 3 additions & 2 deletions include/string_to_decimal_converter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define STRING_TO_DECIMAL_CONVERTER_H

#include <string>
#include <vector>

class StringToDecimalConverter
{
Expand All @@ -13,16 +14,16 @@ class StringToDecimalConverter

static bool is_valid_char(char ch);
static std::string remove_char(std::string str, char ch);
static int count_char(const std::string &str, char ch);
static bool has_multiple_commas_and_points(const std::string &str);
static bool has_separator_at_the_end(const std::string &str);
static bool has_invalid_comma_point_sequence(const std::string &str);

public:
static bool is_comma_as_separator(const std::vector<std::string> &strs);
static bool is_str_NA(const std::string &str);
static bool is_str_valid_characters(const std::string &str);
static bool is_str_correct_format(const std::string &str);
static double parse_str(const std::string &str);
static std::string parse_str(const std::string &str, bool);
};

#endif // STRING_TO_DECIMAL_CONVERTER_H
Loading