From 86cf4c970bd6ad0fa90d503b0c9875afa1f184dd Mon Sep 17 00:00:00 2001 From: adisidev <64905594+adisidev@users.noreply.github.com> Date: Sun, 1 Jun 2025 08:44:37 +0800 Subject: [PATCH 1/4] Add req.txt, instruct activating the venv --- README.md | 3 ++- requirements.txt | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 requirements.txt diff --git a/README.md b/README.md index 485ecd0e..ce420560 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,11 @@ We manage dependencies with a Python virtual environment and Conan 2. The projec Only `Debug` build commands are shown below, but the same commands can be run with `Release` build by replacing `Debug` with `Release`. -1. Create a virtual environment with the required dependencies: +1. Create a virtual environment with the required dependencies and activate it: ``` shell virtualenv .venv && .venv/bin/pip install -r requirements.txt +source .venv/bin/activate ``` 2. Setup Conan diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..4c8687e3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +conan==2.16.1 +cmake==3.30.0 +pip +wheel From 70c9f580957fc0dc4da310b9d93033b9b605cd97 Mon Sep 17 00:00:00 2001 From: amishn2008 <168089586+amishn2008@users.noreply.github.com> Date: Sun, 1 Jun 2025 09:15:57 +0800 Subject: [PATCH 2/4] Add link under the cartogram image --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ce420560..495f1399 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@

