1 /*
2  *    Copyright 2014 Thomas Schöps
3  *    Copyright 2014, 2015 Kai Pastor
4  *
5  *    This file is part of OpenOrienteering.
6  *
7  *    OpenOrienteering is free software: you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation, either version 3 of the License, or
10  *    (at your option) any later version.
11  *
12  *    OpenOrienteering is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with OpenOrienteering.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 
22 #include "draw_point_gps_tool.h"
23 
24 #include <Qt>
25 #include <QtGlobal>
26 #include <QCursor>
27 #include <QKeyEvent>
28 #include <QLabel>
29 #include <QPainter>
30 #include <QPixmap>
31 #include <QPoint>
32 #include <QString>
33 
34 #include "core/map.h"
35 #include "core/map_coord.h"
36 #include "core/map_view.h"
37 #include "core/objects/object.h"
38 #include "core/renderables/renderable.h"
39 #include "core/symbols/point_symbol.h"
40 #include "core/symbols/symbol.h"
41 #include "gui/map/map_editor.h"
42 #include "gui/map/map_widget.h"
43 #include "sensors/gps_display.h"
44 #include "tools/tool.h"
45 #include "undo/object_undo.h"
46 #include "util/util.h"
47 
48 
49 namespace OpenOrienteering {
50 
DrawPointGPSTool(GPSDisplay * gps_display,MapEditorController * editor,QAction * tool_action)51 DrawPointGPSTool::DrawPointGPSTool(GPSDisplay* gps_display, MapEditorController* editor, QAction* tool_action)
52 : MapEditorToolBase(QCursor(QPixmap(QString::fromLatin1(":/images/cursor-draw-point.png")), 11, 11), DrawPoint, editor, tool_action)
53 , renderables(new MapRenderables(map()))
54 {
55 	useTouchCursor(false);
56 
57 	preview_object = nullptr;
58 	if (gps_display->hasValidPosition())
59 		newGPSPosition(gps_display->getLatestGPSCoord(), gps_display->getLatestGPSCoordAccuracy());
60 
61 	connect(gps_display, &GPSDisplay::mapPositionUpdated, this, &DrawPointGPSTool::newGPSPosition);
62 	connect(editor, &MapEditorController::activeSymbolChanged, this, &DrawPointGPSTool::activeSymbolChanged);
63 	connect(map(), &Map::symbolDeleted, this, &DrawPointGPSTool::symbolDeleted);
64 }
65 
~DrawPointGPSTool()66 DrawPointGPSTool::~DrawPointGPSTool()
67 {
68 	if (preview_object)
69 	{
70 		renderables->removeRenderablesOfObject(preview_object, false);
71 		delete preview_object;
72 	}
73 	if (help_label)
74 		editor->deletePopupWidget(help_label);
75 }
76 
initImpl()77 void DrawPointGPSTool::initImpl()
78 {
79 	if (editor->isInMobileMode())
80 	{
81 		help_label = new QLabel(tr("Touch the map to finish averaging"));
82 		editor->showPopupWidget(help_label, QString{});
83 	}
84 }
85 
newGPSPosition(const MapCoordF & coord,float accuracy)86 void DrawPointGPSTool::newGPSPosition(const MapCoordF& coord, float accuracy)
87 {
88 	auto point = reinterpret_cast<PointSymbol*>(editor->activeSymbol());
89 
90 	// Calculate weight from accuracy. This is arbitrarily chosen.
91 	float weight;
92 	if (accuracy < 0)
93 		weight = 1; // accuracy unknown
94 	else
95 		weight = 1.0f / qMax(0.5f, accuracy);
96 
97 	if (! preview_object)
98 	{
99 		// This is the first received position.
100 		preview_object = new PointObject(point);
101 
102 		x_sum = weight * coord.x();
103 		y_sum = weight * coord.y();
104 		weights_sum = weight;
105 	}
106 	else
107 	{
108 		renderables->removeRenderablesOfObject(preview_object, false);
109 		if (preview_object->getSymbol() != point)
110 		{
111 			bool success = preview_object->setSymbol(point, true);
112 			Q_ASSERT(success);
113 			Q_UNUSED(success);
114 		}
115 
116 		x_sum += weight * coord.x();
117 		y_sum += weight * coord.y();
118 		weights_sum += weight;
119 	}
120 
121 	MapCoordF avg_position(x_sum / weights_sum, y_sum / weights_sum);
122 	preview_object->setPosition(avg_position);
123 	preview_object->setRotation(0);
124 	preview_object->update();
125 	renderables->insertRenderablesOfObject(preview_object);
126 	updateDirtyRect();
127 }
128 
clickRelease()129 void DrawPointGPSTool::clickRelease()
130 {
131 	if (! preview_object)
132 		return;
133 
134 	auto point = preview_object->duplicate()->asPoint();
135 
136 	int index = map()->addObject(point);
137 	map()->clearObjectSelection(false);
138 	map()->addObjectToSelection(point, true);
139 	map()->setObjectsDirty();
140 	map()->clearDrawingBoundingBox();
141 	renderables->removeRenderablesOfObject(preview_object, false);
142 
143 	auto undo_step = new DeleteObjectsUndoStep(map());
144 	undo_step->addObject(index);
145 	map()->push(undo_step);
146 
147 	setEditingInProgress(false);
148 	deactivate();
149 }
150 
keyPress(QKeyEvent * event)151 bool DrawPointGPSTool::keyPress(QKeyEvent* event)
152 {
153 	switch (event->key())
154 	{
155 	case Qt::Key_Tab:
156 		deactivate();
157 		return true;
158 
159 	}
160 	return false;
161 }
162 
drawImpl(QPainter * painter,MapWidget * widget)163 void DrawPointGPSTool::drawImpl(QPainter* painter, MapWidget* widget)
164 {
165 	if (preview_object)
166 	{
167 		const MapView* map_view = widget->getMapView();
168 		painter->save();
169 		painter->translate(widget->width() / 2.0 + map_view->panOffset().x(),
170 						   widget->height() / 2.0 + map_view->panOffset().y());
171 		painter->setWorldTransform(map_view->worldTransform(), true);
172 
173 		RenderConfig config = { *map(), map_view->calculateViewedRect(widget->viewportToView(widget->rect())), map_view->calculateFinalZoomFactor(), RenderConfig::Tool, 0.5 };
174 		renderables->draw(painter, config);
175 
176 		painter->restore();
177 	}
178 }
179 
updateDirtyRectImpl(QRectF & rect)180 int DrawPointGPSTool::updateDirtyRectImpl(QRectF& rect)
181 {
182 	if (preview_object)
183 	{
184 		rectIncludeSafe(rect, preview_object->getExtent());
185 		return 0;
186 	}
187 	else
188 		return -1;
189 }
190 
updateStatusText()191 void DrawPointGPSTool::updateStatusText()
192 {
193 	setStatusBarText( tr("<b>Click</b>: Finish setting the object. "));
194 }
195 
objectSelectionChangedImpl()196 void DrawPointGPSTool::objectSelectionChangedImpl()
197 {
198 	// NOP
199 }
200 
activeSymbolChanged(const Symbol * symbol)201 void DrawPointGPSTool::activeSymbolChanged(const Symbol* symbol)
202 {
203 	if (!symbol || symbol->getType() != Symbol::Point || symbol->isHidden())
204 	{
205 		if (symbol && symbol->isHidden())
206 			deactivate();
207 		else
208 			switchToDefaultDrawTool(symbol);
209 	}
210 	else
211 		last_used_symbol = symbol;
212 }
213 
symbolDeleted(int pos,const Symbol * old_symbol)214 void DrawPointGPSTool::symbolDeleted(int pos, const Symbol* old_symbol)
215 {
216 	Q_UNUSED(pos);
217 
218 	if (last_used_symbol == old_symbol)
219 		deactivate();
220 }
221 
222 
223 }  // namespace OpenOrienteering
224