mapbox / geometry.hpp

C++ geometry types
ISC License
92 stars 37 forks source link

Add I/O operators #56

Closed kkaefer closed 3 years ago

kkaefer commented 6 years ago

It'd be cool to have I/O operators to write features/geometry objects to an STL output stream.

kkaefer commented 6 years ago

For reference, these are the operators I've been using:


template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::point<T>& point) {
    return os << "[" << point.x << "," << point.y << "]";
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::line_string<T>& line_string) {
    os << "[";
    bool first = true;
    for (const auto& point : line_string) {
        if (first) first = false; else os << ",";
        os << point;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::linear_ring<T>& linear_ring) {
    os << "[";
    bool first = true;
    for (const auto& point : linear_ring) {
        if (first) first = false; else os << ",";
        os << point;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::polygon<T>& polygon) {
    os << "[";
    bool first = true;
    for (const auto& ring : polygon) {
        if (first) first = false; else os << ",";
        os << ring;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::multi_point<T>& multi_point) {
    os << "[";
    bool first = true;
    for (const auto& point : multi_point) {
        if (first) first = false; else os << ",";
        os << point;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::multi_line_string<T>& multi_line_string) {
    os << "[";
    bool first = true;
    for (const auto& line_string : multi_line_string) {
        if (first) first = false; else os << ",";
        os << line_string;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::multi_polygon<T>& multi_polygon) {
    os << "[";
    bool first = true;
    for (const auto& polygon : multi_polygon) {
        if (first) first = false; else os << ",";
        os << polygon;
    }
    os << "]";
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream&, const mapbox::geometry::geometry_collection<T>&);

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::geometry<T>& geometry) {    
    geometry.match(
        [&os] (const mapbox::geometry::point<T>& point) {
            os << "Point" << point;
        },
        [&os] (const mapbox::geometry::line_string<T>& line_string) {
            os << "LineString" << line_string;
        },
        [&os] (const mapbox::geometry::polygon<T>& polygon) {
            os << "Polygon" << polygon;
        },
        [&os] (const mapbox::geometry::multi_point<T>& multi_point) {
            os << "MultiPoint" << multi_point;
        },
        [&os] (const mapbox::geometry::multi_line_string<T>& multi_line_string) {
            os << "MultiLineString" << multi_line_string;
        },
        [&os] (const mapbox::geometry::multi_polygon<T>& multi_polygon) {
            os << "MultiPolygon" << multi_polygon;
        },
        [&os] (const mapbox::geometry::geometry_collection<T>& geometry_collection) {
            os << "GeometryCollection" << geometry_collection;
        }
    );
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::geometry_collection<T>& geometry_collection) {
    os << "[";
    bool first = true;
    for (const auto& geometry : geometry_collection) {
        if (first) first = false; else os << ",";
        os << geometry;
    }
    os << "]";
    return os;
}

std::ostream& operator<<(std::ostream& os, const mapbox::geometry::null_value_t&) {
    return os << "<null>";
}

std::ostream& operator<<(std::ostream& os, const mapbox::geometry::value&);

std::ostream& operator<<(std::ostream& os, const mapbox::geometry::property_map& map) {
    for (const auto& kv : map) {
        os << "  - " << kv.first << ":" << kv.second << std::endl;
    }
    return os;
}

std::ostream& operator<<(std::ostream& os, const mapbox::geometry::value& value) {
    value.match(
        [&os] (const std::vector<mapbox::geometry::value>& array) {
            os << "[";
            bool first = true;
            for (const auto& v : array) {
                if (first) first = false; else os << ",";
                os << v;
            }
            os << "]";
        },
        [&os] (const mapbox::geometry::property_map& map) {
            for (const auto& kv : map) {
                os << "  - " << kv.first << ":" << kv.second << std::endl;
            }
        },
        [&os] (const auto& other) {
            os << other;
        }
    );
    return os;
}

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::feature<T>& feature) {
    os << "Feature";
    if (feature.id) {
        os << " (ID: ";
        feature.id->match([&os] (const auto& id) { os << id; });
        os << ")";
    }
    os << std::endl;
    os << feature.properties;
    os << feature.geometry;
    os << std::endl;
    return os;
}
springmeyer commented 6 years ago

👍 I think this would be quite useful. Ideally we'd add this in a separate header so that the functionality is zero cost.

SymbolixAU commented 5 years ago

I know this is implied, but the feature_collection stream would be

template <class T>
std::ostream& operator<<(std::ostream& os, const mapbox::geometry::feature_collection<T>& feature_collection) {
  os << "FeatureCollection" << std::endl;
  for (const auto& feature : feature_collection) {
    os << feature;
  }
  return os;
}
kkaefer commented 3 years ago

Fixed as part of https://github.com/mapbox/geometry.hpp/pull/60