Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Q
qgroundcontrol
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Valentin Platzgummer
qgroundcontrol
Commits
4948045b
Commit
4948045b
authored
Dec 31, 2020
by
Valentin Platzgummer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MeasurementArea prepared for new NemoInterface
parent
0e658bb3
Changes
21
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
474 additions
and
375 deletions
+474
-375
QGCExternalLibs.pri
QGCExternalLibs.pri
+0
-5
qgroundcontrol.pro
qgroundcontrol.pro
+8
-2
CircularGenerator.cpp
src/MeasurementComplexItem/CircularGenerator.cpp
+13
-22
HashFunctions.cpp
src/MeasurementComplexItem/HashFunctions.cpp
+1
-0
HashFunctions.h
src/MeasurementComplexItem/HashFunctions.h
+32
-0
LinearGenerator.cpp
src/MeasurementComplexItem/LinearGenerator.cpp
+12
-20
MeasurementComplexItem.cc
src/MeasurementComplexItem/MeasurementComplexItem.cc
+1
-1
NemoInterface.cpp
src/MeasurementComplexItem/NemoInterface.cpp
+3
-3
MeasurementArea.cc
src/MeasurementComplexItem/geometry/MeasurementArea.cc
+190
-225
MeasurementArea.h
src/MeasurementComplexItem/geometry/MeasurementArea.h
+16
-20
ProgressArray.h
src/MeasurementComplexItem/geometry/ProgressArray.h
+10
-0
TileDiff.h
src/MeasurementComplexItem/geometry/TileDiff.h
+11
-0
geometry.h
src/MeasurementComplexItem/geometry/geometry.h
+3
-2
MeasurementTile.cpp
...MeasurementComplexItem/nemo_interface/MeasurementTile.cpp
+52
-0
MeasurementTile.h
src/MeasurementComplexItem/nemo_interface/MeasurementTile.h
+39
-0
SnakeTile.cpp
src/MeasurementComplexItem/nemo_interface/SnakeTile.cpp
+0
-22
SnakeTile.h
src/MeasurementComplexItem/nemo_interface/SnakeTile.h
+0
-22
SnakeTiles.h
src/MeasurementComplexItem/nemo_interface/SnakeTiles.h
+2
-2
MeasurementAreaMapVisual.qml
src/MeasurementComplexItem/qml/MeasurementAreaMapVisual.qml
+28
-29
QmlObjectListHelper.cpp
src/comm/QmlObjectListHelper.cpp
+1
-0
QmlObjectListHelper.h
src/comm/QmlObjectListHelper.h
+52
-0
No files found.
QGCExternalLibs.pri
View file @
4948045b
...
@@ -226,11 +226,6 @@ contains (DEFINES, DISABLE_AIRMAP) {
...
@@ -226,11 +226,6 @@ contains (DEFINES, DISABLE_AIRMAP) {
}
}
}
}
# GeograpicLib (TODO: add Windows support!)
LinuxBuild {
LIBS += -L$$PWD/libs/libGeographic -lGeographic # libGeograpic.so.17
}
# google or-tools (TODO: add Windows support!)
# google or-tools (TODO: add Windows support!)
LinuxBuild {
LinuxBuild {
OR_TOOLS_PATH = $$PWD/libs/or-tools-src-ubuntu
OR_TOOLS_PATH = $$PWD/libs/or-tools-src-ubuntu
...
...
qgroundcontrol.pro
View file @
4948045b
...
@@ -445,7 +445,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
...
@@ -445,7 +445,11 @@ contains (DEFINES, QGC_ENABLE_PAIRING) {
#
#
HEADERS
+=
\
HEADERS
+=
\
src
/
MeasurementComplexItem
/
geometry
/
ProgressArray
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
TileDiff
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
geometry
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
geometry
.
h
\
src
/
MeasurementComplexItem
/
HashFunctions
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
MeasurementTile
.
h
\
src
/
QmlControls
/
QmlUnitsConversion
.
h
\
src
/
QmlControls
/
QmlUnitsConversion
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
GeoArea
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
GeoArea
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
MeasurementArea
.
h
\
src
/
MeasurementComplexItem
/
geometry
/
MeasurementArea
.
h
\
...
@@ -487,7 +491,6 @@ HEADERS += \
...
@@ -487,7 +491,6 @@ HEADERS += \
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoHeartbeat
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoHeartbeat
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTile
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTileLocal
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTileLocal
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTiles
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTiles
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTilesLocal
.
h
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTilesLocal
.
h
\
...
@@ -498,6 +501,7 @@ HEADERS += \
...
@@ -498,6 +501,7 @@ HEADERS += \
src
/
api
/
QmlComponentInfo
.
h
\
src
/
api
/
QmlComponentInfo
.
h
\
src
/
GPS
/
Drivers
/
src
/
base_station
.
h
\
src
/
GPS
/
Drivers
/
src
/
base_station
.
h
\
src
/
Settings
/
WimaSettings
.
h
\
src
/
Settings
/
WimaSettings
.
h
\
src
/
comm
/
QmlObjectListHelper
.
h
\
src
/
comm
/
ros_bridge
/
include
/
RosBridgeClient
.
h
\
src
/
comm
/
ros_bridge
/
include
/
RosBridgeClient
.
h
\
src
/
comm
/
ros_bridge
/
include
/
com_private
.
h
\
src
/
comm
/
ros_bridge
/
include
/
com_private
.
h
\
src
/
comm
/
ros_bridge
/
include
/
message_traits
.
h
\
src
/
comm
/
ros_bridge
/
include
/
message_traits
.
h
\
...
@@ -525,6 +529,8 @@ SOURCES += \
...
@@ -525,6 +529,8 @@ SOURCES += \
src
/
MeasurementComplexItem
/
geometry
/
MeasurementArea
.
cc
\
src
/
MeasurementComplexItem
/
geometry
/
MeasurementArea
.
cc
\
src
/
MeasurementComplexItem
/
geometry
/
SafeArea
.
cc
\
src
/
MeasurementComplexItem
/
geometry
/
SafeArea
.
cc
\
src
/
MeasurementComplexItem
/
geometry
/
geometry
.
cpp
\
src
/
MeasurementComplexItem
/
geometry
/
geometry
.
cpp
\
src
/
MeasurementComplexItem
/
HashFunctions
.
cpp
\
src
/
MeasurementComplexItem
/
nemo_interface
/
MeasurementTile
.
cpp
\
src
/
Vehicle
/
VehicleEscStatusFactGroup
.
cc
\
src
/
Vehicle
/
VehicleEscStatusFactGroup
.
cc
\
src
/
MeasurementComplexItem
/
AreaData
.
cc
\
src
/
MeasurementComplexItem
/
AreaData
.
cc
\
src
/
api
/
QGCCorePlugin
.
cc
\
src
/
api
/
QGCCorePlugin
.
cc
\
...
@@ -541,7 +547,7 @@ SOURCES += \
...
@@ -541,7 +547,7 @@ SOURCES += \
src
/
MeasurementComplexItem
/
geometry
/
GeoPoint3D
.
cpp
\
src
/
MeasurementComplexItem
/
geometry
/
GeoPoint3D
.
cpp
\
src
/
MeasurementComplexItem
/
NemoInterface
.
cpp
\
src
/
MeasurementComplexItem
/
NemoInterface
.
cpp
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
cc
\
src
/
MeasurementComplexItem
/
nemo_interface
/
QNemoProgress
.
cc
\
src
/
MeasurementComplexItem
/
nemo_interface
/
SnakeTile
.
cpp
\
src
/
comm
/
QmlObjectListHelper
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
RosBridgeClient
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
RosBridgeClient
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
com_private
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
com_private
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
messages
/
geographic_msgs
/
geopoint
.
cpp
\
src
/
comm
/
ros_bridge
/
include
/
messages
/
geographic_msgs
/
geopoint
.
cpp
\
...
...
src/MeasurementComplexItem/CircularGenerator.cpp
View file @
4948045b
...
@@ -10,7 +10,7 @@
...
@@ -10,7 +10,7 @@
#include "geometry/MeasurementArea.h"
#include "geometry/MeasurementArea.h"
#include "geometry/SafeArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "geometry/clipper/clipper.hpp"
#include "nemo_interface/
Snake
Tile.h"
#include "nemo_interface/
Measurement
Tile.h"
QGC_LOGGING_CATEGORY
(
CircularGeneratorLog
,
"CircularGeneratorLog"
)
QGC_LOGGING_CATEGORY
(
CircularGeneratorLog
,
"CircularGeneratorLog"
)
...
@@ -118,31 +118,22 @@ bool CircularGenerator::get(Work &work) {
...
@@ -118,31 +118,22 @@ bool CircularGenerator::get(Work &work) {
auto
pPolygon
=
std
::
make_shared
<
geometry
::
FPolygon
>
();
auto
pPolygon
=
std
::
make_shared
<
geometry
::
FPolygon
>
();
geometry
::
areaToEnu
(
origin
,
geoPolygon
,
*
pPolygon
);
geometry
::
areaToEnu
(
origin
,
geoPolygon
,
*
pPolygon
);
// Progress and tiles.
// Collect tiles with progress == 100 %.
const
auto
&
progress
=
measurementArea
->
progress
();
const
auto
*
tiles
=
measurementArea
->
tiles
();
const
auto
*
tiles
=
measurementArea
->
tiles
();
auto
pTiles
=
std
::
make_shared
<
std
::
vector
<
geometry
::
FPolygon
>>
();
auto
pTiles
=
std
::
make_shared
<
std
::
vector
<
geometry
::
FPolygon
>>
();
if
(
progress
.
size
()
==
tiles
->
count
())
{
for
(
int
i
=
0
;
i
<
tiles
->
count
();
++
i
)
{
for
(
int
i
=
0
;
i
<
tiles
->
count
();
++
i
)
{
const
auto
tile
=
if
(
progress
[
i
]
==
100
)
{
qobject_cast
<
const
MeasurementTile
*>
(
tiles
->
operator
[](
i
));
const
auto
*
obj
=
(
*
tiles
)[
int
(
i
)];
if
(
tile
!=
nullptr
)
{
const
auto
*
tile
=
qobject_cast
<
const
SnakeTile
*>
(
obj
);
if
(
qFuzzyCompare
(
tile
->
progress
(),
100
))
{
geometry
::
FPolygon
tileENU
;
if
(
tile
!=
nullptr
)
{
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
);
geometry
::
FPolygon
tileENU
;
pTiles
->
push_back
(
std
::
move
(
tileENU
));
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
);
pTiles
->
push_back
(
std
::
move
(
tileENU
));
}
else
{
qCDebug
(
CircularGeneratorLog
)
<<
"get(): progress.size() != tiles->count()."
;
return
false
;
}
}
}
}
else
{
qCDebug
(
CircularGeneratorLog
)
<<
"get(): tile == nullptr."
;
return
false
;
}
}
}
else
{
qCDebug
(
CircularGeneratorLog
)
<<
"get(): progress.size() != tiles->count()."
;
return
false
;
}
}
auto
serviceArea
=
getGeoArea
<
const
SafeArea
*>
(
*
this
->
_d
->
areaList
());
auto
serviceArea
=
getGeoArea
<
const
SafeArea
*>
(
*
this
->
_d
->
areaList
());
...
...
src/MeasurementComplexItem/HashFunctions.cpp
0 → 100644
View file @
4948045b
#include "HashFunctions.h"
src/MeasurementComplexItem/HashFunctions.h
0 → 100644
View file @
4948045b
#ifndef HASHFUNCTIONS_H
#define HASHFUNCTIONS_H
#include <QGeoCoordinate>
#include <functional>
#include <QmlObjectListModel.h>
namespace
std
{
template
<>
struct
hash
<
QGeoCoordinate
>
{
std
::
size_t
operator
()(
const
QGeoCoordinate
&
c
)
{
hash
<
double
>
h
;
return
h
(
c
.
latitude
())
^
h
(
c
.
longitude
())
^
h
(
c
.
altitude
());
}
};
template
<
template
<
class
>
class
Container
,
class
EntryType
>
struct
hash
<
Container
<
EntryType
>>
{
std
::
size_t
operator
()(
const
Container
<
EntryType
>
&
list
)
{
std
::
size_t
value
=
0
;
hash
<
EntryType
>
h
;
for
(
const
auto
it
=
std
::
begin
(
list
);
it
!=
std
::
end
(
list
);
++
it
)
{
value
^=
h
(
*
it
);
}
return
value
;
}
};
}
// namespace std
#endif // HASHFUNCTIONS_H
src/MeasurementComplexItem/LinearGenerator.cpp
View file @
4948045b
...
@@ -9,7 +9,7 @@
...
@@ -9,7 +9,7 @@
#include "geometry/clipper/clipper.hpp"
#include "geometry/clipper/clipper.hpp"
#include "RoutingThread.h"
#include "RoutingThread.h"
#include "nemo_interface/
Snake
Tile.h"
#include "nemo_interface/
Measurement
Tile.h"
namespace
routing
{
namespace
routing
{
...
@@ -100,29 +100,21 @@ bool LinearGenerator::get(Work &generator) {
...
@@ -100,29 +100,21 @@ bool LinearGenerator::get(Work &generator) {
geometry
::
areaToEnu
(
origin
,
geoPolygon
,
*
pPolygon
);
geometry
::
areaToEnu
(
origin
,
geoPolygon
,
*
pPolygon
);
// Progress and tiles.
// Progress and tiles.
const
auto
&
progress
=
measurementArea
->
progress
();
const
auto
*
tiles
=
measurementArea
->
tiles
();
const
auto
*
tiles
=
measurementArea
->
tiles
();
auto
pTiles
=
std
::
make_shared
<
std
::
vector
<
geometry
::
FPolygon
>>
();
auto
pTiles
=
std
::
make_shared
<
std
::
vector
<
geometry
::
FPolygon
>>
();
if
(
progress
.
size
()
==
tiles
->
count
())
{
for
(
int
i
=
0
;
i
<
tiles
->
count
();
++
i
)
{
for
(
int
i
=
0
;
i
<
tiles
->
count
();
++
i
)
{
const
auto
tile
=
if
(
progress
[
i
]
==
100
)
{
qobject_cast
<
const
MeasurementTile
*>
(
tiles
->
operator
[](
i
));
const
QObject
*
obj
=
(
*
tiles
)[
int
(
i
)];
if
(
qFuzzyCompare
(
tile
->
progress
(),
100
))
{
const
auto
*
tile
=
qobject_cast
<
const
SnakeTile
*>
(
obj
);
if
(
tile
!=
nullptr
)
{
geometry
::
FPolygon
tileENU
;
if
(
tile
!=
nullptr
)
{
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
);
geometry
::
FPolygon
tileENU
;
pTiles
->
push_back
(
std
::
move
(
tileENU
));
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
);
}
else
{
pTiles
->
push_back
(
std
::
move
(
tileENU
));
qCDebug
(
LinearGeneratorLog
)
<<
"get(): tile == nullptr"
;
}
else
{
return
false
;
qCDebug
(
LinearGeneratorLog
)
<<
"get(): tile == nullptr"
;
return
false
;
}
}
}
}
}
}
else
{
qCDebug
(
LinearGeneratorLog
)
<<
"get(): progress.size() != tiles->count()."
;
return
false
;
}
}
auto
serviceArea
=
getGeoArea
<
const
SafeArea
*>
(
*
this
->
_d
->
areaList
());
auto
serviceArea
=
getGeoArea
<
const
SafeArea
*>
(
*
this
->
_d
->
areaList
());
...
...
src/MeasurementComplexItem/MeasurementComplexItem.cc
View file @
4948045b
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
#include "geometry/SafeArea.h"
#include "geometry/SafeArea.h"
#include "geometry/clipper/clipper.hpp"
#include "geometry/clipper/clipper.hpp"
#include "geometry/geometry.h"
#include "geometry/geometry.h"
#include "nemo_interface/
Snake
Tile.h"
#include "nemo_interface/
Measurement
Tile.h"
// QGC
// QGC
#include "JsonHelper.h"
#include "JsonHelper.h"
...
...
src/MeasurementComplexItem/NemoInterface.cpp
View file @
4948045b
...
@@ -17,7 +17,7 @@
...
@@ -17,7 +17,7 @@
#include "geometry/geometry.h"
#include "geometry/geometry.h"
#include "nemo_interface/QNemoHeartbeat.h"
#include "nemo_interface/QNemoHeartbeat.h"
#include "nemo_interface/QNemoProgress.h"
#include "nemo_interface/QNemoProgress.h"
#include "nemo_interface/
Snake
Tile.h"
#include "nemo_interface/
Measurement
Tile.h"
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/geographic_msgs/geopoint.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
#include "ros_bridge/include/messages/jsk_recognition_msgs/polygon_array.h"
...
@@ -158,7 +158,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
...
@@ -158,7 +158,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
UniqueLock
lk2
(
this
->
tilesENUMutex
,
std
::
adopt_lock
);
UniqueLock
lk2
(
this
->
tilesENUMutex
,
std
::
adopt_lock
);
const
auto
*
obj
=
tileData
.
tiles
[
0
];
const
auto
*
obj
=
tileData
.
tiles
[
0
];
const
auto
*
tile
=
qobject_cast
<
const
Snake
Tile
*>
(
obj
);
const
auto
*
tile
=
qobject_cast
<
const
Measurement
Tile
*>
(
obj
);
if
(
tile
!=
nullptr
)
{
if
(
tile
!=
nullptr
)
{
if
(
tile
->
coordinateList
().
size
()
>
0
)
{
if
(
tile
->
coordinateList
().
size
()
>
0
)
{
if
(
tile
->
coordinateList
().
first
().
isValid
())
{
if
(
tile
->
coordinateList
().
first
().
isValid
())
{
...
@@ -167,7 +167,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
...
@@ -167,7 +167,7 @@ void NemoInterface::Impl::setTileData(const TileData &tileData) {
this
->
tilesENU
.
polygons
().
clear
();
this
->
tilesENU
.
polygons
().
clear
();
for
(
int
i
=
0
;
i
<
tileData
.
tiles
.
count
();
++
i
)
{
for
(
int
i
=
0
;
i
<
tileData
.
tiles
.
count
();
++
i
)
{
obj
=
tileData
.
tiles
[
i
];
obj
=
tileData
.
tiles
[
i
];
tile
=
qobject_cast
<
const
Snake
Tile
*>
(
obj
);
tile
=
qobject_cast
<
const
Measurement
Tile
*>
(
obj
);
if
(
tile
!=
nullptr
)
{
if
(
tile
!=
nullptr
)
{
SnakeTileLocal
tileENU
;
SnakeTileLocal
tileENU
;
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
.
path
());
geometry
::
areaToEnu
(
origin
,
tile
->
coordinateList
(),
tileENU
.
path
());
...
...
src/MeasurementComplexItem/geometry/MeasurementArea.cc
View file @
4948045b
#include "MeasurementArea.h"
#include "MeasurementArea.h"
#include "
QtConcurrentRun
"
#include "
HashFunctions.h
"
#include "geometry.h"
#include "geometry.h"
#include "nemo_interface/SnakeTile.h"
#include "nemo_interface/MeasurementTile.h"
#include <ctime>
#include <ctime>
#include "QtConcurrentRun"
#include <QJsonArray>
#include <QQmlEngine>
#include <boost/units/systems/si.hpp>
#include <boost/units/systems/si.hpp>
#include "JsonHelper.h"
#include "JsonHelper.h"
#include "QGCLoggingCategory.h"
#include "QGCLoggingCategory.h"
#include "QmlObjectListHelper.h"
#include <QJsonArray>
#ifndef MAX_TILES
#define MAX_TILES 1000
#ifndef SNAKE_MAX_TILES
#define SNAKE_MAX_TILES 1000
#endif
#endif
using
namespace
geometry
;
using
namespace
geometry
;
...
@@ -26,142 +30,31 @@ bool getTiles(const FPolygon &area, Length tileHeight, Length tileWidth,
...
@@ -26,142 +30,31 @@ bool getTiles(const FPolygon &area, Length tileHeight, Length tileWidth,
QGC_LOGGING_CATEGORY
(
MeasurementAreaLog
,
"MeasurementAreaLog"
)
QGC_LOGGING_CATEGORY
(
MeasurementAreaLog
,
"MeasurementAreaLog"
)
namespace
{
namespace
{
const
char
*
tileCenterPointsKey
=
"TileCenterPoints"
;
const
char
*
tileArrayKey
=
"TileArray"
;
const
char
*
tileArrayKey
=
"TileArray"
;
}
// namespace
}
// namespace
TileData
::
TileData
()
:
tiles
(
this
)
{}
TileData
::
TileData
()
{}
TileData
::~
TileData
()
{}
TileData
::~
TileData
()
{
tiles
.
clearAndDeleteContents
();
}
TileData
&
TileData
::
operator
=
(
const
TileData
&
other
)
{
this
->
tiles
.
clearAndDeleteContents
();
for
(
std
::
size_t
i
=
0
;
i
<
std
::
size_t
(
other
.
tiles
.
count
());
++
i
)
{
const
auto
*
obj
=
other
.
tiles
[
i
];
const
auto
*
tile
=
qobject_cast
<
const
SnakeTile
*>
(
obj
);
if
(
tile
!=
nullptr
)
{
this
->
tiles
.
append
(
tile
->
clone
(
this
));
}
else
{
qCWarning
(
MeasurementAreaLog
)
<<
"TileData::operator=: nullptr"
;
}
}
this
->
tileCenterPoints
=
other
.
tileCenterPoints
;
return
*
this
;
}
bool
TileData
::
operator
==
(
const
TileData
&
other
)
const
{
if
(
this
->
tileCenterPoints
==
other
.
tileCenterPoints
&&
this
->
tiles
.
count
()
==
other
.
tiles
.
count
())
{
for
(
int
i
=
0
;
i
<
other
.
tiles
.
count
();
++
i
)
{
if
(
this
->
tiles
[
i
]
!=
other
.
tiles
[
i
])
{
return
false
;
}
}
return
true
;
}
else
{
return
false
;
}
}
TileData
&
TileData
::
operator
=
(
const
TileData
&
other
)
{
return
*
this
;
}
bool
TileData
::
operator
==
(
const
TileData
&
other
)
const
{
return
false
;
}
bool
TileData
::
operator
!=
(
const
TileData
&
other
)
const
{
bool
TileData
::
operator
!=
(
const
TileData
&
other
)
const
{
return
!
this
->
operator
==
(
other
);
return
operator
==
(
other
);
}
void
TileData
::
saveToJson
(
QJsonObject
&
json
)
{
// save center points
QJsonValue
jsonCenterPoints
;
JsonHelper
::
saveGeoCoordinateArray
(
tileCenterPoints
,
false
,
jsonCenterPoints
);
json
[
tileCenterPointsKey
]
=
std
::
move
(
jsonCenterPoints
);
// save tiles
QJsonArray
jsonTiles
;
for
(
int
i
=
0
;
i
<
tiles
.
count
();
++
i
)
{
auto
tile
=
tiles
.
value
<
SnakeTile
*>
(
i
);
if
(
tile
!=
nullptr
)
{
QJsonObject
jsonTile
;
tile
->
saveToJson
(
jsonTile
);
jsonTiles
.
append
(
jsonTile
);
}
else
{
qCritical
()
<<
"TileData::saveToJson(): Object other than SnakeTile "
"inside tiles (QmlObjectListModel))"
;
Q_ASSERT
(
tile
!=
nullptr
);
}
}
json
[
tileArrayKey
]
=
std
::
move
(
jsonTiles
);
}
}
void
TileData
::
saveToJson
(
QJsonObject
&
json
)
{}
bool
TileData
::
loadFromJson
(
const
QJsonObject
&
json
,
QString
&
errorString
)
{
bool
TileData
::
loadFromJson
(
const
QJsonObject
&
json
,
QString
&
errorString
)
{
clear
();
return
false
;
// load tiles
if
(
json
.
contains
(
tileArrayKey
)
&&
json
[
tileArrayKey
].
isArray
())
{
QString
e
;
for
(
const
auto
&
jsonTile
:
json
[
tileArrayKey
].
toArray
())
{
auto
tile
=
new
SnakeTile
(
this
);
if
(
tile
->
loadFromJson
(
jsonTile
.
toObject
(),
e
))
{
tiles
.
append
(
tile
);
}
else
{
tile
->
deleteLater
();
errorString
.
append
(
e
);
return
false
;
}
}
}
else
{
errorString
.
append
(
tr
(
"Not able to load tiles.
\n
"
));
qCWarning
(
MeasurementAreaLog
)
<<
"Not able to load tiles. tileArrayKey missing or wrong type."
;
if
(
json
.
contains
(
tileArrayKey
))
{
qCWarning
(
MeasurementAreaLog
)
<<
"tile array type: "
<<
json
[
tileArrayKey
].
type
();
}
return
false
;
}
// load center points
if
(
json
.
contains
(
tileCenterPointsKey
)
&&
json
[
tileCenterPointsKey
].
isArray
())
{
QString
e
;
if
(
!
JsonHelper
::
loadGeoCoordinateArray
(
json
[
tileCenterPointsKey
],
false
,
tileCenterPoints
,
e
))
{
errorString
.
append
(
e
);
errorString
.
append
(
"
\n
"
);
return
false
;
}
}
else
{
errorString
.
append
(
tr
(
"Not able to load center points.
\n
"
));
qCWarning
(
MeasurementAreaLog
)
<<
"Not able to load center points. tileCenterPointsKey missing or "
"wrong type."
;
if
(
json
.
contains
(
tileCenterPointsKey
))
{
qCWarning
(
MeasurementAreaLog
)
<<
"center points type: "
<<
json
[
tileCenterPointsKey
].
type
();
}
return
false
;
}
return
true
;
}
void
TileData
::
clear
()
{
this
->
tiles
.
clearAndDeleteContents
();
this
->
tileCenterPoints
.
clear
();
}
}
size_t
TileData
::
size
()
const
{
void
TileData
::
clear
()
{}
if
(
tiles
.
count
()
==
tileCenterPoints
.
size
())
{
std
::
size_t
TileData
::
size
()
const
{
return
0
;
}
return
tiles
.
count
();
}
else
{
return
0
;
}
}
const
char
*
MeasurementArea
::
settingsGroup
=
"MeasurementArea"
;
const
char
*
MeasurementArea
::
settingsGroup
=
"MeasurementArea"
;
const
char
*
tileHeightKey
=
"TileHeight"
;
const
char
*
tileHeightKey
=
"TileHeight"
;
const
char
*
tileWidthName
=
"TileWidth"
;
const
char
*
tileWidthName
=
"TileWidth"
;
const
char
*
minTileAreaKey
=
"MinTileAreaPercent"
;
const
char
*
minTileAreaKey
=
"MinTileAreaPercent"
;
const
char
*
showTilesKey
=
"ShowTiles"
;
const
char
*
showTilesKey
=
"ShowTiles"
;
const
char
*
progressKey
=
"Progress"
;
const
char
*
tileKey
=
"Tiles"
;
const
char
*
tileKey
=
"Tiles"
;
const
char
*
MeasurementArea
::
nameString
=
"Measurement Area"
;
const
char
*
MeasurementArea
::
nameString
=
"Measurement Area"
;
...
@@ -179,7 +72,8 @@ MeasurementArea::MeasurementArea(QObject *parent)
...
@@ -179,7 +72,8 @@ MeasurementArea::MeasurementArea(QObject *parent)
this
/* QObject parent */
)),
this
/* QObject parent */
)),
_showTiles
(
SettingsFact
(
settingsGroup
,
_metaDataMap
[
showTilesKey
],
_showTiles
(
SettingsFact
(
settingsGroup
,
_metaDataMap
[
showTilesKey
],
this
/* QObject parent */
)),
this
/* QObject parent */
)),
_holdProgress
(
false
),
_state
(
STATE
::
IDLE
)
{
_tiles
(
new
QmlObjectListModel
()),
_holdProgress
(
false
),
_state
(
STATE
::
IDLE
)
{
init
();
init
();
}
}
...
@@ -197,7 +91,8 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
...
@@ -197,7 +91,8 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
this
/* QObject parent */
)),
this
/* QObject parent */
)),
_showTiles
(
SettingsFact
(
settingsGroup
,
_metaDataMap
[
showTilesKey
],
_showTiles
(
SettingsFact
(
settingsGroup
,
_metaDataMap
[
showTilesKey
],
this
/* QObject parent */
)),
this
/* QObject parent */
)),
_holdProgress
(
false
),
_state
(
STATE
::
IDLE
)
{
_tiles
(
new
QmlObjectListModel
()),
_holdProgress
(
false
),
_state
(
STATE
::
IDLE
)
{
init
();
init
();
disableUpdate
();
disableUpdate
();
...
@@ -207,8 +102,12 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
...
@@ -207,8 +102,12 @@ MeasurementArea::MeasurementArea(const MeasurementArea &other, QObject *parent)
_showTiles
=
other
.
_showTiles
;
_showTiles
=
other
.
_showTiles
;
if
(
other
.
ready
())
{
if
(
other
.
ready
())
{
_progress
=
other
.
_progress
;
for
(
int
i
=
0
;
i
<
other
.
_tiles
->
count
();
++
i
)
{
_tileData
=
other
.
_tileData
;
_tiles
->
append
(
qobject_cast
<
const
MeasurementTile
*>
(
other
.
_tiles
->
operator
[](
i
))
->
clone
(
_tiles
.
get
()));
}
_tileMap
=
other
.
_tileMap
;
enableUpdate
();
enableUpdate
();
}
else
{
}
else
{
enableUpdate
();
enableUpdate
();
...
@@ -226,8 +125,13 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
...
@@ -226,8 +125,13 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
_showTiles
=
other
.
_showTiles
;
_showTiles
=
other
.
_showTiles
;
if
(
other
.
ready
())
{
if
(
other
.
ready
())
{
_progress
=
other
.
_progress
;
_tiles
->
clearAndDeleteContents
();
_tileData
=
other
.
_tileData
;
for
(
int
i
=
0
;
i
<
other
.
_tiles
->
count
();
++
i
)
{
_tiles
->
append
(
qobject_cast
<
const
MeasurementTile
*>
(
other
.
_tiles
->
operator
[](
i
))
->
clone
(
_tiles
.
get
()));
}
_tileMap
=
other
.
_tileMap
;
enableUpdate
();
enableUpdate
();
}
else
{
}
else
{
enableUpdate
();
enableUpdate
();
...
@@ -236,7 +140,7 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
...
@@ -236,7 +140,7 @@ MeasurementArea &MeasurementArea::operator=(const MeasurementArea &other) {
return
*
this
;
return
*
this
;
}
}
MeasurementArea
::~
MeasurementArea
()
{}
MeasurementArea
::~
MeasurementArea
()
{
_tiles
->
clearAndDeleteContents
();
}
QString
MeasurementArea
::
mapVisualQML
()
const
{
QString
MeasurementArea
::
mapVisualQML
()
const
{
return
QStringLiteral
(
"MeasurementAreaMapVisual.qml"
);
return
QStringLiteral
(
"MeasurementAreaMapVisual.qml"
);
...
@@ -259,32 +163,21 @@ Fact *MeasurementArea::minTileArea() { return &_minTileAreaPercent; }
...
@@ -259,32 +163,21 @@ Fact *MeasurementArea::minTileArea() { return &_minTileAreaPercent; }
Fact
*
MeasurementArea
::
showTiles
()
{
return
&
_showTiles
;
}
Fact
*
MeasurementArea
::
showTiles
()
{
return
&
_showTiles
;
}
QmlObjectListModel
*
MeasurementArea
::
tiles
()
{
return
&
this
->
_tileData
.
tiles
;
}
QmlObjectListModel
*
MeasurementArea
::
tiles
()
{
return
_tiles
.
get
();
}
const
QVector
<
int
>
&
MeasurementArea
::
progress
()
const
{
return
this
->
_progress
;
}
QVector
<
int
>
MeasurementArea
::
progressQml
()
const
{
return
this
->
_progress
;
}
const
QmlObjectListModel
*
MeasurementArea
::
tiles
()
const
{
const
QmlObjectListModel
*
MeasurementArea
::
tiles
()
const
{
return
&
this
->
_tileData
.
tiles
;
return
_tiles
.
get
()
;
}
}
const
QVariantList
&
MeasurementArea
::
tileCenterPoints
()
const
{
int
MeasurementArea
::
maxTiles
()
const
{
return
MAX_TILES
;
}
return
this
->
_tileData
.
tileCenterPoints
;
}
const
TileData
&
MeasurementArea
::
tileData
()
const
{
return
this
->
_tileData
;
}
int
MeasurementArea
::
maxTiles
()
const
{
return
SNAKE_MAX_TILES
;
}
bool
MeasurementArea
::
ready
()
const
{
return
this
->
_state
==
STATE
::
IDLE
;
}
bool
MeasurementArea
::
ready
()
const
{
return
this
->
_state
==
STATE
::
IDLE
;
}
bool
MeasurementArea
::
measurementCompleted
()
const
{
bool
MeasurementArea
::
measurementCompleted
()
const
{
if
(
ready
())
{
if
(
ready
())
{
for
(
const
auto
&
p
:
_progress
)
{
for
(
int
i
=
0
;
i
<
_tiles
->
count
();
++
i
)
{
if
(
p
!=
100
)
{
const
auto
tile
=
qobject_cast
<
const
MeasurementTile
*>
(
_tiles
->
get
(
i
));
if
(
!
qFuzzyCompare
(
tile
->
progress
(),
100
))
{
return
false
;
return
false
;
}
}
}
}
...
@@ -303,17 +196,15 @@ bool MeasurementArea::saveToJson(QJsonObject &json) {
...
@@ -303,17 +196,15 @@ bool MeasurementArea::saveToJson(QJsonObject &json) {
json
[
showTilesKey
]
=
_showTiles
.
rawValue
().
toBool
();
json
[
showTilesKey
]
=
_showTiles
.
rawValue
().
toBool
();
json
[
areaTypeKey
]
=
nameString
;
json
[
areaTypeKey
]
=
nameString
;
// save progess
QJsonArray
jsonProgess
;
for
(
const
auto
&
p
:
_progress
)
{
jsonProgess
.
append
(
p
);
}
json
[
progressKey
]
=
std
::
move
(
jsonProgess
);
// save tiles
// save tiles
QJsonObject
jsonTiles
;
QJsonArray
jsonTileArray
;
_tileData
.
saveToJson
(
jsonTiles
);
for
(
int
i
=
0
;
i
<
_tiles
->
count
();
++
i
)
{
json
[
tileKey
]
=
std
::
move
(
jsonTiles
);
auto
tile
=
qobject_cast
<
MeasurementTile
*>
(
_tiles
->
get
(
i
));
QJsonObject
jsonTile
;
tile
->
saveToJson
(
jsonTile
);
jsonTileArray
.
append
(
jsonTile
);
}
json
[
tileArrayKey
]
=
std
::
move
(
jsonTileArray
);
return
true
;
return
true
;
}
else
{
}
else
{
...
@@ -359,52 +250,39 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
...
@@ -359,52 +250,39 @@ bool MeasurementArea::loadFromJson(const QJsonObject &json,
_showTiles
.
setRawValue
(
json
[
showTilesKey
].
toBool
());
_showTiles
.
setRawValue
(
json
[
showTilesKey
].
toBool
());
}
}
// load tiles
and progress
// load tiles
bool
tileError
=
false
;
bool
tileError
=
false
;
if
(
json
.
contains
(
tileKey
)
&&
json
[
tileKey
].
isObject
())
{
if
(
json
.
contains
(
tileArrayKey
)
&&
json
[
tileArrayKey
].
isArray
())
{
QString
e
;
QString
e
;
if
(
!
_tileData
.
loadFromJson
(
json
[
tileKey
].
toObject
(),
e
))
{
_tiles
->
clearAndDeleteContents
();
qCWarning
(
MeasurementAreaLog
)
<<
"TileData::loadFromJson(): "
<<
e
;
tileError
=
true
;
for
(
auto
&&
jsonTile
:
json
[
tileArrayKey
].
toArray
())
{
}
else
{
progressChanged
();
}
}
else
{
tileError
=
true
;
}
bool
progressError
=
false
;
auto
tile
=
new
MeasurementTile
(
this
);
QVector
<
int
>
prog
;
if
(
json
.
contains
(
progressKey
)
&&
json
[
progressKey
].
isArray
())
{
if
(
tile
->
loadFromJson
(
jsonTile
.
toObject
(),
e
))
{
for
(
const
auto
&
p
:
json
[
progressKey
].
toArray
())
{
_tiles
->
append
(
tile
);
if
(
p
.
isDouble
())
{
prog
.
append
(
p
.
toDouble
());
}
else
{
}
else
{
progressError
=
true
;
tile
->
deleteLater
();
qCWarning
(
MeasurementAreaLog
)
<<
e
;
tileError
=
true
;
break
;
break
;
}
}
}
}
// check if entries are in range
if
(
!
progressError
)
{
for
(
const
auto
&
p
:
prog
)
{
if
(
p
<
0
||
p
>
100
)
{
progressError
=
true
;
break
;
}
}
}
}
else
{
}
else
{
progressError
=
true
;
qCWarning
(
MeasurementAreaLog
)
}
<<
"Not able to load tiles. tileArrayKey missing or wrong type."
;
if
(
!
progressError
)
{
if
(
json
.
contains
(
tileArrayKey
))
{
_progress
.
swap
(
prog
);
qCWarning
(
MeasurementAreaLog
)
emit
progressChanged
();
<<
"tile array type: "
<<
json
[
tileArrayKey
].
type
();
}
tileError
=
true
;
}
}
// do update if error occurred.
// do update if error occurred.
enableUpdate
();
enableUpdate
();
if
(
progressError
||
tileError
)
{
if
(
tileError
)
{
doUpdate
();
doUpdate
();
}
}
...
@@ -426,28 +304,44 @@ bool MeasurementArea::isCorrect() {
...
@@ -426,28 +304,44 @@ bool MeasurementArea::isCorrect() {
return
false
;
return
false
;
}
}
bool
MeasurementArea
::
setProgress
(
const
QVector
<
int
>
&
p
)
{
void
MeasurementArea
::
updateProgress
(
const
ProgressArray
&
array
)
{
if
(
ready
())
{
if
(
ready
()
&&
!
_holdProgress
&&
array
.
size
()
>
0
)
{
if
(
!
_holdProgress
&&
p
.
size
()
==
this
->
tiles
()
->
count
()
&&
bool
anyChanges
=
false
;
this
->
_progress
!=
p
)
{
for
(
const
auto
&
pair
:
array
)
{
this
->
_progress
=
p
;
const
auto
&
id
=
pair
.
first
;
const
auto
&
progress
=
pair
.
second
;
auto
it
=
_tileMap
.
find
(
id
);
if
(
it
!=
_tileMap
.
end
())
{
if
(
!
qFuzzyCompare
(
progress
,
it
->
second
->
progress
()))
{
it
->
second
->
setProgress
(
progress
);
anyChanges
=
true
;
}
}
}
if
(
anyChanges
)
{
emit
progressChanged
();
emit
progressChanged
();
emit
progressAccepted
();
return
true
;
}
}
}
}
emit
progressNotAccepted
();
return
false
;
}
}
void
MeasurementArea
::
randomProgress
()
{
void
MeasurementArea
::
randomProgress
()
{
if
(
ready
())
{
if
(
ready
())
{
std
::
srand
(
std
::
time
(
nullptr
));
std
::
srand
(
std
::
time
(
nullptr
));
for
(
auto
&
p
:
_progress
)
{
for
(
int
i
=
0
;
i
<
_tiles
->
count
();
++
i
)
{
auto
tile
=
_tiles
->
value
<
MeasurementTile
*>
(
i
);
Q_ASSERT
(
tile
!=
nullptr
);
auto
p
=
tile
->
progress
();
p
+=
std
::
rand
()
%
125
;
p
+=
std
::
rand
()
%
125
;
if
(
p
>
100
)
{
if
(
p
>
100
)
{
p
=
100
;
p
=
100
;
}
}
tile
->
setProgress
(
p
);
}
}
emit
progressChanged
();
emit
progressChanged
();
...
@@ -456,13 +350,25 @@ void MeasurementArea::randomProgress() {
...
@@ -456,13 +350,25 @@ void MeasurementArea::randomProgress() {
void
MeasurementArea
::
resetProgress
()
{
void
MeasurementArea
::
resetProgress
()
{
if
(
ready
())
{
if
(
ready
())
{
for
(
auto
&
p
:
_progress
)
{
bool
anyChanges
=
false
;
p
=
0
;
for
(
int
i
=
0
;
i
<
_tiles
->
count
();
++
i
)
{
auto
tile
=
_tiles
->
value
<
MeasurementTile
*>
(
i
);
Q_ASSERT
(
tile
!=
nullptr
);
if
(
!
qFuzzyCompare
(
tile
->
progress
(),
0
))
{
tile
->
setProgress
(
0
);
anyChanges
=
true
;
}
}
}
emit
progressChanged
();
if
(
anyChanges
)
{
emit
progressChanged
();
}
}
}
}
}
//!
//!
//! \brief MeasurementArea::doUpdate
//! \brief MeasurementArea::doUpdate
//! \pre MeasurementArea::deferUpdate must be called first, don't call
//! \pre MeasurementArea::deferUpdate must be called first, don't call
...
@@ -480,7 +386,7 @@ void MeasurementArea::doUpdate() {
...
@@ -480,7 +386,7 @@ void MeasurementArea::doUpdate() {
const
auto
totalArea
=
this
->
area
()
*
si
::
meter
*
si
::
meter
;
const
auto
totalArea
=
this
->
area
()
*
si
::
meter
*
si
::
meter
;
const
auto
estNumTiles
=
totalArea
/
tileArea
;
const
auto
estNumTiles
=
totalArea
/
tileArea
;
// Check some conditions.
// Check some conditions.
if
(
long
(
std
::
ceil
(
estNumTiles
.
value
()))
<=
SNAKE_
MAX_TILES
&&
if
(
long
(
std
::
ceil
(
estNumTiles
.
value
()))
<=
MAX_TILES
&&
this
->
GeoArea
::
isCorrect
())
{
this
->
GeoArea
::
isCorrect
())
{
setState
(
STATE
::
UPDATEING
);
setState
(
STATE
::
UPDATEING
);
...
@@ -491,10 +397,11 @@ void MeasurementArea::doUpdate() {
...
@@ -491,10 +397,11 @@ void MeasurementArea::doUpdate() {
const
auto
minArea
=
const
auto
minArea
=
this
->
_minTileAreaPercent
.
rawValue
().
toDouble
()
/
100
*
tileArea
;
this
->
_minTileAreaPercent
.
rawValue
().
toDouble
()
/
100
*
tileArea
;
auto
*
th
=
this
->
thread
();
auto
*
th
=
this
->
thread
();
auto
future
=
QtConcurrent
::
run
([
polygon
,
th
,
height
,
width
,
minArea
]
{
auto
future
=
QtConcurrent
::
run
([
polygon
,
th
,
height
,
width
,
minArea
]
{
auto
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
auto
start
=
std
::
chrono
::
high_resolution_clock
::
now
();
DataPtr
pData
(
new
TileData
());
TilePtr
pData
(
new
QmlObjectListModel
());
// Convert to ENU system.
// Convert to ENU system.
QGeoCoordinate
origin
=
polygon
.
first
();
QGeoCoordinate
origin
=
polygon
.
first
();
FPolygon
polygonENU
;
FPolygon
polygonENU
;
...
@@ -505,19 +412,17 @@ void MeasurementArea::doUpdate() {
...
@@ -505,19 +412,17 @@ void MeasurementArea::doUpdate() {
if
(
getTiles
(
polygonENU
,
height
,
width
,
minArea
,
tilesENU
,
bbox
))
{
if
(
getTiles
(
polygonENU
,
height
,
width
,
minArea
,
tilesENU
,
bbox
))
{
// Convert to geo system.
// Convert to geo system.
for
(
const
auto
&
t
:
tilesENU
)
{
for
(
const
auto
&
t
:
tilesENU
)
{
auto
geoTile
=
new
SnakeTile
(
pData
.
get
());
auto
geoTile
=
new
MeasurementTile
(
pData
.
get
());
std
::
size_t
hashValue
=
0
;
std
::
hash
<
QGeoCoordinate
>
hashFun
;
for
(
const
auto
&
v
:
t
.
outer
())
{
for
(
const
auto
&
v
:
t
.
outer
())
{
QGeoCoordinate
geoVertex
;
QGeoCoordinate
geoVertex
;
fromENU
(
origin
,
v
,
geoVertex
);
fromENU
(
origin
,
v
,
geoVertex
);
geoTile
->
push_back
(
geoVertex
);
geoTile
->
push_back
(
geoVertex
);
hashValue
^=
hashFun
(
geoVertex
);
}
}
pData
->
tiles
.
append
(
geoTile
);
geoTile
->
setId
(
long
(
hashValue
));
// Calculate center.
pData
->
append
(
geoTile
);
geometry
::
FPoint
center
;
geometry
::
polygonCenter
(
t
,
center
);
QGeoCoordinate
geoCenter
;
fromENU
(
origin
,
center
,
geoCenter
);
pData
->
tileCenterPoints
.
append
(
QVariant
::
fromValue
(
geoCenter
));
}
}
}
}
pData
->
moveToThread
(
th
);
pData
->
moveToThread
(
th
);
...
@@ -547,10 +452,10 @@ void MeasurementArea::deferUpdate() {
...
@@ -547,10 +452,10 @@ void MeasurementArea::deferUpdate() {
if
(
this
->
_state
==
STATE
::
IDLE
||
this
->
_state
==
STATE
::
DEFERED
)
{
if
(
this
->
_state
==
STATE
::
IDLE
||
this
->
_state
==
STATE
::
DEFERED
)
{
qCDebug
(
MeasurementAreaLog
)
<<
"defereUpdate(): defer update."
;
qCDebug
(
MeasurementAreaLog
)
<<
"defereUpdate(): defer update."
;
if
(
this
->
_state
==
STATE
::
IDLE
)
{
if
(
this
->
_state
==
STATE
::
IDLE
)
{
this
->
_
progress
.
clear
();
this
->
_
tileMap
.
clear
();
this
->
_tile
Data
.
clear
();
this
->
_tile
s
->
clearAndDeleteContents
();
emit
t
his
->
progres
sChanged
();
emit
t
ile
sChanged
();
emit
this
->
tile
sChanged
();
emit
progres
sChanged
();
}
}
this
->
setState
(
STATE
::
DEFERED
);
this
->
setState
(
STATE
::
DEFERED
);
this
->
_timer
.
start
(
100
);
this
->
_timer
.
start
(
100
);
...
@@ -566,11 +471,49 @@ void MeasurementArea::storeTiles() {
...
@@ -566,11 +471,49 @@ void MeasurementArea::storeTiles() {
if
(
this
->
_state
==
STATE
::
UPDATEING
)
{
if
(
this
->
_state
==
STATE
::
UPDATEING
)
{
qCDebug
(
MeasurementAreaLog
)
<<
"storeTiles(): update."
;
qCDebug
(
MeasurementAreaLog
)
<<
"storeTiles(): update."
;
this
->
_tileData
=
*
this
->
_watcher
.
result
();
_tiles
->
clearAndDeleteContents
();
this
->
_tiles
=
this
->
_watcher
.
result
();
this
->
_watcher
.
result
().
reset
();
QQmlEngine
::
setObjectOwnership
(
this
->
_tiles
.
get
(),
QQmlEngine
::
CppOwnership
);
// update tileMap
this
->
_tileMap
.
clear
();
for
(
int
i
=
0
;
i
<
_tiles
->
count
();
++
i
)
{
auto
tile
=
qobject_cast
<
MeasurementTile
*>
(
_tiles
->
get
(
i
));
auto
it
=
_tileMap
.
find
(
tile
->
id
());
// find unique id
if
(
it
!=
_tileMap
.
end
())
{
long
newId
=
tile
->
id
()
+
1
;
constexpr
long
counterMax
=
1e6
;
unsigned
long
counter
=
0
;
for
(;
counter
<=
counterMax
;
++
counter
)
{
it
=
_tileMap
.
find
(
newId
);
if
(
it
==
_tileMap
.
end
())
{
break
;
}
else
{
++
newId
;
}
}
if
(
counter
!=
counterMax
)
{
tile
->
setId
(
newId
);
tile
->
setProgress
(
0.0
);
}
else
{
qCritical
()
<<
"MeasurementArea::storeTiles(): not able to find unique id!"
;
continue
;
}
}
_tileMap
.
insert
(
std
::
make_pair
(
tile
->
id
(),
tile
));
}
// This is expensive. Drawing tiles is expensive too.
// This is expensive. Drawing tiles is expensive too.
this
->
_progress
=
QVector
<
int
>
(
this
->
_tileData
.
tiles
.
count
(),
0
);
this
->
progressChanged
();
emit
this
->
tilesChanged
();
emit
this
->
tilesChanged
();
emit
progressChanged
();
setState
(
STATE
::
IDLE
);
setState
(
STATE
::
IDLE
);
}
else
if
(
this
->
_state
==
STATE
::
RESTARTING
)
{
}
else
if
(
this
->
_state
==
STATE
::
RESTARTING
)
{
qCDebug
(
MeasurementAreaLog
)
<<
"storeTiles(): restart."
;
qCDebug
(
MeasurementAreaLog
)
<<
"storeTiles(): restart."
;
...
@@ -587,7 +530,7 @@ void MeasurementArea::storeTiles() {
...
@@ -587,7 +530,7 @@ void MeasurementArea::storeTiles() {
}
}
void
MeasurementArea
::
disableUpdate
()
{
void
MeasurementArea
::
disableUpdate
()
{
setState
(
STATE
::
IDLE
);
setState
(
STATE
::
STOP
);
this
->
_timer
.
stop
();
this
->
_timer
.
stop
();
}
}
...
@@ -599,6 +542,9 @@ void MeasurementArea::enableUpdate() {
...
@@ -599,6 +542,9 @@ void MeasurementArea::enableUpdate() {
void
MeasurementArea
::
init
()
{
void
MeasurementArea
::
init
()
{
this
->
setObjectName
(
nameString
);
this
->
setObjectName
(
nameString
);
QQmlEngine
::
setObjectOwnership
(
this
->
_tiles
.
get
(),
QQmlEngine
::
CppOwnership
);
connect
(
&
this
->
_tileHeight
,
&
Fact
::
rawValueChanged
,
this
,
connect
(
&
this
->
_tileHeight
,
&
Fact
::
rawValueChanged
,
this
,
&
MeasurementArea
::
deferUpdate
);
&
MeasurementArea
::
deferUpdate
);
connect
(
&
this
->
_tileWidth
,
&
Fact
::
rawValueChanged
,
this
,
connect
(
&
this
->
_tileWidth
,
&
Fact
::
rawValueChanged
,
this
,
...
@@ -632,6 +578,25 @@ void MeasurementArea::setHoldProgress(bool holdProgress) {
...
@@ -632,6 +578,25 @@ void MeasurementArea::setHoldProgress(bool holdProgress) {
}
}
}
}
void
MeasurementArea
::
updateIds
(
const
QList
<
TileDiff
>
&
array
)
{
for
(
const
auto
&
diff
:
array
)
{
auto
it
=
_tileMap
.
find
(
diff
.
oldTile
.
id
());
if
(
it
!=
_tileMap
.
end
()
&&
diff
.
oldTile
.
coordinateList
()
==
it
->
second
->
coordinateList
())
{
// Change id and update _tileMap.
const
auto
newId
=
diff
.
newTile
.
id
();
auto
tile
=
it
->
second
;
tile
->
setId
(
newId
);
_tileMap
.
erase
(
it
);
auto
ret
=
_tileMap
.
insert
(
std
::
make_pair
(
newId
,
tile
));
Q_ASSERT
(
ret
.
second
==
true
/*insert success?*/
);
Q_UNUSED
(
ret
);
}
}
}
bool
getTiles
(
const
FPolygon
&
area
,
Length
tileHeight
,
Length
tileWidth
,
bool
getTiles
(
const
FPolygon
&
area
,
Length
tileHeight
,
Length
tileWidth
,
Area
minTileArea
,
std
::
vector
<
FPolygon
>
&
tiles
,
Area
minTileArea
,
std
::
vector
<
FPolygon
>
&
tiles
,
BoundingBox
&
bbox
)
{
BoundingBox
&
bbox
)
{
...
...
src/MeasurementComplexItem/geometry/MeasurementArea.h
View file @
4948045b
...
@@ -6,14 +6,14 @@
...
@@ -6,14 +6,14 @@
#include <QTimer>
#include <QTimer>
#include "GeoArea.h"
#include "GeoArea.h"
#include "MeasurementComplexItem/nemo_interface/MeasurementTile.h"
#include "ProgressArray.h"
#include "TileDiff.h"
#include "SettingsFact.h"
#include "SettingsFact.h"
class
TileData
:
public
QObject
{
class
TileData
:
public
QObject
{
public:
public:
QmlObjectListModel
tiles
;
QVariantList
tileCenterPoints
;
TileData
();
TileData
();
~
TileData
();
~
TileData
();
...
@@ -26,12 +26,15 @@ public:
...
@@ -26,12 +26,15 @@ public:
void
clear
();
void
clear
();
std
::
size_t
size
()
const
;
std
::
size_t
size
()
const
;
QmlObjectListModel
tiles
;
QVariantList
tileCenterPoints
;
};
};
class
MeasurementArea
:
public
GeoArea
{
class
MeasurementArea
:
public
GeoArea
{
Q_OBJECT
Q_OBJECT
enum
class
STATE
{
IDLE
,
DEFERED
,
UPDATEING
,
RESTARTING
,
STOP
};
enum
class
STATE
{
IDLE
,
DEFERED
,
UPDATEING
,
RESTARTING
,
STOP
};
using
DataPtr
=
QSharedPointer
<
TileData
>
;
using
TilePtr
=
QSharedPointer
<
QmlObjectListModel
>
;
public:
public:
MeasurementArea
(
QObject
*
parent
=
nullptr
);
MeasurementArea
(
QObject
*
parent
=
nullptr
);
...
@@ -45,7 +48,6 @@ public:
...
@@ -45,7 +48,6 @@ public:
Q_PROPERTY
(
Fact
*
showTiles
READ
showTiles
CONSTANT
)
Q_PROPERTY
(
Fact
*
showTiles
READ
showTiles
CONSTANT
)
Q_PROPERTY
(
QmlObjectListModel
*
tiles
READ
tiles
NOTIFY
tilesChanged
)
Q_PROPERTY
(
QmlObjectListModel
*
tiles
READ
tiles
NOTIFY
tilesChanged
)
Q_PROPERTY
(
int
maxTiles
READ
maxTiles
NOTIFY
maxTilesChanged
)
Q_PROPERTY
(
int
maxTiles
READ
maxTiles
NOTIFY
maxTilesChanged
)
Q_PROPERTY
(
QVector
<
int
>
progress
READ
progressQml
NOTIFY
progressChanged
)
Q_PROPERTY
(
bool
holdProgress
READ
holdProgress
WRITE
setHoldProgress
NOTIFY
Q_PROPERTY
(
bool
holdProgress
READ
holdProgress
WRITE
setHoldProgress
NOTIFY
holdProgressChanged
)
holdProgressChanged
)
...
@@ -63,11 +65,7 @@ public:
...
@@ -63,11 +65,7 @@ public:
Fact
*
minTileArea
();
Fact
*
minTileArea
();
Fact
*
showTiles
();
Fact
*
showTiles
();
QmlObjectListModel
*
tiles
();
QmlObjectListModel
*
tiles
();
const
QVector
<
int
>
&
progress
()
const
;
QVector
<
int
>
progressQml
()
const
;
const
QmlObjectListModel
*
tiles
()
const
;
const
QmlObjectListModel
*
tiles
()
const
;
const
QVariantList
&
tileCenterPoints
()
const
;
// List of QGeoCoordinate
const
TileData
&
tileData
()
const
;
int
maxTiles
()
const
;
int
maxTiles
()
const
;
bool
ready
()
const
;
bool
ready
()
const
;
...
@@ -92,14 +90,13 @@ public:
...
@@ -92,14 +90,13 @@ public:
signals:
signals:
void
tilesChanged
();
void
tilesChanged
();
void
maxTilesChanged
();
void
maxTilesChanged
();
void
progressChanged
();
void
progressAccepted
();
void
progressNotAccepted
();
void
readyChanged
();
void
readyChanged
();
void
holdProgressChanged
();
void
holdProgressChanged
();
void
progressChanged
();
public
slots
:
public
slots
:
bool
setProgress
(
const
QVector
<
int
>
&
p
);
void
updateIds
(
const
QList
<
TileDiff
>
&
array
);
void
updateProgress
(
const
ProgressArray
&
array
);
Q_INVOKABLE
void
randomProgress
();
Q_INVOKABLE
void
randomProgress
();
Q_INVOKABLE
void
resetProgress
();
Q_INVOKABLE
void
resetProgress
();
...
@@ -123,12 +120,11 @@ private:
...
@@ -123,12 +120,11 @@ private:
SettingsFact
_minTileAreaPercent
;
// 0..100
SettingsFact
_minTileAreaPercent
;
// 0..100
SettingsFact
_showTiles
;
SettingsFact
_showTiles
;
QVector
<
int
>
_progress
;
bool
_holdProgress
;
// Tile stuff.
// Tile stuff.
// Tile stuff.
TilePtr
_tiles
;
mutable
QTimer
_timer
;
std
::
map
<
long
/*id*/
,
MeasurementTile
*>
_tileMap
;
mutable
STATE
_state
;
bool
_holdProgress
;
mutable
TileData
_tileData
;
QTimer
_timer
;
mutable
QFutureWatcher
<
DataPtr
>
_watcher
;
STATE
_state
;
QFutureWatcher
<
TilePtr
>
_watcher
;
};
};
src/MeasurementComplexItem/geometry/ProgressArray.h
0 → 100644
View file @
4948045b
#ifndef PROGRESSARRAY_H
#define PROGRESSARRAY_H
#include <QVector>
#include <tuple>
typedef
std
::
pair
<
long
/*id*/
,
double
/*progress*/
>
TaggedProgress
;
typedef
QVector
<
TaggedProgress
>
ProgressArray
;
#endif // PROGRESSARRAY_H
src/MeasurementComplexItem/geometry/TileDiff.h
0 → 100644
View file @
4948045b
#ifndef TILEDIFF_H
#define TILEDIFF_H
#include "nemo_interface/MeasurementTile.h"
struct
TileDiff
{
MeasurementTile
oldTile
;
MeasurementTile
newTile
;
};
#endif // TILEDIFF_H
src/MeasurementComplexItem/geometry/geometry.h
View file @
4948045b
...
@@ -105,8 +105,9 @@ struct BoundingBox {
...
@@ -105,8 +105,9 @@ struct BoundingBox {
FPolygon
corners
;
FPolygon
corners
;
};
};
constexpr
int
earth_radius
=
6371000
;
// meters (m)
static
constexpr
int
earth_radius
=
6371000
;
// meters (m)
constexpr
double
epsilon
=
std
::
numeric_limits
<
double
>::
epsilon
();
// meters (m)
static
constexpr
double
epsilon
=
std
::
numeric_limits
<
double
>::
epsilon
();
// meters (m)
template
<
class
GeoPoint1
,
class
GeoPoint2
>
template
<
class
GeoPoint1
,
class
GeoPoint2
>
void
toENU
(
const
GeoPoint1
&
origin
,
const
GeoPoint2
&
in
,
FPoint
&
out
)
{
void
toENU
(
const
GeoPoint1
&
origin
,
const
GeoPoint2
&
in
,
FPoint
&
out
)
{
...
...
src/MeasurementComplexItem/nemo_interface/MeasurementTile.cpp
0 → 100644
View file @
4948045b
#include "MeasurementTile.h"
MeasurementTile
::
MeasurementTile
(
QObject
*
parent
)
:
GeoArea
(
parent
),
_progress
(
0
),
_id
(
0
)
{
init
();
}
MeasurementTile
::
MeasurementTile
(
const
MeasurementTile
&
other
,
QObject
*
parent
)
:
GeoArea
(
other
,
parent
),
_progress
(
other
.
_progress
),
_id
(
other
.
_id
)
{
init
();
}
MeasurementTile
::~
MeasurementTile
()
{}
MeasurementTile
&
MeasurementTile
::
operator
=
(
const
MeasurementTile
&
other
)
{
GeoArea
::
operator
=
(
other
);
setProgress
(
other
.
_progress
);
setId
(
other
.
_id
);
return
*
this
;
}
QString
MeasurementTile
::
mapVisualQML
()
const
{
return
QStringLiteral
(
""
);
}
QString
MeasurementTile
::
editorQML
()
const
{
return
QStringLiteral
(
""
);
}
MeasurementTile
*
MeasurementTile
::
clone
(
QObject
*
parent
)
const
{
return
new
MeasurementTile
(
*
this
,
parent
);
}
void
MeasurementTile
::
push_back
(
const
QGeoCoordinate
&
c
)
{
this
->
appendVertex
(
c
);
}
void
MeasurementTile
::
init
()
{
this
->
setObjectName
(
"Tile"
);
}
uint64_t
MeasurementTile
::
id
()
const
{
return
_id
;
}
void
MeasurementTile
::
setId
(
const
uint64_t
&
id
)
{
if
(
_id
!=
id
)
{
_id
=
id
;
emit
idChanged
();
}
}
double
MeasurementTile
::
progress
()
const
{
return
_progress
;
}
void
MeasurementTile
::
setProgress
(
double
progress
)
{
if
(
_progress
!=
progress
)
{
_progress
=
progress
;
emit
progressChanged
();
}
}
src/MeasurementComplexItem/nemo_interface/MeasurementTile.h
0 → 100644
View file @
4948045b
#pragma once
#include "geometry/GeoArea.h"
#include <QGeoCoordinate>
class
MeasurementTile
:
public
GeoArea
{
Q_OBJECT
public:
MeasurementTile
(
QObject
*
parent
=
nullptr
);
MeasurementTile
(
const
MeasurementTile
&
other
,
QObject
*
parent
=
nullptr
);
~
MeasurementTile
();
MeasurementTile
&
operator
=
(
const
MeasurementTile
&
other
);
Q_PROPERTY
(
double
progress
READ
progress
NOTIFY
progressChanged
)
Q_PROPERTY
(
long
id
READ
id
NOTIFY
idChanged
)
virtual
QString
mapVisualQML
()
const
override
;
virtual
QString
editorQML
()
const
override
;
virtual
MeasurementTile
*
clone
(
QObject
*
parent
)
const
;
void
push_back
(
const
QGeoCoordinate
&
c
);
double
progress
()
const
;
void
setProgress
(
double
progress
);
uint64_t
id
()
const
;
void
setId
(
const
uint64_t
&
id
);
signals:
void
progressChanged
();
void
idChanged
();
private:
void
init
();
double
_progress
;
long
_id
;
};
src/MeasurementComplexItem/nemo_interface/SnakeTile.cpp
deleted
100644 → 0
View file @
0e658bb3
#include "SnakeTile.h"
SnakeTile
::
SnakeTile
(
QObject
*
parent
)
:
GeoArea
(
parent
)
{
init
();
}
SnakeTile
::
SnakeTile
(
const
SnakeTile
&
other
,
QObject
*
parent
)
:
GeoArea
(
other
,
parent
)
{
init
();
}
SnakeTile
::~
SnakeTile
()
{}
QString
SnakeTile
::
mapVisualQML
()
const
{
return
QStringLiteral
(
""
);
}
QString
SnakeTile
::
editorQML
()
const
{
return
QStringLiteral
(
""
);
}
SnakeTile
*
SnakeTile
::
clone
(
QObject
*
parent
)
const
{
return
new
SnakeTile
(
*
this
,
parent
);
}
void
SnakeTile
::
push_back
(
const
QGeoCoordinate
&
c
)
{
this
->
appendVertex
(
c
);
}
void
SnakeTile
::
init
()
{
this
->
setObjectName
(
"Tile"
);
}
src/MeasurementComplexItem/nemo_interface/SnakeTile.h
deleted
100644 → 0
View file @
0e658bb3
#pragma once
#include "geometry/GeoArea.h"
#include <QGeoCoordinate>
class
SnakeTile
:
public
GeoArea
{
Q_OBJECT
public:
SnakeTile
(
QObject
*
parent
=
nullptr
);
SnakeTile
(
const
SnakeTile
&
other
,
QObject
*
parent
=
nullptr
);
~
SnakeTile
();
virtual
QString
mapVisualQML
()
const
override
;
virtual
QString
editorQML
()
const
override
;
virtual
SnakeTile
*
clone
(
QObject
*
parent
)
const
;
void
push_back
(
const
QGeoCoordinate
&
c
);
private:
void
init
();
};
src/MeasurementComplexItem/nemo_interface/SnakeTiles.h
View file @
4948045b
#pragma once
#pragma once
#include "
Snake
Tile.h"
#include "
Measurement
Tile.h"
#include "Wima/Geometry/GenericPolygonArray.h"
#include "Wima/Geometry/GenericPolygonArray.h"
using
SnakeTiles
=
GenericPolygonArray
<
Snake
Tile
,
QVector
>
;
using
SnakeTiles
=
GenericPolygonArray
<
Measurement
Tile
,
QVector
>
;
src/MeasurementComplexItem/qml/MeasurementAreaMapVisual.qml
View file @
4948045b
...
@@ -48,7 +48,34 @@ Item {
...
@@ -48,7 +48,34 @@ Item {
interiorOpacity
:
_root
.
opacity
interiorOpacity
:
_root
.
opacity
}
}
// Add Snake tiles to the map
Repeater
{
id
:
progressRepeater
property
bool
enable
:
geoArea
.
showTiles
.
value
model
:
enable
?
geoArea
.
tiles
:
[]
Item
{
property
var
_tileComponent
Component.onCompleted
:
{
_tileComponent
=
tileComponent
.
createObject
(
map
)
_tileComponent
.
polygon
.
path
=
Qt
.
binding
(
function
()
{
return
object
.
path
})
_tileComponent
.
polygon
.
opacity
=
0.6
_tileComponent
.
polygon
.
border
.
color
=
"
black
"
_tileComponent
.
polygon
.
border
.
width
=
1
_tileComponent
.
polygon
.
color
=
Qt
.
binding
(
function
()
{
return
getColor
(
object
.
progress
)
})
}
Component.onDestruction
:
{
_tileComponent
.
destroy
()
}
}
}
Component
{
Component
{
id
:
tileComponent
id
:
tileComponent
...
@@ -86,32 +113,4 @@ Item {
...
@@ -86,32 +113,4 @@ Item {
return
"
limegreen
"
return
"
limegreen
"
}
}
Repeater
{
id
:
progressRepeater
property
bool
enable
:
geoArea
.
showTiles
.
value
model
:
enable
?
geoArea
.
tiles
:
[]
Item
{
property
var
_tileComponent
property
int
_progress
:
_root
.
geoArea
.
progress
[
index
]
?
_root
.
geoArea
.
progress
[
index
]
:
0
Component.onCompleted
:
{
_tileComponent
=
tileComponent
.
createObject
(
map
)
_tileComponent
.
polygon
.
path
=
Qt
.
binding
(
function
()
{
return
object
.
path
})
_tileComponent
.
polygon
.
opacity
=
0.6
_tileComponent
.
polygon
.
border
.
color
=
"
black
"
_tileComponent
.
polygon
.
border
.
width
=
1
_tileComponent
.
polygon
.
color
=
Qt
.
binding
(
function
()
{
return
getColor
(
_progress
)
})
}
Component.onDestruction
:
{
_tileComponent
.
destroy
()
}
}
}
}
}
src/comm/QmlObjectListHelper.cpp
0 → 100644
View file @
4948045b
#include "QmlObjectListHelper.h"
src/comm/QmlObjectListHelper.h
0 → 100644
View file @
4948045b
#ifndef QMLOBJECTLISTHELPER_H
#define QMLOBJECTLISTHELPER_H
#include "QmlObjectListModel.h"
#include <type_traits>
template
<
class
PtrType
>
inline
bool
contains
(
const
QmlObjectListModel
&
in
,
const
PtrType
toFind
)
{
static_assert
(
std
::
is_pointer
<
PtrType
>::
value
,
"PtrType must be a pointer."
);
typedef
typename
std
::
remove_pointer
<
PtrType
>::
type
Type
;
for
(
int
i
=
0
;
i
<
in
.
count
();
++
i
)
{
const
auto
obj
=
qobject_cast
<
const
Type
*>
(
in
[
i
]);
Q_ASSERT
(
obj
!=
nullptr
);
Q_ASSERT
(
toFind
!=
nullptr
);
if
(
*
obj
==
*
toFind
)
{
return
true
;
}
}
return
false
;
}
template
<
class
PtrType
>
inline
bool
equals
(
const
QmlObjectListModel
&
list
,
const
QmlObjectListModel
&
otherList
)
{
static_assert
(
std
::
is_pointer
<
PtrType
>::
value
,
"PtrType must be a pointer."
);
typedef
typename
std
::
remove_pointer
<
PtrType
>::
type
Type
;
if
(
list
.
count
()
==
otherList
.
count
())
{
for
(
int
i
=
0
;
i
<
list
.
count
();
++
i
)
{
const
auto
obj
=
qobject_cast
<
const
Type
*>
(
list
[
i
]);
const
auto
otherObj
=
qobject_cast
<
const
Type
*>
(
otherList
[
i
]);
Q_ASSERT
(
obj
!=
nullptr
);
Q_ASSERT
(
otherObj
!=
nullptr
);
if
(
*
obj
!=
*
otherObj
)
{
return
false
;
}
}
return
true
;
}
else
{
return
false
;
}
}
#endif // QMLOBJECTLISTHELPER_H
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment