2019-01-25 22:18:21 +01:00
|
|
|
#include <QJsonDocument>
|
|
|
|
#include <QJsonArray>
|
|
|
|
#include "geojsonparser.h"
|
|
|
|
|
|
|
|
|
2019-01-31 01:46:53 +01:00
|
|
|
GeoJSONParser::Type GeoJSONParser::type(const QJsonObject &json)
|
2019-01-25 22:18:21 +01:00
|
|
|
{
|
|
|
|
QString str(json["type"].toString());
|
|
|
|
|
|
|
|
if (str == "Point")
|
|
|
|
return Point;
|
|
|
|
else if (str == "MultiPoint")
|
|
|
|
return MultiPoint;
|
|
|
|
else if (str == "LineString")
|
|
|
|
return LineString;
|
|
|
|
else if (str == "MultiLineString")
|
|
|
|
return MultiLineString;
|
|
|
|
else if (str == "Polygon")
|
|
|
|
return Polygon;
|
|
|
|
else if (str == "MultiPolygon")
|
|
|
|
return MultiPolygon;
|
|
|
|
else if (str == "GeometryCollection")
|
|
|
|
return GeometryCollection;
|
|
|
|
else if (str == "Feature")
|
|
|
|
return Feature;
|
|
|
|
else if (str == "FeatureCollection")
|
|
|
|
return FeatureCollection;
|
|
|
|
else
|
|
|
|
return Unknown;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::point(const QJsonArray &coordinates, Waypoint &waypoint,
|
|
|
|
const QJsonObject &properties)
|
|
|
|
{
|
|
|
|
if (coordinates.count() < 2 || !coordinates.at(0).isDouble()
|
|
|
|
|| !coordinates.at(1).isDouble()) {
|
|
|
|
_errorString = "Invalid Point Coordinates";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
waypoint.setCoordinates(Coordinates(coordinates.at(0).toDouble(),
|
|
|
|
coordinates.at(1).toDouble()));
|
|
|
|
if (coordinates.count() == 3 && coordinates.at(2).isDouble())
|
|
|
|
waypoint.setElevation(coordinates.at(2).toDouble());
|
|
|
|
if (properties.contains("title") && properties["title"].isString())
|
|
|
|
waypoint.setName(properties["title"].toString());
|
2019-01-31 01:46:53 +01:00
|
|
|
if (properties.contains("name") && properties["name"].isString())
|
|
|
|
waypoint.setName(properties["name"].toString());
|
2019-01-25 22:18:21 +01:00
|
|
|
if (properties.contains("description")
|
|
|
|
&& properties["description"].isString())
|
|
|
|
waypoint.setDescription(properties["description"].toString());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::multiPoint(const QJsonArray &coordinates,
|
|
|
|
QVector<Waypoint> &waypoints, const QJsonObject &properties)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < coordinates.size(); i++) {
|
|
|
|
if (!coordinates.at(i).isArray()) {
|
2019-01-31 01:46:53 +01:00
|
|
|
_errorString = "Invalid MultiPoint coordinates";
|
2019-01-25 22:18:21 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
waypoints.resize(waypoints.size() + 1);
|
|
|
|
if (!point(coordinates.at(i).toArray(), waypoints.last(), properties))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track,
|
|
|
|
const QJsonObject &properties)
|
|
|
|
{
|
|
|
|
if (properties.contains("title") && properties["title"].isString())
|
|
|
|
track.setName(properties["title"].toString());
|
2019-01-31 01:46:53 +01:00
|
|
|
if (properties.contains("name") && properties["name"].isString())
|
|
|
|
track.setName(properties["name"].toString());
|
2019-01-25 22:18:21 +01:00
|
|
|
if (properties.contains("description")
|
|
|
|
&& properties["description"].isString())
|
|
|
|
track.setDescription(properties["description"].toString());
|
|
|
|
|
|
|
|
for (int i = 0; i < coordinates.size(); i++) {
|
|
|
|
QJsonArray point(coordinates.at(i).toArray());
|
|
|
|
if (point.count() < 2 || !point.at(0).isDouble()
|
|
|
|
|| !point.at(1).isDouble()) {
|
2019-01-31 01:46:53 +01:00
|
|
|
_errorString = "Invalid LineString coordinates";
|
2019-01-25 22:18:21 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
Trackpoint t(Coordinates(point.at(0).toDouble(),
|
|
|
|
point.at(1).toDouble()));
|
|
|
|
if (point.count() == 3 && point.at(2).isDouble())
|
|
|
|
t.setElevation(point.at(2).toDouble());
|
|
|
|
track.append(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::multiLineString(const QJsonArray &coordinates,
|
|
|
|
QList<TrackData> &tracks, const QJsonObject &properties)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < coordinates.size(); i++) {
|
|
|
|
if (!coordinates.at(i).isArray()) {
|
2019-01-31 01:46:53 +01:00
|
|
|
_errorString = "Invalid MultiLineString coordinates";
|
2019-01-25 22:18:21 +01:00
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
tracks.append(TrackData());
|
|
|
|
if (!lineString(coordinates.at(i).toArray(), tracks.last(),
|
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-01-31 01:46:53 +01:00
|
|
|
bool GeoJSONParser::polygon(const QJsonArray &coordinates, ::Polygon &pg)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < coordinates.size(); i++) {
|
|
|
|
if (!coordinates.at(i).isArray()) {
|
|
|
|
_errorString = "Invalid Polygon linear ring";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QJsonArray lr(coordinates.at(i).toArray());
|
|
|
|
pg.append(QVector<Coordinates>());
|
|
|
|
QVector<Coordinates> &data = pg.last();
|
|
|
|
|
|
|
|
for (int j = 0; j < lr.size(); j++) {
|
|
|
|
QJsonArray point(lr.at(j).toArray());
|
|
|
|
if (point.count() < 2 || !point.at(0).isDouble()
|
|
|
|
|| !point.at(1).isDouble()) {
|
|
|
|
_errorString = "Invalid Polygon linear ring coordinates";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
data.append(Coordinates(point.at(0).toDouble(),
|
|
|
|
point.at(1).toDouble()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::polygon(const QJsonArray &coordinates, Area &area,
|
2019-01-25 22:18:21 +01:00
|
|
|
const QJsonObject &properties)
|
2019-01-31 01:46:53 +01:00
|
|
|
{
|
|
|
|
if (properties.contains("title") && properties["title"].isString())
|
|
|
|
area.setName(properties["title"].toString());
|
|
|
|
if (properties.contains("name") && properties["name"].isString())
|
|
|
|
area.setName(properties["name"].toString());
|
|
|
|
if (properties.contains("description")
|
|
|
|
&& properties["description"].isString())
|
|
|
|
area.setDescription(properties["description"].toString());
|
|
|
|
|
|
|
|
area.append(::Polygon());
|
|
|
|
return polygon(coordinates, area.last());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::multiPolygon(const QJsonArray &coordinates,
|
|
|
|
Area &area, const QJsonObject &properties)
|
|
|
|
{
|
|
|
|
if (properties.contains("title") && properties["title"].isString())
|
|
|
|
area.setName(properties["title"].toString());
|
|
|
|
if (properties.contains("name") && properties["name"].isString())
|
|
|
|
area.setName(properties["name"].toString());
|
|
|
|
if (properties.contains("description")
|
|
|
|
&& properties["description"].isString())
|
|
|
|
area.setDescription(properties["description"].toString());
|
|
|
|
|
|
|
|
for (int i = 0; i < coordinates.size(); i++) {
|
|
|
|
if (!coordinates.at(i).isArray()) {
|
|
|
|
_errorString = "Invalid MultiPolygon coordinates";
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
area.append(::Polygon());
|
|
|
|
if (!polygon(coordinates.at(i).toArray(), area.last()))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::geometryCollection(const QJsonObject &json,
|
|
|
|
QList<TrackData> &tracks, QList<Area> &areas,
|
|
|
|
QVector<Waypoint> &waypoints, const QJsonObject &properties)
|
2019-01-25 22:18:21 +01:00
|
|
|
{
|
|
|
|
if (!json.contains("geometries") || !json["geometries"].isArray()) {
|
|
|
|
_errorString = "Invalid/missing GeometryCollection geometries array";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QJsonArray geometries(json["geometries"].toArray());
|
|
|
|
for (int i = 0; i < geometries.size(); i++) {
|
|
|
|
QJsonObject geometry(geometries.at(i).toObject());
|
|
|
|
switch (type(geometry)) {
|
|
|
|
case Point:
|
|
|
|
waypoints.resize(waypoints.size() + 1);
|
|
|
|
if (!point(geometry["coordinates"].toArray(), waypoints.last(),
|
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case MultiPoint:
|
|
|
|
if (!multiPoint(geometry["coordinates"].toArray(), waypoints,
|
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case LineString:
|
|
|
|
tracks.append(TrackData());
|
|
|
|
if (!lineString(geometry["coordinates"].toArray(),
|
|
|
|
tracks.last(), properties))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case MultiLineString:
|
|
|
|
if (!multiLineString(geometry["coordinates"].toArray(), tracks,
|
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case Polygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
if (!polygon(geometry["coordinates"].toArray(), areas.last(),
|
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
break;
|
2019-01-25 22:18:21 +01:00
|
|
|
case MultiPolygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
if (!multiPolygon(geometry["coordinates"].toArray(),
|
|
|
|
areas.last(), properties))
|
|
|
|
return false;
|
2019-01-25 22:18:21 +01:00
|
|
|
break;
|
|
|
|
case GeometryCollection:
|
2019-01-31 01:46:53 +01:00
|
|
|
if (!geometryCollection(geometry, tracks, areas, waypoints,
|
2019-01-25 22:18:21 +01:00
|
|
|
properties))
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
_errorString = geometry["type"].toString()
|
|
|
|
+ ": invalid/missing geometry type";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::feature(const QJsonObject &json, QList<TrackData> &tracks,
|
2019-01-31 01:46:53 +01:00
|
|
|
QList<Area> &areas, QVector<Waypoint> &waypoints)
|
2019-01-25 22:18:21 +01:00
|
|
|
{
|
|
|
|
QJsonObject properties(json["properties"].toObject());
|
|
|
|
QJsonObject geometry(json["geometry"].toObject());
|
|
|
|
|
|
|
|
switch (type(geometry)) {
|
|
|
|
case Point:
|
|
|
|
waypoints.resize(waypoints.size() + 1);
|
|
|
|
return point(geometry["coordinates"].toArray(), waypoints.last(),
|
|
|
|
properties);
|
|
|
|
case MultiPoint:
|
|
|
|
return multiPoint(geometry["coordinates"].toArray(), waypoints,
|
|
|
|
properties);
|
|
|
|
case LineString:
|
|
|
|
tracks.append(TrackData());
|
|
|
|
return lineString(geometry["coordinates"].toArray(), tracks.last(),
|
|
|
|
properties);
|
|
|
|
case MultiLineString:
|
|
|
|
return multiLineString(geometry["coordinates"].toArray(), tracks,
|
|
|
|
properties);
|
|
|
|
case GeometryCollection:
|
2019-01-31 01:46:53 +01:00
|
|
|
return geometryCollection(geometry, tracks, areas, waypoints);
|
2019-01-25 22:18:21 +01:00
|
|
|
case Polygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
return polygon(geometry["coordinates"].toArray(), areas.last(),
|
|
|
|
properties);
|
2019-01-25 22:18:21 +01:00
|
|
|
case MultiPolygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
return multiPolygon(geometry["coordinates"].toArray(), areas.last(),
|
|
|
|
properties);
|
2019-01-25 22:18:21 +01:00
|
|
|
default:
|
|
|
|
_errorString = geometry["type"].toString()
|
|
|
|
+ ": invalid/missing Feature geometry";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GeoJSONParser::featureCollection(const QJsonObject &json,
|
2019-01-31 01:46:53 +01:00
|
|
|
QList<TrackData> &tracks, QList<Area> &areas,
|
|
|
|
QVector<Waypoint> &waypoints)
|
2019-01-25 22:18:21 +01:00
|
|
|
{
|
|
|
|
if (!json.contains("features") || !json["features"].isArray()) {
|
|
|
|
_errorString = "Invalid/missing FeatureCollection features array";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QJsonArray features(json["features"].toArray());
|
|
|
|
for (int i = 0; i < features.size(); i++)
|
2019-01-31 01:46:53 +01:00
|
|
|
if (!feature(features.at(i).toObject(), tracks, areas, waypoints))
|
2019-01-25 22:18:21 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool GeoJSONParser::parse(QFile *file, QList<TrackData> &tracks,
|
2019-01-31 01:46:53 +01:00
|
|
|
QList<RouteData> &routes, QList<Area> &areas, QVector<Waypoint> &waypoints)
|
2019-01-25 22:18:21 +01:00
|
|
|
{
|
|
|
|
Q_UNUSED(routes);
|
|
|
|
QJsonParseError error;
|
|
|
|
QJsonDocument doc(QJsonDocument::fromJson(file->readAll(), &error));
|
|
|
|
|
|
|
|
if (doc.isNull()) {
|
|
|
|
_errorString = "JSON parse error: " + error.errorString() + " ["
|
|
|
|
+ QString::number(error.offset) + "]";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QJsonObject json(doc.object());
|
|
|
|
|
|
|
|
switch (type(json)) {
|
|
|
|
case Point:
|
|
|
|
waypoints.resize(waypoints.size() + 1);
|
|
|
|
return point(json["coordinates"].toArray(), waypoints.last());
|
|
|
|
case MultiPoint:
|
|
|
|
return multiPoint(json["coordinates"].toArray(), waypoints);
|
|
|
|
case LineString:
|
|
|
|
tracks.append(TrackData());
|
|
|
|
return lineString(json["coordinates"].toArray(), tracks.last());
|
|
|
|
case MultiLineString:
|
|
|
|
return multiLineString(json["coordinates"].toArray(), tracks);
|
|
|
|
case GeometryCollection:
|
2019-01-31 01:46:53 +01:00
|
|
|
return geometryCollection(json, tracks, areas, waypoints);
|
2019-01-25 22:18:21 +01:00
|
|
|
case Feature:
|
2019-01-31 01:46:53 +01:00
|
|
|
return feature(json, tracks, areas, waypoints);
|
2019-01-25 22:18:21 +01:00
|
|
|
case FeatureCollection:
|
2019-01-31 01:46:53 +01:00
|
|
|
return featureCollection(json, tracks, areas, waypoints);
|
2019-01-25 22:18:21 +01:00
|
|
|
case Polygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
return polygon(json["coordinates"].toArray(), areas.last());
|
2019-01-25 22:18:21 +01:00
|
|
|
case MultiPolygon:
|
2019-01-31 01:46:53 +01:00
|
|
|
areas.append(Area());
|
|
|
|
return multiPolygon(json["coordinates"].toArray(), areas.last());
|
2019-01-25 22:18:21 +01:00
|
|
|
case Unknown:
|
|
|
|
if (json["type"].toString().isNull())
|
|
|
|
_errorString = "Not a GeoJSON file";
|
|
|
|
else
|
|
|
|
_errorString = json["type"].toString()
|
|
|
|
+ ": unknown GeoJSON object";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|