From 50306ecb84c55e0c2a5a8387526400fb8ab42aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Thu, 31 Jan 2019 01:46:53 +0100 Subject: [PATCH] Added support for polygon objects --- gpxsee.pro | 9 +- src/GUI/areaitem.cpp | 155 +++++++++++++++++++++++++++++++++++ src/GUI/areaitem.h | 46 +++++++++++ src/GUI/cadencegraph.cpp | 10 ++- src/GUI/elevationgraph.cpp | 8 +- src/GUI/gearratiograph.cpp | 7 +- src/GUI/gui.cpp | 57 +++++++++++-- src/GUI/gui.h | 11 +-- src/GUI/heartrategraph.cpp | 10 ++- src/GUI/mapview.cpp | 104 +++++++++++++++++++---- src/GUI/mapview.h | 40 ++++----- src/GUI/optionsdialog.cpp | 58 ++++++++----- src/GUI/optionsdialog.h | 6 ++ src/GUI/pathitem.cpp | 3 +- src/GUI/percentslider.cpp | 1 + src/GUI/powergraph.cpp | 10 ++- src/GUI/settings.h | 8 ++ src/GUI/speedgraph.cpp | 15 ++-- src/GUI/temperaturegraph.cpp | 10 ++- src/GUI/tooltip.cpp | 3 + src/data/area.h | 39 +++++++++ src/data/csvparser.cpp | 4 +- src/data/csvparser.h | 2 +- src/data/data.cpp | 31 ++++--- src/data/data.h | 17 ++-- src/data/fitparser.cpp | 4 +- src/data/fitparser.h | 2 +- src/data/geojsonparser.cpp | 136 +++++++++++++++++++++++------- src/data/geojsonparser.h | 28 ++++++- src/data/gpxparser.cpp | 5 +- src/data/gpxparser.h | 4 +- src/data/graph.h | 10 ++- src/data/igcparser.cpp | 4 +- src/data/igcparser.h | 4 +- src/data/kmlparser.cpp | 149 +++++++++++++++++++++++++++++---- src/data/kmlparser.h | 25 ++++-- src/data/locparser.cpp | 4 +- src/data/locparser.h | 4 +- src/data/nmeaparser.cpp | 4 +- src/data/nmeaparser.h | 4 +- src/data/oziparsers.cpp | 12 ++- src/data/oziparsers.h | 6 +- src/data/parser.h | 4 +- src/data/poi.cpp | 28 +++++++ src/data/poi.h | 5 +- src/data/polygon.cpp | 15 ++++ src/data/polygon.h | 17 ++++ src/data/route.h | 4 +- src/data/slfparser.cpp | 4 +- src/data/slfparser.h | 4 +- src/data/tcxparser.cpp | 4 +- src/data/tcxparser.h | 4 +- src/data/track.h | 4 +- 53 files changed, 949 insertions(+), 213 deletions(-) create mode 100644 src/GUI/areaitem.cpp create mode 100644 src/GUI/areaitem.h create mode 100644 src/data/area.h create mode 100644 src/data/polygon.cpp create mode 100644 src/data/polygon.h diff --git a/gpxsee.pro b/gpxsee.pro index cb0f7dbb..6e0e612f 100644 --- a/gpxsee.pro +++ b/gpxsee.pro @@ -153,7 +153,10 @@ HEADERS += src/common/config.h \ src/data/locparser.h \ src/data/slfparser.h \ src/data/dem.h \ - src/map/polarstereographic.h + src/map/polarstereographic.h \ + src/data/polygon.h \ + src/data/area.h \ + src/GUI/areaitem.h SOURCES += src/main.cpp \ src/common/coordinates.cpp \ src/common/rectc.cpp \ @@ -264,7 +267,9 @@ SOURCES += src/main.cpp \ src/data/slfparser.cpp \ src/data/dem.cpp \ src/map/polarstereographic.cpp \ - src/map/rectd.cpp + src/map/rectd.cpp \ + src/data/polygon.cpp \ + src/GUI/areaitem.cpp greaterThan(QT_MAJOR_VERSION, 4) { HEADERS += src/data/geojsonparser.h diff --git a/src/GUI/areaitem.cpp b/src/GUI/areaitem.cpp new file mode 100644 index 00000000..e53eb57e --- /dev/null +++ b/src/GUI/areaitem.cpp @@ -0,0 +1,155 @@ +#include +#include +#include +#include +#include "map/map.h" +#include "tooltip.h" +#include "areaitem.h" + + +QString AreaItem::toolTip() const +{ + ToolTip tt; + + if (!_area.name().isEmpty()) + tt.insert(qApp->translate("PolygonItem", "Name"), _area.name()); + if (!_area.description().isEmpty()) + tt.insert(qApp->translate("PolygonItem", "Description"), + _area.description()); + + return tt.toString(); +} + +AreaItem::AreaItem(const Area &area, Map *map, QGraphicsItem *parent) + : QGraphicsItem(parent), _area(area) +{ + _map = map; + _digitalZoom = 0; + + _width = 2; + _opacity = 0.5; + QBrush brush(Qt::SolidPattern); + _pen = QPen(brush, _width); + + updatePainterPath(); + + setCursor(Qt::ArrowCursor); + setAcceptHoverEvents(true); + + setToolTip(toolTip()); +} + + +QPainterPath AreaItem::painterPath(const Polygon &polygon) +{ + QPainterPath path; + + const QVector &lr = polygon.first(); + path.moveTo(_map->ll2xy(lr.first())); + for (int i = 1; i < lr.size(); i++) + path.lineTo(_map->ll2xy(lr.at(i))); + path.closeSubpath(); + + for (int i = 1; i < polygon.size(); i++) { + const QVector &lr = polygon.at(i); + QPainterPath hole; + hole.moveTo(_map->ll2xy(lr.first())); + for (int j = 1; j < lr.size(); j++) + hole.lineTo(_map->ll2xy(lr.at(j))); + hole.closeSubpath(); + path = path.subtracted(hole); + } + + return path; +} + +void AreaItem::updatePainterPath() +{ + _painterPath = QPainterPath(); + + for (int i = 0; i < _area.size(); i++) + _painterPath.addPath(painterPath(_area.at(i))); +} + +void AreaItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + painter->setPen(_pen); + painter->drawPath(_painterPath); + painter->fillPath(_painterPath, _brush); + +/* + QPen p = QPen(QBrush(Qt::red), 0); + painter->setPen(p); + painter->drawRect(boundingRect()); +*/ +} + +void AreaItem::setMap(Map *map) +{ + prepareGeometryChange(); + + _map = map; + + updatePainterPath(); +} + +void AreaItem::setColor(const QColor &color) +{ + if (_pen.color() == color) + return; + + QColor bc(color); + bc.setAlphaF(_opacity * color.alphaF()); + + _pen.setColor(color); + _brush = QBrush(bc); + update(); +} + +void AreaItem::setOpacity(qreal opacity) +{ + if (_opacity == opacity) + return; + + _opacity = opacity; + QColor bc(_pen.color()); + bc.setAlphaF(_opacity * _pen.color().alphaF()); + _brush = QBrush(bc); + + update(); +} + +void AreaItem::setWidth(qreal width) +{ + if (_width == width) + return; + + prepareGeometryChange(); + + _width = width; + _pen.setWidthF(_width * pow(2, -_digitalZoom)); +} + +void AreaItem::setStyle(Qt::PenStyle style) +{ + if (_pen.style() == style) + return; + + _pen.setStyle(style); + update(); +} + +void AreaItem::setDigitalZoom(int zoom) +{ + if (_digitalZoom == zoom) + return; + + prepareGeometryChange(); + + _digitalZoom = zoom; + _pen.setWidthF(_width * pow(2, -_digitalZoom)); +} diff --git a/src/GUI/areaitem.h b/src/GUI/areaitem.h new file mode 100644 index 00000000..9c1482aa --- /dev/null +++ b/src/GUI/areaitem.h @@ -0,0 +1,46 @@ +#ifndef AREAITEM_H +#define AREAITEM_H + +#include +#include "data/area.h" + +class Map; + +class AreaItem : public QGraphicsItem +{ +public: + AreaItem(const Area &area, Map *map, QGraphicsItem *parent = 0); + + QPainterPath shape() const {return _painterPath;} + QRectF boundingRect() const {return _painterPath.boundingRect();} + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, + QWidget *widget); + + const Area &area() const {return _area;} + + void setMap(Map *map); + + void setColor(const QColor &color); + void setOpacity(qreal opacity); + void setWidth(qreal width); + void setStyle(Qt::PenStyle style); + void setDigitalZoom(int zoom); + +private: + QPainterPath painterPath(const Polygon &polygon); + void updatePainterPath(); + QString toolTip() const; + + Area _area; + Map *_map; + int _digitalZoom; + + qreal _width; + QPen _pen; + QBrush _brush; + qreal _opacity; + + QPainterPath _painterPath; +}; + +#endif // AREAITEM_H diff --git a/src/GUI/cadencegraph.cpp b/src/GUI/cadencegraph.cpp index a1da7758..d5e5a915 100644 --- a/src/GUI/cadencegraph.cpp +++ b/src/GUI/cadencegraph.cpp @@ -32,15 +32,16 @@ QList CadenceGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Graph &graph = data.tracks().at(i)->cadence(); + const Track &track = data.tracks().at(i); + const Graph &graph = track.cadence(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { CadenceGraphItem *gi = new CadenceGraphItem(graph, _graphType); GraphView::addGraph(gi); - _avg.append(QPointF(data.tracks().at(i)->distance(), gi->avg())); + _avg.append(QPointF(track.distance(), gi->avg())); graphs.append(gi); } } @@ -50,6 +51,9 @@ QList CadenceGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/elevationgraph.cpp b/src/GUI/elevationgraph.cpp index 678d4059..daa70fa5 100644 --- a/src/GUI/elevationgraph.cpp +++ b/src/GUI/elevationgraph.cpp @@ -69,7 +69,7 @@ void ElevationGraph::setInfo() GraphItem *ElevationGraph::loadGraph(const Graph &graph, Type type) { - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); return 0; } @@ -97,9 +97,11 @@ QList ElevationGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) - graphs.append(loadGraph(data.tracks().at(i)->elevation(), Track)); + graphs.append(loadGraph(data.tracks().at(i).elevation(), Track)); for (int i = 0; i < data.routes().count(); i++) - graphs.append(loadGraph(data.routes().at(i)->elevation(), Route)); + graphs.append(loadGraph(data.routes().at(i).elevation(), Route)); + for (int i = 0; i < data.areas().count(); i++) + skipColor(); setInfo(); redraw(); diff --git a/src/GUI/gearratiograph.cpp b/src/GUI/gearratiograph.cpp index 909d726f..27e5faa3 100644 --- a/src/GUI/gearratiograph.cpp +++ b/src/GUI/gearratiograph.cpp @@ -34,9 +34,9 @@ QList GearRatioGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Graph &graph = data.tracks().at(i)->ratio(); + const Graph &graph = data.tracks().at(i).ratio(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { @@ -55,6 +55,9 @@ QList GearRatioGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/gui.cpp b/src/GUI/gui.cpp index 929157ef..e01664ca 100644 --- a/src/GUI/gui.cpp +++ b/src/GUI/gui.cpp @@ -88,6 +88,7 @@ GUI::GUI() _trackCount = 0; _routeCount = 0; _waypointCount = 0; + _areaCount = 0; _trackDistance = 0; _routeDistance = 0; _time = 0; @@ -329,6 +330,11 @@ void GUI::createActions() _showWaypointsAction->setCheckable(true); connect(_showWaypointsAction, SIGNAL(triggered(bool)), _mapView, SLOT(showWaypoints(bool))); + _showAreasAction = new QAction(tr("Show areas"), this); + _showAreasAction->setMenuRole(QAction::NoRole); + _showAreasAction->setCheckable(true); + connect(_showAreasAction, SIGNAL(triggered(bool)), _mapView, + SLOT(showAreas(bool))); _showWaypointLabelsAction = new QAction(tr("Waypoint labels"), this); _showWaypointLabelsAction->setMenuRole(QAction::NoRole); _showWaypointLabelsAction->setCheckable(true); @@ -521,6 +527,7 @@ void GUI::createMenus() dataMenu->addSeparator(); dataMenu->addAction(_showTracksAction); dataMenu->addAction(_showRoutesAction); + dataMenu->addAction(_showAreasAction); dataMenu->addAction(_showWaypointsAction); QMenu *settingsMenu = menuBar()->addMenu(tr("&Settings")); @@ -753,10 +760,11 @@ bool GUI::loadFile(const QString &fileName) if (data.isValid()) { for (int i = 0; i < data.tracks().count(); i++) { - _trackDistance += data.tracks().at(i)->distance(); - _time += data.tracks().at(i)->time(); - _movingTime += data.tracks().at(i)->movingTime(); - const QDate &date = data.tracks().at(i)->date().date(); + const Track &track = data.tracks().at(i); + _trackDistance += track.distance(); + _time += track.time(); + _movingTime += track.movingTime(); + const QDate &date = track.date().date(); if (_dateRange.first.isNull() || _dateRange.first > date) _dateRange.first = date; if (_dateRange.second.isNull() || _dateRange.second < date) @@ -765,16 +773,17 @@ bool GUI::loadFile(const QString &fileName) _trackCount += data.tracks().count(); for (int i = 0; i < data.routes().count(); i++) - _routeDistance += data.routes().at(i)->distance(); + _routeDistance += data.routes().at(i).distance(); _routeCount += data.routes().count(); _waypointCount += data.waypoints().count(); + _areaCount += data.areas().count(); if (_pathName.isNull()) { if (data.tracks().count() == 1 && !data.routes().count()) - _pathName = data.tracks().first()->name(); + _pathName = data.tracks().first().name(); else if (data.routes().count() == 1 && !data.tracks().count()) - _pathName = data.routes().first()->name(); + _pathName = data.routes().first().name(); } else _pathName = QString(); @@ -893,8 +902,11 @@ void GUI::openOptions() SET_VIEW_OPTION(backgroundColor, setBackgroundColor); SET_VIEW_OPTION(trackWidth, setTrackWidth); SET_VIEW_OPTION(routeWidth, setRouteWidth); + SET_VIEW_OPTION(areaWidth, setAreaWidth); SET_VIEW_OPTION(trackStyle, setTrackStyle); SET_VIEW_OPTION(routeStyle, setRouteStyle); + SET_VIEW_OPTION(areaStyle, setAreaStyle); + SET_VIEW_OPTION(areaOpacity, setAreaOpacity); SET_VIEW_OPTION(waypointSize, setWaypointSize); SET_VIEW_OPTION(waypointColor, setWaypointColor); SET_VIEW_OPTION(poiSize, setPOISize); @@ -1000,6 +1012,9 @@ void GUI::statistics() if (_showWaypointsAction->isChecked() && _waypointCount > 1) text.append("" + tr("Waypoints") + ":" + l.toString(_waypointCount) + ""); + if (_showAreasAction->isChecked() && _areaCount > 1) + text.append("" + tr("Areas") + ":" + + l.toString(_areaCount) + ""); if (_dateRange.first.isValid()) { if (_dateRange.first == _dateRange.second) { @@ -1065,6 +1080,8 @@ void GUI::plot(QPrinter *printer) info.insert(tr("Routes"), l.toString(_routeCount)); if (_showWaypointsAction->isChecked() && _waypointCount > 1) info.insert(tr("Waypoints"), l.toString(_waypointCount)); + if (_showAreasAction->isChecked() && _areaCount > 1) + info.insert(tr("Areas"), l.toString(_areaCount)); } if (_dateRange.first.isValid() && _options.printDate) { @@ -1138,6 +1155,7 @@ void GUI::reloadFile() _trackCount = 0; _routeCount = 0; _waypointCount = 0; + _areaCount = 0; _trackDistance = 0; _routeDistance = 0; _time = 0; @@ -1171,6 +1189,7 @@ void GUI::closeFiles() _trackCount = 0; _routeCount = 0; _waypointCount = 0; + _areaCount = 0; _trackDistance = 0; _routeDistance = 0; _time = 0; @@ -1457,7 +1476,8 @@ bool GUI::updateMapView() if (_options.alwaysShowMap) _mapView->setHidden(false); else - _mapView->setHidden(!(_trackCount + _routeCount + _waypointCount)); + _mapView->setHidden(!(_trackCount + _routeCount + _waypointCount + + _areaCount)); return (hidden != _mapView->isHidden()); } @@ -1692,6 +1712,8 @@ void GUI::writeSettings() if (_showWaypointsAction->isChecked() != SHOW_WAYPOINTS_DEFAULT) settings.setValue(SHOW_WAYPOINTS_SETTING, _showWaypointsAction->isChecked()); + if (_showAreasAction->isChecked() != SHOW_AREAS_DEFAULT) + settings.setValue(SHOW_AREAS_SETTING, _showAreasAction->isChecked()); if (_showWaypointLabelsAction->isChecked() != SHOW_WAYPOINT_LABELS_DEFAULT) settings.setValue(SHOW_WAYPOINT_LABELS_SETTING, _showWaypointLabelsAction->isChecked()); @@ -1732,10 +1754,16 @@ void GUI::writeSettings() settings.setValue(TRACK_WIDTH_SETTING, _options.trackWidth); if (_options.routeWidth != ROUTE_WIDTH_DEFAULT) settings.setValue(ROUTE_WIDTH_SETTING, _options.routeWidth); + if (_options.areaWidth != AREA_WIDTH_DEFAULT) + settings.setValue(AREA_WIDTH_SETTING, _options.areaWidth); if (_options.trackStyle != TRACK_STYLE_DEFAULT) settings.setValue(TRACK_STYLE_SETTING, (int)_options.trackStyle); if (_options.routeStyle != ROUTE_STYLE_DEFAULT) settings.setValue(ROUTE_STYLE_SETTING, (int)_options.routeStyle); + if (_options.areaStyle != AREA_STYLE_DEFAULT) + settings.setValue(AREA_STYLE_SETTING, (int)_options.areaStyle); + if (_options.areaOpacity != AREA_OPACITY_DEFAULT) + settings.setValue(AREA_OPACITY_SETTING, (int)_options.areaOpacity); if (_options.waypointSize != WAYPOINT_SIZE_DEFAULT) settings.setValue(WAYPOINT_SIZE_SETTING, _options.waypointSize); if (_options.waypointColor != WAYPOINT_COLOR_DEFAULT) @@ -1931,6 +1959,10 @@ void GUI::readSettings() _mapView->showWaypoints(false); else _showWaypointsAction->setChecked(true); + if (!settings.value(SHOW_AREAS_SETTING, SHOW_AREAS_DEFAULT).toBool()) + _mapView->showAreas(false); + else + _showAreasAction->setChecked(true); if (!settings.value(SHOW_WAYPOINT_LABELS_SETTING, SHOW_WAYPOINT_LABELS_DEFAULT).toBool()) _mapView->showWaypointLabels(false); @@ -1976,10 +2008,16 @@ void GUI::readSettings() TRACK_WIDTH_DEFAULT).toInt(); _options.routeWidth = settings.value(ROUTE_WIDTH_SETTING, ROUTE_WIDTH_DEFAULT).toInt(); + _options.areaWidth = settings.value(AREA_WIDTH_SETTING, + AREA_WIDTH_DEFAULT).toInt(); _options.trackStyle = (Qt::PenStyle) settings.value(TRACK_STYLE_SETTING, (int)TRACK_STYLE_DEFAULT).toInt(); _options.routeStyle = (Qt::PenStyle) settings.value(ROUTE_STYLE_SETTING, (int)ROUTE_STYLE_DEFAULT).toInt(); + _options.areaStyle = (Qt::PenStyle) settings.value(AREA_STYLE_SETTING, + (int)AREA_STYLE_DEFAULT).toInt(); + _options.areaOpacity = settings.value(AREA_OPACITY_SETTING, + AREA_OPACITY_DEFAULT).toInt(); _options.pathAntiAliasing = settings.value(PATH_AA_SETTING, PATH_AA_DEFAULT) .toBool(); _options.waypointSize = settings.value(WAYPOINT_SIZE_SETTING, @@ -2058,8 +2096,11 @@ void GUI::readSettings() _mapView->setBackgroundColor(_options.backgroundColor); _mapView->setTrackWidth(_options.trackWidth); _mapView->setRouteWidth(_options.routeWidth); + _mapView->setAreaWidth(_options.areaWidth); _mapView->setTrackStyle(_options.trackStyle); _mapView->setRouteStyle(_options.routeStyle); + _mapView->setAreaStyle(_options.areaStyle); + _mapView->setAreaOpacity(_options.areaOpacity); _mapView->setWaypointSize(_options.waypointSize); _mapView->setWaypointColor(_options.waypointColor); _mapView->setPOISize(_options.poiSize); diff --git a/src/GUI/gui.h b/src/GUI/gui.h index e06b3777..edfa7d31 100644 --- a/src/GUI/gui.h +++ b/src/GUI/gui.h @@ -190,6 +190,7 @@ private: QAction *_showRoutesAction; QAction *_showWaypointsAction; QAction *_showWaypointLabelsAction; + QAction *_showAreasAction; QAction *_showRouteWaypointsAction; QAction *_openOptionsAction; QAction *_mapsEnd; @@ -215,13 +216,9 @@ private: FileBrowser *_browser; QList _files; - int _trackCount; - int _routeCount; - int _waypointCount; - qreal _trackDistance; - qreal _routeDistance; - qreal _time; - qreal _movingTime; + int _trackCount, _routeCount, _areaCount, _waypointCount; + qreal _trackDistance, _routeDistance; + qreal _time, _movingTime; DateRange _dateRange; QString _pathName; diff --git a/src/GUI/heartrategraph.cpp b/src/GUI/heartrategraph.cpp index 4777f587..f3d41394 100644 --- a/src/GUI/heartrategraph.cpp +++ b/src/GUI/heartrategraph.cpp @@ -32,15 +32,16 @@ QList HeartRateGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Graph &graph = data.tracks().at(i)->heartRate(); + const Track &track = data.tracks().at(i); + const Graph &graph = track.heartRate(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { HeartRateGraphItem *gi = new HeartRateGraphItem(graph, _graphType); GraphView::addGraph(gi); - _avg.append(QPointF(data.tracks().at(i)->distance(), gi->avg())); + _avg.append(QPointF(track.distance(), gi->avg())); graphs.append(gi); } } @@ -50,6 +51,9 @@ QList HeartRateGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/mapview.cpp b/src/GUI/mapview.cpp index 0e106568..1fc4d607 100644 --- a/src/GUI/mapview.cpp +++ b/src/GUI/mapview.cpp @@ -11,6 +11,7 @@ #include "trackitem.h" #include "routeitem.h" #include "waypointitem.h" +#include "areaitem.h" #include "scaleitem.h" #include "keys.h" #include "mapview.h" @@ -50,13 +51,14 @@ MapView::MapView(Map *map, POI *poi, QWidget *parent) _units = Metric; _coordinatesFormat = DecimalDegrees; - _opacity = 1.0; + _mapOpacity = 1.0; _backgroundColor = Qt::white; _markerColor = Qt::red; _showMap = true; _showTracks = true; _showRoutes = true; + _showAreas = true; _showWaypoints = true; _showWaypointLabels = true; _showPOI = true; @@ -96,8 +98,8 @@ void MapView::centerOn(const QPointF &pos) PathItem *MapView::addTrack(const Track &track) { - if (track.isNull()) { - _palette.nextColor(); + if (!track.isValid()) { + skipColor(); return 0; } @@ -121,8 +123,8 @@ PathItem *MapView::addTrack(const Track &track) PathItem *MapView::addRoute(const Route &route) { - if (route.isNull()) { - _palette.nextColor(); + if (!route.isValid()) { + skipColor(); return 0; } @@ -147,6 +149,28 @@ PathItem *MapView::addRoute(const Route &route) return ri; } +void MapView::addArea(const Area &area) +{ + if (!area.isValid()) { + skipColor(); + return; + } + + AreaItem *ai = new AreaItem(area, _map); + _areas.append(ai); + _ar |= ai->area().boundingRect(); + ai->setColor(_palette.nextColor()); + ai->setWidth(_areaWidth); + ai->setStyle(_areaStyle); + ai->setOpacity(_areaOpacity); + ai->setDigitalZoom(_digitalZoom); + ai->setVisible(_showAreas); + _scene->addItem(ai); + + if (_showAreas) + addPOI(_poi->points(ai->area())); +} + void MapView::addWaypoints(const QVector &waypoints) { for (int i = 0; i < waypoints.count(); i++) { @@ -175,12 +199,15 @@ QList MapView::loadData(const Data &data) int zoom = _map->zoom(); for (int i = 0; i < data.tracks().count(); i++) - paths.append(addTrack(*(data.tracks().at(i)))); + paths.append(addTrack(data.tracks().at(i))); for (int i = 0; i < data.routes().count(); i++) - paths.append(addRoute(*(data.routes().at(i)))); + paths.append(addRoute(data.routes().at(i))); + for (int i = 0; i < data.areas().count(); i++) + addArea(data.areas().at(i)); addWaypoints(data.waypoints()); - if (_tracks.empty() && _routes.empty() && _waypoints.empty()) + if (_tracks.empty() && _routes.empty() && _waypoints.empty() + && _areas.empty()) return paths; if (fitMapZoom() != zoom) @@ -195,7 +222,7 @@ QList MapView::loadData(const Data &data) int MapView::fitMapZoom() const { - RectC br = _tr | _rr | _wr; + RectC br = _tr | _rr | _wr | _ar; return _map->zoomFit(viewport()->size() - QSize(2*MARGIN, 2*MARGIN), br.isNull() ? RectC(_map->xy2ll(_map->bounds().topLeft()), @@ -204,7 +231,7 @@ int MapView::fitMapZoom() const QPointF MapView::contentCenter() const { - RectC br = _tr | _rr | _wr; + RectC br = _tr | _rr | _wr | _ar; return br.isNull() ? sceneRect().center() : _map->ll2xy(br.center()); } @@ -239,6 +266,8 @@ void MapView::rescale() _tracks.at(i)->setMap(_map); for (int i = 0; i < _routes.size(); i++) _routes.at(i)->setMap(_map); + for (int i = 0; i < _areas.size(); i++) + _areas.at(i)->setMap(_map); for (int i = 0; i < _waypoints.size(); i++) _waypoints.at(i)->setMap(_map); @@ -258,6 +287,8 @@ void MapView::setPalette(const Palette &palette) _tracks.at(i)->setColor(_palette.nextColor()); for (int i = 0; i < _routes.count(); i++) _routes.at(i)->setColor(_palette.nextColor()); + for (int i = 0; i < _areas.count(); i++) + _areas.at(i)->setColor(_palette.nextColor()); } void MapView::setMap(Map *map) @@ -285,6 +316,8 @@ void MapView::setMap(Map *map) _tracks.at(i)->setMap(map); for (int i = 0; i < _routes.size(); i++) _routes.at(i)->setMap(map); + for (int i = 0; i < _areas.size(); i++) + _areas.at(i)->setMap(map); for (int i = 0; i < _waypoints.size(); i++) _waypoints.at(i)->setMap(map); @@ -327,6 +360,9 @@ void MapView::updatePOI() if (_showRoutes) for (int i = 0; i < _routes.size(); i++) addPOI(_poi->points(_routes.at(i)->path())); + if (_showAreas) + for (int i = 0; i < _areas.size(); i++) + addPOI(_poi->points(_areas.at(i)->area())); if (_showWaypoints) for (int i = 0; i< _waypoints.size(); i++) addPOI(_poi->points(_waypoints.at(i)->waypoint())); @@ -419,6 +455,8 @@ void MapView::digitalZoom(int zoom) _tracks.at(i)->setDigitalZoom(_digitalZoom); for (int i = 0; i < _routes.size(); i++) _routes.at(i)->setDigitalZoom(_digitalZoom); + for (int i = 0; i < _areas.size(); i++) + _areas.at(i)->setDigitalZoom(_digitalZoom); for (int i = 0; i < _waypoints.size(); i++) _waypoints.at(i)->setDigitalZoom(_digitalZoom); for (it = _pois.constBegin(); it != _pois.constEnd(); it++) @@ -536,7 +574,7 @@ void MapView::plot(QPainter *painter, const QRectF &target, qreal scale, painter->device()->logicalDpiY() / (qreal)metric(QPaintDevice::PdmDpiY)); adj = QRect(0, 0, adj.width() * s.x(), adj.height() * s.y()); - _map->zoomFit(adj.size(), _tr | _rr | _wr); + _map->zoomFit(adj.size(), _tr | _rr | _wr | _ar); rescale(); QPointF center = contentCenter(); @@ -579,6 +617,7 @@ void MapView::clear() _pois.clear(); _tracks.clear(); _routes.clear(); + _areas.clear(); _waypoints.clear(); _scene->removeItem(_mapScale); @@ -590,6 +629,7 @@ void MapView::clear() _tr = RectC(); _rr = RectC(); _wr = RectC(); + _ar = RectC(); digitalZoom(0); @@ -627,6 +667,16 @@ void MapView::showWaypoints(bool show) updatePOI(); } +void MapView::showAreas(bool show) +{ + _showAreas = show; + + for (int i = 0; i < _areas.count(); i++) + _areas.at(i)->setVisible(show); + + updatePOI(); +} + void MapView::showWaypointLabels(bool show) { _showWaypointLabels = show; @@ -697,6 +747,14 @@ void MapView::setRouteWidth(int width) _routes.at(i)->setWidth(width); } +void MapView::setAreaWidth(int width) +{ + _areaWidth = width; + + for (int i = 0; i < _areas.count(); i++) + _areas.at(i)->setWidth(width); +} + void MapView::setTrackStyle(Qt::PenStyle style) { _trackStyle = style; @@ -713,6 +771,22 @@ void MapView::setRouteStyle(Qt::PenStyle style) _routes.at(i)->setStyle(style); } +void MapView::setAreaStyle(Qt::PenStyle style) +{ + _areaStyle = style; + + for (int i = 0; i < _areas.count(); i++) + _areas.at(i)->setStyle(style); +} + +void MapView::setAreaOpacity(int opacity) +{ + _areaOpacity = opacity / 100.0; + + for (int i = 0; i < _areas.count(); i++) + _areas.at(i)->setOpacity(_areaOpacity); +} + void MapView::setWaypointSize(int size) { _waypointSize = size; @@ -751,7 +825,7 @@ void MapView::setPOIColor(const QColor &color) void MapView::setMapOpacity(int opacity) { - _opacity = opacity / 100.0; + _mapOpacity = opacity / 100.0; reloadMap(); } @@ -769,8 +843,8 @@ void MapView::drawBackground(QPainter *painter, const QRectF &rect) QRectF ir = rect.intersected(_map->bounds()); Map::Flags flags = Map::NoFlags; - if (_opacity < 1.0) - painter->setOpacity(_opacity); + if (_mapOpacity < 1.0) + painter->setOpacity(_mapOpacity); if (_plot) flags = Map::Block; @@ -867,6 +941,8 @@ void MapView::setDevicePixelRatio(qreal deviceRatio, qreal mapRatio) _tracks.at(i)->setMap(_map); for (int i = 0; i < _routes.size(); i++) _routes.at(i)->setMap(_map); + for (int i = 0; i < _areas.size(); i++) + _areas.at(i)->setMap(_map); for (int i = 0; i < _waypoints.size(); i++) _waypoints.at(i)->setMap(_map); diff --git a/src/GUI/mapview.h b/src/GUI/mapview.h index 740049a3..ca7c0174 100644 --- a/src/GUI/mapview.h +++ b/src/GUI/mapview.h @@ -12,6 +12,7 @@ #include "units.h" #include "format.h" #include "palette.h" +#include "data/polygon.h" class Data; class POI; @@ -24,6 +25,8 @@ class WaypointItem; class ScaleItem; class PathItem; class GraphItem; +class AreaItem; +class Area; class MapView : public QGraphicsView { @@ -46,8 +49,11 @@ public: void setMarkerColor(const QColor &color); void setTrackWidth(int width); void setRouteWidth(int width); + void setAreaWidth(int width); void setTrackStyle(Qt::PenStyle style); void setRouteStyle(Qt::PenStyle style); + void setAreaStyle(Qt::PenStyle style); + void setAreaOpacity(int opacity); void setWaypointSize(int size); void setWaypointColor(const QColor &color); void setPOISize(int size); @@ -65,6 +71,7 @@ public slots: void showPOILabels(bool show); void showTracks(bool show); void showRoutes(bool show); + void showAreas(bool show); void showWaypoints(bool show); void showRouteWaypoints(bool show); void clearMapCache(); @@ -78,6 +85,7 @@ private slots: private: PathItem *addTrack(const Track &track); PathItem *addRoute(const Route &route); + void addArea(const Area &area); void addWaypoints(const QVector &waypoints); void addPOI(const QList &waypoints); void loadPOI(); @@ -90,6 +98,7 @@ private: void zoom(int zoom, const QPoint &pos); void digitalZoom(int zoom); void updatePOIVisibility(); + void skipColor() {_palette.nextColor();} void mouseDoubleClickEvent(QMouseEvent *event); void wheelEvent(QWheelEvent *event); @@ -104,37 +113,28 @@ private: QList _tracks; QList _routes; QList _waypoints; + QList _areas; QHash, WaypointItem*> _pois; - RectC _tr, _rr, _wr; + RectC _tr, _rr, _wr, _ar; qreal _res; Map *_map; POI *_poi; + Palette _palette; Units _units; CoordinatesFormat _coordinatesFormat; + qreal _mapOpacity; - qreal _opacity; - QColor _backgroundColor; - bool _showMap; - bool _showTracks; - bool _showRoutes; - bool _showWaypoints; - bool _showWaypointLabels; - bool _showPOI; - bool _showPOILabels; + bool _showMap, _showTracks, _showRoutes, _showAreas, _showWaypoints, + _showWaypointLabels, _showPOI, _showPOILabels, _showRouteWaypoints; bool _overlapPOIs; - bool _showRouteWaypoints; - int _trackWidth; - int _routeWidth; - Qt::PenStyle _trackStyle; - Qt::PenStyle _routeStyle; - int _waypointSize; - int _poiSize; - QColor _waypointColor; - QColor _poiColor; - QColor _markerColor; + int _trackWidth, _routeWidth, _areaWidth; + Qt::PenStyle _trackStyle, _routeStyle, _areaStyle; + int _waypointSize, _poiSize; + QColor _backgroundColor, _waypointColor, _poiColor, _markerColor; + qreal _areaOpacity; int _digitalZoom; bool _plot; diff --git a/src/GUI/optionsdialog.cpp b/src/GUI/optionsdialog.cpp index e577683e..07c14914 100644 --- a/src/GUI/optionsdialog.cpp +++ b/src/GUI/optionsdialog.cpp @@ -93,22 +93,7 @@ QWidget *OptionsDialog::createMapPage() QWidget *OptionsDialog::createAppearancePage() { - // Paths - _baseColor = new ColorBox(); - _baseColor->setColor(_options->palette.color()); - _colorOffset = new QDoubleSpinBox(); - _colorOffset->setMinimum(0); - _colorOffset->setMaximum(1.0); - _colorOffset->setSingleStep(0.01); - _colorOffset->setValue(_options->palette.shift()); - QFormLayout *paletteLayout = new QFormLayout(); - paletteLayout->addRow(tr("Base color:"), _baseColor); - paletteLayout->addRow(tr("Palette shift:"), _colorOffset); -#ifndef Q_OS_MAC - QGroupBox *colorBox = new QGroupBox(tr("Colors")); - colorBox->setLayout(paletteLayout); -#endif // Q_OS_MAC - + // Tracks _trackWidth = new QSpinBox(); _trackWidth->setValue(_options->trackWidth); _trackWidth->setMinimum(1); @@ -125,6 +110,7 @@ QWidget *OptionsDialog::createAppearancePage() trackBox->setLayout(trackLayout); #endif // Q_OS_MAC + // Routes _routeWidth = new QSpinBox(); _routeWidth->setValue(_options->routeWidth); _routeWidth->setMinimum(1); @@ -141,6 +127,17 @@ QWidget *OptionsDialog::createAppearancePage() routeBox->setLayout(routeLayout); #endif // Q_OS_MAC + _baseColor = new ColorBox(); + _baseColor->setColor(_options->palette.color()); + _colorOffset = new QDoubleSpinBox(); + _colorOffset->setMinimum(0); + _colorOffset->setMaximum(1.0); + _colorOffset->setSingleStep(0.01); + _colorOffset->setValue(_options->palette.shift()); + QFormLayout *paletteLayout = new QFormLayout(); + paletteLayout->addRow(tr("Base color:"), _baseColor); + paletteLayout->addRow(tr("Palette shift:"), _colorOffset); + _pathAA = new QCheckBox(tr("Use anti-aliasing")); _pathAA->setChecked(_options->pathAntiAliasing); QFormLayout *pathAALayout = new QFormLayout(); @@ -149,22 +146,40 @@ QWidget *OptionsDialog::createAppearancePage() QWidget *pathTab = new QWidget(); QVBoxLayout *pathTabLayout = new QVBoxLayout(); #ifdef Q_OS_MAC - pathTabLayout->addLayout(paletteLayout); - pathTabLayout->addWidget(line()); pathTabLayout->addLayout(trackLayout); pathTabLayout->addWidget(line()); pathTabLayout->addLayout(routeLayout); pathTabLayout->addWidget(line()); #else // Q_OS_MAC - pathTabLayout->addWidget(colorBox); pathTabLayout->addWidget(trackBox); pathTabLayout->addWidget(routeBox); #endif // Q_OS_MAC + pathTabLayout->addLayout(paletteLayout); pathTabLayout->addLayout(pathAALayout); pathTabLayout->addStretch(); pathTab->setLayout(pathTabLayout); + // Areas + _areaWidth = new QSpinBox(); + _areaWidth->setValue(_options->areaWidth); + _areaWidth->setMinimum(1); + _areaStyle = new StyleComboBox(); + _areaStyle->setValue(_options->areaStyle); + _areaOpacity = new PercentSlider(); + _areaOpacity->setValue(_options->areaOpacity); + QFormLayout *areaLayout = new QFormLayout(); + areaLayout->addRow(tr("Border width:"), _areaWidth); + areaLayout->addRow(tr("Border style:"), _areaStyle); + areaLayout->addRow(tr("Fill opacity:"), _areaOpacity); + + QWidget *areaTab = new QWidget(); + QVBoxLayout *areaTabLayout = new QVBoxLayout(); + areaTabLayout->addLayout(areaLayout); + areaTabLayout->addStretch(); + areaTab->setLayout(areaTabLayout); + + // Waypoints _waypointSize = new QSpinBox(); _waypointSize->setMinimum(1); @@ -255,6 +270,7 @@ QWidget *OptionsDialog::createAppearancePage() QTabWidget *appearancePage = new QTabWidget(); appearancePage->addTab(pathTab, tr("Paths")); + appearancePage->addTab(areaTab, tr("Areas")); appearancePage->addTab(pointTab, tr("Points")); appearancePage->addTab(graphTab, tr("Graphs")); appearancePage->addTab(mapTab, tr("Map")); @@ -630,6 +646,10 @@ void OptionsDialog::accept() _options->routeStyle = (Qt::PenStyle) _routeStyle->itemData( _routeStyle->currentIndex()).toInt(); _options->pathAntiAliasing = _pathAA->isChecked(); + _options->areaWidth = _areaWidth->value(); + _options->areaStyle = (Qt::PenStyle) _areaStyle->itemData( + _areaStyle->currentIndex()).toInt(); + _options->areaOpacity = _areaOpacity->value(); _options->waypointSize = _waypointSize->value(); _options->waypointColor = _waypointColor->color(); _options->poiSize = _poiSize->value(); diff --git a/src/GUI/optionsdialog.h b/src/GUI/optionsdialog.h index 06a17340..38a49980 100644 --- a/src/GUI/optionsdialog.h +++ b/src/GUI/optionsdialog.h @@ -21,8 +21,11 @@ struct Options { Palette palette; int trackWidth; int routeWidth; + int areaWidth; Qt::PenStyle trackStyle; Qt::PenStyle routeStyle; + Qt::PenStyle areaStyle; + int areaOpacity; QColor waypointColor; QColor poiColor; int waypointSize; @@ -101,6 +104,9 @@ private: StyleComboBox *_trackStyle; QSpinBox *_routeWidth; StyleComboBox *_routeStyle; + QSpinBox *_areaWidth; + StyleComboBox *_areaStyle; + PercentSlider *_areaOpacity; QCheckBox *_pathAA; QSpinBox *_waypointSize; ColorBox *_waypointColor; diff --git a/src/GUI/pathitem.cpp b/src/GUI/pathitem.cpp index 8c362a51..35df8818 100644 --- a/src/GUI/pathitem.cpp +++ b/src/GUI/pathitem.cpp @@ -14,11 +14,10 @@ static unsigned segments(qreal distance) } PathItem::PathItem(const Path &path, Map *map, QGraphicsItem *parent) - : QGraphicsObject(parent) + : QGraphicsObject(parent), _path(path) { Q_ASSERT(path.count() >= 2); - _path = path; _map = map; _digitalZoom = 0; diff --git a/src/GUI/percentslider.cpp b/src/GUI/percentslider.cpp index a1b14dc2..4687c065 100644 --- a/src/GUI/percentslider.cpp +++ b/src/GUI/percentslider.cpp @@ -22,6 +22,7 @@ PercentSlider::PercentSlider(QWidget *parent) : QWidget(parent) QFontMetrics fm(_label->font()); _label->setFixedWidth(fm.boundingRect(format(_slider->maximum())).width()); _label->setAlignment(Qt::AlignRight); + _label->setText(format(_slider->value())); connect(_slider, SIGNAL(sliderMoved(int)), this, SLOT(updateLabel(int))); diff --git a/src/GUI/powergraph.cpp b/src/GUI/powergraph.cpp index 950124d1..0f0f54a2 100644 --- a/src/GUI/powergraph.cpp +++ b/src/GUI/powergraph.cpp @@ -32,15 +32,16 @@ QList PowerGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Graph &graph = data.tracks().at(i)->power(); + const Track &track = data.tracks().at(i); + const Graph &graph = track.power(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { PowerGraphItem *gi = new PowerGraphItem(graph, _graphType); GraphView::addGraph(gi); - _avg.append(QPointF(data.tracks().at(i)->distance(), gi->avg())); + _avg.append(QPointF(track.distance(), gi->avg())); graphs.append(gi); } } @@ -50,6 +51,9 @@ QList PowerGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/settings.h b/src/GUI/settings.h index c798eb2a..b7d38322 100644 --- a/src/GUI/settings.h +++ b/src/GUI/settings.h @@ -52,6 +52,8 @@ #define SHOW_ROUTES_DEFAULT true #define SHOW_WAYPOINTS_SETTING "waypoints" #define SHOW_WAYPOINTS_DEFAULT true +#define SHOW_AREAS_SETTING "areas" +#define SHOW_AREAS_DEFAULT true #define SHOW_ROUTE_WAYPOINTS_SETTING "routeWaypoints" #define SHOW_ROUTE_WAYPOINTS_DEFAULT true #define SHOW_WAYPOINT_LABELS_SETTING "waypointLabels" @@ -90,10 +92,16 @@ #define TRACK_WIDTH_DEFAULT 3 #define ROUTE_WIDTH_SETTING "routeWidth" #define ROUTE_WIDTH_DEFAULT 3 +#define AREA_WIDTH_SETTING "areaWidth" +#define AREA_WIDTH_DEFAULT 2 #define TRACK_STYLE_SETTING "trackStyle" #define TRACK_STYLE_DEFAULT Qt::SolidLine #define ROUTE_STYLE_SETTING "routeStyle" #define ROUTE_STYLE_DEFAULT Qt::DotLine +#define AREA_STYLE_SETTING "areaStyle" +#define AREA_STYLE_DEFAULT Qt::SolidLine +#define AREA_OPACITY_SETTING "areaOpacity" +#define AREA_OPACITY_DEFAULT 50 #define WAYPOINT_SIZE_SETTING "waypointSize" #define WAYPOINT_SIZE_DEFAULT 8 #define WAYPOINT_COLOR_SETTING "waypointColor" diff --git a/src/GUI/speedgraph.cpp b/src/GUI/speedgraph.cpp index e6eea358..f2587eff 100644 --- a/src/GUI/speedgraph.cpp +++ b/src/GUI/speedgraph.cpp @@ -40,19 +40,19 @@ QList SpeedGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Track *track = data.tracks().at(i); - const Graph &graph = track->speed(); + const Track &track = data.tracks().at(i); + const Graph &graph = track.speed(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { SpeedGraphItem *gi = new SpeedGraphItem(graph, _graphType, - track->movingTime()); + track.movingTime()); gi->setTimeType(_timeType); GraphView::addGraph(gi); - _avg.append(QPointF(track->distance(), gi->avg())); - _mavg.append(QPointF(track->distance(), gi->mavg())); + _avg.append(QPointF(track.distance(), gi->avg())); + _mavg.append(QPointF(track.distance(), gi->mavg())); graphs.append(gi); } } @@ -62,6 +62,9 @@ QList SpeedGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/temperaturegraph.cpp b/src/GUI/temperaturegraph.cpp index 98918645..6604e805 100644 --- a/src/GUI/temperaturegraph.cpp +++ b/src/GUI/temperaturegraph.cpp @@ -34,16 +34,17 @@ QList TemperatureGraph::loadData(const Data &data) QList graphs; for (int i = 0; i < data.tracks().count(); i++) { - const Graph &graph = data.tracks().at(i)->temperature(); + const Track &track = data.tracks().at(i); + const Graph &graph = track.temperature(); - if (graph.size() < 2) { + if (!graph.isValid()) { skipColor(); graphs.append(0); } else { TemperatureGraphItem *gi = new TemperatureGraphItem(graph, _graphType); GraphView::addGraph(gi); - _avg.append(QPointF(data.tracks().at(i)->distance(), gi->avg())); + _avg.append(QPointF(track.distance(), gi->avg())); graphs.append(gi); } } @@ -53,6 +54,9 @@ QList TemperatureGraph::loadData(const Data &data) graphs.append(0); } + for (int i = 0; i < data.areas().count(); i++) + skipColor(); + setInfo(); redraw(); diff --git a/src/GUI/tooltip.cpp b/src/GUI/tooltip.cpp index 9baa6fe9..d957930e 100644 --- a/src/GUI/tooltip.cpp +++ b/src/GUI/tooltip.cpp @@ -7,6 +7,9 @@ void ToolTip::insert(const QString &key, const QString &value) QString ToolTip::toString() { + if (_list.isEmpty()) + return QString(); + QString ret = ""; for (int i = 0; i < _list.count(); i++) diff --git a/src/data/area.h b/src/data/area.h new file mode 100644 index 00000000..02eba2df --- /dev/null +++ b/src/data/area.h @@ -0,0 +1,39 @@ +#ifndef AREA_H +#define AREA_H + +#include +#include +#include "polygon.h" + +class Area : public QList +{ +public: + const QString& name() const {return _name;} + const QString& description() const {return _desc;} + void setName(const QString &name) {_name = name;} + void setDescription(const QString &desc) {_desc = desc;} + + bool isValid() const + { + if (isEmpty()) + return false; + for (int i = 0; i < size(); i++) + if (!at(i).isValid()) + return false; + return true; + } + + RectC boundingRect() const + { + RectC ret; + for (int i = 0; i < size(); i++) + ret |= at(i).boundingRect(); + return ret; + } + +private: + QString _name; + QString _desc; +}; + +#endif // AREA_H diff --git a/src/data/csvparser.cpp b/src/data/csvparser.cpp index 58a23dfd..ed8f00d9 100644 --- a/src/data/csvparser.cpp +++ b/src/data/csvparser.cpp @@ -1,10 +1,12 @@ #include "csvparser.h" bool CSVParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(tracks); Q_UNUSED(routes); + Q_UNUSED(polygons); bool res; _errorLine = 1; diff --git a/src/data/csvparser.h b/src/data/csvparser.h index 7d967504..987185d3 100644 --- a/src/data/csvparser.h +++ b/src/data/csvparser.h @@ -9,7 +9,7 @@ public: CSVParser() : _errorLine(0) {} bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} diff --git a/src/data/data.cpp b/src/data/data.cpp index 62794919..d42b8b9a 100644 --- a/src/data/data.cpp +++ b/src/data/data.cpp @@ -63,20 +63,13 @@ static QHash parsers() QHash Data::_parsers = parsers(); bool Data::_useDEM = false; -Data::~Data() +void Data::processData(const QList &trackData, + const QList &routeData) { - for (int i = 0; i < _tracks.count(); i++) - delete _tracks.at(i); - for (int i = 0; i < _routes.count(); i++) - delete _routes.at(i); -} - -void Data::processData() -{ - for (int i = 0; i < _trackData.count(); i++) - _tracks.append(new Track(_trackData.at(i))); - for (int i = 0; i < _routeData.count(); i++) - _routes.append(new Route(_routeData.at(i))); + for (int i = 0; i < trackData.count(); i++) + _tracks.append(Track(trackData.at(i))); + for (int i = 0; i < routeData.count(); i++) + _routes.append(Route(routeData.at(i))); for (int i = 0; i < _waypoints.size(); i++) { if (!_waypoints.at(i).hasElevation() || _useDEM) { qreal elevation = DEM::elevation(_waypoints.at(i).coordinates()); @@ -90,6 +83,8 @@ Data::Data(const QString &fileName, bool poi) { QFile file(fileName); QFileInfo fi(fileName); + QList trackData; + QList routeData; _valid = false; _errorLine = 0; @@ -101,9 +96,10 @@ Data::Data(const QString &fileName, bool poi) QHash::iterator it; if ((it = _parsers.find(fi.suffix().toLower())) != _parsers.end()) { - if (it.value()->parse(&file, _trackData, _routeData, _waypoints)) { + if (it.value()->parse(&file, trackData, routeData, _polygons, + _waypoints)) { if (!poi) - processData(); + processData(trackData, routeData); _valid = true; return; } else { @@ -112,9 +108,10 @@ Data::Data(const QString &fileName, bool poi) } } else { for (it = _parsers.begin(); it != _parsers.end(); it++) { - if (it.value()->parse(&file, _trackData, _routeData, _waypoints)) { + if (it.value()->parse(&file, trackData, routeData, _polygons, + _waypoints)) { if (!poi) - processData(); + processData(trackData, routeData); _valid = true; return; } diff --git a/src/data/data.h b/src/data/data.h index 311c2e0e..d651f67d 100644 --- a/src/data/data.h +++ b/src/data/data.h @@ -15,15 +15,15 @@ class Data { public: Data(const QString &fileName, bool poi = false); - ~Data(); bool isValid() const {return _valid;} const QString &errorString() const {return _errorString;} int errorLine() const {return _errorLine;} - const QList &tracks() const {return _tracks;} - const QList &routes() const {return _routes;} + const QList &tracks() const {return _tracks;} + const QList &routes() const {return _routes;} const QVector &waypoints() const {return _waypoints;} + const QList &areas() const {return _polygons;} static QString formats(); static QStringList filter(); @@ -31,19 +31,18 @@ public: static void useDEM(bool use); private: - void processData(); + void processData(const QList &trackData, + const QList &routeData); bool _valid; QString _errorString; int _errorLine; - QList _tracks; - QList _routes; + QList _tracks; + QList _routes; + QList _polygons; QVector _waypoints; - QList _trackData; - QList _routeData; - static QHash _parsers; static bool _useDEM; }; diff --git a/src/data/fitparser.cpp b/src/data/fitparser.cpp index ef0f7510..02e073b0 100644 --- a/src/data/fitparser.cpp +++ b/src/data/fitparser.cpp @@ -372,10 +372,12 @@ bool FITParser::parseHeader(CTX &ctx) } bool FITParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, + QList &polygons, QVector &waypoints) { Q_UNUSED(routes); Q_UNUSED(waypoints); + Q_UNUSED(polygons); CTX ctx(file); diff --git a/src/data/fitparser.h b/src/data/fitparser.h index 411712db..067d0935 100644 --- a/src/data/fitparser.h +++ b/src/data/fitparser.h @@ -9,7 +9,7 @@ class FITParser : public Parser { public: bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return 0;} diff --git a/src/data/geojsonparser.cpp b/src/data/geojsonparser.cpp index efbdf045..cce47e5e 100644 --- a/src/data/geojsonparser.cpp +++ b/src/data/geojsonparser.cpp @@ -3,20 +3,7 @@ #include "geojsonparser.h" -enum Type { - Unknown, - Point, - LineString, - MultiPoint, - Polygon, - MultiLineString, - MultiPolygon, - GeometryCollection, - Feature, - FeatureCollection -}; - -static Type type(const QJsonObject &json) +GeoJSONParser::Type GeoJSONParser::type(const QJsonObject &json) { QString str(json["type"].toString()); @@ -57,6 +44,8 @@ bool GeoJSONParser::point(const QJsonArray &coordinates, Waypoint &waypoint, waypoint.setElevation(coordinates.at(2).toDouble()); if (properties.contains("title") && properties["title"].isString()) waypoint.setName(properties["title"].toString()); + if (properties.contains("name") && properties["name"].isString()) + waypoint.setName(properties["name"].toString()); if (properties.contains("description") && properties["description"].isString()) waypoint.setDescription(properties["description"].toString()); @@ -69,7 +58,7 @@ bool GeoJSONParser::multiPoint(const QJsonArray &coordinates, { for (int i = 0; i < coordinates.size(); i++) { if (!coordinates.at(i).isArray()) { - _errorString = "Invalid MultiPoint Coordinates"; + _errorString = "Invalid MultiPoint coordinates"; return false; } else { waypoints.resize(waypoints.size() + 1); @@ -86,6 +75,8 @@ bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, { if (properties.contains("title") && properties["title"].isString()) track.setName(properties["title"].toString()); + if (properties.contains("name") && properties["name"].isString()) + track.setName(properties["name"].toString()); if (properties.contains("description") && properties["description"].isString()) track.setDescription(properties["description"].toString()); @@ -94,7 +85,7 @@ bool GeoJSONParser::lineString(const QJsonArray &coordinates, TrackData &track, QJsonArray point(coordinates.at(i).toArray()); if (point.count() < 2 || !point.at(0).isDouble() || !point.at(1).isDouble()) { - _errorString = "Invalid LineString Coordinates"; + _errorString = "Invalid LineString coordinates"; return false; } @@ -113,7 +104,7 @@ bool GeoJSONParser::multiLineString(const QJsonArray &coordinates, { for (int i = 0; i < coordinates.size(); i++) { if (!coordinates.at(i).isArray()) { - _errorString = "Invalid MultiLineString Coordinates"; + _errorString = "Invalid MultiLineString coordinates"; return false; } else { tracks.append(TrackData()); @@ -126,9 +117,76 @@ bool GeoJSONParser::multiLineString(const QJsonArray &coordinates, return true; } -bool GeoJSONParser::geometryCollection(const QJsonObject &json, - QList &tracks, QVector &waypoints, +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()); + QVector &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, 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()); + + 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 &tracks, QList &areas, + QVector &waypoints, const QJsonObject &properties) { if (!json.contains("geometries") || !json["geometries"].isArray()) { _errorString = "Invalid/missing GeometryCollection geometries array"; @@ -162,10 +220,19 @@ bool GeoJSONParser::geometryCollection(const QJsonObject &json, return false; break; case Polygon: + areas.append(Area()); + if (!polygon(geometry["coordinates"].toArray(), areas.last(), + properties)) + return false; + break; case MultiPolygon: + areas.append(Area()); + if (!multiPolygon(geometry["coordinates"].toArray(), + areas.last(), properties)) + return false; break; case GeometryCollection: - if (!geometryCollection(geometry, tracks, waypoints, + if (!geometryCollection(geometry, tracks, areas, waypoints, properties)) return false; break; @@ -180,7 +247,7 @@ bool GeoJSONParser::geometryCollection(const QJsonObject &json, } bool GeoJSONParser::feature(const QJsonObject &json, QList &tracks, - QVector &waypoints) + QList &areas, QVector &waypoints) { QJsonObject properties(json["properties"].toObject()); QJsonObject geometry(json["geometry"].toObject()); @@ -201,10 +268,15 @@ bool GeoJSONParser::feature(const QJsonObject &json, QList &tracks, return multiLineString(geometry["coordinates"].toArray(), tracks, properties); case GeometryCollection: - return geometryCollection(geometry, tracks, waypoints); + return geometryCollection(geometry, tracks, areas, waypoints); case Polygon: + areas.append(Area()); + return polygon(geometry["coordinates"].toArray(), areas.last(), + properties); case MultiPolygon: - return true; + areas.append(Area()); + return multiPolygon(geometry["coordinates"].toArray(), areas.last(), + properties); default: _errorString = geometry["type"].toString() + ": invalid/missing Feature geometry"; @@ -213,7 +285,8 @@ bool GeoJSONParser::feature(const QJsonObject &json, QList &tracks, } bool GeoJSONParser::featureCollection(const QJsonObject &json, - QList &tracks, QVector &waypoints) + QList &tracks, QList &areas, + QVector &waypoints) { if (!json.contains("features") || !json["features"].isArray()) { _errorString = "Invalid/missing FeatureCollection features array"; @@ -222,7 +295,7 @@ bool GeoJSONParser::featureCollection(const QJsonObject &json, QJsonArray features(json["features"].toArray()); for (int i = 0; i < features.size(); i++) - if (!feature(features.at(i).toObject(), tracks, waypoints)) + if (!feature(features.at(i).toObject(), tracks, areas, waypoints)) return false; return true; @@ -230,7 +303,7 @@ bool GeoJSONParser::featureCollection(const QJsonObject &json, bool GeoJSONParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &areas, QVector &waypoints) { Q_UNUSED(routes); QJsonParseError error; @@ -256,14 +329,17 @@ bool GeoJSONParser::parse(QFile *file, QList &tracks, case MultiLineString: return multiLineString(json["coordinates"].toArray(), tracks); case GeometryCollection: - return geometryCollection(json, tracks, waypoints); + return geometryCollection(json, tracks, areas, waypoints); case Feature: - return feature(json, tracks, waypoints); + return feature(json, tracks, areas, waypoints); case FeatureCollection: - return featureCollection(json, tracks, waypoints); + return featureCollection(json, tracks, areas, waypoints); case Polygon: + areas.append(Area()); + return polygon(json["coordinates"].toArray(), areas.last()); case MultiPolygon: - return true; + areas.append(Area()); + return multiPolygon(json["coordinates"].toArray(), areas.last()); case Unknown: if (json["type"].toString().isNull()) _errorString = "Not a GeoJSON file"; diff --git a/src/data/geojsonparser.h b/src/data/geojsonparser.h index 765fd0af..7a711123 100644 --- a/src/data/geojsonparser.h +++ b/src/data/geojsonparser.h @@ -11,11 +11,25 @@ class GeoJSONParser : public Parser { public: bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &areas, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return 0;} private: + enum Type { + Unknown, + Point, + LineString, + MultiPoint, + Polygon, + MultiLineString, + MultiPolygon, + GeometryCollection, + Feature, + FeatureCollection + }; + + Type type(const QJsonObject &json); bool point(const QJsonArray &coordinates, Waypoint &waypoint, const QJsonObject &properties = QJsonObject()); bool multiPoint(const QJsonArray &coordinates, @@ -24,12 +38,18 @@ private: const QJsonObject &properties = QJsonObject()); bool multiLineString(const QJsonArray &coordinates, QList &tracks, const QJsonObject &properties = QJsonObject()); + bool polygon(const QJsonArray &coordinates, ::Polygon &pg); + bool polygon(const QJsonArray &coordinates, Area &area, + const QJsonObject &properties = QJsonObject()); + bool multiPolygon(const QJsonArray &coordinates, Area &area, + const QJsonObject &properties = QJsonObject()); bool geometryCollection(const QJsonObject &json, QList &tracks, - QVector &waypoints, const QJsonObject &properties = QJsonObject()); + QList &areas, QVector &waypoints, + const QJsonObject &properties = QJsonObject()); bool feature(const QJsonObject &json, QList &tracks, - QVector &waypoints); + QList &areas, QVector &waypoints); bool featureCollection(const QJsonObject &json, QList &tracks, - QVector &waypoints); + QList &areas, QVector &waypoints); QString _errorString; }; diff --git a/src/data/gpxparser.cpp b/src/data/gpxparser.cpp index 037074a3..ef02dec3 100644 --- a/src/data/gpxparser.cpp +++ b/src/data/gpxparser.cpp @@ -216,8 +216,11 @@ void GPXParser::gpx(QList &tracks, QList &routes, } bool GPXParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { + Q_UNUSED(polygons); + _reader.clear(); _reader.setDevice(file); _reader.setNamespaceProcessing(false); diff --git a/src/data/gpxparser.h b/src/data/gpxparser.h index 2e7bdb02..26d42ad7 100644 --- a/src/data/gpxparser.h +++ b/src/data/gpxparser.h @@ -8,8 +8,8 @@ class GPXParser : public Parser { public: - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} diff --git a/src/data/graph.h b/src/data/graph.h index c494aa16..68e31614 100644 --- a/src/data/graph.h +++ b/src/data/graph.h @@ -39,6 +39,14 @@ inline QDebug operator<<(QDebug dbg, const GraphPoint &point) } #endif // QT_NO_DEBUG -typedef QVector Graph; +class Graph : public QVector +{ +public: + Graph() {} + Graph(int size) : QVector(size) {} + Graph(const Graph &other) : QVector(other) {} + + bool isValid() const {return size() >= 2;} +}; #endif // GRAPH_H diff --git a/src/data/igcparser.cpp b/src/data/igcparser.cpp index 4cf7aef0..a1ffa926 100644 --- a/src/data/igcparser.cpp +++ b/src/data/igcparser.cpp @@ -192,9 +192,11 @@ bool IGCParser::readCRecord(RouteData &route, const char *line, int len) } bool IGCParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(waypoints); + Q_UNUSED(polygons); qint64 len; char line[76 + 2 + 1 + 1]; bool route = false, track = false; diff --git a/src/data/igcparser.h b/src/data/igcparser.h index 7356f1fb..548b5402 100644 --- a/src/data/igcparser.h +++ b/src/data/igcparser.h @@ -11,8 +11,8 @@ class IGCParser : public Parser public: IGCParser() : _errorLine(0) {} - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} diff --git a/src/data/kmlparser.cpp b/src/data/kmlparser.cpp index b46b952e..ed394afd 100644 --- a/src/data/kmlparser.cpp +++ b/src/data/kmlparser.cpp @@ -186,6 +186,63 @@ bool KMLParser::lineCoordinates(TrackData &track) return true; } +bool KMLParser::polygonCoordinates(QVector &points) +{ + QString data = _reader.readElementText(); + const QChar *sp, *ep, *cp, *vp; + int c = 0; + qreal val[3]; + bool res; + + + sp = data.constData(); + ep = sp + data.size(); + + for (cp = sp; cp < ep; cp++) + if (!cp->isSpace()) + break; + + for (vp = cp; cp <= ep; cp++) { + if (*cp == ',') { + if (c > 1) + return false; + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + val[c] = QString(vp, cp - vp).toDouble(&res); +#else // QT_VERSION < 5 + val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res); +#endif // QT_VERSION < 5 + if (!res) + return false; + + c++; + vp = cp + 1; + } else if (cp->isSpace() || cp->isNull()) { + if (c < 1 || c > 2) + return false; + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + val[c] = QString(vp, cp - vp).toDouble(&res); +#else // QT_VERSION < 5 + val[c] = QStringRef(&data, vp - sp, cp - vp).toDouble(&res); +#endif // QT_VERSION < 5 + if (!res) + return false; + + points.append(Coordinates(val[0], val[1])); + if (!points.last().isValid()) + return false; + + while (cp->isSpace()) + cp++; + c = 0; + vp = cp; + } + } + + return true; +} + QDateTime KMLParser::timeStamp() { QDateTime ts; @@ -211,6 +268,52 @@ void KMLParser::lineString(TrackData &track) } } +void KMLParser::linearRing(QVector &coordinates) +{ + while (_reader.readNextStartElement()) { + if (_reader.name() == QLatin1String("coordinates")) { + if (!polygonCoordinates(coordinates)) + _reader.raiseError("Invalid coordinates"); + } else + _reader.skipCurrentElement(); + } +} + +void KMLParser::boundary(QVector &coordinates) +{ + while (_reader.readNextStartElement()) { + if (_reader.name() == QLatin1String("LinearRing")) + linearRing(coordinates); + else + _reader.skipCurrentElement(); + } +} + +void KMLParser::polygon(Area &area) +{ + area.append(Polygon()); + Polygon &polygon = area.last(); + + while (_reader.readNextStartElement()) { + if (_reader.name() == QLatin1String("outerBoundaryIs")) { + if (!area.isEmpty()) { + _reader.raiseError("Multiple polygon outerBoundaryIss"); + return; + } + polygon.append(QVector()); + boundary(polygon.last()); + } else if (_reader.name() == QLatin1String("innerBoundaryIs")) { + if (area.isEmpty()) { + _reader.raiseError("Missing polygon outerBoundaryIs"); + return; + } + polygon.append(QVector()); + boundary(polygon.last()); + } else + _reader.skipCurrentElement(); + } +} + void KMLParser::point(Waypoint &waypoint) { while (_reader.readNextStartElement()) { @@ -380,7 +483,7 @@ void KMLParser::multiTrack(TrackData &t) } } -void KMLParser::multiGeometry(QList &tracks, +void KMLParser::multiGeometry(QList &tracks, QList &areas, QVector &waypoints, const QString &name, const QString &desc, const QDateTime timestamp) { @@ -398,12 +501,19 @@ void KMLParser::multiGeometry(QList &tracks, t.setName(name); t.setDescription(desc); lineString(t); + } else if (_reader.name() == QLatin1String("Polygon")) { + areas.append(Area()); + Area &a = areas.last(); + a.setName(name); + a.setDescription(desc); + polygon(a); } else _reader.skipCurrentElement(); } } -void KMLParser::placemark(QList &tracks, QVector &waypoints) +void KMLParser::placemark(QList &tracks, QList &areas, + QVector &waypoints) { QString name, desc; QDateTime timestamp; @@ -416,7 +526,7 @@ void KMLParser::placemark(QList &tracks, QVector &waypoints else if (_reader.name() == QLatin1String("TimeStamp")) timestamp = timeStamp(); else if (_reader.name() == QLatin1String("MultiGeometry")) - multiGeometry(tracks, waypoints, name, desc, timestamp); + multiGeometry(tracks, areas, waypoints, name, desc, timestamp); else if (_reader.name() == QLatin1String("Point")) { waypoints.append(Waypoint()); Waypoint &w = waypoints.last(); @@ -443,51 +553,60 @@ void KMLParser::placemark(QList &tracks, QVector &waypoints t.setName(name); t.setDescription(desc); multiTrack(t); + } else if (_reader.name() == QLatin1String("Polygon")) { + areas.append(Area()); + Area &a = areas.last(); + a.setName(name); + a.setDescription(desc); + polygon(a); } else _reader.skipCurrentElement(); } } -void KMLParser::folder(QList &tracks, QVector &waypoints) +void KMLParser::folder(QList &tracks, QList &areas, + QVector &waypoints) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Placemark")) - placemark(tracks, waypoints); + placemark(tracks, areas, waypoints); else if (_reader.name() == QLatin1String("Folder")) - folder(tracks, waypoints); + folder(tracks, areas, waypoints); else _reader.skipCurrentElement(); } } -void KMLParser::document(QList &tracks, QVector &waypoints) +void KMLParser::document(QList &tracks, QList &areas, + QVector &waypoints) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Placemark")) - placemark(tracks, waypoints); + placemark(tracks, areas, waypoints); else if (_reader.name() == QLatin1String("Folder")) - folder(tracks, waypoints); + folder(tracks, areas, waypoints); else _reader.skipCurrentElement(); } } -void KMLParser::kml(QList &tracks, QVector &waypoints) +void KMLParser::kml(QList &tracks, QList &areas, + QVector &waypoints) { while (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("Document")) - document(tracks, waypoints); + document(tracks, areas, waypoints); else if (_reader.name() == QLatin1String("Placemark")) - placemark(tracks, waypoints); + placemark(tracks, areas, waypoints); else if (_reader.name() == QLatin1String("Folder")) - folder(tracks, waypoints); + folder(tracks, areas, waypoints); else _reader.skipCurrentElement(); } } bool KMLParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &areas, QVector &waypoints) { Q_UNUSED(routes); @@ -496,7 +615,7 @@ bool KMLParser::parse(QFile *file, QList &tracks, if (_reader.readNextStartElement()) { if (_reader.name() == QLatin1String("kml")) - kml(tracks, waypoints); + kml(tracks, areas, waypoints); else _reader.raiseError("Not a KML file"); } diff --git a/src/data/kmlparser.h b/src/data/kmlparser.h index f7f0e858..d497c379 100644 --- a/src/data/kmlparser.h +++ b/src/data/kmlparser.h @@ -8,24 +8,33 @@ class KMLParser : public Parser { public: - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &areas, QVector &waypoints); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} private: - void kml(QList &tracks, QVector &waypoints); - void document(QList &tracks, QVector &waypoints); - void folder(QList &tracks, QVector &waypoints); - void placemark(QList &tracks, QVector &waypoints); - void multiGeometry(QList &tracks, QVector &waypoints, - const QString &name, const QString &desc, const QDateTime timestamp); + void kml(QList &tracks, QList &areas, + QVector &waypoints); + void document(QList &tracks, QList &areas, + QVector &waypoints); + void folder(QList &tracks, QList &areas, + QVector &waypoints); + void placemark(QList &tracks, QList &areas, + QVector &waypoints); + void multiGeometry(QList &tracks, QList &areas, + QVector &waypoints, const QString &name, const QString &desc, + const QDateTime timestamp); void track(TrackData &track); void multiTrack(TrackData &t); void lineString(TrackData &track); + void linearRing(QVector &coordinates); + void boundary(QVector &coordinates); + void polygon(Area &area); void point(Waypoint &waypoint); bool pointCoordinates(Waypoint &waypoint); bool lineCoordinates(TrackData &track); + bool polygonCoordinates(QVector &points); bool coord(Trackpoint &trackpoint); void extendedData(TrackData &track, int start); void schemaData(TrackData &track, int start); diff --git a/src/data/locparser.cpp b/src/data/locparser.cpp index 8d95fea5..5831f341 100644 --- a/src/data/locparser.cpp +++ b/src/data/locparser.cpp @@ -58,10 +58,12 @@ void LOCParser::loc(QVector &waypoints) } bool LOCParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(tracks); Q_UNUSED(routes); + Q_UNUSED(polygons); _reader.clear(); _reader.setDevice(file); diff --git a/src/data/locparser.h b/src/data/locparser.h index 8fe52766..21eab9b8 100644 --- a/src/data/locparser.h +++ b/src/data/locparser.h @@ -7,8 +7,8 @@ class LOCParser : public Parser { public: - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} diff --git a/src/data/nmeaparser.cpp b/src/data/nmeaparser.cpp index 4e563d81..878959a1 100644 --- a/src/data/nmeaparser.cpp +++ b/src/data/nmeaparser.cpp @@ -478,9 +478,11 @@ bool NMEAParser::readZDA(const char *line, int len) } bool NMEAParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(routes); + Q_UNUSED(polygons); qint64 len; char line[80 + 2 + 1 + 1]; diff --git a/src/data/nmeaparser.h b/src/data/nmeaparser.h index 487cb22b..f06e72b7 100644 --- a/src/data/nmeaparser.h +++ b/src/data/nmeaparser.h @@ -10,8 +10,8 @@ class NMEAParser : public Parser public: NMEAParser() : _errorLine(0), _GGA(false) {} - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} diff --git a/src/data/oziparsers.cpp b/src/data/oziparsers.cpp index da72bf40..caf77f5f 100644 --- a/src/data/oziparsers.cpp +++ b/src/data/oziparsers.cpp @@ -27,10 +27,12 @@ static QByteArray &decode(QByteArray &ba) bool PLTParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(waypoints); Q_UNUSED(routes); + Q_UNUSED(polygons); bool res; const GCS *gcs = 0; @@ -113,10 +115,12 @@ bool PLTParser::parse(QFile *file, QList &tracks, } bool RTEParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(waypoints); Q_UNUSED(tracks); + Q_UNUSED(polygons); bool res, record = false; const GCS *gcs = 0; @@ -211,10 +215,12 @@ bool RTEParser::parse(QFile *file, QList &tracks, } bool WPTParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(tracks); Q_UNUSED(routes); + Q_UNUSED(polygons); bool res; const GCS *gcs = 0; diff --git a/src/data/oziparsers.h b/src/data/oziparsers.h index f8a9fcc2..d38d3f9b 100644 --- a/src/data/oziparsers.h +++ b/src/data/oziparsers.h @@ -9,7 +9,7 @@ public: PLTParser() : _errorLine(0) {} bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} @@ -24,7 +24,7 @@ public: RTEParser() : _errorLine(0) {} bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} @@ -39,7 +39,7 @@ public: WPTParser() : _errorLine(0) {} bool parse(QFile *file, QList &tracks, QList &routes, - QVector &waypoints); + QList &polygons, QVector &waypoints); QString errorString() const {return _errorString;} int errorLine() const {return _errorLine;} diff --git a/src/data/parser.h b/src/data/parser.h index b910a967..26a02320 100644 --- a/src/data/parser.h +++ b/src/data/parser.h @@ -8,6 +8,7 @@ #include "trackdata.h" #include "routedata.h" #include "waypoint.h" +#include "area.h" class Parser @@ -16,7 +17,8 @@ public: virtual ~Parser() {} virtual bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) = 0; + QList &routes, QList &polygons, + QVector &waypoints) = 0; virtual QString errorString() const = 0; virtual int errorLine() const = 0; }; diff --git a/src/data/poi.cpp b/src/data/poi.cpp index b1cd4cd9..42041273 100644 --- a/src/data/poi.cpp +++ b/src/data/poi.cpp @@ -4,6 +4,9 @@ #include "common/greatcircle.h" #include "data.h" #include "dem.h" +#include "path.h" +#include "area.h" +#include "common/wgs84.h" #include "poi.h" @@ -177,6 +180,31 @@ QList POI::points(const Waypoint &point) const return ret; } +QList POI::points(const Area &area) const +{ + QList ret; + qreal min[2], max[2]; + QSet set; + QSet::const_iterator it; + + RectC br(area.boundingRect()); + double offset = rad2deg(_radius / WGS84_RADIUS); + + min[0] = br.topLeft().lon() - offset; + min[1] = br.bottomRight().lat() - offset; + max[0] = br.bottomRight().lon() + offset; + max[1] = br.topLeft().lat() + offset; + + _tree.Search(min, max, cb, &set); + + for (it = set.constBegin(); it != set.constEnd(); ++it) + ret.append(_data.at(*it)); + + appendElevation(ret); + + return ret; +} + void POI::enableFile(const QString &fileName, bool enable) { int i; diff --git a/src/data/poi.h b/src/data/poi.h index d607cd3f..7e281a8d 100644 --- a/src/data/poi.h +++ b/src/data/poi.h @@ -7,8 +7,10 @@ #include #include "common/rtree.h" #include "waypoint.h" -#include "path.h" +class Path; +class Area; +class RectC; class POI : public QObject { @@ -28,6 +30,7 @@ public: QList points(const Path &path) const; QList points(const Waypoint &point) const; + QList points(const Area &area) const; const QStringList &files() const {return _files;} void enableFile(const QString &fileName, bool enable); diff --git a/src/data/polygon.cpp b/src/data/polygon.cpp new file mode 100644 index 00000000..d6235c1f --- /dev/null +++ b/src/data/polygon.cpp @@ -0,0 +1,15 @@ +#include "polygon.h" + +RectC Polygon::boundingRect() const +{ + if (isEmpty()) + return RectC(); + if (first().size() < 3) + return RectC(); + + RectC rect; + for (int i = 0; i < first().size(); i++) + rect = rect.united(first().at(i)); + + return rect; +} diff --git a/src/data/polygon.h b/src/data/polygon.h new file mode 100644 index 00000000..c9885aaa --- /dev/null +++ b/src/data/polygon.h @@ -0,0 +1,17 @@ +#ifndef POLYGON_H +#define POLYGON_H + +#include +#include +#include "common/coordinates.h" +#include "common/rectc.h" + +class Polygon : public QList > +{ +public: + bool isValid() const {return !isEmpty() && first().size() >= 3;} + + RectC boundingRect() const; +}; + +#endif // POLYGON_H diff --git a/src/data/route.h b/src/data/route.h index 26869996..65b69265 100644 --- a/src/data/route.h +++ b/src/data/route.h @@ -22,12 +22,12 @@ public: const QString &name() const {return _data.name();} const QString &description() const {return _data.description();} - bool isNull() const {return (_data.count() < 2);} + bool isValid() const {return _data.size() >= 2;} static void useDEM(bool use) {_useDEM = use;} private: - const RouteData &_data; + RouteData _data; QVector _distance; static bool _useDEM; diff --git a/src/data/slfparser.cpp b/src/data/slfparser.cpp index c9563fea..3224fcdc 100644 --- a/src/data/slfparser.cpp +++ b/src/data/slfparser.cpp @@ -99,10 +99,12 @@ void SLFParser::activity(TrackData &track) } bool SLFParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(waypoints); Q_UNUSED(routes); + Q_UNUSED(polygons); _reader.clear(); _reader.setDevice(file); diff --git a/src/data/slfparser.h b/src/data/slfparser.h index 7f08d0ab..02fdb938 100644 --- a/src/data/slfparser.h +++ b/src/data/slfparser.h @@ -8,8 +8,8 @@ class QDateTime; class SLFParser : public Parser { - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} diff --git a/src/data/tcxparser.cpp b/src/data/tcxparser.cpp index fd20aad5..c8ecf6b8 100644 --- a/src/data/tcxparser.cpp +++ b/src/data/tcxparser.cpp @@ -233,9 +233,11 @@ void TCXParser::tcx(QList &tracks, QVector &waypoints) } bool TCXParser::parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints) + QList &routes, QList &polygons, + QVector &waypoints) { Q_UNUSED(routes); + Q_UNUSED(polygons); _reader.clear(); _reader.setDevice(file); diff --git a/src/data/tcxparser.h b/src/data/tcxparser.h index 06e628c4..21a9f271 100644 --- a/src/data/tcxparser.h +++ b/src/data/tcxparser.h @@ -8,8 +8,8 @@ class TCXParser : public Parser { public: - bool parse(QFile *file, QList &tracks, - QList &routes, QVector &waypoints); + bool parse(QFile *file, QList &tracks, QList &routes, + QList &polygons, QVector &waypoints); QString errorString() const {return _reader.errorString();} int errorLine() const {return _reader.lineNumber();} diff --git a/src/data/track.h b/src/data/track.h index fab3e244..6a311f44 100644 --- a/src/data/track.h +++ b/src/data/track.h @@ -33,7 +33,7 @@ public: const QString &name() const {return _data.name();} const QString &description() const {return _data.description();} - bool isNull() const {return (_data.size() < 2);} + bool isValid() const {return _data.size() >= 2;} static void setElevationFilter(int window) {_elevationWindow = window;} static void setSpeedFilter(int window) {_speedWindow = window;} @@ -50,7 +50,7 @@ public: private: bool discardStopPoint(int i) const; - const TrackData &_data; + TrackData _data; QVector _distance; QVector _time;