File WktReader.cpp
File List > detail > io > WktReader.cpp
Go to the documentation of this file
// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2022, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "SFCGAL/detail/io/WktReader.h"
#include <memory>
#include "SFCGAL/GeometryCollection.h"
#include "SFCGAL/LineString.h"
#include "SFCGAL/MultiLineString.h"
#include "SFCGAL/MultiPoint.h"
#include "SFCGAL/MultiPolygon.h"
#include "SFCGAL/MultiSolid.h"
#include "SFCGAL/Point.h"
#include "SFCGAL/Polygon.h"
#include "SFCGAL/PolyhedralSurface.h"
#include "SFCGAL/Solid.h"
#include "SFCGAL/Triangle.h"
#include "SFCGAL/TriangulatedSurface.h"
#include "SFCGAL/Exception.h"
namespace SFCGAL::detail::io {
WktReader::WktReader(std::istream &s) : _reader(s) {}
auto
WktReader::readSRID() -> srid_t
{
srid_t srid = 0;
if (_reader.imatch("SRID=")) {
_reader.read(srid);
if (!_reader.match(";")) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
return srid;
}
auto
WktReader::readGeometry() -> Geometry *
{
GeometryType const geometryType = readGeometryType();
_is3D = _reader.imatch("Z");
_isMeasured = _reader.imatch("M");
switch (geometryType) {
case TYPE_POINT: {
std::unique_ptr<Point> g(new Point());
readInnerPoint(*g);
return g.release();
}
case TYPE_LINESTRING: {
std::unique_ptr<LineString> g(new LineString());
readInnerLineString(*g);
return g.release();
}
case TYPE_TRIANGLE: {
std::unique_ptr<Triangle> g(new Triangle());
readInnerTriangle(*g);
return g.release();
}
case TYPE_POLYGON: {
std::unique_ptr<Polygon> g(new Polygon());
readInnerPolygon(*g);
return g.release();
}
case TYPE_MULTIPOINT: {
std::unique_ptr<MultiPoint> g(new MultiPoint());
readInnerMultiPoint(*g);
return g.release();
}
case TYPE_MULTILINESTRING: {
std::unique_ptr<MultiLineString> g(new MultiLineString());
readInnerMultiLineString(*g);
return g.release();
}
case TYPE_MULTIPOLYGON: {
std::unique_ptr<MultiPolygon> g(new MultiPolygon());
readInnerMultiPolygon(*g);
return g.release();
}
case TYPE_GEOMETRYCOLLECTION: {
std::unique_ptr<GeometryCollection> g(new GeometryCollection());
readInnerGeometryCollection(*g);
return g.release();
}
case TYPE_TRIANGULATEDSURFACE: {
std::unique_ptr<TriangulatedSurface> g(new TriangulatedSurface());
readInnerTriangulatedSurface(*g);
return g.release();
}
case TYPE_POLYHEDRALSURFACE: {
std::unique_ptr<PolyhedralSurface> g(new PolyhedralSurface());
readInnerPolyhedralSurface(*g);
return g.release();
}
case TYPE_SOLID: {
std::unique_ptr<Solid> g(new Solid());
readInnerSolid(*g);
return g.release();
}
case TYPE_MULTISOLID: {
std::unique_ptr<MultiSolid> g(new MultiSolid());
readInnerMultiSolid(*g);
return g.release();
}
}
BOOST_THROW_EXCEPTION(WktParseException("unexpected geometry"));
}
auto
WktReader::readGeometryType() -> GeometryType
{
if (_reader.imatch("POINT")) {
return TYPE_POINT;
}
if (_reader.imatch("LINESTRING")) {
return TYPE_LINESTRING;
}
if (_reader.imatch("POLYGON")) {
return TYPE_POLYGON;
}
if (_reader.imatch("TRIANGLE")) {
// not official
return TYPE_TRIANGLE;
}
if (_reader.imatch("MULTIPOINT")) {
return TYPE_MULTIPOINT;
}
if (_reader.imatch("MULTILINESTRING")) {
return TYPE_MULTILINESTRING;
}
if (_reader.imatch("MULTIPOLYGON")) {
return TYPE_MULTIPOLYGON;
}
if (_reader.imatch("GEOMETRYCOLLECTION")) {
return TYPE_GEOMETRYCOLLECTION;
}
if (_reader.imatch("TIN")) {
return TYPE_TRIANGULATEDSURFACE;
}
if (_reader.imatch("POLYHEDRALSURFACE")) {
return TYPE_POLYHEDRALSURFACE;
}
if (_reader.imatch("SOLID")) {
// not official
return TYPE_SOLID;
}
if (_reader.imatch("MULTISOLID")) {
// not official
return TYPE_MULTISOLID;
}
std::ostringstream oss;
oss << "can't parse WKT geometry type (" << _reader.context() << ")";
BOOST_THROW_EXCEPTION(WktParseException(oss.str()));
}
void
WktReader::readInnerPoint(Point &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
readPointCoordinate(g);
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerLineString(LineString &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Point> p(new Point());
if (readPointCoordinate(*p)) {
g.addPoint(p.release());
} else {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
if (_reader.match(',')) {
continue;
}
break;
}
if (g.numPoints() < 2U) {
BOOST_THROW_EXCEPTION(WktParseException(
"WKT parse error, LineString should have at least 2 points"));
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerPolygon(Polygon &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
for (int i = 0; !_reader.eof(); i++) {
if (i == 0) {
readInnerLineString(g.exteriorRing());
} else {
std::unique_ptr<LineString> interiorRing(new LineString);
readInnerLineString(*interiorRing);
g.addRing(interiorRing.release());
}
// break if not followed by another ring
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerTriangle(Triangle &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
// 4 points to read
std::vector<Point> points;
while (!_reader.eof()) {
points.emplace_back();
readPointCoordinate(points.back());
if (!_reader.match(",")) {
break;
}
}
if (points.size() != 4) {
BOOST_THROW_EXCEPTION(
WktParseException("WKT parse error, expected 4 points for triangle"));
}
if (points.back() != points.front()) {
BOOST_THROW_EXCEPTION(
WktParseException("WKT parse error, first point different of the last "
"point for triangle"));
}
g = Triangle(points[0], points[1], points[2]);
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerMultiPoint(MultiPoint &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Point> p(new Point());
if (!_reader.imatch("EMPTY")) {
// optional open/close parenthesis
bool parenthesisOpen = false;
if (_reader.match('(')) {
parenthesisOpen = true;
}
readPointCoordinate(*p);
if (parenthesisOpen && !_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
if (!p->isEmpty()) {
g.addGeometry(p.release());
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerMultiLineString(MultiLineString &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<LineString> lineString(new LineString());
readInnerLineString(*lineString);
if (!lineString->isEmpty()) {
g.addGeometry(lineString.release());
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerMultiPolygon(MultiPolygon &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Polygon> polygon(new Polygon());
readInnerPolygon(*polygon);
if (!polygon->isEmpty()) {
g.addGeometry(polygon.release());
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerGeometryCollection(GeometryCollection &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
// read a full wkt geometry ex : POINT (2.0 6.0)
Geometry *gg = readGeometry();
if (!gg->isEmpty()) {
g.addGeometry(gg);
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerTriangulatedSurface(TriangulatedSurface &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Triangle> triangle(new Triangle());
readInnerTriangle(*triangle);
g.addTriangle(triangle.release());
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerPolyhedralSurface(PolyhedralSurface &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Polygon> polygon(new Polygon());
readInnerPolygon(*polygon);
g.addPolygon(polygon.release());
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerSolid(Solid &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
// solid begin
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
for (int i = 0; !_reader.eof(); i++) {
if (i == 0) {
readInnerPolyhedralSurface(g.exteriorShell());
} else {
std::unique_ptr<PolyhedralSurface> shell(new PolyhedralSurface);
readInnerPolyhedralSurface(*shell);
g.addInteriorShell(shell.release());
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
// solid end
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
void
WktReader::readInnerMultiSolid(MultiSolid &g)
{
if (_reader.imatch("EMPTY")) {
return;
}
if (!_reader.match('(')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
while (!_reader.eof()) {
std::unique_ptr<Solid> solid(new Solid());
readInnerSolid(*solid);
if (!solid->isEmpty()) {
g.addGeometry(solid.release());
}
// break if not followed by another points
if (!_reader.match(',')) {
break;
}
}
if (!_reader.match(')')) {
BOOST_THROW_EXCEPTION(WktParseException(parseErrorMessage()));
}
}
auto
WktReader::readPointCoordinate(Point &p) -> bool
{
std::vector<Kernel::Exact_kernel::FT> coordinates;
Kernel::Exact_kernel::FT d;
if (_reader.imatch("EMPTY")) {
p = Point();
return false;
}
while (_reader.read(d)) {
coordinates.push_back(d);
}
if (coordinates.size() < 2) {
BOOST_THROW_EXCEPTION(WktParseException(
(boost::format("WKT parse error, Coordinate dimension < 2 (%s)") %
_reader.context())
.str()));
}
if (coordinates.size() > 4) {
BOOST_THROW_EXCEPTION(
WktParseException("WKT parse error, Coordinate dimension > 4"));
}
if (_isMeasured && _is3D) {
// XYZM
if (coordinates.size() != 4) {
BOOST_THROW_EXCEPTION(WktParseException("bad coordinate dimension"));
}
p = Point(coordinates[0], coordinates[1], coordinates[2]);
p.setM(CGAL::to_double(coordinates[3]));
} else if (_isMeasured && !_is3D) {
// XYM
if (coordinates.size() != 3) {
BOOST_THROW_EXCEPTION(WktParseException(
"bad coordinate dimension (expecting XYM coordinates)"));
}
p = Point(coordinates[0], coordinates[1]);
p.setM(CGAL::to_double(coordinates[2]));
} else if (coordinates.size() == 3) {
// XYZ
p = Point(coordinates[0], coordinates[1], coordinates[2]);
} else {
// XY
p = Point(coordinates[0], coordinates[1]);
}
return true;
}
auto
WktReader::parseErrorMessage() -> std::string
{
std::ostringstream oss;
oss << "WKT parse error (" << _reader.context() << ")";
return oss.str();
}
} // namespace SFCGAL::detail::io