Commit 5f454c4b authored by Paul Asmuth's avatar Paul Asmuth
Browse files

load GeoJSON polygons and multi polygons

parent c4b9f93c
Loading
Loading
Loading
Loading
+46 −0
Original line number Original line Diff line number Diff line
@@ -14,6 +14,7 @@
#include "data.h"
#include "data.h"
#include "utils/fileutil.h"
#include "utils/fileutil.h"
#include "utils/csv.h"
#include "utils/csv.h"
#include "utils/geojson.h"
#include "sexpr_conv.h"
#include "sexpr_conv.h"
#include "sexpr_util.h"
#include "sexpr_util.h"
#include <assert.h>
#include <assert.h>
@@ -184,6 +185,51 @@ ReturnCode data_load_strings(
  return expr_to_strings(expr, values);
  return expr_to_strings(expr, values);
}
}


ReturnCode data_load_polys2_geojson(
    const Expr* expr,
    std::vector<Poly2>* data) {
  if (!expr || !expr_is_value(expr)) {
    return errorf(
        ERROR,
        "argument error; expected a filename, got: {}",
        expr_inspect(expr));
  }

  const auto& path = expr_get_value(expr);

  GeoJSONReader reader;
  reader.on_polygons = [data] (const Poly3* polys, size_t poly_count) {
    for (size_t i = 0; i < poly_count; ++i) {
      data->emplace_back(poly3_to_poly2(polys[i]));
    }

    return OK;
  };

  return geojson_read_file(path, reader);
}

ReturnCode data_load_polys2(
    const Expr* expr,
    std::vector<Poly2>* data) {
  if (!expr || !expr_is_list(expr) || !expr_get_list(expr)) {
    return errorf(
        ERROR,
        "argument error; expected a list, got: {}",
        expr_inspect(expr));
  }

  auto args = expr_get_list(expr);

  if (args && expr_is_value_literal(args, "geojson")) {
    return data_load_polys2_geojson(expr_next(args), data);
  }

  return err_invalid_value(expr_inspect(expr), {
    "geojson"
  });
}

ReturnCode data_load(
ReturnCode data_load(
    const Expr* expr,
    const Expr* expr,
    std::vector<Measure>* values) {
    std::vector<Measure>* values) {
+4 −0
Original line number Original line Diff line number Diff line
@@ -52,6 +52,10 @@ ReturnCode data_load_strings(
    const Expr* expr,
    const Expr* expr,
    std::vector<std::string>* values);
    std::vector<std::string>* values);


ReturnCode data_load_polys2(
    const Expr* expr,
    std::vector<Poly2>* data);

ReturnCode data_load(
ReturnCode data_load(
    const Expr* expr,
    const Expr* expr,
    std::vector<Measure>* values);
    std::vector<Measure>* values);
+21 −0
Original line number Original line Diff line number Diff line
@@ -48,5 +48,26 @@ std::ostream& operator <<(std::ostream& os, const Rectangle& r) {
  return os;
  return os;
}
}


PolyLine2 polyline3_to_polyline2(const PolyLine3& p) {
  PolyLine2 p2;

  for (const auto& x : p.vertices) {
    p2.vertices.emplace_back(x);
  }

  return p2;
}

Poly2 poly3_to_poly2(const Poly3& p) {
  Poly2 p2;
  p2.boundary = polyline3_to_polyline2(p.boundary);

  for (const auto& x : p.holes) {
    p2.holes.emplace_back(polyline3_to_polyline2(x));
  }

  return p2;
}

} // namespace clip
} // namespace clip
+29 −0
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@
#include <stdlib.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdint.h>
#include <iostream>
#include <iostream>
#include <vector>
#include "vmath.h"
#include "vmath.h"


namespace clip {
namespace clip {
@@ -33,5 +34,33 @@ struct Rectangle {


std::ostream& operator <<(std::ostream& os, const Rectangle& c);
std::ostream& operator <<(std::ostream& os, const Rectangle& c);


struct PolyLine2 {
  std::vector<vec2> vertices;
};

struct PolyLine3 {
  std::vector<vec3> vertices;
};

/**
 * - The poly lines are assumed to form closed 'rings'.
 */
struct Poly2 {
  PolyLine2 boundary;
  std::vector<PolyLine2> holes;
};

/**
 * - The poly lines are assumed to form closed 'rings'.
 */
struct Poly3 {
  PolyLine3 boundary;
  std::vector<PolyLine3> holes;
};

PolyLine2 polyline3_to_polyline2(const PolyLine3& p);

Poly2 poly3_to_poly2(const Poly3& p);

} // namespace clip
} // namespace clip
+193 −13
Original line number Original line Diff line number Diff line
@@ -14,15 +14,171 @@
#include "geojson.h"
#include "geojson.h"
#include <iostream>
#include <iostream>
#include <fstream>
#include <fstream>
#include <variant>


namespace clip {
namespace clip {


ReturnCode geojson_read_object(std::istream* input);
struct GeoJSONCoord {
ReturnCode geojson_read_objects(std::istream* input);
  double value;
  int rlevel;
};

ReturnCode geojson_read_object(const GeoJSONReader& reader, std::istream* input);
ReturnCode geojson_read_objects(const GeoJSONReader& reader, std::istream* input);

ReturnCode geojson_read_coords(
    std::istream* input,
    std::vector<GeoJSONCoord>* coords) {
  if (auto rc = json_read_array_begin(input); !rc) {
    return rc;
  }

  for (int level = 0, rlevel = 0; level >= 0; ) {
    TokenType token;
    std::string token_data;
    if (auto rc = json_parse(input, &token, &token_data); !rc) {
      return rc;
    }

    switch (token) {
      case JSON_NUMBER: {
        double value;
        try {
          value = std::stod(token_data);
        } catch (...) {
          return errorf(ERROR, "invalid number: '{}'", token_data);
        }

        coords->emplace_back(GeoJSONCoord {
          value: value,
          rlevel: rlevel
        });

        rlevel = level;
        break;
      }

      case JSON_ARRAY_BEGIN:
        ++level;
        continue;

      case JSON_ARRAY_END:
        rlevel = --level;
        continue;

      default:
        return {ERROR, "coordinates must be (nested) arrays of numbers"};
    }
  }

  return OK;
}

ReturnCode geojson_read_polygon(
    const GeoJSONReader& reader,
    const std::vector<GeoJSONCoord>& coords) {
  std::vector<PolyLine3> rings;
  for (size_t i = 0; i < coords.size(); i += 2) {
    if (coords[i].rlevel == 0) {
      rings.emplace_back();
    }

    if (i + 2 > coords.size() || coords[i + 1].rlevel != 2 || rings.empty()) {
      return {ERROR, "invalid coordinate format for 'Polygon' objects"};
    }

    if (i + 2 < coords.size() && coords[i + 2].rlevel == 2) {
      rings.back().vertices.emplace_back(
          coords[i + 0].value,
          coords[i + 2].value,
          coords[i + 2].value);
    } else {
      rings.back().vertices.emplace_back(
          coords[i + 0].value,
          coords[i + 1].value,
          0);
    }
  }

  if (rings.empty()) {
    return {ERROR, "invalid coordinate format for 'Polygon' objects"};
  }

  Poly3 poly;
  poly.boundary = rings[0];
  if (rings.size() > 1) {
    poly.holes = std::vector<PolyLine3>(rings.begin() + 1, rings.end());
  }

  if (reader.on_polygons) {
    if (auto rc = reader.on_polygons(&poly, 1); !rc) {
      return rc;
    }
  }

  return OK;
}

ReturnCode geojson_read_multi_polygon(
    const GeoJSONReader& reader,
    const std::vector<GeoJSONCoord>& coords) {
  std::vector<std::vector<PolyLine3>> rings;
  for (size_t i = 0; i < coords.size(); i += 2) {
    switch (coords[i].rlevel) {
      case 0:
        rings.emplace_back();
        /* fallthrough */
      case 1:
        rings.back().emplace_back();
        break;
    }

    if (i + 2 > coords.size() || coords[i + 1].rlevel != 3 || rings.empty()) {
      return {ERROR, "invalid coordinate format for 'MultiPolygon' objects"};
    }

    if (i + 2 < coords.size() && coords[i + 2].rlevel == 3) {
      rings.back().back().vertices.emplace_back(
          coords[i + 0].value,
          coords[i + 2].value,
          coords[i + 2].value);
    } else {
      rings.back().back().vertices.emplace_back(
          coords[i + 0].value,
          coords[i + 1].value,
          0);
    }
  }

  std::vector<Poly3> polys;
  for (const auto& r : rings) {
    if (r.empty()) {
      return {ERROR, "invalid coordinate format for 'MultiPolygon' objects"};
    }

    Poly3 poly;
    poly.boundary = r[0];
    if (r.size() > 1) {
      poly.holes = std::vector<PolyLine3>(r.begin() + 1, r.end());
    }

    polys.emplace_back(std::move(poly));
  }

  if (reader.on_polygons) {
    if (auto rc = reader.on_polygons(polys.data(), polys.size()); !rc) {
      return rc;
    }
  }

  return OK;
}


ReturnCode geojson_read_object_data(
ReturnCode geojson_read_object_data(
    const GeoJSONReader& reader,
    std::istream* input) {
    std::istream* input) {
  std::string obj_type;
  std::string type;
  std::vector<GeoJSONCoord> coords;


  for (TokenType token = JSON_OBJECT_BEGIN; token != JSON_OBJECT_END; ) {
  for (TokenType token = JSON_OBJECT_BEGIN; token != JSON_OBJECT_END; ) {
    std::string token_data;
    std::string token_data;
@@ -40,7 +196,7 @@ ReturnCode geojson_read_object_data(
    }
    }


    if (token_data == "type") {
    if (token_data == "type") {
      if (auto rc = json_read_string(input, &obj_type); !rc) {
      if (auto rc = json_read_string(input, &type); !rc) {
        return rc;
        return rc;
      }
      }


@@ -48,7 +204,7 @@ ReturnCode geojson_read_object_data(
    }
    }


    if (token_data == "features") {
    if (token_data == "features") {
      if (auto rc = geojson_read_objects(input); !rc) {
      if (auto rc = geojson_read_objects(reader, input); !rc) {
        return rc;
        return rc;
      }
      }


@@ -56,7 +212,15 @@ ReturnCode geojson_read_object_data(
    }
    }


    if (token_data == "geometry") {
    if (token_data == "geometry") {
      if (auto rc = geojson_read_object(input); !rc) {
      if (auto rc = geojson_read_object(reader, input); !rc) {
        return rc;
      }

      continue;
    }

    if (token_data == "coordinates") {
      if (auto rc = geojson_read_coords(input, &coords); !rc) {
        return rc;
        return rc;
      }
      }


@@ -66,11 +230,25 @@ ReturnCode geojson_read_object_data(
    json_skip(input);
    json_skip(input);
  }
  }


  std::cerr << "read obj: " << obj_type << std::endl;
  if (type == "Feature" ||
      type == "FeatureCollection" ||
      type == "GeometryCollection") {
    return OK;
    return OK;
  }
  }


  if (type == "Polygon") {
    return geojson_read_polygon(reader, coords);
  }

  if (type == "MultiPolygon") {
    return geojson_read_multi_polygon(reader, coords);
  }

  return errorf(ERROR, "invalid object type: {}", type);
}

ReturnCode geojson_read_objects(
ReturnCode geojson_read_objects(
    const GeoJSONReader& reader,
    std::istream* input) {
    std::istream* input) {
  if (auto rc = json_read_array_begin(input); !rc) {
  if (auto rc = json_read_array_begin(input); !rc) {
    return rc;
    return rc;
@@ -84,7 +262,7 @@ ReturnCode geojson_read_objects(


    switch (token) {
    switch (token) {
      case JSON_OBJECT_BEGIN:
      case JSON_OBJECT_BEGIN:
        if (auto rc = geojson_read_object_data(input); !rc) {
        if (auto rc = geojson_read_object_data(reader, input); !rc) {
          return rc;
          return rc;
        }
        }


@@ -100,26 +278,28 @@ ReturnCode geojson_read_objects(
}
}


ReturnCode geojson_read_object(
ReturnCode geojson_read_object(
    const GeoJSONReader& reader,
    std::istream* input) {
    std::istream* input) {
  if (auto rc = json_read_object_begin(input); !rc) {
  if (auto rc = json_read_object_begin(input); !rc) {
    return rc;
    return rc;
  }
  }


  if (auto rc = geojson_read_object_data(input); !rc) {
  if (auto rc = geojson_read_object_data(reader, input); !rc) {
    return rc;
    return rc;
  }
  }


  return OK;
  return OK;
}
}


ReturnCode geojson_parse_file(
ReturnCode geojson_read_file(
    const std::string& path) {
    const std::string& path,
    const GeoJSONReader& reader) {
  std::ifstream input(path, std::ios::binary);
  std::ifstream input(path, std::ios::binary);
  if (!input) {
  if (!input) {
    return errorf(ERROR, "unable to open file '{}': {}", path, std::strerror(errno));
    return errorf(ERROR, "unable to open file '{}': {}", path, std::strerror(errno));
  }
  }


  return geojson_read_object(&input);
  return geojson_read_object(reader, &input);
}
}


} // namespace clip
} // namespace clip
Loading