+The website can be accessed using this link. This program uses the fast flow-based method developed by Michael T. Gastner, Vivien Seguy, and Pratyush More. For more information, you may refer to the following [paper](https://www.pnas.org/content/115/10/E2156): From de109959c6056f44f118c8ad33141cd20fc6362f Mon Sep 17 00:00:00 2001 From: amishn2008 <168089586+amishn2008@users.noreply.github.com> Date: Sun, 8 Jun 2025 10:27:24 +0800 Subject: [PATCH 3/4] Add link and instructions for repo or website --- README.md | 1 + src/cartogram_info/cartogram_info.cpp | 6 ++++++ src/draw/draw.cpp | 21 +++++++++++++++++++++ src/main.cpp | 4 ++++ 4 files changed, 32 insertions(+) diff --git a/README.md b/README.md index 495f1399..35d279ae 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@

The website can be accessed using this link. +This repository contains the underlying C++ program that powers go-cart.io. If you'd only like to create a cartogram, you may find it easier to visit the website directly. Otherwise, if you'd like to make code contributions, feature suggestions and/or play with the inner workings of our cartogram generator, you're at the right place! This program uses the fast flow-based method developed by Michael T. Gastner, Vivien Seguy, and Pratyush More. For more information, you may refer to the following [paper](https://www.pnas.org/content/115/10/E2156): diff --git a/src/cartogram_info/cartogram_info.cpp b/src/cartogram_info/cartogram_info.cpp index 08762d12..7d7d3e1c 100644 --- a/src/cartogram_info/cartogram_info.cpp +++ b/src/cartogram_info/cartogram_info.cpp @@ -435,6 +435,12 @@ void CartogramInfo::write_shifted_insets() void CartogramInfo::write_svg(const std::string &suffix) { + // Skip SVG creation if output is being redirected to stdout + + if (args_.redirect_exports_to_stdout) { + return; + } + InsetState insets_combined = convert_to_inset_state(); insets_combined.rescale_map(); diff --git a/src/draw/draw.cpp b/src/draw/draw.cpp index 041c11e7..fb9e8a17 100644 --- a/src/draw/draw.cpp +++ b/src/draw/draw.cpp @@ -72,6 +72,13 @@ void InsetState::write_delaunay_triangles( const std::string &filename, const bool draw_projected_points) { + + + + // Skip if redirecting to stdout + if (args_.redirect_exports_to_stdout) { + return; + } std::cerr << "Writing " << filename << ".svg" << std::endl; Canvas cvs(filename + ".svg", lx_, ly_); @@ -118,6 +125,10 @@ void InsetState::write_delaunay_triangles( void InsetState::write_quadtree(const std::string &filename) { + // Skip if redirecting to stdout + if (args_.redirect_exports_to_stdout) { + return; + } std::cerr << "Writing " << filename << ".svg" << std::endl; Canvas cvs(filename + ".svg", lx_, ly_); @@ -135,6 +146,10 @@ void InsetState::write_quadtree(const std::string &filename) void InsetState::write_intersections_image() { + // Skip if redirecting to stdout + if (args_.redirect_exports_to_stdout) { + return; + } unsigned int res = intersections_resolution; std::string svg_name = inset_name() + "_intersections_" + std::to_string(n_finished_integrations()) + ".svg"; @@ -165,6 +180,12 @@ void InsetState::write_map( const bool equal_area_map, const std::unordered_map vectors) const { + std::cerr << "Redirect value: " << args_.redirect_exports_to_stdout << std::endl; + + // Skip if redirecting to stdout + if (args_.redirect_exports_to_stdout) { + return; + } const auto svg_name = file_name + ".svg"; std::cerr << "Writing " << svg_name << std::endl; diff --git a/src/main.cpp b/src/main.cpp index cf62e2d7..3195bf55 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2,8 +2,12 @@ #include "parse_arguments.hpp" #include "progress_tracker.hpp" + int main(const int argc, const char *argv[]) { + + std::cerr << "Cartogram C++ version 0.1.0" << std::endl; + // Parse command-line arguments Arguments args = parse_arguments(argc, argv); From d079fac66699f6911666a86611d39eb489b5bc3b Mon Sep 17 00:00:00 2001 From: amishn2008 <168089586+amishn2008@users.noreply.github.com> Date: Sun, 8 Jun 2025 10:38:04 +0800 Subject: [PATCH 4/4] Allow Scientific Notation for CSV Files --- include/string_to_decimal_converter.hpp | 14 ++++ src/misc/string_to_decimal_converter.cpp | 90 ++++++++++++++++++++---- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/include/string_to_decimal_converter.hpp b/include/string_to_decimal_converter.hpp index 8d4a741f..fc6ff2b1 100644 --- a/include/string_to_decimal_converter.hpp +++ b/include/string_to_decimal_converter.hpp @@ -4,6 +4,20 @@ #include #include +/** + * @brief A utility class for converting string representations of numbers to decimal format. + * + * This class handles various number formats including: + * - Regular decimal numbers (e.g. "123.456", "123,456") + * - Scientific notation (e.g. "1.23e-4", "1.23E4") + * - Special value "NA" + * + * For scientific notation: + * - Both 'e' and 'E' are supported as exponent markers + * - The mantissa can use either '.' or ',' as decimal separator + * - The exponent must be an integer and can be negative + * - The mantissa must be a valid decimal number + */ class StringToDecimalConverter { private: diff --git a/src/misc/string_to_decimal_converter.cpp b/src/misc/string_to_decimal_converter.cpp index 29e6b322..2967af43 100644 --- a/src/misc/string_to_decimal_converter.cpp +++ b/src/misc/string_to_decimal_converter.cpp @@ -7,7 +7,7 @@ const std::string StringToDecimalConverter::NA_ = "NA"; bool StringToDecimalConverter::is_valid_char(char ch) { - return (std::isdigit(ch)) || ch == point_ || ch == comma_ || ch == minus_; + return (std::isdigit(ch)) || ch == point_ || ch == comma_ || ch == minus_ || ch == 'e' || ch == 'E'; } std::string StringToDecimalConverter::remove_char(std::string str, char ch) @@ -87,22 +87,66 @@ bool StringToDecimalConverter::is_str_valid_characters(const std::string &str) return true; } - // Only 0 to 9, '.', '-', and ',' are allowed + // Only 0 to 9, '.', '-', ',', 'e', and 'E' are allowed for (const auto &c : str) { if (!is_valid_char(c)) { return false; } } - // '-' can only be used once - if (count_char(str, minus_) > 1) { - return false; + // '-' can only be used once in the mantissa and once in the exponent + size_t first_e = str.find_first_of("eE"); + if (first_e != std::string::npos) { + // Check mantissa part + std::string mantissa = str.substr(0, first_e); + if (count_char(mantissa, minus_) > 1) { + return false; + } + if (count_char(mantissa, minus_) == 1 && mantissa[0] != minus_) { + return false; + } + // Check exponent part + std::string exponent = str.substr(first_e + 1); + if (count_char(exponent, minus_) > 1) { + return false; + } + if (count_char(exponent, minus_) == 1 && exponent[0] != minus_) { + return false; + } + } else { + // No scientific notation, check as before + if (count_char(str, minus_) > 1) { + return false; + } + if (count_char(str, minus_) == 1 && str[0] != minus_) { + return false; + } } - // '-' can only be used at the beginning - if (count_char(str, minus_) == 1 and str[0] != minus_) { + // Check for valid scientific notation format + size_t e_count = count_char(str, 'e') + count_char(str, 'E'); + if (e_count > 1) { return false; } + if (e_count == 1) { + size_t e_pos = str.find_first_of("eE"); + // Must have digits before and after 'e'/'E' + if (e_pos == 0 || e_pos == str.length() - 1) { + return false; + } + // Must have at least one digit after 'e'/'E' + bool has_digit_after = false; + for (size_t i = e_pos + 1; i < str.length(); i++) { + if (std::isdigit(str[i])) { + has_digit_after = true; + break; + } + } + if (!has_digit_after) { + return false; + } + } + return true; } @@ -111,19 +155,37 @@ bool StringToDecimalConverter::is_str_correct_format(const std::string &str) assert(is_str_valid_characters(str)); assert(is_str_NA(str) == false); - // if the number of commas and points both are more than 1, then this format - // does not belong to any known convention + // Handle scientific notation separately + size_t e_pos = str.find_first_of("eE"); + if (e_pos != std::string::npos) { + // Check mantissa part + std::string mantissa = str.substr(0, e_pos); + if (has_multiple_commas_and_points(mantissa)) { + return false; + } + if (has_invalid_comma_point_sequence(mantissa)) { + return false; + } + if (has_separator_at_the_end(mantissa)) { + return false; + } + // Exponent part should only contain digits and optional minus sign + std::string exponent = str.substr(e_pos + 1); + for (char c : exponent) { + if (!std::isdigit(c) && c != minus_) { + return false; + } + } + return true; + } + + // Original format validation for non-scientific notation if (has_multiple_commas_and_points(str)) { return false; } - - // Check for commas before and after a point, or points before and after a - // comma if (has_invalid_comma_point_sequence(str)) { return false; } - - // Check for separators at the end of the string if (has_separator_at_the_end(str)) { return false; }