Initial commit

This commit is contained in:
torsten committed 2026-08-13 18:33:10 +02:00
commit b7d90b9afe
16245 files changed
+3396666

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
#
# @RDE_HEADER@
# Small hack to provide the missing smybol to 'typeinfo for std::bad_array_new_length@Qt_5'
# $Id$
# $HeadURL$
# $Author$
#
IF(UNIX)
NGL_ADD_LIBRARY(WS SV AAR ArcGISQt5Hack SHARED arcgisqt5hack.cpp arcgisqt5hack.version) # SV,AAR for CES - expand polygon
NGL_TARGET_LINK_LIBRARIES(ArcGISQt5Hack PRIVATE "-Wl,--version-script,${CMAKE_CURRENT_SOURCE_DIR}/arcgisqt5hack.version")
ELSE()
NGL_ADD_LIBRARY(WS SV AAR ArcGISQt5Hack STATIC arcgisqt5hack.cpp) # SV,AAR for CES - expand polygon
ENDIF()
NGL_INSTALL_TARGET(ArcGISQt5Hack)
+21
View File
@@ -0,0 +1,21 @@
/**
* @RDE_HEADER@
* Small hack to provide the missing smybol to 'typeinfo for std::bad_array_new_length@Qt_5'
* $Id$
* $HeadURL$
* $Author$
*/
#ifdef WIN32
__declspec(dllexport) int foo42(int) { return 42;} // just a dummy to get windows build running
#else
#include <new>
extern "C++"
{
__attribute__((visibility("default"))) void operator delete(void* ptr, std::size_t) noexcept
{
::operator delete(ptr);
}
}
#endif
@@ -0,0 +1,3 @@
Qt_5 {
*;
};
+587
View File
@@ -0,0 +1,587 @@
#
# $Id$
# $HeadURL$
# $Author$
#
FIND_PACKAGE(ArcGIS REQUIRED)
ADD_SUBDIRECTORY(ArcGISQt5Hack)
# During development: (instead top CMakeLists.txt"
IF(NOT MSVC)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wextra")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wextra")
ENDIF()
OPTION(BUILD_SITMAP_UNIT_TESTS "Build situation map unit tests" ON)
SET(GraphicalObjects_SRCS
GraphicalObjects/abstract_graphical_object.cpp
GraphicalObjects/ellipse_parameters.cpp
GraphicalObjects/graphical_object_attributes.h
GraphicalObjects/graphical_object_attributes.cpp
GraphicalObjects/graphical_object_ellipse.cpp
GraphicalObjects/graphical_object_multi_path.cpp
GraphicalObjects/graphical_object_path.cpp
GraphicalObjects/graphical_object_picture_marker.cpp
GraphicalObjects/graphical_object_range.cpp
GraphicalObjects/graphical_object_text.cpp
GraphicalObjects/tactical_appearance.cpp
GraphicalObjects/graphical_object_cube.cpp
GraphicalObjects/graphical_object_scene_marker.cpp
GraphicalObjects/graphical_object_model_scene_marker.cpp
GraphicalObjects/graphical_object_renderer_attributes.cpp
)
SET(GraphicalObjects_HDRS
GraphicalObjects/abstract_graphical_object.h
GraphicalObjects/ellipse_parameters.h
GraphicalObjects/graphical_object_ellipse.h
GraphicalObjects/graphical_object_multi_path.h
GraphicalObjects/graphical_object_picture_marker.h
GraphicalObjects/graphical_object_path.h
GraphicalObjects/graphical_object_range.h
GraphicalObjects/graphical_object_text.h
GraphicalObjects/stub_graphical_object.h
GraphicalObjects/tactical_appearance.h
GraphicalObjects/graphical_object_cube.h
GraphicalObjects/graphical_object_scene_marker.h
GraphicalObjects/graphical_object_model_scene_marker.h
GraphicalObjects/graphical_object_renderer_attributes.cpp
)
QT5_ADD_RESOURCES(GraphicalObjects_SRCS Resources/situation_map.qrc)
SET(Map_SRCS
Map/bounding_box.cpp
Map/fill_style.cpp
Map/line_style.cpp
Map/map.cpp
Map/map_notifier.cpp
Map/map_settings.cpp
Map/map_initializer.cpp
Map/object_container.cpp
Map/object_factory.cpp
Map/operator_map_settings.cpp
# Map/point3D.cpp
Map/party_id_to_color_functor.cpp
# Map/scene_marker_data.cpp
Map/situation_map_utils.cpp
Map/terrain_profile_parameter.cpp
Map/text_style.cpp
Map/geometry_model_renderer.cpp
Map/viewshed_parameter.cpp
)
SET(Map_HDRS
Map/situation_map_ahtc.ini
Map/situation_map_defaults.ini
Map/situation_map_guez.ini
Map/situation_map_qatar.ini
Map/situation_map_guez_tablet.ini
Map/situation_map_ahtc_tablet.ini
Map/abstract_effect_simulation.h
Map/abstract_map_object.h
Map/bounding_box.h
Map/camera.h
Map/camera.cpp
Map/coord_translation_functor.h
# Map/drop_event_manager.h
Map/fill_style.h
Map/generic_object_factory.h
Map/grid.h
Map/map.h
Map/map_notifier.h
Map/map_settings.h
Map/map_initializer.h
Map/measurement.h
Map/layer_factory.h
Map/layer_parameter.h
Map/layer_parameter.cpp
Map/location_display.h
Map/line_style.h
Map/object_container.h
Map/object_factory.h
Map/object_indicator.h
Map/operator_map_settings.h
Map/overview_map_handler.h
Map/party_id_to_color_functor.h
Map/pixel_based_textbox_handler.h
Map/point3D.h
# Map/scene_marker_data.cpp
Map/sidc_symbol.h
Map/sight_fan.h
Map/situation_map_utils.h
Map/situation_map_types.h
Map/terrain_profile_parameter.h
Map/terrain_profile.h
Map/text_style.h
Map/viewpoint2D.h
Map/viewpoint2D.cpp
Map/viewshed_parameter.h
Map/viewshed.h
Map/camera.h
Map/camera.cpp
Map/overlay_handler.h
Map/geometry_model_renderer.h
)
SET(MapImpl_SRCS
MapImpl/abstract_effect_simulation_impl.cpp
MapImpl/abstract_effect_simulation_impl.h
MapImpl/abstract_map_object_impl.cpp
MapImpl/abstract_map_object_impl.h
MapImpl/coordinate_translation.cpp
MapImpl/coordinate_translation_thread.cpp
MapImpl/grid_impl.cpp
MapImpl/jump_point_model.cpp
MapImpl/jump_point_custom_view.cpp
MapImpl/jump_point_widget.cpp
MapImpl/logical_layer.cpp
MapImpl/layer_factory_impl.cpp
MapImpl/layer_control_widget.cpp
MapImpl/screen_location_elevation_helper_base.cpp
MapImpl/location_display_impl.cpp
MapImpl/location_to_elevation_helper.cpp
MapImpl/location_to_elevation_bulk.cpp
MapImpl/map_impl.cpp
MapImpl/map_layer.cpp
MapImpl/measurement_impl.cpp
MapImpl/object_container_impl.cpp
MapImpl/object_indicator_impl.cpp
MapImpl/overview_map_handler_impl.cpp
MapImpl/pixel_based_textbox_handler_impl.cpp
MapImpl/screen_to_location_helper.cpp
MapImpl/sidc_symbol_impl.cpp
MapImpl/sight_fan_impl.cpp
MapImpl/terrain_profile_impl.cpp
MapImpl/terrain_profile_processor.cpp
# MapImpl/tms_layer.cpp
MapImpl/viewshed_base.cpp
MapImpl/viewshed_analysis.cpp
MapImpl/viewshed_geo_processing.cpp
MapImpl/viewshed_base.cpp
MapImpl/viewshed_impl.cpp
MapImpl/jump_point_namer.cpp
MapImpl/geometry_model_renderer_esri.cpp
MapImpl/overlay_handler_impl.cpp
)
SET(MapImpl_HDRS
MapImpl/abstract_effect_simulation_impl.h
MapImpl/abstract_map_object_impl.h
MapImpl/coordinate_translation.h
MapImpl/coordinate_translation_thread.h
MapImpl/coord_translation_functor_impl.h
MapImpl/geometry_model_renderer_esri.h
MapImpl/grid_impl.cpp
MapImpl/grid_impl.h
MapImpl/jump_point_model.h
MapImpl/jump_point_custom_view.h
MapImpl/jump_point_widget.h
MapImpl/logical_layer.h
MapImpl/layer_control_widget.h
MapImpl/layer_factory_impl.h
MapImpl/screen_location_elevation_helper_base.h
MapImpl/location_display_impl.h
MapImpl/location_to_elevation_helper.h
MapImpl/location_to_elevation_bulk.h
MapImpl/map_impl.h
MapImpl/map_layer.h
MapImpl/measurement_impl.h
MapImpl/object_container_impl.h
MapImpl/object_indicator_impl.h
MapImpl/overview_map_handler_impl.h
MapImpl/overlay_handler_impl.h
MapImpl/pixel_based_textbox_handler_impl.h
MapImpl/screen_to_location_helper.h
MapImpl/sidc_symbol_impl.h
MapImpl/sight_fan_impl.h
MapImpl/terrain_profile_impl.h
MapImpl/terrain_profile_processor.h
# MapImpl/tms_layer.h
MapImpl/viewshed_base.h
MapImpl/viewshed_analysis.h
MapImpl/viewshed_geo_processing.h
MapImpl/viewshed_impl.h
MapImpl/jump_point_namer.h
)
SET(MapObj_SRCS
MapObjects/geometry.cpp
)
SET(MapObj_HDRS
MapObjects/abstract_multi_point_map_object.h
MapObjects/abstract_single_point_map_object.h
MapObjects/air_support.h
MapObjects/area_fire.h
MapObjects/biological_agent.h
MapObjects/building_outline.h
MapObjects/chemical_agent.h
MapObjects/cube.h
MapObjects/danger_area.h
MapObjects/functional_area.h
MapObjects/geo_image.h
MapObjects/geometry.h
MapObjects/hit_line.h
MapObjects/line_of_sight.h
MapObjects/map_object_attributes.h
MapObjects/map_object_indicator.h
MapObjects/mine.h
MapObjects/mine_field.h
MapObjects/nuclear_agent.h
MapObjects/participant.h
MapObjects/polygon.h
MapObjects/polyline.h
MapObjects/single_fire.h
# MapObjects/single_fire_central.h
MapObjects/sound_area.h
MapObjects/text.h
MapObjects/virtual_mine.h
#situation_map_objects_export.h
)
SET (LayerAggregation_SRCS
LayerAggregation/layer_aggregation_widget.cpp
LayerAggregation/participant_aggregation_dialog.cpp
)
SET (LayerAggregation_HDRS
LayerAggregation/layer_aggregation_widget.h
LayerAggregation/participant_aggregation_dialog.h
)
SET(Rubberbands_SRCS
Rubberbands/abstract_event_dispatcher.cpp
Rubberbands/drawable_ellipse.cpp
Rubberbands/drawable_line.cpp
Rubberbands/drawable_painter_path.cpp
Rubberbands/drawable_smooth_path.cpp
Rubberbands/event_delegater.cpp
Rubberbands/event_dispatcher.cpp
Rubberbands/event_mouse_navigator.cpp
Rubberbands/gestures.cpp
Rubberbands/rubberband_base.cpp
Rubberbands/rubberband.cpp
Rubberbands/rubberband_3d.cpp
Rubberbands/rubberband_arrow.cpp
Rubberbands/rubberband_corridor.cpp
Rubberbands/rubberband_drawable.cpp
Rubberbands/rubberband_factory.cpp
Rubberbands/rubberband_frame_handle.cpp
Rubberbands/rubberband_free_hand.cpp
Rubberbands/rubberband_ellipse.cpp
Rubberbands/rubberband_ellipse_handle.cpp
Rubberbands/rubberband_handle.cpp
Rubberbands/rubberband_handle_proxy.cpp
Rubberbands/rubberband_internal.cpp
Rubberbands/rubberband_line_handle.cpp
Rubberbands/rubberband_multi_point_feature.cpp
Rubberbands/rubberband_multi_ellipse_feature.cpp
Rubberbands/rubberband_multi_point_feature_3d.cpp
Rubberbands/rubberband_point_handle.cpp
Rubberbands/rubberband_point_cluster.cpp
Rubberbands/rubberband_point_feature.cpp
Rubberbands/rubberband_point_feature_3d.cpp
Rubberbands/rubberband_polyline_width.cpp
Rubberbands/rubberband_rectangle.cpp
Rubberbands/rubberband_smooth_line.cpp
Rubberbands/rubberband_state_select.cpp
Rubberbands/rubberband_transform.cpp
Rubberbands/rubberband_transform_move.cpp
Rubberbands/rubberband_transform_rotate.cpp
Rubberbands/rubberband_transform_scale.cpp
Rubberbands/transient_tactical_symbol.cpp
)
SET(Rubberbands_HDRS
Rubberbands/abstract_event_dispatcher.h
Rubberbands/drawable_ellipse.h
Rubberbands/drawable_line.h
Rubberbands/drawable_painter_path.h
Rubberbands/drawable_smooth_path.h
Rubberbands/event_delegater.h
Rubberbands/event_dispatcher.h
Rubberbands/event_mouse_navigator.h
Rubberbands/gestures.h
Rubberbands/rubberband_free_hand.h
Rubberbands/rubberband_base.h
Rubberbands/rubberband_base_internal.h
Rubberbands/rubberband.h
Rubberbands/rubberband_3d.h
Rubberbands/rubberband_arrow.h
Rubberbands/rubberband_corridor.h
Rubberbands/rubberband_drawable.h
Rubberbands/rubberband_ellipse.h
Rubberbands/rubberband_ellipse_handle.h
Rubberbands/rubberband_factory.h
Rubberbands/rubberband_frame_handle.h
Rubberbands/rubberband_handle.h
Rubberbands/rubberband_handle_proxy.h
Rubberbands/rubberband_internal.h
Rubberbands/rubberband_line_handle.h
Rubberbands/rubberband_multi_point_feature.h
Rubberbands/rubberband_multi_ellipse_feature.h
Rubberbands/rubberband_multi_point_feature_3d.h
Rubberbands/rubberband_point_cluster.h
Rubberbands/rubberband_point_feature.h
Rubberbands/rubberband_point_feature_3d.h
Rubberbands/rubberband_point_handle.h
Rubberbands/rubberband_polyline_width.h
Rubberbands/rubberband_rectangle.h
Rubberbands/rubberband_smooth_line.h
Rubberbands/rubberband_state_select.h
Rubberbands/rubberband_transform.h
Rubberbands/rubberband_transform_move.h
Rubberbands/rubberband_transform_rotate.h
Rubberbands/rubberband_transform_scale.h
Rubberbands/transient_tactical_symbol.h
)
SET(Utils_SRCS
situation_map_export.h
Utils/movement_track.cpp
Utils/movement_track_manager.cpp
Utils/terrain_profile_dialog.cpp
)
SET(Utils_HDRS
Utils/movement_track.h
Utils/movement_track_manager.h
Utils/terrain_profile_dialog.h
)
SET(MathUtils_SRCS
MathUtils/clipper.cpp
MathUtils/curve.cpp
MathUtils/geometry_engine.cpp
MathUtils/matrix2d.cpp
MathUtils/path_rasterer.cpp
MathUtils/simple_helper.cpp
)
SET(MathUtils_HDRS
MathUtils/clipper.hpp
MathUtils/curve.h
MathUtils/geometry_engine.h
MathUtils/matrix2d.h
MathUtils/math_const.h
MathUtils/path_rasterer.h
MathUtils/simple_helper.h
)
NGL_WRAP_UI(Utils_HDRS
MapImpl/jump_point_custom_view.ui
Utils/terrain_profile_dialog.ui
)
NGL_WRAP_UI(MapImpl_SRCS
MapImpl/jump_point_namer.ui
)
SOURCE_GROUP(GraphicalObjects FILES ${GraphicalObjects_SRCS} ${GraphicalObjects_HDRS})
SOURCE_GROUP(Map FILES ${Map_SRCS} ${Map_HDRS} ${MapImpl_SRCS} ${MapImpl_HDRS})
SOURCE_GROUP(MapObjects FILES ${MapObj_HDRS})
SOURCE_GROUP(LayerAggregation FILES ${LayerAggregation_SRCS} ${LayerAggregation_HDRS})
SOURCE_GROUP(Rubberband FILES ${Rubberbands_SRCS} ${Rubberbands_HDRS})
SOURCE_GROUP(Utils FILES ${Utils_SRCS} ${Utils_HDRS})
SOURCE_GROUP(MathUtils FILES ${MathUtils_SRCS} ${MathUtils_HDRS})
SOURCE_GROUP(SideBar FILES ${SideBar_SRCS} ${SideBar_HDRS})
NGL_ADD_LIBRARY(WS situationMap SHARED
# ${FunctionDialog_SRCS}
${Map_SRCS} ${Map_HDRS} ${MapImpl_SRCS}
${MapObj_SRCS} ${MapObj_HDRS}
${GraphicalObjects_SRCS} ${GraphicalObjects_HDRS}
${LayerAggregation_SRCS} ${LayerAggregation_HDRS}
${Rubberbands_SRCS}
${Utils_SRCS} ${Utils_HDRS}
${MathUtils_SRCS} ${MathUtils_HDRS}
)
NGL_TARGET_INCLUDE_DIRECTORIES(situationMap
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
)
NGL_TARGET_LINK_LIBRARIES(situationMap
PRIVATE
NGLLogger
ArcGIS::ArcGIS
Qt5::Concurrent
Qt5::Network
Qt5::PrintSupport
LtsCoordTrans
ArcGISQt5Hack
PUBLIC
Qt5::Core
Qt5::Widgets
LtsUtilsCoreHeaderOnly # QPoint3D
LtsWidgets
LtsDataView
situationMapCommon
situationMapElevationMap
situationMapSideBar
advancedSymbology
situationMapGeometryModel
ngltools
)
# Copy on LIBchanges:
IF(TARGET situationMap)
SET(SYMBOLE_CSV symbole2.csv)
ADD_CUSTOM_COMMAND(TARGET situationMap POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/FunctionDialog/${SYMBOLE_CSV} $<TARGET_FILE_DIR:situationMap>/${SYMBOLE_CSV}
)
# copy ini to build-dir/share/Lts/situation_map/:
SET(INI_DEST_DIR ${CMAKE_BINARY_DIR}/share/Lts/situation_map)
SET(SITUATIONMAPSETTINGS_INI situation_map_defaults.ini)
SET(SITUATIONMAPSETTINGS_INI_PATH Map/${SITUATIONMAPSETTINGS_INI})
ADD_CUSTOM_COMMAND(TARGET situationMap POST_BUILD
COMMAND ${CMAKE_COMMAND}
ARGS -E copy_if_different ${CMAKE_CURRENT_SOURCE_DIR}/${SITUATIONMAPSETTINGS_INI_PATH} ${INI_DEST_DIR}/${SITUATIONMAPSETTINGS_INI}
)
SET(SITUATIONMAPSETTINGS_INI situation_map_defaults.ini)
SET(SITUATIONMAPSETTINGS_INI_PATH Map/${SITUATIONMAPSETTINGS_INI})
#check if we need to change it
IF(BUILD_RPM AND DEFINED _pname)
IF(UNIX AND "${_pname}" STREQUAL "guez")
MESSAGE("Install situationmap ini for GUEZ")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_guez.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_guez_tablet.ini)
ElseIF(UNIX AND "${_pname}" STREQUAL "ahtc")
MESSAGE("Install situationmap ini for AHTC")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_ahtc.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_ahtc_tablet.ini)
ElseIF(UNIX AND "${_pname}" STREQUAL "l400p2")
MESSAGE("Install situationmap ini for L400P2")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_land400.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_l400p2_tablet.ini)
ElseIF(UNIX AND "${_pname}" STREQUAL "qatar")
MESSAGE("Install situationmap ini for Qatar")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_qatar.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_qatar_tablet.ini)
ElseIF(UNIX AND "${_pname}" STREQUAL "qatarmout")
MESSAGE("Install situationmap ini for Qatar Mout")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_qatarmout.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_qatar_tablet.ini)
ElseIF(UNIX AND "${_pname}" STREQUAL "commaster")
MESSAGE("Install situationmap ini for ComMaster/Luxemburg")
SET(SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_luxemburg.ini)
SET(TABLET_SITUATIONMAPSETTINGS_INI_PATH Map/situation_map_luxemburg_tablet.ini)
ENDIF()
ENDIF()
#now that we set the situation map ini if neccecsary we install it depending of the tablet build or not
IF(TABLET_MODE)
NGL_INSTALL(WS FILES ${TABLET_SITUATIONMAPSETTINGS_INI_PATH} DESTINATION "./share/Lts/situation_map" RENAME ${SITUATIONMAPSETTINGS_INI})
ELSE()
NGL_INSTALL(WS FILES ${SITUATIONMAPSETTINGS_INI_PATH} DESTINATION "./share/Lts/situation_map" RENAME ${SITUATIONMAPSETTINGS_INI})
ENDIF()
ENDIF()
IF(BUILD_TESTING AND BUILD_SITMAP_UNIT_TESTS)
NGL_ADD_LIBRARY(WS NO_TRANSLATION situationMap_static STATIC
${FunctionDialog_SRCS}
${Map_SRCS} ${Map_HDRS} ${MapImpl_SRCS}
${MapObj_SRCS} ${MapObj_HDRS}
${GraphicalObjects_SRCS}
${Rubberbands_SRCS}
${Utils_SRCS} ${Utils_HDRS}
${MathUtils_SRCS} ${MathUtils_HDRS}
)
NGL_TARGET_COMPILE_DEFINITIONS(situationMap_static PUBLIC "SITUATIONMAP_EXPORT=")
NGL_TARGET_INCLUDE_DIRECTORIES(situationMap_static
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}
)
NGL_TARGET_LINK_LIBRARIES(situationMap_static
PRIVATE
NGLLogger
Qt5::Concurrent
Qt5::Network
Qt5::PrintSupport
LtsCoordTrans
ArcGISQt5Hack
PUBLIC
Qt5::Core
Qt5::Widgets
LtsUtilsCoreHeaderOnly # QPoint3D
LtsWidgets
LtsDataView
situationMapCommon
situationMapElevationMap
situationMapSideBar
advancedSymbology_static
situationMapGeometryModel
ngltools
)
ENDIF()
NGL_INSTALL_TARGET(situationMap)
# Choose logical order instead of alphabetical.
# UnitTest should be performed BEFORE ModuleTest
ADD_SUBDIRECTORY(Common)
ADD_SUBDIRECTORY(ElevationMap)
ADD_SUBDIRECTORY(Symbology)
ADD_SUBDIRECTORY(SymbologyGui)
ADD_SUBDIRECTORY(SymbologyXML)
ADD_SUBDIRECTORY(MapObjectsImpl)
#ADD_SUBDIRECTORY(LocalSrv)
ADD_SUBDIRECTORY(SideBar)
ADD_SUBDIRECTORY(FunctionDialog)
ADD_SUBDIRECTORY(GeometryModel)
ADD_SUBDIRECTORY(Portal)
ADD_SUBDIRECTORY(Tests)
IF(BUILD_SITMAP_UNIT_TESTS)
ADD_SUBDIRECTORY(EvalApps)
ADD_SUBDIRECTORY(UnitTests)
ADD_SUBDIRECTORY(UnitTestsGeoModel)
ADD_SUBDIRECTORY(ModuleTests)
ENDIF()
+44
View File
@@ -0,0 +1,44 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# LIBRARY
SET(Common_SRCS
id_handler.cpp
referenced.cpp
resource_path.cpp
situation_map_logging.cpp
)
SET (Common_HDRS
exception.h
id_handler.h
ref_pointer.h
referenced.h
resource_path.h
situation_map_common_export.h
situation_map_logging.h
simple_value.h
)
SOURCE_GROUP(Common FILES ${Common_SRCS} ${Common_HDRS})
NGL_ADD_LIBRARY(WS AAR HLA SV situationMapCommon SHARED ${Common_SRCS} ${Common_HDRS})
NGL_TARGET_LINK_LIBRARIES(situationMapCommon
PRIVATE
ngltools
PUBLIC
NGLLogger
Qt5::Core
)
NGL_TARGET_INCLUDE_DIRECTORIES(situationMapCommon
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/..
)
NGL_INSTALL_TARGET(situationMapCommon)
+93
View File
@@ -0,0 +1,93 @@
/**
**
** @RDE_HEADER@
** Implementation of Splines
** $Id$
** $HeadURL$
** $Author$
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Joerg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 19.06.2012, JT : Urversion for use in VIntel project
** 12.08.2015, JT : adapted to SituationMap
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <QtCore/QString>
namespace SituationMap
{
/**
\class Exception exception.h
\brief Basisklasse for Exception-Handling
The class Exception defines a very simple exception handler, that will be able to return
a string message when an exception is thrown
*/
class Exception
{
public:
Exception()
: _message(QLatin1String("Error"))
{}
Exception(const Exception &) = default;
explicit Exception(const QString &msg)
: _message(msg)
{}
// explicit is not used here because
// throw SituationMap::Exception(__FILE__, __LINE__, "Message" );
// will become more complicated
Exception(const char *Source
, unsigned int lineNo
, const char *msg)
: Exception(QLatin1String(Source), lineNo, QLatin1String(msg))
{}
Exception(const char *Source
, unsigned int lineNo
, const QString &msg)
: Exception(QLatin1String(Source), lineNo, msg)
{}
Exception(const QString &Source
, unsigned int lineNo
, const QString &msg)
: _message(QStringLiteral("\"%1\" in %2 at %3").arg(msg, Source).arg(lineNo))
{}
~Exception() = default;
Exception &operator = (const Exception &e) = default;
QString getMessage() const { return _message; } //!< returns a specific error message as QString
protected:
QString _message;
};
} // namespace SituationMap
+345
View File
@@ -0,0 +1,345 @@
/**
**
** @RDE_HEADER@
** Simple Id Handler
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Linux, Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** ??.??.1994, JT : First edition for use with Tcl/Tk in START Feature Editor fe
** 16.08.2004, JT : Refactoring for use in Geo-Map Library GeISiR
** 11.05.2015, JT : Refactoring for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "id_handler.h"
#include <typeinfo>
#include "referenced.h"
namespace SituationMap
{
const IdHandler::UniqueId IdHandler::BadId = static_cast<IdHandler::UniqueId>(-1);
bool IdHandler::isIdValid(const UniqueId &id) { return id != BadId; }
IdHandler::IdHandler(unsigned int maxItems, unsigned int blockS)
: _blockSize(blockS)
, _maxIds(maxItems)
, _freeIds(maxItems)
, _firstFree(BadId)
, _lastAlloc(BadId)
, _notify(getLogger("IdHandler"))
, _idVector()
{
} // IdHandler::IdHandler
IdHandler::~IdHandler()
{
if (!_idVector.isEmpty())
{
if (_lastAlloc != BadId)
{
for (UniqueId i = 0; i <= _lastAlloc; ++i)
_idVector[i] = nullptr;
}
_maxIds = 0;
_freeIds = 0;
_firstFree = BadId;
_lastAlloc = BadId;
}
} // IdHandler::~IdHandler
bool IdHandler::initMemHandler()
{
if (_idVector.empty())
{
_idVector.resize(_maxIds);
_freeIds = _maxIds;
_firstFree = 0;
_lastAlloc = BadId;
return false;
}
return true;
} // IdHandler::initMemHandler
void IdHandler::lookForNextFreeId()
{
++_firstFree;
while (_firstFree < _lastAlloc)
{
if (!_idVector[_firstFree]) break;
++_firstFree;
}
if (_firstFree >= _lastAlloc) _firstFree = _maxIds;
} //IdHandler::lookForNextFreeId
bool IdHandler::valid() const
{
return ((static_cast<quint32>(_idVector.size()) == _maxIds) && (_firstFree != BadId));
} // IdHandler::valid
unsigned int IdHandler::countReservedIds() const { return _maxIds; }
unsigned int IdHandler::countUsedIds() const { return _maxIds - _freeIds; }
unsigned int IdHandler::countFreeIds() const { return _freeIds; }
bool IdHandler::resize(quint32 maxItems)
{
if (initMemHandler())
{
if (maxItems > static_cast<quint32>(_idVector.size()))
{
_freeIds += maxItems - _maxIds;
_maxIds = maxItems;
_idVector.resize(_maxIds);
}
}
return (static_cast<quint32>(_idVector.size()) == _maxIds);
} // IdHandler::resize
IdHandler::UniqueId IdHandler::createId(AccessableId *idPtr)
{
initMemHandler();
UniqueId id((_lastAlloc == BadId) ? 0 : _lastAlloc + 1);
if (id < _maxIds)
{
_idVector[id] = idPtr;
_lastAlloc = id;
if (_firstFree == id) ++_firstFree;
}
else
{
if (_firstFree >= _maxIds)
{
resize(_maxIds + _blockSize);
if (id < _maxIds)
{
_idVector[id] = idPtr;
_lastAlloc = id;
if (_firstFree == id)
++_firstFree;
}
else
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< QStringLiteral("Id table overflow, object <%1> of type <%2>, reserved Ids %3")
.arg(reinterpret_cast<qulonglong>(idPtr), 0, 16)
.arg(QLatin1String(typeid(idPtr).name()))
.arg(_maxIds);
return BadId;
}
}
else
{
id = _firstFree;
_idVector[id] = idPtr;
lookForNextFreeId();
}
}
_freeIds--;
return (id);
} // IdHandler::createId
bool IdHandler::reserveId(UniqueId id, AccessableId *idPtr)
{
initMemHandler();
if (!valid())
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< QStringLiteral("IdHandler is not initialzed, object <%1> of type <%2> ")
.arg(reinterpret_cast<qulonglong>(idPtr), 0, 16)
.arg(QLatin1String(typeid(idPtr).name()));
return false;
}
if (id == BadId)
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< " Requested id is bad id <" << id << ">";
return false;
}
if (id >= _maxIds)
{
if (!resize((((id + 1) / _blockSize) + 1) * _blockSize))
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< QStringLiteral("Requested id is to large, id <%1> object <%2> of type <%3>.")
.arg(id)
.arg(reinterpret_cast<qulonglong>(idPtr), 0, 16)
.arg(QLatin1String(typeid(idPtr).name()));
return false;
}
}
if (_idVector[id])
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< QStringLiteral("Requested id is already in use, object <%1> of type <%2>.")
.arg(reinterpret_cast<qulonglong>(idPtr), 0, 16)
.arg(QLatin1String(typeid(idPtr).name()));
return false;
}
_idVector[id] = idPtr;
if ((_lastAlloc == BadId) || (id > _lastAlloc)) _lastAlloc = id;
if (id == _firstFree)
{
++_firstFree;
lookForNextFreeId();
}
_freeIds--;
return true;
} // IdHandler::reserveId
bool IdHandler::freeId(UniqueId id)
{
if (valid() && (_lastAlloc != BadId) && (id != BadId))
{
if (id <= _lastAlloc)
{
if (_idVector[id])
{
_idVector[id] = nullptr;
if (id < _firstFree) _firstFree = id;
if (id == _lastAlloc)
{
/* ---------------------------------------------------------
* Last allocated id is now free. Search new lastAlloc ...
* ---------------------------------------------------------- */
if (_lastAlloc > 0)
{
--_lastAlloc;
while (!_idVector[_lastAlloc])
{
if (_lastAlloc < 1)
{
_lastAlloc = BadId;
break;
}
else --_lastAlloc;
} // while
}
else _lastAlloc = BadId;
}
_freeIds++;
return true;
}
}
else
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< "Id is not in use, id <" << id << ">";
}
}
else
{
if (id != BadId)
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< "IdHandler is not initialzed.";
}
else
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< "Bad id, id <" << id << ">";
}
}
return false;
} // IdHandler::freeId
IdHandler::AccessableId *IdHandler::accessId(UniqueId id) const
{
if (valid())
{
if ((id != BadId) && (id <= _lastAlloc))
return _idVector[id].get();
else
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< "Bad id or id is not in use, id <" << id << ">";
#ifdef UseDebugBreaks
#ifdef WIN32
#ifdef _DEBUG
__asm int 3
#endif
#endif
#endif
}
}
else
{
ERROR_LOG(_notify)
<< LOGFUNCTION
<< "IdHandler is not initialzed";
}
return nullptr;
} // IdHandler::accessId
bool IdHandler::isIdInUse(UniqueId id) const
{
if (valid())
{
if ((id != BadId) && (id <= _lastAlloc))
return (_idVector[id].get() != nullptr);
}
return false;
} // IdHandler::isIdInUse
} // SituationMap
+120
View File
@@ -0,0 +1,120 @@
/**
**
** @RDE_HEADER@
** Simple Id Handler
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Linux, Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** ??.??.1994, JT : First edition for use with Tcl/Tk in START Feature Editor fe
** 16.08.2004, JT : Refactoring for use in Geo-Map Library GeISiR
** 11.05.2015, JT : Refactoring for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
//#include <Map/situation_map_types.h>
#include "referenced.h"
#include "ref_pointer.h"
#include "situation_map_logging.h"
#include "situation_map_common_export.h"
#include <QVector>
namespace SituationMap
{
/**
@class IdHandler id_handler.h
@brief Simple IdHandler to map id's to data pointers
IdHandler is a class to manage integer ids and map them to data pointers. The IdHandler stores
the data pointer in a vector container. The index of the data pointer in this vector is used as
id value. If the vector gets out of size new vector elements will be added automaticaly. This
will be done blockwises. The initial size of the vector and the block size can be set as
parameter of IdHandler ctor. The default values are 10000 for the initial vector size and 1000
for the block size.
*/
class SITUATIONMAPCOMMON_EXPORT IdHandler
{
public:
using AccessableId = Referenced;
using UniqueId = quint32; // see UniqueMapId @ situation_map_types.h
static const UniqueId BadId;
static bool isIdValid(const UniqueId &id); //!< Checks if the \c id is not a BadId
IdHandler(const IdHandler &) = delete;
IdHandler &operator = (const IdHandler &) = delete;
/**
@brief Construction of IdHandler
Initialize IdHandler instances. The vector container to store data pointers is
reserved with \c maxItems. If the vector gets out of size new vector elements
will be added automaticaly
@param maxItems Initial size of the vector container to store the data pointer.
@param blockS Block size is used when the vector container will be extended.
*/
IdHandler(unsigned int maxItems = 10000, unsigned int blockS = 1000);
/**
@brief Destruction of IdHandler
*/
virtual ~IdHandler();
bool valid() const; //!< Checks if IdHandler is valid
quint32 countReservedIds() const; //!< Returns count of reserved ids.
quint32 countUsedIds() const; //!< Returns count of used ids.
quint32 countFreeIds() const; //!< Returnes count of free ids
bool resize(quint32 maxItems); //!< Resize idMem to store now maxItems ids. Returns true on success.
UniqueId createId(AccessableId *); //!< Create new \c id and returns this new id.
bool reserveId(UniqueId id, AccessableId *); //!< Reserve \c id if the id is unused. Returns true if \c id is not already in use and reservation of \c id is successful elsewise false.
bool freeId(UniqueId id); //!< Frees \c id. Returns true on success elsewise false.
AccessableId *accessId(UniqueId id) const; //!< Returns pointer to object for \c id. Returns pointer on success elsewise \c nullptr.
bool isIdInUse(UniqueId id) const; //!< Checks if a id is already in use.
protected:
quint32 _blockSize;
quint32 _maxIds;
quint32 _freeIds;
UniqueId _firstFree;
UniqueId _lastAlloc;
LOGGER_T _notify;
mutable QVector<RefPointer<AccessableId>> _idVector;
private:
bool initMemHandler();
void lookForNextFreeId();
}; /* IdHandler */
} /* SituationMap */
+226
View File
@@ -0,0 +1,226 @@
/**
**
** @RDE_HEADER@
** Base class for reference counted objects
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Umgebung: Intel-PC
** Software-Umgebung: Linux, Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Pgrm-Sprache ....: C++ 11
**
** HIER/SACHN ......: P: -- ---- - --- QE
**
** Version .........: $Revision$
** -----------------------------------------------------------------------------------------------
**
** Autor ...........: Nikolaus Hanekamp, SEDG, Tel. 2906
**
** -----------------------------------------------------------------------------------------------
**
** Notizen .........:
**
** 18.08.2004, NH : Urversion
** 13.05.2015, JT : Refactoring for use with Qt5, ArcGIS 10.2.5
** adding methodes according to std::share_ptr like reset
**
** -----------------------------------------------------------------------------------------------
**
** Copyright (C) 2004 Rheinmetall Defence Electronics GmbH, D-28309 BREMEN
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include "referenced.h"
namespace SituationMap
{
/**
@class RefPointer ref_pointer.h
@brief Smart pointer for handling referenced counted objects
Simple class template to implement reference counted pointers or smart pointers. The RefPointer class controls the
reference counter of the object \c T the pointer points to. The class \c T have to be derived from class \c Referenced,
where the reference counter member variable is located. In case of this reference counter gets zero delete will be called
automatically.
@see Referenced
*/
template<class T> class RefPointer
{
public:
using ElementType = T;
/**
@name Construction and Destruction
@{
*/
RefPointer() : RefPointer(nullptr) {}
RefPointer(const RefPointer &rp) : RefPointer(rp._ptr) {}
explicit RefPointer(ElementType *t)
: _ptr(t)
{
static_assert(std::is_base_of<Referenced, T>::value
, "Class of template SituationMap::RefPointer have to be derived from class SituationMap::Referenced");
if (_ptr) _ptr->ref();
} /* RefPointer */
~RefPointer()
{
if (_ptr) _ptr->unref();
_ptr = nullptr;
} /* ~RefPointer */
/** @} */
/**
Checks if pointer is valid.
*/
explicit inline operator bool() const { return (_ptr != nullptr); }
/**
@name Casting
@{
*/
/**
To create reference pointer from a derived class directly this contructor template can be used.
How to use it is shown in the following example:
\code
RefPointer<AbstractMapObject> objPtr = getAbstractMapObject();
RefPointer<Participant> participantObj( objPtr );
if (participantObj != NULL)
{
...
}
\endcode
Alternatively it is posible to create \b participantObj from example above as follows:
\code
RefPointer<Participant> participantObj = RefPointer<Participant>(dynamic_cast<Participant*>(objPtr.get()));
\endcode
*/
template<class Y> explicit RefPointer(RefPointer<Y> assignPtr)
{
_ptr = dynamic_cast<T *>(assignPtr.get());
if (_ptr) _ptr->ref();
}
template<class Y> explicit RefPointer(Y *assignPtr)
{
_ptr = dynamic_cast<T *>(assignPtr);
if (_ptr) _ptr->ref();
}
/** @} */
inline RefPointer &operator = (const RefPointer &rp)
{
if (_ptr == rp._ptr) return *this;
T *tmp_ptr = _ptr;
_ptr = rp._ptr;
if (_ptr) _ptr->ref();
// unref second to prevent any deletion of any object which might
// be referenced by the other object. i.e rp is child of the
// original _ptr.
if (tmp_ptr) tmp_ptr->unref();
return *this;
}
inline RefPointer &operator = (ElementType *ptr)
{
if (_ptr == ptr) return *this;
T *tmp_ptr = _ptr;
_ptr = ptr;
if (_ptr) _ptr->ref();
// unref second to prevent any deletion of any object which might
// be referenced by the other object. i.e rp is child of the
// original _ptr.
if (tmp_ptr) tmp_ptr->unref();
return *this;
}
inline void reset() { *this = nullptr; }
inline void reset(T *ptr) { *this = ptr; }
/**
@name Comparison Operators
Compares the pointer element \c _ptr with other pointers.
@{
*/
inline bool operator == (const RefPointer &rp) const { return (_ptr == rp._ptr); }
inline bool operator != (const RefPointer &rp) const { return (_ptr != rp._ptr); }
inline bool operator < (const RefPointer &rp) const { return (_ptr < rp._ptr); }
inline bool operator > (const RefPointer &rp) const { return (_ptr > rp._ptr); }
inline bool operator == (const ElementType *ptr) const { return (_ptr == ptr); }
inline bool operator != (const ElementType *ptr) const { return (_ptr != ptr); }
inline bool operator < (const ElementType *ptr) const { return (_ptr < ptr); }
inline bool operator > (const ElementType *ptr) const { return (_ptr > ptr); }
/** @} */
/**
@name Access Operators
Get access to the pointer or the object it points to.
@{
*/
inline ElementType &operator*() { return *_ptr; }
inline const ElementType &operator*() const { return *_ptr; }
inline ElementType *operator->() { return _ptr; }
inline const ElementType *operator->() const { return _ptr; }
inline ElementType *get() { return _ptr; }
inline const ElementType *get() const { return _ptr; }
/** @} */
/**
@name Checks if the pointer is valid.
@{
*/
inline bool valid() const { return _ptr != nullptr; }
inline bool operator!() const { return _ptr == nullptr; }
/** @} */
/**
* @name Control Over Object Pointer
* Take control over the object pointed to by RefPointer, unreference but do not delete even if ref count goes to 0,
* return the pointer to the object.
* Note, do not use this unless you are 100% sure your code handles the deletion of the object correctly, and
* only use when absolutely required.
@{
*/
inline ElementType *take() { return release();}
inline ElementType *release() { T *tmp = _ptr; if (_ptr) _ptr->unref_nodelete(); _ptr = nullptr; return tmp;}
/** @} */
private:
ElementType *_ptr;
};
} // SituationMap
+76
View File
@@ -0,0 +1,76 @@
/**
**
** @RDE_HEADER@
** Base class for reference counted objects
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Umgebung: Intel-PC
** Software-Umgebung: Linux, Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Pgrm-Sprache ....: C++ 11
**
** HIER/SACHN ......: P: -- ---- - --- QE
**
** Version .........: $Revision$
** -----------------------------------------------------------------------------------------------
**
** Autor ...........: Nikolaus Hanekamp, SEDG, Tel. 2906
** Joerg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notizen .........:
**
** 18.08.2004, NH : Urversion
** 13.05.2015, JT : Refactoring for use with Qt5, ArcGIS 10.2.5
**
** -----------------------------------------------------------------------------------------------
*/
#include "referenced.h"
#include <QDebug>
#include <typeinfo>
#include <memory>
namespace SituationMap
{
std::shared_ptr<DeleteHandler> & s_deleteHandler()
{
static std::shared_ptr<DeleteHandler> deleteHandler(nullptr);
return deleteHandler;
} // std::shared_ptr<DeleteHandler> & s_deleteHandler()
void Referenced::setDeleteHandler(DeleteHandler *handler)
{
s_deleteHandler().reset(handler);
} // Referenced::setDeleteHandler
DeleteHandler *Referenced::getDeleteHandler() { return s_deleteHandler().get(); }
Referenced::~Referenced()
{
if (_refCount > 0)
{
qCritical() << "Deleting still referenced object < " << QString::number((qint64)this, 0, 16) << ">"
<< "of type <" << typeid(this).name() << ">"
<< "refCnt: <" << _refCount;
}
} // Referenced::~Referenced
} // end of namespace SituationMap
+157
View File
@@ -0,0 +1,157 @@
/**
**
** @RDE_HEADER@
** Base class for reference counted objects
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Umgebung: Intel-PC
** Software-Umgebung: Linux, Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Pgrm-Sprache ....: C++ 11
**
** HIER/SACHN ......: P: -- ---- - --- QE
**
** Version .........: $Revision$
** -----------------------------------------------------------------------------------------------
**
** Autor ...........: Nikolaus Hanekamp, SEDG, Tel. 2906
** Joerg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notizen .........:
**
** 18.08.2004, NH : Urversion
** 13.05.2015, JT : Refactoring for use with Qt5, ArcGIS 10.2.5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include "situation_map_common_export.h"
namespace SituationMap
{
// forward declar, declared after Refenced below.
class DeleteHandler;
/**
@class Referenced referenced.h
@brief Base class for referenced counted object classes
Base class from providing referencing counted objects. Objects from type "Referenced" should
be used in combination with the class "RefPointer" for memory handling. In this case calling
delete direcly for those objects will result in an error at runtime. Unfortunately there is
no way to check this at compile time - to declare delete operator protected does not work.
@sa RefPointer
*/
class SITUATIONMAPCOMMON_EXPORT Referenced
{
public:
/**
@name Construction
@{
*/
Referenced() { _refCount = 0; }
Referenced(const Referenced &) { _refCount = 0; }
/** @} */
/**
Assign operator has to do nothing because is is permitted to change _refCount during assignment operation.
*/
inline Referenced &operator = (const Referenced &) { return *this; }
friend class DeleteHandler;
/** Set a DeleteHandler to which deletion of all referenced counted objects
* will be delegated to.*/
static void setDeleteHandler(DeleteHandler *handler);
/** Get a DeleteHandler.*/
static DeleteHandler *getDeleteHandler();
/** Increment the reference count by one, indicating that
this object has another pointer which is referencing it.*/
inline void ref() const { ++_refCount; }
/** Decrement the reference count by one, indicating that
a pointer to this object is referencing it. If the
reference count goes to zero, it is assumed that this object
is no longer referenced and is automatically deleted.*/
inline void unref() const;
/** Decrement the reference count by one, indicating that
a pointer to this object is referencing it. However, do
not delete it, even if ref count goes to 0. Warning, unref_nodelete()
should only be called if the user knows exactly who will
be resonsible for, one should prefer unref() over unref_nodelete()
as the later can lead to memory leaks.*/
inline void unref_nodelete() const { --_refCount; }
/** Return the number pointers currently referencing this object. */
inline int referenceCount() const { return _refCount; }
protected:
/**
Declaration of destructor is protected because only methode unref() or the delete
handler itself are allowed to destroy referenced objects.
*/
virtual ~Referenced();
mutable int _refCount;
};
/** Class for override the default delete behavior so that users can implement their own object
* deletion schemes. This might be done to help implement protection of multiple threads from deleting
* objects unintentionally.
* Note, the DeleteHandler cannot itself be reference counted, otherwise it
* would be responsible for deleting itself!
* An static auto_ptr<> is used internally in Referenced.cpp to manage the
* DeleteHandler's memory.*/
class SITUATIONMAPCOMMON_EXPORT DeleteHandler
{
public:
DeleteHandler() = default;
virtual ~DeleteHandler() = default;
/** Flush any cache of objects that need to be deleted by doing an actual delete.*/
virtual void flush() {}
static inline void doDelete(const Referenced *object) { delete object; }
/** Request the deletion of an object.
* Depending on users implementation of DeleteHandler, the delete of the object may occur
* straight away or be delayed until doDelete is called.
* The default implementation does a delete straight away.*/
virtual void requestDelete(const Referenced *object) { doDelete(object); }
}; /* DeleteHandler */
inline void Referenced::unref() const
{
--_refCount;
if (_refCount == 0)
{
if (getDeleteHandler()) getDeleteHandler()->requestDelete(this);
else delete this;
}
else if (_refCount < 0)
throw 2325;
} /* Referenced::unref */
} // SituationMap
+51
View File
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* Helper function to get the resource path
* $Id$
* $HeadURL$
* $Author$
*/
#include "resource_path.h"
#include <ngltools/ngltools_filesystem.h> // for NGL::directories
// Qt
#include <QDir>
#include <QVector>
//----------------------------------------------------------------------------
namespace SituationMap
{
QString getResourcePath()
{
const QString resDir = NGL::directory(NGL::FileType::ConfigFile, QLatin1String("resources"));
if (!resDir.isEmpty())
return resDir;
QVector<QString> pathsToSearch;
// for old systems
#ifdef Q_OS_WIN
pathsToSearch += QLatin1String("C:/ExtFw/resources/");
#else
pathsToSearch += QLatin1String("/usr/share/arcgis/resources");
#endif
for (const QString &path : qAsConst(pathsToSearch))
{
if (QDir(path).exists())
return path + QLatin1Char('/');
}
return QString();
}
} // namespace SituationMap
+24
View File
@@ -0,0 +1,24 @@
/**
* @RDE_HEADER@
* Helper function to get the resource path
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "situation_map_common_export.h"
// Qt
#include <QString>
//----------------------------------------------------------------------------
namespace SituationMap
{
QString SITUATIONMAPCOMMON_EXPORT getResourcePath();
} // namespace SituationMap
+96
View File
@@ -0,0 +1,96 @@
/**
* @RDE_HEADER@
* Helper class for storing a simple value and an implizit changed flag
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Qt
#include <sstream>
//----------------------------------------------------------------------------
namespace SituationMap
{
/*! \class SimpleValue
\brief Simple storing fo values
*/
template <class TYPE> class SimpleValue
{
protected:
TYPE m_value = TYPE(); // '{}' to initialize the member (and avoid unconditional jumps@valgrind)
bool m_changed = true;
public:
SimpleValue() = default;
// cppcheck-suppress noExplicitConstructor
SimpleValue(const TYPE &value) // should not be declared explicit to avoid additional writing stuff to cast values
: m_value(value)
{}
SimpleValue<TYPE> &operator=(const TYPE &newValue)
{
if (this->m_value != newValue)
{
this->m_value = newValue;
m_changed = true;
}
return *this;
}
const TYPE &operator()() const
{
return this->m_value;
}
const TYPE &value() const
{
return this->m_value;
}
TYPE &refValue()
{
m_changed = true;
return this->m_value;
}
const TYPE &refValue() const
{
return this->m_value;
}
bool changed() const
{
return m_changed;
}
void reset()
{
m_changed = false;
}
void setChanged()
{
m_changed = true;
}
friend std::ostream &operator<<(std::ostream &stream,
const SimpleValue &simpleVal)
{
stream << simpleVal.value();
return stream;
}
};
} // namespace SituationMap
@@ -0,0 +1,21 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <qglobal.h>
#ifndef SITUATIONMAPCOMMON_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define SITUATIONMAPCOMMON_EXPORT
# else
# if defined(MAKE_SITUATIONMAPCOMMON_LIB)
# define SITUATIONMAPCOMMON_EXPORT Q_DECL_EXPORT
# else
# define SITUATIONMAPCOMMON_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
+51
View File
@@ -0,0 +1,51 @@
/**
* @RDE_HEADER@
* Logging within SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#include "situation_map_logging.h"
#include <NGLLogger/NGLLoggerBootstrap.h>
#include <QUuid>
namespace SituationMap
{
LOGGER_T getLogger(const char *name, int id)
{
static const QLatin1String prefix("SituationMap.");
std::string loggerName;
const size_t len = strlen(name);
loggerName.reserve(prefix.size() + len);
loggerName.append(prefix.data(), prefix.size());
loggerName.append(name, len);
if (id > 0)
{
std::stringstream loggerStream;
loggerStream << loggerName << "." << id;
loggerName = loggerStream.str();
}
try
{
return NGL::NGLLoggerBootstrap::getNGLRootLogger().
getNGLLogger(loggerName);
}
catch (const std::exception &ex)
{
fprintf(stderr, "Error retrieving logger: %s", ex.what());
abort();
}
}
} // namespace SituationMap
std::ostream &operator<< (std::ostream &stream,
const QUuid &value)
{
stream << "QUuid(" << value.toString() << ")";
return stream;
}
+38
View File
@@ -0,0 +1,38 @@
/**
* @RDE_HEADER@
* Logging within SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <NGLLogger/INGLLogger.h>
#include "situation_map_common_export.h"
class QUuid;
namespace SituationMap
{
using LOGGER_T = NGL::INGLLogger&;
// export for executables within 'Tests'
LOGGER_T SITUATIONMAPCOMMON_EXPORT getLogger(const char *name, int id = -1);
} // namespace SituationMap
SITUATIONMAPCOMMON_EXPORT std::ostream &operator<< (std::ostream &stream,
const QUuid &value);
+37
View File
@@ -0,0 +1,37 @@
#
# $Id$
# $HeadURL$
# $Author$
#
FIND_PACKAGE(GDAL REQUIRED)
SET(Elevation_SRCS
elevation_map.cpp
elevation_map.h
elevation_map_manager.cpp
elevation_map_manager.h
line_of_sight_calculation.cpp
line_of_sight_calculation.h
)
NGL_ADD_LIBRARY2(AAR SV WS HLA situationMapElevationMap SHARED ${Elevation_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES(situationMapElevationMap
PRIVATE
${GDAL_INCLUDE_DIR}
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/../
${CMAKE_CURRENT_BINARY_DIR}/../
)
NGL_TARGET_LINK_LIBRARIES(situationMapElevationMap
PRIVATE
situationMapCommon
Qt5::Concurrent
Qt5::Gui
NGLLogger
${GDAL_LIBRARIES}
LtsCoordTrans
PUBLIC
Qt5::Core
LtsUtilsCoreHeaderOnly
)
+975
View File
@@ -0,0 +1,975 @@
/**
* @RDE_HEADER@
* adapting GeISiR source to SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#include "elevation_map.h"
#include <QtConcurrent>
#include <QPolygonF>
#include <MathUtils/math_const.h>
#include <QElapsedTimer>
#include <gdal.h>
#include <iostream>
#include <limits>
#include <memory>
#include <math.h>
namespace SituationMap
{
class BoundingRect
{
double m_top = 0.0;
double m_bottom = 0.0;
double m_left = 0.0;
double m_right = 0.0;
void swapBounds()
{
if (m_top < m_bottom)
std::swap(m_bottom, m_top);
if (m_right < m_left)
std::swap(m_left, m_right);
} // void swapBounds()
public:
BoundingRect() = default;
BoundingRect(double left, double bottom, double right, double top)
: m_top(top), m_bottom(bottom), m_left(left), m_right(right)
{
swapBounds();
} // BoundingRect( double left, double bottom, double right, double top)
BoundingRect(const BoundingRect &) = default;
BoundingRect &operator =(const BoundingRect &) = default;
~BoundingRect() = default;
inline void operator()(double left, double bottom, double right, double top)
{
m_top = top;
m_bottom = bottom;
m_left = left;
m_right = right;
swapBounds();
} // inline void operator() (double left, double bottom, double right, double top)
double top() const { return m_top; }
double bottom() const { return m_bottom; }
double left() const { return m_left; }
double right() const { return m_right; }
bool contains(const QPointF &p) const { return ((p.x() >= m_left) && (p.x() <= m_right) && (p.y() >= m_bottom) && (p.y() <= m_top)); }
QRectF toQRectF() const
{
return QRectF(QPointF(m_left, m_top), QPointF(m_right, m_bottom));
} // toQRectF
}; // BoundingRect
struct ElevationMap::Internal
{
BoundingRect _location; // Lage der Karte in Lat/Lon
struct PointDouble
{
void setX(double x) { _x = x;}
void setY(double y) { _y = y;}
double x() { return _x;}
double y() { return _y;}
double _x{0.0};
double _y{0.0};
};
PointDouble _elexelSize; // LatLon Size of 1 elevation sample, redundat
int _totalColumns = 0;
int _totalRows = 0;
double _toMeterFactor = 1.0;
std::vector<int> _elevationField;
template<class BufferType> bool copyBufferData(GDALRasterBandH hBand, int xBlkSize, int yBlkSize)
{
// NLDEVCTRL-804: Konsistenz d. Z-Werte zwischen 2D/3D (also ElevationMap vs. ESRI::BaseSurface)
// This ElevationMap was using an approach similar to following.
// Now it´s more or less identical and the difference using 'Altmark_dt1.tif'
// are gone. AHTC is also fine :-)
/*
CPLErr GetHistogram( GDALRasterBand *poBand, GUIntBig *panHistogram )
{
memset( panHistogram, 0, sizeof(GUIntBig) * 256 );
CPLAssert( poBand->GetRasterDataType() == GDT_Byte );
int nXBlockSize, nYBlockSize;
poBand->GetBlockSize( &nXBlockSize, &nYBlockSize );
int nXBlocks = (poBand->GetXSize() + nXBlockSize - 1) / nXBlockSize;
int nYBlocks = (poBand->GetYSize() + nYBlockSize - 1) / nYBlockSize;
GByte *pabyData = (GByte *) CPLMalloc(nXBlockSize * nYBlockSize);
for( int iYBlock = 0; iYBlock < nYBlocks; iYBlock++ )
{
for( int iXBlock = 0; iXBlock < nXBlocks; iXBlock++ )
{
int nXValid, nYValid;
poBand->ReadBlock( iXBlock, iYBlock, pabyData );
// Compute the portion of the block that is valid
// for partial edge blocks.
poBand->GetActualBlockSize(iXBlock, iYBlock, &nXValid, &nYValid)
// Collect the histogram counts.
for( int iY = 0; iY < nYValid; iY++ )
{
for( int iX = 0; iX < nXValid; iX++ )
{
panHistogram[pabyData[iX + iY * nXBlockSize]] += 1;
}
}
}
}
}
Source https://gdal.org/api/gdalrasterband_cpp.html
*/
const size_t bufSize = xBlkSize * yBlkSize;
std::unique_ptr<BufferType[]> bufferPtr(new BufferType[bufSize]);
int nXBlocks = (GDALGetRasterBandXSize(hBand) + xBlkSize - 1) / xBlkSize;
int nYBlocks = (GDALGetRasterBandYSize(hBand) + yBlkSize - 1) / yBlkSize;
for (int iYBlock = 0; iYBlock < nYBlocks; ++iYBlock)
{
for (int iXBlock = 0; iXBlock < nXBlocks; ++iXBlock)
{
CPLErr err = GDALReadBlock( hBand, iXBlock, iYBlock, bufferPtr.get() ); // Gdal liest spaltenweise !! x <-> y
if (err != CE_None)
{
return false;
}
int nXValid, nYValid;
// Unfortunately GDALGetActualBlockSize is not available at Suse 42.3 (gdal 2.1.3)
// so check bounds using _totalColumns, _totalRows:
#if GDAL_VERSION_NUM >= GDAL_COMPUTE_VERSION(2, 2, 0)
# define HAS_ACTUAL_BLOCKSIZE
#endif
#ifdef HAS_ACTUAL_BLOCKSIZE
GDALGetActualBlockSize(hBand, iXBlock, iYBlock, &nXValid, &nYValid);
#else
nXValid = xBlkSize;
nYValid = yBlkSize;
#endif // HAS_ACTUAL_BLOCKSIZE
const int curYOffset = (iYBlock * yBlkSize);
const int curXOffset = (iXBlock * xBlkSize);
for (int iY = 0; iY < nYValid; ++iY)
{
const int row = iY + curYOffset; // Line
#ifndef HAS_ACTUAL_BLOCKSIZE
if (row >= _totalRows)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
for (int iX = 0; iX < nXValid; ++iX)
{
// identical to gdallocationinfo
const int column = iX + curXOffset; // Pixel
#ifndef HAS_ACTUAL_BLOCKSIZE
if (column >= _totalColumns)
continue;
#endif // HAS_ACTUAL_BLOCKSIZE
int bi = iX + (iY * xBlkSize);
const int z = static_cast<int>( static_cast<double>(bufferPtr[bi]) / _toMeterFactor );
_elevationField[column + row * _totalColumns] = z;
} // for
} // for
} // for
} // for
return true;
} // template<class BufferType> void copyBufferData(BufferType * bufferPtr, int xBlkSize, int yBlkSize)
}; // struct ElevationMap::Internal
ElevationMap::ElevationMap(const QFileInfo &fileInfo)
: m_internal(new Internal())
, m_aproximation(true)
, m_valid(false)
, m_logger(getLogger("ElevationMap"))
{
GDALAllRegister();
m_fileName = fileInfo.absoluteFilePath();
INFO_LOG(m_logger) << " ctor ElevationMap: map of file '" << m_fileName << "'"
<< ", this=" << this;
if (!fileInfo.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' doesn't exists.";
return;
}
if (!fileInfo.isReadable())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << m_fileName << "\' isn't readable.";
return;
}
auto cb = [this]()
{
return readFromFile();
};
auto cbFinished = [this]()
{
NOTICE_LOG(m_logger) << " Finished reading elevation map of file '" << m_fileName << "'"
<< ", this=" << this;
};
QObject::connect(&m_gdalReadWatcher, &QFutureWatcher<bool>::finished,
&m_gdalReadWatcher, cbFinished);
m_gdalReadWatcher.setFuture(QtConcurrent::run(cb));
// sync init, takes ~800ms during sitmap loading 72MB file
// QElapsedTimer t;
// t.start();
// m_gdalReadWatcher.waitForFinished();
// NOTICE_LOG(m_logger) << "Time loading elevation map: " << t.elapsed();
} // ElevationMap::ElevationMap( const QFile &file )
ElevationMap::~ElevationMap()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
m_gdalReadWatcher.cancel();
m_gdalReadWatcher.waitForFinished();
delete m_internal;
m_internal = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
} // ElevationMap::~ElevationMap
bool ElevationMap::waitForInitialization()
{
m_gdalReadWatcher.waitForFinished();
return m_valid;
}
bool ElevationMap::isValid() const
{
// N. Thoden, 27.04.2020
// isFinished seems to be unreliable:
// if mainThread request ::isValid() diretly after ::waitForInitialization()
// you get false (by 'if (!m_gdalReadWatcher.isFinished())'
// but m_valid is true. So skip the additional check.
// if (!m_gdalReadWatcher.isFinished())
// return false;
return m_valid;
}
QString ElevationMap::filename() const
{
return m_fileName;
}
bool ElevationMap::readFromFile()
{
m_valid = false;
GDALDatasetH hDataset = GDALOpen(m_fileName.toLocal8Bit().constData(), GA_ReadOnly);
if (nullptr == hDataset)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDALOpen failed, may be calling of GDALAllRegister is missing";
return false;
}
// Detect basic parameters
//
GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
int nRasterXSize = GDALGetRasterXSize(hDataset);
int nRasterYSize = GDALGetRasterYSize(hDataset);
double adfGeoTransform[6];
CPLErr err = GDALGetGeoTransform(hDataset, adfGeoTransform); // XY Spatial Referenz WGS84 (4326) expected!!!
if (err != CE_None)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to get geo raster transform.";
GDALClose(hDataset);
return false;
}
if (nullptr == hDriver) { GDALClose(hDataset); return false; }
if (nRasterXSize <= 0) { GDALClose(hDataset); return false; }
if (nRasterYSize <= 0) { GDALClose(hDataset); return false; }
double pixWidth = adfGeoTransform[1];
double pixHeight = adfGeoTransform[5];
auto toRAD = [](double x) -> double { return x * MathConst::pi / 180.0; };
m_internal->_location(toRAD(adfGeoTransform[0]) // adfGeoTransform is in degrees
, toRAD(adfGeoTransform[3])
, toRAD(adfGeoTransform[0] + double(nRasterXSize) * pixWidth + double(nRasterYSize) * adfGeoTransform[2])
, toRAD(adfGeoTransform[3] + double(nRasterYSize) * pixHeight + double(nRasterXSize) * adfGeoTransform[4]));
m_internal->_elexelSize.setX(fabs(toRAD(pixWidth)));
m_internal->_elexelSize.setY(fabs(toRAD(pixHeight)));
GDALRasterBandH hBand = GDALGetRasterBand(hDataset, 1);
if (nullptr == hBand)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read raster band.";
GDALClose(hDataset);
return false;
}
int xBlkSize;
int yBlkSize;
GDALGetBlockSize(hBand, &xBlkSize, &yBlkSize);
if (xBlkSize <0 || yBlkSize < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no buffer, next operations will possible fail....";
return false;
}
m_internal->_totalColumns = nRasterXSize;
m_internal->_totalRows = nRasterYSize;
m_internal->_elevationField.resize(nRasterXSize * nRasterYSize);
const GDALDataType type = GDALGetRasterDataType(hBand);
switch (type)
{
case GDT_UInt16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<unsigned short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Int16:
m_internal->_toMeterFactor = 1.0;
m_valid = m_internal->copyBufferData<short int> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float32:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<float> ( hBand, xBlkSize, yBlkSize );
break;
case GDT_Float64:
m_internal->_toMeterFactor = 0.01;
m_valid = m_internal->copyBufferData<double> ( hBand, xBlkSize, yBlkSize );
break;
default:
{
ERROR_LOG(m_logger) << LOGFUNCTION << " data type isn't supported type or a floating point type"
<< " (type=" << type << ")";
GDALClose(hDataset);
return false;
}
} // switch
if (!m_valid)
{
CPLErrorNum errNum = CPLGetLastErrorNo();
ERROR_LOG(m_logger) << LOGFUNCTION << " GDAL failed to read data block (error no " << int(errNum) << ")";
}
GDALClose(hDataset);
INFO_LOG(m_logger) << LOGFUNCTION << " loaded, filename =" << m_fileName << "'"
<< ", m_valid=" << m_valid;
return m_valid;
} // bool ElevationMap::readFromFile(const QString & fileName)
QRectF ElevationMap::extent() const
{
if (!isValid())
return QRectF();
return m_internal->_location.toQRectF();
}
QPair<double, double> ElevationMap::elexelSize() const
{
return {m_internal->_elexelSize._x, m_internal->_elexelSize._y};
}
int ElevationMap::getElevation(int column, int row) const
{
row = m_internal->_totalRows - row;
const size_t index = column + row * m_internal->_totalColumns;
if (m_internal->_elevationField.empty() || m_internal->_elevationField.size() < index)
return std::numeric_limits<int>::min();
return m_internal->_elevationField.at(index);
} // int ElevationMap::getElevation( int column, int row)
double ElevationMap::elevation(double lon, double lat) const
{
if (!isValid())
return 0;
return getElevation(lon, lat) * m_internal->_toMeterFactor;
} // double ElevationMap::elevation( double lon, double lat)
double ElevationMap:: getElevation(double lon, double lat) const
{
if (!m_internal->_location.contains(QPointF(lon, lat)))
{
return std::numeric_limits<int>::min(); // ausserhalb der Höhenkarte !!
}
double x = (lon - m_internal->_location.left()) / m_internal->_elexelSize.x();
int column = floor(x);
double y = (lat - m_internal->_location.bottom()) / m_internal->_elexelSize.y();
int row = floor(y);
int elevation = getElevation(column, row);
if (elevation == std::numeric_limits<int>::min())
return elevation;
if (!m_aproximation)
return elevation;
/*
** Approximation: e0, e1, e2, e3 Hoehenwerte der 4 benachbarten Stützpunkte
**
** + e3 + e2
** y
** ^ + er er = e0 + x*(e1-e0) + y*(e3-e0) + x*y(e2-e3+e0-e1)
** |
** |
** |
** | + e0 + e1
** -----> x
**
** Besonderheiten: die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Rasters,
** so dass je nach Groesse von x, y andere Nachbarpunkte zu berücksichtigen sind, und x, sowie y entsprechend
** angepasst werden muesen.
** Bei Randrasterpunkten kann keine Interpolation erfolgen, so dass hier entsprechend extrapoliert wird.
*/
x = (x - (double)column); // Residuum normiert auf Stützpunktabstand
y = (y - (double)row); // Residuum normiert auf Stützpunktabstand
x -= 0.5; // Mitte berücksichtigen
y -= 0.5; // Mitte berücksichtigen
if (x < 0.0) { column--; x += 1.0;} // linke Spalte zur Interpolation nutzen
if (y < 0.0) { row--; y += 1.0;} // untere Reihe zur Interpolation nutzen
// Randrasterpunkte berücksichtigen: extrapolieren statt interpolieren
if (column <= -1) { x = -x; column = 0;}
else if (column >= (m_internal->_totalColumns - 1)) { x += 1.0; column = m_internal->_totalColumns - 2;}
if (row == -1) { y = -y; row = 0;}
else if (row >= (m_internal->_totalRows - 1)) { y += 1.0; row = m_internal->_totalRows - 2;}
// Die Approximation:
double substitute = elevation;
double e0 = getOrSubstituteElevation(column, row, substitute);
double e1 = getOrSubstituteElevation(column + 1, row, substitute);
double e2 = getOrSubstituteElevation(column + 1, row + 1, substitute);
double e3 = getOrSubstituteElevation(column, row + 1, substitute);
double er = e0 + x * (e1 - e0) + y * (e3 - e0) + x * y * (e2 - e3 + e0 - e1);
return er; // floor(er+0.5);; runden herausgenommen 19.8.2008 JT
} // double ElevationMap:: getElevation( double lon, double lat)
double ElevationMap::getOrSubstituteElevation(int column, int row, double substitute) const
{
int elevation = getElevation(column, row);
if (elevation != std::numeric_limits<int>::min())
return elevation;
int substCount = 0;
double calcSubstitute = 0.0;
elevation = getElevation(column + 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column - 1, row);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row + 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
elevation = getElevation(column, row - 1);
if (elevation != std::numeric_limits<int>::min()) {substCount++; calcSubstitute += elevation; }
if (substCount)
substitute = calcSubstitute / substCount;
return substitute;
} // double ElevationMap::getOrSubstituteElevation( int column, int row, double substitute)
double ElevationMap::distance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb{ der Höhenkarte !!
}
double start_z = getElevation(start.x(), start.y());
if (start_z <= std::numeric_limits<int>::min()) return -1.0;
double end_z = getElevation(end.x(), end.y());
if (end_z <= std::numeric_limits<int>::min()) return -1.0;
const QPointF &dist = distance_in_meter_WGS84(start, end);
double dz = end_z - start_z;
double d = sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
return d;
} // double ElevationMap::distance( const QPointF &start, const QPointF &end )
double ElevationMap::profileDistance(const QPointF &start, const QPointF &end) const
{
if (!isValid())
return 0;
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
return -1.0; // ausserhalb der Höhenkarte !!
}
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
// Einzellaengen aufsummieren, Stützstellen mit undefinierten Hoehen werden ausgelassen!
Point3D curPos(*elevProfile.begin());
double sumLng = 0.0;
for (Vec3dVector::iterator itr = elevProfile.begin() + 1; itr != elevProfile.end(); ++itr)
{
if ((*itr).z() > std::numeric_limits<int>::min())
{
if (curPos.z() > std::numeric_limits<int>::min())
{
const QPointF &dist = distance_in_meter_WGS84(QPointF(curPos.x(), curPos.y()), QPointF(itr->x(), itr->y()));
double dz = itr->z() - curPos.z();
sumLng += sqrt(dist.x() * dist.x() + dist.y() * dist.y() + dz * dz) ;
}
else
{
double dx = itr->x() - curPos.x();
double dy = itr->y() - curPos.y();
sumLng += sqrt(dx * dx + dy * dy);
}
curPos = *itr;
}
}
return sumLng;
} // double ElevationMap::profileDistance( const QPointF &start, const QPointF &end)
unsigned int ElevationMap::elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const
{
unsigned int startSize = elevProfile.size();
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin() + startSize; itr != elevProfile.end(); ++itr)
itr->setZ(getElevation(itr->x(), itr->y()));
return (elevProfile.size() - startSize);
} // unsigned int ElevationMap::elevationProfile( const QPointF &start, const QPointF &end, Vec3dVector &elevProfile)
bool ElevationMap::contains(const QPointF &point) const
{
return m_internal->_location.contains(point);
}
bool ElevationMap::intersects(const QPolygonF &polygon) const
{
const BoundingRect &bbox = m_internal->_location;
// maybe QPointF uses float, but we can accept double to be reduced to float:
const QPolygonF &ourBorder = QPolygonF({QPointF(bbox.left() , bbox.bottom()),
QPointF(bbox.left() , bbox.top()),
QPointF(bbox.right(), bbox.top()),
QPointF(bbox.right(), bbox.bottom())});
#if QT_VERSION < QT_VERSION_CHECK(5,10,0)
return ourBorder.intersected(polygon).size() > 0;
#else
return ourBorder.intersects(polygon);
#endif
}
void ElevationMap::getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const
{
sPnts.clear();
sPnts.push_back(Point3D::fromQPointF(start));
bool endIsClipped = false;
QPointF cStart(start);
QPointF cEnd(end);
if (!m_internal->_location.contains(start) || !m_internal->_location.contains(end))
{
int res = clipp2Map(start, end, cStart, cEnd);
if (res == 0) // no intersection
{
sPnts.push_back(Point3D::fromQPointF(end));
return;
}
if (res & 1) // start clipped
sPnts.push_back(Point3D::fromQPointF(cStart));
if (res & 2) // end clipped
endIsClipped = true;
}
QPointF delta(cEnd - cStart);
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den x-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startCol = floor((cStart.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (startCol < 0) startCol = 0;
else if (startCol >= m_internal->_totalColumns) startCol = m_internal->_totalColumns - 1;
int endCol = floor((cEnd.x() - m_internal->_location.left()) / m_internal->_elexelSize.x());
if (endCol < 0) endCol = 0;
else if (endCol >= m_internal->_totalColumns) endCol = m_internal->_totalColumns - 1;
int incCol = 0;
if (endCol > startCol) incCol = 1;
else if (endCol < startCol) incCol = -1;
double m = delta.y() / delta.x();
Vec2dVector xCuts;
xCuts.reserve(std::abs(endCol - startCol));
for (int col = startCol; col != endCol; col += incCol)
{
double x = m_internal->_location.left() + ((col + 0.5) + (incCol * 0.5)) * m_internal->_elexelSize.x();
double y = cStart.y() + (x - cStart.x()) * m;
xCuts.push_back(QPointF(x, y));
}
// Berechne alle Schnittpunkte des Vektors( cStart, cEnd) mit den y-Grenzen der Hoehenstuetzrechtecke.
// Die Stützpunkte liegen definitiongemaess jeweils in der Mitte des Raster-Rechtecks
int startRow = floor((cStart.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (startRow < 0) startRow = 0;
else if (startRow >= m_internal->_totalRows) startRow = m_internal->_totalRows - 1;
int endRow = floor((end.y() - m_internal->_location.bottom()) / m_internal->_elexelSize.y());
if (endRow < 0) endRow = 0;
else if (endRow >= m_internal->_totalRows) endRow = m_internal->_totalRows - 1;
int incRow = 0;
if (endRow > startRow) incRow = 1;
else if (endRow < startRow) incRow = -1;
m = delta.x() / delta.y();
Vec2dVector yCuts;
yCuts.reserve(std::abs(endRow - startRow));
for (int row = startRow; row != endRow; row += incRow)
{
double y = m_internal->_location.bottom() + ((row + 0.5) + (incRow * 0.5)) * m_internal->_elexelSize.y();
double x = cStart.x() + (y - cStart.y()) * m;
yCuts.push_back(QPointF(x, y));
}
// xCuts und yCuts zusammenführen, unter Berücksichtigung der Reihenfolge
Vec2dVector mergeCuts;
Vec2dVector::iterator xCutItr = xCuts.begin();
Vec2dVector::iterator yCutItr = yCuts.begin();
while ((xCutItr != xCuts.end()) && (yCutItr != yCuts.end()))
{
if (incCol > 0)
{
if ((*xCutItr).x() < (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
else
{
if ((*xCutItr).x() > (*yCutItr).x()) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
else { mergeCuts.push_back(*yCutItr); ++yCutItr; }
}
}
while ((xCutItr != xCuts.end())) { mergeCuts.push_back(*xCutItr); ++xCutItr; }
while ((yCutItr != yCuts.end())) { mergeCuts.push_back(*yCutItr); ++yCutItr; }
// mergeCuts enthält sequenziell alle Schnittpunkte des Vektors mit den Hoehengridkanten
// Die 2 Schnittpunkte pro Hoehenwert werden gemittelt zu einem zusammengefasst
if (mergeCuts.size() > 1)
{
for (Vec2dVector::iterator itr = mergeCuts.begin() + 1; itr != mergeCuts.end(); ++itr)
sPnts.push_back(Point3D::fromQPointF((*(itr - 1) + *itr) * 0.5));
}
sPnts.push_back(Point3D::fromQPointF(cEnd));
// DEBUG_LOG(m_logger) << " xCuts.si " << xCuts.size()
// << ", yCuts.si " << yCuts.size()
// << ", mergeCuts.si " << mergeCuts.size();
if (endIsClipped) sPnts.push_back(Point3D::fromQPointF(end));
} // void ElevationMap::getSamplePoints( const QPointF &start, const QPointF &end, Vec3dVector & sPnts)
// start und/oder end liegt ausserhalb !! in Lat/Lon !!
int ElevationMap::clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const
{
Vec2dVector clippVec;
if (end.x() != start.x())
{
double x = m_internal->_location.right();
double m = (end.y() - start.y()) / (end.x() - start.x());
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * m;
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
x = m_internal->_location.left();
if (isInIntervall(x, start.x(), end.x()))
{
double yc = start.y() + (x - start.x()) * (end.y() - start.y()) / (end.x() - start.x());
if ((yc <= m_internal->_location.top()) && (yc >= m_internal->_location.bottom()))
clippVec.push_back(QPointF(x, yc));
}
}
if (end.y() != start.y())
{
double y = m_internal->_location.top();
double m = (end.x() - start.x()) / (end.y() - start.y());
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
y = m_internal->_location.bottom();
if (isInIntervall(y, start.y(), end.y()))
{
double xc = start.x() + (y - start.y()) * m;
if ((xc < m_internal->_location.right()) && (xc > m_internal->_location.left()))
clippVec.push_back(QPointF(xc, y));
}
}
int size = (int)clippVec.size();
if (size == 0)
{
return 0; // komlett ausserhalb ( oder komplett innerhalb)
}
if (size == 1)
{
if (m_internal->_location.contains(start))
{
cEnd = clippVec[0]; return 2;
}
else
{
cStart = clippVec[0]; return 1;
}
}
if (size == 2)
{
if (squareLength(clippVec[0] - start) < squareLength(clippVec[1] - start))
{
cStart = clippVec[0];
cEnd = clippVec[1];
}
else
{
cStart = clippVec[1];
cEnd = clippVec[0];
}
return 3;
}
ERROR_LOG(m_logger) << LOGFUNCTION << "Internal Clipp Error!";
return 0;
} // int ElevationMap::clipp2Map( const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd)
// Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid
void ElevationMap::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile)
{
m_gdalReadWatcher.waitForFinished();
if (!m_internal->_location.contains(start)) return ; // ausserhalb der Höhenkarte !!
bool clipped = false;
double totalLng;
QPointF startG = start;
QPointF endG = end;
if (!m_internal->_location.contains(end)) // Clippen
{
clipped = true;
totalLng = length(endG - startG);
if (endG.x() < m_internal->_location.left())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.left();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
else if (endG.x() > m_internal->_location.right())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = m_internal->_location.right();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
if (endG.y() < m_internal->_location.bottom())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.bottom();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
else if (endG.y() > m_internal->_location.top())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = m_internal->_location.top();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
}
// Korrektursteigung aufgrund WGS84 Erdellipsoid, Lineare Approximation, Fehler bis 200 km: < 1.0m
double startRadius = radiusWGS84(startG.y());
double corrM = (radiusWGS84(endG.y()) - startRadius) / length(endG - startG);
Vec3dVector elevProfile;
getSamplePoints(start, end, elevProfile);
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
{
double dist = length(distance_in_meter_WGS84(start, itr->toPointF()));
double height = elevation(itr->x(), itr->y());
height += corrM * dist; // WGS84 Erdellipsoid Korrektur
height -= (startRadius * (1 - cos(atan(dist / startRadius)))); // Einbeziehen der Erdkruemmung
// alternativ als Naeherung: height -= ( dist * dist) / (startRadius * 2.0);
losProfile.push_back(LOSentry(dist, height));
}
if (clipped)
losProfile.push_back(LOSentry(totalLng, -NAN));
qCritical() << " losProfile.isze() = " << losProfile.size();
} // void ElevationMap::LOSProfile( const QPointF &start, const QPointF &end, LOSVector &losProfile)
} // SituationMap
/* -----------------------------------------------------------------------------------------------
** CVS-Id/Release ...: $Header$
** -----------------------------------------------------------------------------------------------
**
** Historie:
**
** $Log: not supported by cvs2svn $
** Revision 1.17.1.1 2008/06/16 06:05:37 joerg
** Transparenz und Bias einheitlich für alle Objekte implementiert
**
** Revision 1.17 2006/03/24 09:05:01 klaus
** Elevationprofile: Begrenzung auf Kartengröße aufgehoben ( Clipping)
**
** Revision 1.16 2005/11/09 13:44:36 klaus
** Umsetzung auf Linux;
**
** Revision 1.15 2005/04/15 13:05:45 joerg
** Resource Control
**
** Revision 1.14 2005/03/04 14:29:26 klaus
** Elevationmap mit CachedTileList versehen und automatischem Entladen von Tiles
**
** Revision 1.13 2005/03/03 12:37:53 klaus
** dynamischen Laden und Entladen der ElevationTiles für Reliefmap
**
** Revision 1.12 2005/03/01 12:36:59 klaus
** Reliefmap eingeführt, aus Elevationmap entfernt
**
** Revision 1.11 2005/02/25 15:32:56 klaus
** Ergänzung um Reliefkarte
**
** Revision 1.10 2005/02/10 14:15:19 klaus
** Bias in Basisklasse MapObject überführt
**
** Revision 1.9 2005/02/10 13:50:33 klaus
** Bias für SightFans eingeführt
**
** Revision 1.8 2005/02/09 16:45:26 klaus
** 1. Version der SightFans
**
** Revision 1.7 2005/01/26 12:26:23 klaus
** todos bearbeitet, Fehlerrückmeldnugen
**
** Revision 1.6 2004/12/14 10:29:48 klaus
** 7. Erweiterung für Höhenkarten
**
** Revision 1.5 2004/12/07 11:08:28 klaus
** 5. Erweiterung für Höhenkarten
**
** Revision 1.4 2004/12/03 16:25:14 klaus
** 4. Erweiterung für Höhenkarten
**
** Revision 1.3 2004/11/30 13:18:13 klaus
** 3. Erweiterung für Höhenkarten
**
** Revision 1.2 2004/11/23 14:36:40 klaus
** 2. Erweiterung für Höhenkarten
**
**
** ----------------------------------------------------------------------------------------------- */
+168
View File
@@ -0,0 +1,168 @@
/**
* @RDE_HEADER@
* adapting GeISiR source to SituationMap
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "elevation_map_helper.h"
#include <ElevationMap/situationmapelevationmap_export.h>
#include <LtsUtilsCore/qpoint3d.h>
#include <Common/situation_map_logging.h>
#include <QFutureWatcher>
#include <QPointF>
#include <QRectF>
#include <QVector>
class QFileInfo;
namespace SituationMap
{
//------------------------------------------------------------------------------------------------------------
// ElevationMap
//------------------------------------------------------------------------------------------------------------
class SITUATIONMAPELEVATIONMAP_EXPORT ElevationMap final
{
friend class ElevationMapManager;
public:
ElevationMap() = delete;
ElevationMap(const ElevationMap &) = delete;
ElevationMap &operator = (const ElevationMap &) = delete;
explicit ElevationMap(const QFileInfo &fileInfo);
~ElevationMap();
bool waitForInitialization();
bool isValid() const;
QString filename() const;
QRectF extent() const;
QPair<double, double> elexelSize() const;
double elevation(double lon, double lat) const; // rad
double distance(const QPointF &start, const QPointF &end) const;
double profileDistance(const QPointF &start, const QPointF &end) const;
unsigned int elevationProfile(const QPointF &start, const QPointF &end, Vec3dVector &elevProfile) const;
void interpolation(bool on) { m_aproximation = on; }
bool interpolation() const { return m_aproximation; }
bool contains(const QPointF &point) const;
bool intersects(const QPolygonF &polygon) const;
// Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid
void LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile);
protected:
int getElevation(int column, int row) const;
double getElevation(double lon, double lat) const;
double getOrSubstituteElevation(int column, int row, double substitute) const;
// getSamplePoints rastert den Vector( start, end) in Lat/Lon entsprechend dem Höhenraster
void getSamplePoints(const QPointF &start, const QPointF &end, Vec3dVector &sPnts) const;
int clipp2Map(const QPointF &start, const QPointF &end, QPointF &cStart, QPointF &cEnd) const;
bool readFromFile();
struct Internal;
Internal *m_internal;
QString m_fileName;
bool m_aproximation;
bool m_valid;
QFutureWatcher<bool> m_gdalReadWatcher;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
}; // ElevationMap
} // SituationMap
/* -----------------------------------------------------------------------------------------------
** CVS-Id/Release ...: $Header$
** -----------------------------------------------------------------------------------------------
**
** Historie:
**
** $Log: not supported by cvs2svn $
** Revision 1.20 2006/09/26 07:51:12 Jörg
** Syntax Fehler unter Suse Linux 10.1 behoben.
**
** Revision 1.19 2006/03/24 09:05:24 klaus
** Elevationprofile: Begrenzung auf Kartengröße aufgehoben ( Clipping)
**
** Revision 1.18 2005/11/09 11:12:27 klaus
** Anpassung an Linux: Kommentar hinter #endif mit "//" versehen
**
** Revision 1.17 2005/08/22 14:15:02 joerg
** typeId()
**
** Revision 1.16 2005/08/12 09:37:44 joerg
** Koordinaten-Umprojezieren bei Wechsel der Projektion
**
** Revision 1.15 2005/04/15 13:04:35 joerg
** Resource Control
**
** Revision 1.14 2005/03/04 14:29:13 klaus
** Elevationmap mit CachedTileList versehen und automatischem Entladen von Tiles
**
** Revision 1.13 2005/03/03 12:37:14 klaus
** dynamischen Laden und Entladen der ElevationTiles für Reliefmap
**
** Revision 1.12 2005/03/01 12:33:12 klaus
** Reliefmap eingeführt, aus Elevationmap entfernt
**
** Revision 1.11 2005/02/25 15:26:41 klaus
** Ergänzung um Reliefkarte
**
** Revision 1.10 2005/02/10 14:13:21 klaus
** Bias in Basisklasse MapObject überführt
**
** Revision 1.9 2005/02/10 13:51:01 klaus
** Bias für SightFans eingeführt
**
** Revision 1.8 2005/02/09 16:53:06 klaus
** 1. Version der SightFans
**
** Revision 1.7 2005/01/26 12:25:41 klaus
** todos bearbeitet, Fehlerrückmeldnugen
**
** Revision 1.6 2004/12/14 09:43:25 klaus
** 7. Erweiterung für Höhenkarten
**
** Revision 1.5 2004/12/07 11:07:20 klaus
** 5. Erweiterung für Höhenkarten
**
** Revision 1.4 2004/12/03 16:24:12 klaus
** 4. Erweiterung für Höhenkarten
**
** Revision 1.3 2004/11/30 13:15:28 klaus
** 3. Erweiterung für Höhenkarten
**
** Revision 1.2 2004/11/23 14:36:14 klaus
** 2. Erweiterung für Höhenkarten
**
**
** ----------------------------------------------------------------------------------------------- */
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* Some functions and struct for ElevationMap/LineOfSight (LOS)
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <LtsUtilsCore/qpoint3d.h>
#include <math.h>
#include <MathUtils/math_const.h>
#include <QPointF>
#include <QVector>
namespace SituationMap
{
inline bool isInIntervall(double x, double x1, double x2)
{
return (((x > x1) && (x < x2)) || (((x > x2) && (x < x1))));
} // inline bool isInIntervall(double x, double x1, double x2)
inline double radiusWGS84(double lat)
{
double sinLAT = sin(lat);
return (MathConst::WGS84_Major_a * sqrt(1.0 - MathConst::WGS84_SQR_EXC * sinLAT * sinLAT));
} // inline double radiusWGS84(double lat)
inline QPointF distance_in_meter_WGS84(const QPointF &start, const QPointF &end) // start, end in LonLat
{
// todo: prüfen ob Distanz zu groß, dann verbesserte Berechnung !!
double lat = (end.y() + start.y()) / 2.0;
double faktorLon = MathConst::WGS84_Major_a * cos(lat); // radiusWGS84(lat) * 2.0 * PI;
double faktorLat = MathConst::WGS84_Minor_b;
return QPointF(faktorLon * (end.x() - start.x()), faktorLat * (end.y() - start.y()));
} // QPointF distance_in_meter_WGS84(const QPointF &start, const QPointF &end)
inline double squareLength(const QPointF &v)
{
return v.x() * v.x() + v.y() * v.y();
} // inline double squareLength(const QPoint & v)
inline double length(const QPointF &v)
{
return sqrt(squareLength(v));
} // inline double length(const QPoint & v)
struct LOSentry // Defines 1 Entry for Line of Sight
{
LOSentry() = default;
LOSentry(double dist, double height) : _dist(dist), _height(height) { ; }
double &dist() { return _dist; }
double &height() { return _height; }
double dist() const { return _dist; }
double height() const { return _height; }
bool isUndefined() const { return std::isnan(_height); }
private:
double _dist = 0; // Distance from Start
double _height = -NAN;
}; /* LOSentry */
using Point3D = LtsUtils::QPoint3D; ///< x = lon ; y = lat , z = elevation/altitude in meters
using LOSVector = QVector<LOSentry>;
using Vec2dVector = QVector<QPointF>;
using Vec3dVector = QVector<Point3D>;
} // SituationMap
@@ -0,0 +1,531 @@
/**
* @RDE_HEADER@
* Manage and combine several ElevationMap
* $Id$
* $HeadURL$
* $Author$
*/
#include "elevation_map_manager.h"
#include "elevation_map.h"
// Qt
#include <QPointer>
#include <QFileInfo>
#include <QRectF>
#include <QPolygonF>
// system
#include <vector>
namespace SituationMap
{
static long s_counter{0};
//------------------------------------------------------------------------------------------------------------
// ElevationMapManager::Private
//------------------------------------------------------------------------------------------------------------
class ElevationMapManager::Private
{
public:
// fine to rough
std::vector<ElevationMap *> m_elevationMapCont;
QRectF m_simpleExtent;
};
//------------------------------------------------------------------------------------------------------------
// ElevationMapManager
//------------------------------------------------------------------------------------------------------------
ElevationMapManager::ElevationMapManager()
: BaseClass()
, d(new Private())
, m_logger(getLogger("ElevationMapManager"))
{
++s_counter;
TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
}
ElevationMapManager::~ElevationMapManager()
{
--s_counter;
TRACE_LOG(m_logger) << LOGFUNCTION << " START, s_counter=" << s_counter << " this=" << this;
cleanupCurrentContent();
delete d;
d = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, s_counter=" << s_counter << " this=" << this;
}
int ElevationMapManager::init(const QStringList &filePaths_rough_to_fine)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " START" << " filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(" ; "));
cleanupCurrentContent();
d->m_elevationMapCont.reserve(filePaths_rough_to_fine.size());
int cnt = 0;
for (const QString &filePath : filePaths_rough_to_fine)
{
QFileInfo fileInfo(filePath);
const QString &fileName = fileInfo.absoluteFilePath();
if (!fileInfo.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << fileName << "\' doesn't exists.";
continue;
}
if (!fileInfo.isReadable())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " elevation data file \'" << fileName << "\' isn't readable.";
continue;
}
// ctor will spawn thread via QtConcurrent and read file async:
d->m_elevationMapCont.insert(d->m_elevationMapCont.begin(), new ElevationMap(fileInfo));
++cnt;
} // for
NOTICE_LOG(m_logger) << LOGFUNCTION << " END" << " filePaths_rough_to_fine=" << filePaths_rough_to_fine.join(QStringLiteral(" ; "))
<< ", following number of files are going to be loaded: " << cnt;
return cnt;
}
void ElevationMapManager::cleanupCurrentContent()
{
qDeleteAll(d->m_elevationMapCont);
d->m_elevationMapCont.clear();
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
bool ElevationMapManager::waitForInitialization()
{
for (ElevationMap *elevationMap : d->m_elevationMapCont)
elevationMap->waitForInitialization();
return isValid();
}
bool ElevationMapManager::isValid() const
{
bool allAreValid = true;
for (const ElevationMap *elevationMap : d->m_elevationMapCont)
allAreValid = allAreValid && elevationMap->isValid();
// FATAL_LOG(m_logger) << LOGFUNCTION << " allAreValid = " << allAreValid;
return allAreValid;
}
double ElevationMapManager::elevation(double lon_rad, double lat_rad, bool *bIsCovered) const
{
// assume, that all children are ready
for (const ElevationMap *elevationMap : d->m_elevationMapCont)
{
// qCritical() << " EMM::elevation: "
// << " lon_rad=" << lon_rad
// << " lat_rad=" << lat_rad
// << " (try: " << elevationMap->filename() << ")...";
double singleResult = elevationMap->elevation(lon_rad, lat_rad);
if (singleResult > -300) // -300 -> ausserhalb der Höhenkarte !!
{
// qCritical() << " EMM::elevation: elevation= " << singleResult << " (taken from: " << elevationMap->filename() << ")";
if (bIsCovered)
*bIsCovered = true;
return singleResult;
}
}
// DEBUG_LOG(m_logger) << LOGFUNCTION << " invalid= "
// << " lon_rad=" << lon_rad
// << " lat_rad=" << lat_rad;
if (bIsCovered)
*bIsCovered = false;
return std::numeric_limits<double>::min();
}
double ElevationMapManager::elevation(double lon_rad, double lat_rad, double defaultValue, bool *bIsCovered) const
{
bool bIsCoveredLocal = false;
const double elevationValue = elevation(lon_rad, lat_rad, &bIsCoveredLocal);
// in any case: if provided, transfer isCovered back to caller
if (bIsCovered)
*bIsCovered = bIsCoveredLocal;
// which value to be returned?
if (!bIsCoveredLocal)
return defaultValue;
return elevationValue;
}
QRectF ElevationMapManager::simpleUnitedExtent() const
{
if (d->m_simpleExtent.isNull())
{
bool bFirst = true;
for (const ElevationMap *elevationMap : d->m_elevationMapCont)
{
const QRectF &singleExtent = elevationMap->extent();
if (bFirst)
{
bFirst = false;
d->m_simpleExtent = singleExtent;
// qCritical() << " bFirst.left " << singleExtent.left() << " bFirst.right " << singleExtent.right();
// qCritical() << " bFirst.top " << singleExtent.top() << " bFirst.bottom " << singleExtent.bottom();
} else {
// prefer following instead QRectF::united (top vs. bottom):
// qCritical() << " next.left " << singleExtent.left() << " secnextright " << singleExtent.right();
// qCritical() << " next.top " << singleExtent.top() << " next.bottom " << singleExtent.bottom();
d->m_simpleExtent.setLeft (std::min(d->m_simpleExtent.left(), singleExtent.left()));
d->m_simpleExtent.setTop (std::max(d->m_simpleExtent.top(), singleExtent.top())); // top - >use max !!!
d->m_simpleExtent.setBottom(std::min(d->m_simpleExtent.bottom(), singleExtent.bottom())); // bottom -> use min
d->m_simpleExtent.setRight (std::max(d->m_simpleExtent.right(), singleExtent.right()));
}
} // for
}
return d->m_simpleExtent;
}
bool ElevationMapManager::contains(const QPointF &point) const
{
for (const ElevationMap *elevationMap : d->m_elevationMapCont)
{
if (elevationMap->contains(point))
return true;
}
return false;
}
void ElevationMapManager::LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (d->m_elevationMapCont.empty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationMap";
return;
}
waitForInitialization();
QRectF simpleExtentLocal = simpleUnitedExtent();
// qCritical() << " simpleExtent.left " << simpleExtentLocal.left() << " simpleExtent.right " << simpleExtentLocal.right();
// qCritical() << " simpleExtent.top " << simpleExtentLocal.top() << " simpleExtent.bottom " << simpleExtentLocal.bottom();
if (!simpleExtentLocal.contains(start))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " start coordinate not within area"
<< ", start: x=" << start.x() << " ; y=" << start.y()
<< ", area: "
<< " left " << simpleExtentLocal.left() << ", right " << simpleExtentLocal.right()
<< " top " << simpleExtentLocal.top() << ", bottom " << simpleExtentLocal.bottom()
;
return; // ausserhalb der Höhenkarte !!
}
bool clipped = false;
double totalLng;
QPointF startG = start;
QPointF endG = end;
if (!simpleExtentLocal.contains(end)) // Clippen
{
clipped = true;
totalLng = length(endG - startG);
if (endG.x() < simpleExtentLocal.left())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = simpleExtentLocal.left();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
else if (endG.x() > simpleExtentLocal.right())
{
double m = (endG.y() - startG.y()) / (endG.x() - startG.x());
double x = simpleExtentLocal.right();
endG.setX(x);
endG.setY(startG.y() + m * (x - startG.x()));
}
if (endG.y() < simpleExtentLocal.bottom())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = simpleExtentLocal.bottom();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
else if (endG.y() > simpleExtentLocal.top())
{
double m = (endG.x() - startG.x()) / (endG.y() - startG.y());
double y = simpleExtentLocal.top();
endG.setY(y);
endG.setX(startG.x() + m * (y - startG.y()));
}
}
// Korrektursteigung aufgrund WGS84 Erdellipsoid, Lineare Approximation, Fehler bis 200 km: < 1.0m
double startRadius = radiusWGS84(startG.y());
double corrM = (radiusWGS84(endG.y()) - startRadius) / length(endG - startG);
//
// getSamplePoints(start, end, elevProfile);
// find best elevationMap for equested path {startG, endG}
//
// 1st find maps covering the desired pathG:
const QPolygonF pathG( QVector<QPointF>{startG, endG} );
std::vector<ElevationMap *> coveredElevationMaps;
coveredElevationMaps.reserve(d->m_elevationMapCont.size());
for (auto curItem : qAsConst(d->m_elevationMapCont))
{
if (curItem && curItem->intersects(pathG))
coveredElevationMaps.push_back(curItem);
} // for
if (m_logger.isDebugEnabled())
{
DEBUG_LOG(m_logger) << " coveredElevationMap.size() = " << coveredElevationMaps.size();
for (auto curItem : qAsConst(coveredElevationMaps))
DEBUG_LOG(m_logger) << " coveredElevationMap = " << curItem->filename();
}
// if no maps covers the desired path, stay on old approach: use most bottom elevation map:
if (coveredElevationMaps.empty())
coveredElevationMaps = d->m_elevationMapCont;
// 2nd: find elevation map with highest resolution to achieve best results:
using ELE_VEC_IT = std::vector<ElevationMap*>::iterator;
ELE_VEC_IT eleIt = coveredElevationMaps.begin();
for (ELE_VEC_IT curItem = coveredElevationMaps.begin()+1; curItem != coveredElevationMaps.end(); ++curItem)
{
ElevationMap *elePtr = *curItem;
if (!elePtr)
continue;
if (elePtr->elexelSize().first < (*eleIt)->elexelSize().first)
eleIt = curItem;
} // for
Q_ASSERT_X((*eleIt), "ElevationMapManager::LOSProfile", "eleIt failed");
Vec3dVector elevProfile;
(*eleIt)->getSamplePoints(start, end, elevProfile);
INFO_LOG(m_logger) << "Using ElevationMap '" << (*eleIt)->filename() << "'"
<< " (of size = " << d->m_elevationMapCont.size() << ")"
<< " with_elexelSize " << (*eleIt)->elexelSize().first
<< " -> # SamplePoints = " << elevProfile.size();
for (Vec3dVector::iterator itr = elevProfile.begin(); itr != elevProfile.end(); ++itr)
{
double dist = length(distance_in_meter_WGS84(start, itr->toPointF()));
double height = elevation(itr->x(), itr->y()); // use elevation over all seperate elevationMaps
height += corrM * dist; // WGS84 Erdellipsoid Korrektur
height -= (startRadius * (1 - cos(atan(dist / startRadius)))); // Einbeziehen der Erdkruemmung
// alternativ als Naeherung: height -= ( dist * dist) / (startRadius * 2.0);
losProfile.push_back(LOSentry(dist, height));
}
if (clipped)
losProfile.push_back(LOSentry(totalLng, -NAN));
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const LOSVector &losProfile,
double centerHat,
double objHat) const
{
// Analog zu SightFanImpl::calculateSightBeam
if (losProfile.size() < 2) // Startpunkt undefiniert oder ausserhalb der Karte
{
return LineOfSight::LOS_ERROR;
}
LOSVector::const_iterator itr = losProfile.begin();
const double centerHeight = itr->height() + centerHat;
double d1 = 0.0;
double height1 = itr->height();
// // überspringen bis _innerRadius erreicht ist !
// while( ++itr != losProfile.end())
// {
// double d2 = itr->dist(); // *EarthRadius;
// double height2 = itr->height();
// if( d2 >= m_internal->m_innerRadius)
// {
// height1 = height1 + (m_internal->m_innerRadius - d1) * ( height2 - height1)/(d2 - d1);
// d1 = m_internal->m_innerRadius;
// --itr;
// break;
// }
// d1 = d2;
// height1 = height2;
// }
// bool visible = true;
// SightMode mode = START;
d1 = itr->dist();
double curM = (height1 - centerHeight ) / d1; // bestimmende Steigung
// sBeam.add( SightEntry( d1, START) );
while (++itr != losProfile.end())
{
double d2 = itr->dist(); // *EarthRadius;
double height2 = itr->height();
if (std::isnan(height2)) // Undefined
{
return LineOfSight::LOS_UNDEFINED;
}
double hObj = (height2 + objHat - centerHeight );
double objM = hObj / d2; // Steigung bezogen auf Objekte( Objekte verdecken nicht !)
LineOfSight newMode = ((objM >= curM)) ? LineOfSight::LOS_VISIBLE : LineOfSight::LOS_INVISIBLE;
if (newMode == LineOfSight::LOS_INVISIBLE)
return LineOfSight::LOS_INVISIBLE;
// if (newMode != mode)
// {
// if ( (mode != LineOfSight::LOS_UNDEFINED) && (d2 != d1) )
// {
// // Korrekturwert für Distanz berechnen, unter Berücksichtigung der Objekthoehe !!
// double localM = (height2 - height1) /(d2 - d1);
// // localM == curM kann nicht auftreten, da newMode != mode
// double dCorr = (hObj - (curM * d2)) / (localM - curM);
// if (dCorr < 0.0)
// dCorr = 0.0;
// dCorr = d2 - dCorr;
// if (dCorr < d1)
// dCorr = d1;
// sBeam.add( SightEntry( dCorr, mode) );
// }
// mode = newMode;
// }
double terrM = (height2 - centerHeight) / d2; // Steigung bezogen auf Gelaende
if (terrM >= curM)
{
curM = terrM; // neue bestimmende Steigung
}
// height1 = height2;
// d1 = d2;
} // while
return LineOfSight::LOS_VISIBLE;
}
ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ,
const LtsUtils::QPoint3D &endPointGeo_absZ)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START" << ", startPointGeo_absZ = " << startPointGeo_absZ << ", endPointGeo_absZ = " << endPointGeo_absZ;
if (!isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, LtsElevationManager not valid";
return LineOfSight::LOS_ERROR;
}
// 1st: a) centerHat / startHat:
const double startHat = startPointGeo_absZ.z() - this->elevation(startPointGeo_absZ.x(), startPointGeo_absZ.y(), -10000);
if (startHat < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, startHat is negativ";
return LineOfSight::LOS_ERROR;
}
// 1st: b) endHat / objHat:
const double endHat = endPointGeo_absZ.z() - this->elevation(endPointGeo_absZ.x(), endPointGeo_absZ.y(), -10000);
if (endHat < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, objHat is negativ"
<< ", startPointGeo_absZ=" << startPointGeo_absZ
<< ", endPointGeo_absZ=" << endPointGeo_absZ;
return LineOfSight::LOS_ERROR;
}
SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = lineOfSightCalculation(startPointGeo_absZ.toPointF(), startHat,
endPointGeo_absZ.toPointF(), endHat);
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", startPointGeo = " << startPointGeo_absZ << ", endPointGeo = " << endPointGeo_absZ
<< " -> LineOfSight " << static_cast<int>(eleMapMgrResult);
return eleMapMgrResult;
}
ElevationMapManager::LineOfSight ElevationMapManager::lineOfSightCalculation(const QPointF &startPointGeo, double startHat,
const QPointF &endPointGeo, double endHat)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< ", startPointGeo = " << startPointGeo << ", startHat = " << startHat
<< ", endPointGeo = " << endPointGeo << ", endHat = " << endHat ;
if (!isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, LtsElevationManager not valid";
return LineOfSight::LOS_ERROR;
}
if (startHat < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, startHat is negativ";
return LineOfSight::LOS_ERROR;
}
if (endHat < 0)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, endHat is negativ";
return LineOfSight::LOS_ERROR;
}
// 1st: create LOSVector:
SituationMap::LOSVector losProfile;
LOSProfile(startPointGeo, endPointGeo, losProfile);
if (losProfile.size() < 2)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, losProfile not valid"
<< ", losProfile.size = " << losProfile.size();
return LineOfSight::LOS_ERROR;
}
// 2nd: lineOfSightCalculation
SituationMap::ElevationMapManager::LineOfSight eleMapMgrResult = this->lineOfSightCalculation(losProfile, startHat, endHat);
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< ", startPointGeo = " << startPointGeo << ", startHat = " << startHat
<< ", endPointGeo = " << endPointGeo << ", endPointHat = " << endHat
<< " -> LineOfSight " << static_cast<int>(eleMapMgrResult);
return eleMapMgrResult;
}
} // SituationMap
@@ -0,0 +1,84 @@
/**
* @RDE_HEADER@
* Manage and combine several ElevationMap
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "elevation_map_helper.h"
#include <ElevationMap/situationmapelevationmap_export.h>
#include <Common/situation_map_logging.h>
#include <QObject>
namespace SituationMap
{
// forward:
class ElevationMap;
//------------------------------------------------------------------------------------------------------------
// ElevationMapManager
//------------------------------------------------------------------------------------------------------------
class SITUATIONMAPELEVATIONMAP_EXPORT ElevationMapManager final : public QObject
{
using BaseClass = QObject;
public:
ElevationMapManager();
ElevationMapManager(const ElevationMapManager &) = delete;
ElevationMapManager &operator = (const ElevationMapManager &) = delete;
~ElevationMapManager();
int init(const QStringList &filePaths_rough_to_fine);
void cleanupCurrentContent();
bool waitForInitialization();
bool isValid() const;
double elevation(double lon_rad, double lat_rad, bool *bIsCovered = nullptr) const;
double elevation(double lon_rad, double lat_rad, double defaultValue, bool *bIsCovered = nullptr) const;
// united extent of seperate files:
QRectF simpleUnitedExtent() const;
bool contains(const QPointF &point) const;
// Line of Sight Profile: berücksichtigt Erdkrümmung und WGS84 Erdellipsoid
void LOSProfile(const QPointF &start, const QPointF &end, LOSVector &losProfile);
/*! \name Line of Sight */
//@{
enum class LineOfSight { LOS_UNDEFINED,
LOS_ERROR,
LOS_VISIBLE,
LOS_INVISIBLE };
LineOfSight lineOfSightCalculation(const LOSVector &losProfile,
double centerHat,
double objHat) const;
LineOfSight lineOfSightCalculation(const LtsUtils::QPoint3D &startPointGeo_absZ,
const LtsUtils::QPoint3D &endPointGeo_absZ);
LineOfSight lineOfSightCalculation(const QPointF &startPointGeo, double startHat,
const QPointF &endPointGeo, double endHat);
//@}
protected:
class Private;
Private *d;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
}; // ElevationMapManager
} // SituationMap
@@ -0,0 +1,229 @@
/**
* @RDE_HEADER@
* LineOfSightCalculation (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, NLE, 06.04.2020
*/
#include "line_of_sight_calculation.h"
#include "elevation_map.h"
#include <LtsCoordTrans/lts_coord_trans.h>
//
// ESRI / ARCGIS
// if you link libEsriCommonQt, you propably have to add ArcGISQt5Hack AND: # make sure it's loaded *before* libEsriCommonQt.so
// this may effect all executables linking situationMapElevationMap. CHECK!
//
// Qt
#include <QTimer>
#include <QElapsedTimer>
// some helper functions
inline Q_CONSTEXPR double pi() { return M_PI; }
inline Q_CONSTEXPR double deg2rad() { return pi() / 180.; }
inline Q_CONSTEXPR double rad2deg() { return 1. / deg2rad(); }
namespace SituationMap
{
LineOfSightCalculation::LineOfSightCalculation(ElevationMap *elevationMap)
: m_logger(getLogger("LineOfSightCalculation"))
, m_elevationMap(elevationMap)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LineOfSightCalculation::~LineOfSightCalculation()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LineOfSightCalculation::calculate(ObserverTargetData &observerTarget, unsigned int sampleIntervallMeter)
{
// same checks:
if (!m_elevationMap || !m_elevationMap->isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no elevationMap configured or not valid";
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined);
return false;
}
if (!observerTarget.isObserverAndTargetValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " observer and/or target not valid";
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined);
return false;
}
if (observerTarget.observer().toPointF() == observerTarget.target().toPointF())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " observer and target are identical regarding x and y";
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined);
return false;
}
bool success = true;
QElapsedTimer timer;
if (m_logger.isInfoEnabled())
timer.start();
//
//
// consider using ElevationMap::LOSProfile
//
//
// determine how many iterations we need:
double distanceInMeter = distanceMeter(observerTarget.observer(),
observerTarget.target());
//ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter;
int maxNumberOfIterations = distanceInMeter / sampleIntervallMeter; // check visibility every ~5m
// maxNumberOfIterations = 1000000;
if (maxNumberOfIterations<=0)
maxNumberOfIterations = 5;
double distanceFromObserverToTargetAsFraction = 0;
int i = 0;
while (i<maxNumberOfIterations)
{
++i;
/*
z-axis (Elevation) pCandidate
|---------- /
| ----------
| ----------
| ----------
| ----------
|------------------------------------------------------------ xy-axis
^ ^ ^ ^ ^ ^ ^ ^ samples (every ~5meters)
^ ^
ObserverPosition TargetPosition
On every sample position, check if directLineElevation (maximum) is
obstructed by elevationMapElevation (real value)
if (elevationMapElevation>directLineElevation) -> obstructed/break
*/
distanceFromObserverToTargetAsFraction = (double)i/maxNumberOfIterations;
const Point3D &pCandidate = movePoint(observerTarget.observer(),
observerTarget.target(),
distanceFromObserverToTargetAsFraction);
// {
// static Point3D oldCandidate;
// double distanceInMeter = distanceMeter(pCandidate, oldCandidate);
// ERROR_LOG(m_logger) << LOGFUNCTION << " distanceMeter = " << distanceInMeter;
// oldCandidate = pCandidate;
// }
// z is the elevation of a *direct* line:
const double directLineElevation = pCandidate.z();
const double elevationMapElevation = m_elevationMap->elevation(pCandidate.x(), pCandidate.y());
// Coord covered by elevationMap?
if (elevationMapElevation <= std::numeric_limits<int>::min())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Error, coordinate not covered be elevationData. Finish this LineOfSightCalculation."
<< " i=" << i
<< ", pCandidate = " << pCandidate;
// write back data
observerTarget.setObstacle(pCandidate);
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Undefined);
// finish the loop:
success = false;
break;
}
// analyze / check visibility:
if (elevationMapElevation>directLineElevation)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " i=" << i
<< " break - pCandidate = " << pCandidate
<< " -> directLineElevation = " << directLineElevation
<< " -> elevationMapElevation = " << elevationMapElevation;
// write result
observerTarget.setObstacle(pCandidate);
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Obstructed);
// finish the loop:
break;
} else {
observerTarget.setLineOfSightVisibility(LineOfSightVisibility::Visible);
}
} // while
if (m_logger.isInfoEnabled())
{
INFO_LOG(m_logger) << LOGFUNCTION << " END"
<< ", success = " << success
<< ", " << i << " of "<< maxNumberOfIterations << " iterations"
<< ", distanceFromObserverToTargetAsFraction = " << distanceFromObserverToTargetAsFraction
<< ", pCandidate=" << observerTarget.obstacle()
<< ", lineOfSightVisibility=" << static_cast<int>(observerTarget.lineOfSightVisibility())
<< ", took [ms] " << timer.elapsed();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END" << ", success = " << success;
return success;
}
Point3D LineOfSightCalculation::movePoint(const Point3D &oberserver, const Point3D &target, double distanceFromObserverToTargetAsFraction) // 0.0 ... 1.0
{
// positions are lon/lat @ rad.
// for evaluation we do that very simple:
// strange, but: using Point3D seems to be faster then using QPointF
const Point3D &dist = target - oberserver;
const Point3D &point = oberserver + (dist * distanceFromObserverToTargetAsFraction);
return point;
}
double LineOfSightCalculation::distanceMeter(const Point3D &oberserver, const Point3D &target)
{
// Try to avoid the dependency to ESRI (keep ArcGISQt5Hack in mind)
// And good accuracy is not required here
// use LtsCoordTrans::distance
// Esri::ArcGISRuntime::Point geom1(oberserver.x()*rad2deg(), oberserver.y()*rad2deg(), oberserver.z(), Esri::ArcGISRuntime::SpatialReference::wgs84());
// Esri::ArcGISRuntime::Point geom2(target.x() *rad2deg(), target.y() *rad2deg(), target.z() , Esri::ArcGISRuntime::SpatialReference::wgs84());
// Esri::ArcGISRuntime::GeometryEngine *gEngine = Esri::ArcGISRuntime::GeometryEngine::instance();
// Esri::ArcGISRuntime::GeodeticDistanceResult geodeticDistanceResult = gEngine->distanceGeodetic(geom1, geom2,
// Esri::ArcGISRuntime::LinearUnitId::Meters,
// Esri::ArcGISRuntime::AngularUnitId::Degrees,
// Esri::ArcGISRuntime::GeodeticCurveType::Geodesic);
// double distance = geodeticDistanceResult.distance();
double distance = LtsCoordTrans::distance(oberserver.toPointF(), target.toPointF());
return distance;
}
} // namespace SituationMap
@@ -0,0 +1,92 @@
/**
* @RDE_HEADER@
* LineOfSightCalculation (L400)
* $Id$
* $HeadURL$
* $Author$
* Created by N. Thoden, NLE, 06.04.2020
*/
#pragma once
#include <ElevationMap/situationmapelevationmap_export.h>
#include <Common/situation_map_logging.h>
#include <LtsUtilsCore/qpoint3d.h>
using Point3D = LtsUtils::QPoint3D; ///< x = lon ; y = lat , z = elevation/altitude in meters
namespace SituationMap
{
// forward
class ElevationMap;
//----------------------------------------------------------------------------
/*!
\brief Define visibility.
*/
enum class LineOfSightVisibility
{
Undefined, /*!< no data at that location? */
Visible,
Obstructed
};
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
class SITUATIONMAPELEVATIONMAP_EXPORT ObserverTargetData
{
public:
explicit ObserverTargetData(const Point3D &oberserver = Point3D(), // z-value: incl. observerHeight
const Point3D &target = Point3D()) // z-value: incl. targetHeight
: m_oberserver(oberserver)
, m_target(target)
{}
const Point3D &observer() const { return m_oberserver; }
const Point3D &target() const { return m_target; }
const Point3D &obstacle() const { return m_obstacle; }
void setObstacle(const Point3D &obstacle) { m_obstacle = obstacle; }
LineOfSightVisibility lineOfSightVisibility() const { return m_visible; }
void setLineOfSightVisibility(const LineOfSightVisibility &visible) { m_visible = visible; }
bool isObserverAndTargetValid() const { return (!m_oberserver.isNull()) && (!m_target.isNull()); }
private:
Point3D m_oberserver;
Point3D m_target;
Point3D m_obstacle;
LineOfSightVisibility m_visible = LineOfSightVisibility::Undefined;
};
//----------------------------------------------------------------------------
class SITUATIONMAPELEVATIONMAP_EXPORT LineOfSightCalculation
{
public:
explicit LineOfSightCalculation(ElevationMap *elevationMap);
~LineOfSightCalculation();
bool calculate(ObserverTargetData &observerTarget, unsigned int sampleIntervallMeter = 5);
protected:
static Point3D movePoint(const Point3D &oberserver,
const Point3D &target,
double distanceFromObserverToTargetAsFraction);
static double distanceMeter(const Point3D &oberserver,
const Point3D &target);
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
ElevationMap *m_elevationMap = nullptr;
ObserverTargetData m_observerTarget;
};
} // namespace SituationMap
+9
View File
@@ -0,0 +1,9 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# ADD_SUBDIRECTORY(Viewshed3D)
+43
View File
@@ -0,0 +1,43 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# Evaluation application for 3D viewshed
FIND_PACKAGE(ArcGIS REQUIRED)
INCLUDE_DIRECTORIES(${ARCGIS_INCLUDE_DIR}/arcgis)
SET(EXE_SRCS
main.cpp
ViewshedLocation.cpp
)
SET(EXE_HDRS
ViewshedLocation.h
)
SOURCE_GROUP(EvalViewshed3DApp FILES ${EXE_SRCS} ${EXE_HDRS})
NGL_ADD_EXECUTABLE(WS NO_TRANSLATION EvalViewshed3DApp ${EXE_SRCS} ${EXE_HDRS})
NGL_TARGET_LINK_LIBRARIES(
EvalViewshed3DApp
ArcGIS::ArcGIS
Qt5::Core
Qt5::Gui
Qt5::Widgets
Qt5::Network
)
IF(MSVC)
# Configure the template file
SET(VS_USER_FILE EvalViewshed3DApp.vcxproj.user)
configure_file( ../../SituationMap.vcxproj.user.in ${CMAKE_CURRENT_BINARY_DIR}/${VS_USER_FILE} @ONLY)
ENDIF(MSVC)
NGL_INSTALL_TARGET(EvalViewshed3DApp)
@@ -0,0 +1,58 @@
#-------------------------------------------------
# Copyright 2015 Esri.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#-------------------------------------------------
mac {
cache()
}
CONFIG += c++11 esri_runtime_qt100_0_0 debug
QT += core gui opengl xml network positioning sensors
win32:CONFIG += \
embed_manifest_exe
TARGET = LocationViewshed
TEMPLATE = app
SOURCES += \
main.cpp \
ViewshedLocation.cpp
HEADERS += \
ViewshedLocation.h
win32 {
INCLUDEPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/include"
debug:LIBPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/windows/x64/lib/Debug"
release:LIBPATH += "C:/Program Files (x86)/ArcGIS SDKs/Qt100.0/sdk/windows/x64/lib/Release"
debug:LIBS += -lEsriCommonQtd
release:LIBS += -lEsriCommonQt
debug:LIBS += -lEsriRuntimeQtd
release:LIBS += -lEsriRuntimeQt
}
linux {
INCLUDEPATH += /usr/include/arcgis
LIBS += -lEsriCommonQt
LIBS += -lEsriRuntimeQt
LIBS += -lc++
}
macx {
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.11
}
@@ -0,0 +1,274 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
// [WriteFile Name=ViewshedLocation, Category=Analysis]
// [Legal]
// Copyright 2017 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]
#include "ViewshedLocation.h"
#include "ArcGISTiledElevationSource.h"
#include "Scene.h"
#include "SceneGraphicsView.h"
#include "LocationViewshed.h"
#include "Camera.h"
#include "Point.h"
#include "AnalysisOverlay.h"
#include "QColor"
using namespace Esri::ArcGISRuntime;
ViewshedLocation::ViewshedLocation(QObject* parent /* = nullptr */):
QObject(parent)
{
}
void ViewshedLocation::init(Esri::ArcGISRuntime::SceneGraphicsView* sceneView)
{
m_sceneView = sceneView;
// Register classes for QML
// qmlRegisterType<SceneQuickView>("Esri.Samples", 1, 0, "SceneView");
// qmlRegisterType<ViewshedLocation>("Esri.Samples", 1, 0, "ViewshedLocationSample");
Surface* surface = new Surface(this);
surface->elevationSources()->append(
new ArcGISTiledElevationSource(
QUrl(QStringLiteral("http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer")),
this));
m_sceneView->arcGISScene()->setBaseSurface(surface);
// set initial viewpoint
setInitialViewpoint();
// connect signals
connectSignals();
}
void ViewshedLocation::setInitialViewpoint()
{
// Set a viewpoint
double x = 6.86088;
double y = 45.3604;
double z = 3582.55;
Point point(x, y, z, SpatialReference(4326));
double heading = 345;
double pitch = 70;
double roll = 0;
Camera camera(point, heading, pitch, roll);
m_sceneView->setViewpointCamera(camera);
}
void ViewshedLocation::connectSignals()
{
// on mouse click perform the location viewshed
connect(m_sceneView, &SceneGraphicsView::mouseClicked, this, [this](QMouseEvent& event)
{
if (!m_locationViewshed[0])
createViewshed(event.x(), event.y());
else
{
for (int i = 0; i < 3; ++i)
{
const Point pt = m_sceneView->screenToBaseSurface(event.x(), event.y());
m_locationViewshed[i]->setLocation(pt);
}
}
});
connect(m_sceneView, &SceneGraphicsView::mousePressedAndHeld, this, [this](QMouseEvent& event)
{
if (!m_locationViewshed[0])
createViewshed(event.x(), event.y());
m_calculating = true;
});
connect(m_sceneView, &SceneGraphicsView::mouseMoved, this, [this](QMouseEvent& event)
{
if (m_calculating)
{
for (int i = 0; i < 3; ++i)
{
const Point pt = m_sceneView->screenToBaseSurface(event.x(), event.y());
m_locationViewshed[i]->setLocation(pt);
}
}
});
connect(m_sceneView, &SceneGraphicsView::mouseReleased, this, [this](QMouseEvent)
{
m_calculating = false;
});
}
void ViewshedLocation::createViewshed(double x, double y)
{
m_analysisOverlay = new AnalysisOverlay(this);
m_sceneView->analysisOverlays()->append(m_analysisOverlay);
const Point pt = m_sceneView->screenToBaseSurface(x, y);
qCritical() << " pt=";
qCritical() << " pt=" << pt.x();
qCritical() << " pt=" << pt.y();
qCritical() << " pt=" << pt.z();
for (int i = 0; i < 3; ++i)
{
double heading = i * 120.0;
m_locationViewshed[i] = new LocationViewshed(pt
, heading
, 90.0 // pitch
, 120.0 // horizontalAngle
, 90.0 // verticalAngle
, 10.0 // minDistance
, 20000
, this);
m_locationViewshed[i]->setVisible(m_viewshedVisible);
// Add the Viewshed to the Analysis Overlay
m_analysisOverlay->analyses()->append(m_locationViewshed[i]);
m_locationViewshed[i]->setFrustumOutlineVisible(true);
// QColor frustumColor(0, 0, 0, 255);
// m_locationViewshed->setFrustumOutlineColor(frustumColor);
}
return;
}
// Getters/Setters for each Q_PROPERTY
bool ViewshedLocation::isViewshedVisible() const
{
return m_viewshedVisible;
}
void ViewshedLocation::setViewshedVisible(bool viewshedVisible)
{
for (int i = 0; i < 3; ++i)
{
if (m_locationViewshed[i])
{
if (m_locationViewshed[i]->isVisible() == viewshedVisible)
{
continue;
}
m_locationViewshed[i]->setVisible(viewshedVisible);
}
}
if (m_viewshedVisible != viewshedVisible)
{
m_viewshedVisible = viewshedVisible;
emit viewshedVisibleChanged();
}
}
bool ViewshedLocation::isFrustumOutlineVisible() const
{
return m_locationViewshed[0] ? m_locationViewshed[0]->isFrustumOutlineVisible() : m_frustumVisible;
}
void ViewshedLocation::setFrustumOutlineVisible(bool frustumVisible)
{
for (int i = 0; i < 3; ++i)
{
if (m_locationViewshed[i])
{
if (m_locationViewshed[i]->isFrustumOutlineVisible() == frustumVisible)
{
continue;
}
m_frustumVisible = frustumVisible;
m_locationViewshed[i]->setFrustumOutlineVisible(frustumVisible);
}
}
if (m_frustumVisible != frustumVisible)
{
m_frustumVisible = frustumVisible;
emit frustumVisibleChanged();
}
}
double ViewshedLocation::minDistance() const
{
return m_locationViewshed[0] ? m_locationViewshed[0]->minDistance() : m_minDistance;
}
void ViewshedLocation::setMinDistance(double minDistance)
{
for (int i = 0; i < 3; ++i)
{
if (m_locationViewshed[i])
{
if (m_locationViewshed[i]->minDistance() == minDistance)
{
continue;
}
m_locationViewshed[i]->setMinDistance(minDistance);
}
}
if (m_minDistance != minDistance)
{
m_minDistance = minDistance;
emit minDistanceChanged();
}
}
double ViewshedLocation::maxDistance() const
{
return m_locationViewshed[0] ? m_locationViewshed[0]->maxDistance() : m_maxDistance;
}
void ViewshedLocation::setMaxDistance(double maxDistance)
{
for (int i = 0; i < 3; ++i)
{
if (m_locationViewshed[i])
{
if (m_locationViewshed[i]->maxDistance() == maxDistance)
{
continue;
}
m_locationViewshed[i]->setMaxDistance(maxDistance);
}
}
if (m_maxDistance != maxDistance)
{
m_maxDistance = maxDistance;
emit maxDistanceChanged();
}
}
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* Declaration/Definition
* $Id$
* $HeadURL$
* $Author$
*/
// [WriteFile Name=ViewshedLocation, Category=Analysis]
// [Legal]
// Copyright 2017 Esri.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// [Legal]
#ifndef VIEWSHEDLOCATION_H
#define VIEWSHEDLOCATION_H
#include <QObject>
#include <QColor>
namespace Esri
{
namespace ArcGISRuntime
{
class SceneGraphicsView;
class LocationViewshed;
class AnalysisOverlay;
}
}
class ViewshedLocation : public QObject
{
Q_OBJECT
public:
explicit ViewshedLocation(QObject* parent = nullptr);
~ViewshedLocation() = default;
// void componentComplete() Q_DECL_OVERRIDE;
void init(Esri::ArcGISRuntime::SceneGraphicsView* sceneView);
Q_PROPERTY(bool viewshedVisible READ isViewshedVisible WRITE setViewshedVisible NOTIFY viewshedVisibleChanged)
Q_PROPERTY(bool frustumVisible READ isFrustumOutlineVisible WRITE setFrustumOutlineVisible NOTIFY frustumVisibleChanged)
Q_PROPERTY(double minDistance READ minDistance WRITE setMinDistance NOTIFY minDistanceChanged)
Q_PROPERTY(double maxDistance READ maxDistance WRITE setMaxDistance NOTIFY maxDistanceChanged)
bool isViewshedVisible() const;
void setViewshedVisible(bool viewshedVisible);
bool isFrustumOutlineVisible() const;
void setFrustumOutlineVisible(bool frustumVisible);
double minDistance() const;
void setMinDistance(double minDistance);
double maxDistance() const;
void setMaxDistance(double maxDistance);
signals:
void viewshedVisibleChanged();
void frustumVisibleChanged();
void minDistanceChanged();
void maxDistanceChanged();
private:
void connectSignals();
void setInitialViewpoint();
void createViewshed(double x, double y);
Esri::ArcGISRuntime::SceneGraphicsView *m_sceneView = nullptr;
Esri::ArcGISRuntime::LocationViewshed *m_locationViewshed[3] = { nullptr, nullptr, nullptr };
Esri::ArcGISRuntime::AnalysisOverlay *m_analysisOverlay = nullptr;
bool m_viewshedVisible = true;
bool m_frustumVisible = false;
double m_minDistance = 50;
double m_maxDistance = 1000;
double m_horizontalAngle = 45;
double m_veriticalAngle = 90;
double m_heading = 0;
double m_pitch = 90;
bool m_calculating = false;
};
#endif // VIEWSHEDLOCATION_H
+245
View File
@@ -0,0 +1,245 @@
/*
**
** @RDE_HEADER@
** Showing artefacts when drawing 3D map.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Linux, Qt 5.9. ArcGISRuntimeQt 100.1
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 08.11.2017, JT : Initial
**
** -----------------------------------------------------------------------------------------------
*/
#include <QTimer>
#include <QtCore/QDir>
#include <QtCore/QUrl>
#include <QtCore/QDebug>
#include <QtGui/QSurface>
#include <QtNetwork/QNetworkProxy>
#include <QtWidgets/QApplication>
#include <QtWidgets/QDesktopWidget>
#include <QtWidgets/QComboBox>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QGraphicsScene>
#include <QtWidgets/QGraphicsProxyWidget>
#include <QtWidgets/QMainWindow>
#include <Map.h>
#include <Scene.h>
#include <SceneGraphicsView.h>
#include <GraphicsOverlay.h>
#include <GeoView.h>
#include <Basemap.h>
#include <ArcGISTiledLayer.h>
#include <Layer.h>
#include <GraphicsOverlay.h>
#include "ViewshedLocation.h"
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
class MapWindow : public QWidget
{
Esri::ArcGISRuntime::Basemap * m_basemap;
Esri::ArcGISRuntime::Scene * m_scene;
Esri::ArcGISRuntime::SceneGraphicsView * m_sceneGraphicsView;
Esri::ArcGISRuntime::GeoView * m_mapView;
Esri::ArcGISRuntime::GraphicsOverlay * m_graphicsOverlay;
Qt::MouseButton m_pressedMouseButton;
QPoint m_mouseMoveRefPos;
Esri::ArcGISRuntime::TaskWatcher m_pickingTask;
bool m_pendingRequest;
public:
MapWindow() : QWidget(), m_graphicsOverlay(nullptr), m_pickingTask(), m_pendingRequest(false)
{
m_pressedMouseButton = Qt::MouseButton::NoButton;
m_mouseMoveRefPos = QPoint();
m_basemap = new Esri::ArcGISRuntime::Basemap(this);
m_scene = new Esri::ArcGISRuntime::Scene(m_basemap,this);
connect( m_basemap, &Esri::ArcGISRuntime::Map::errorOccurred , this, &MapWindow::onErrorBasemap );
connect( m_scene , &Esri::ArcGISRuntime::Scene::errorOccurred, this, &MapWindow::onErrorScene );
connect( m_scene , &Esri::ArcGISRuntime::Scene::doneLoading , this, &MapWindow::onDoneLoading );
m_sceneGraphicsView = nullptr;
try
{
m_sceneGraphicsView = new Esri::ArcGISRuntime::SceneGraphicsView(); // no parent,
m_sceneGraphicsView->setObjectName(QLatin1String("EsriSceneView"));
m_sceneGraphicsView->setFrameShape(QFrame::NoFrame); // frameless
}
catch (std::exception &stdEx)
{
const char *what = stdEx.what();
if (what)
qCritical()<< "MapWindow init SceneGraphicsView: std Exception " << " what: '" << what << "'";
else
qCritical() << "MapWindow init SceneGraphicsView: std Exception";
throw; // re-throw
}
catch (...)
{
qCritical() << "MapWindow init SceneGraphicsView: unknown Exception";
throw; // re-throw
}
if (!m_sceneGraphicsView)
return;
m_sceneGraphicsView->setArcGISScene(m_scene);
QHBoxLayout *layout = new QHBoxLayout(this);
layout->addWidget(m_sceneGraphicsView);
initMapLayer(QLatin1String("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer"));
initMapLayer(QLatin1String("http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"));
Esri::ArcGISRuntime::Camera camera(28.4, 83.9, 10010.0, 10.0, 80.0, 300.0);
m_sceneGraphicsView->setViewpointCameraAndWait(camera);// set the viewpoint
/*
QPushButton * aButton = new QPushButton(tr("Click me")); // toplevel widget
QGraphicsProxyWidget *proxy = m_sceneGraphicsView->QGraphicsView::scene()->addWidget(aButton);
proxy->setOpacity(0.99);
connect( aButton, &QPushButton::clicked, this, []() {qCritical() << "A Button has been clicked!";} );
*/
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
timer->setInterval(5000);
connect(timer, &QTimer::timeout, this, [this]() {
qCritical() << "Create viewshed ...";
ViewshedLocation * vLoc = new ViewshedLocation(this);
vLoc->init(m_sceneGraphicsView);
});
timer->start();
} // MapWindow()
~MapWindow() = default;
void initMapLayer( const QString &mapServiceUrl )
{
Esri::ArcGISRuntime::ArcGISTiledLayer *tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(QUrl(mapServiceUrl), this);
tiledLayer->setName(mapServiceUrl);
tiledLayer->setObjectName(tiledLayer->name());
m_basemap->baseLayers()->append(tiledLayer);
} // void initMapLayer()
private:
void onDoneLoading(const Esri::ArcGISRuntime::Error &loadError)
{
} // void onDoneLoading(const Esri::ArcGISRuntime::Error &loadError)
void onErrorBasemap(const Esri::ArcGISRuntime::Error &error)
{
qCritical() << " ---EsriError,Basemap --- occured: "
<< " code=" << error.code()
<< " domain=" << (int)error.domain()
<< " extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'";
} // void onErrorBasemap(const Esri::ArcGISRuntime::Error &error)
void onErrorScene(const Esri::ArcGISRuntime::Error &error)
{
qCritical() << " ---EsriError, Scene --- occured: "
<< " code=" << error.code()
<< " domain=" << (int)error.domain()
<< " extendedErrorType=" << (int)error.extendedErrorType()
<< ", message='" << error.message() << "'";
} // void onErrorScene(const Esri::ArcGISRuntime::Error &error)
}; // MapWindow
int main(int argc, char* argv[])
{
QApplication app(argc,argv);
// ESRI :
#ifdef Q_OS_WIN
// Force usage of OpenGL ES through ANGLE on Windows
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
// : ESRI
#define PROXY
#ifdef PROXY
QNetworkProxy proxy;
proxy.setType(QNetworkProxy::HttpProxy);
proxy.setHostName(QStringLiteral("proxy-rde"));
proxy.setPort(8080);
proxy.setUser(QStringLiteral("norbert.thoden@rheinmetall.com"));
proxy.setPassword(QStringLiteral("RDEWWW11"));
QNetworkProxy::setApplicationProxy(proxy);
#endif
MapWindow * mapWin = new MapWindow();
QDesktopWidget wid;
int width = mapWin->frameGeometry().width();
int height = mapWin->frameGeometry().height();
int screenWidth = wid.screen()->width();
int screenHeight = wid.screen()->height();
mapWin->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height);
mapWin->show();
app.setQuitOnLastWindowClosed(true);
int retval = app.exec();
delete mapWin;
return retval;
} // main
+46
View File
@@ -0,0 +1,46 @@
#
# $Id$
# $HeadURL$
# $Author$
#
FIND_PACKAGE(ArcGIS REQUIRED)
# LIBRARY
SET(FunctionDialog_SRCS
function_dialog.cpp
simple_esri_layer.cpp
)
SET(FunctionDialog_HDRS
function_dialog_export.h
function_dialog.h
simple_esri_layer.h
)
SET(FunctionDialog_ICONS
state_icon_b.xpm
state_icon_a.xpm
state_icon_c.xpm
)
SOURCE_GROUP(FunctionDialog FILES ${FunctionDialog_SRCS} ${FunctionDialog_HDRS})
NGL_ADD_LIBRARY(WS situationMapFunctionDialog SHARED ${FunctionDialog_SRCS} ${FunctionDialog_HDRS})
NGL_TARGET_LINK_LIBRARIES(situationMapFunctionDialog
PRIVATE
NGLLogger
LtsCoordTrans
LtsWidgets
advancedSymbologyGui
PUBLIC
ArcGIS::ArcGIS
situationMap
situationMapObjects
situationMapPortal
Qt5::Core
Qt5::Widgets
)
NGL_INSTALL_TARGET(situationMapFunctionDialog)
File diff suppressed because it is too large. Load diff
+289
View File
@@ -0,0 +1,289 @@
/**
* @RDE_HEADER@
* FunctionDialog
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "function_dialog_export.h"
#include <Map/map.h>
#include <QWidget>
#include <Symbology/symbol_dictionary.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/GraphicsOverlay.h>
// #define USE_LOCAL_SERVER
class QStandardItemModel;
//----------------------------------------------------------------------------
namespace SituationMap
{
namespace RB
{
class RubberbandBase;
}
class Map;
// TODO: nur fuer Tests exportiert
class FUNCTIONDIALOG_EXPORT FunctionDialog : public QWidget
{
Q_OBJECT
public:
explicit FunctionDialog(Esri::ArcGISRuntime::Map *esriMap,
Esri::ArcGISRuntime::MapGraphicsView *esriView,
QWidget *parent);
explicit FunctionDialog(SituationMap::Map *map);
virtual ~FunctionDialog();
static void addButtonToMap(SituationMap::Map *map, bool isVisible = true);
void init();
void setGraphicsLayer(Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer);
void setMapPositionString(const QString &mapPosition);
void setPixelSizeString(const QString &text);
void addMapPositionString(const QString &mapPosition);
FunctionDialog(const FunctionDialog &other) = delete; // No copy
FunctionDialog &operator=(const FunctionDialog &other) = delete; // No copy
protected:
void addSimpleLayerTab();
void addLogicalLayerTab();
void addLayerPerformanceTab();
void addGraphicsTab();
void addGraphics3DTab();
void addMessageTab();
void addMapObjectTab();
void addRubberbandTab();
void addRubberband3DTab();
void addToolsTab();
void addViewshedTab();
void addOverlayPlanTab();
void addIconRenderTab();
void addPortalTab();
void addMovePointLuxemburgTab();
void getRotationAndAngle(const QPointF &sectStart, const QPointF &sectEnd, double &rotationDegOut, double &openAngleDegOut);
// void addYourTab();
Esri::ArcGISRuntime::Graphic *createCube(const SituationMap::PositionCont &mapPosCont,
int baseHeight,
double objHeight,
const QString &partyNameValue = QString());
public Q_SLOTS:
void onDestroyed();
void onLayerPerfCreateLayer();
void onGenerateGraphics();
void onGenerateCube();
void onGenerateCubeArray();
void onGenerateStackedCubes();
void onGenerateGraphicsKristenStrandford();
void onGenerateUniqueVaueRendererMountingPoints();
void onGenerate3DGraphicSphereRENDERER();
void onSphereOpacityChangedRENDERER(int opacityValue);
void onGenerate3DGraphicSphere();
void onSphereOpacityChanged(int opacityValue);
void onSphereZValueChanged(int ZValue);
void onGenerate3DGraphicCone();
void onConeOpacityChanged(int opacityValue);
void onConeZValueChanged(int ZValue);
void onConeHeadingChanged(float angle);
void onConeRollChanged(float angle);
void onConePitchChanged(float angle);
void on3dModel();
void on3dModelZValueChanged(int ZValue);
void onGenerate3DGraphicConeUniqueValueRenderer();
void onAddCivilSymbols();
void onMessage();
void onMessageSelect();
void onAddSymbol();
void onSomeParticipants();
void onRubberbandApply(RB::RubberbandBase *);
void onRubberbandAbort(RB::RubberbandBase *);
void onRubberband3DApply(RB::RubberbandBase *);
void onRubberband3DAbort(RB::RubberbandBase *);
void onRubberbandDefineArrow();
void onRubberbandDefineEllipse();
void onRubberbandBoobyTrapSector();
void onRubberbandCheckCoordinates();
void onRubberbandStateSelect();
void onRubberbandTwoInOne();
void onRectCheckCoordinates();
void onPolygonCreate();
void onPolylineCreate();
void onPolylineUpdate();
void onTextCreate();
void onTextUpdate();
void onMapObjectIndicatorCreate();
void onParticipantGetPosition();
void onParticipantGetPositionApplied(RB::RubberbandBase *rubberband = nullptr);
bool createParticipant(const Point3D &origin,
const QString &debugInfo);
void onParticipantCreate();
void onParticipantUpdate();
void onAreaFireCreate();
void onAreaFireUpdate();
void onBuildingCreate();
void onBuildingUpdate();
void onBiologicalAgentCreate();
void onBiologicalAgentUpdate();
void onChemicalAgentCreate();
void onCubeCreate();
void onDangerAreaCreate();
void onDangerAreaUpdate();
void onFunctionalAreaCreate();
void onFunctionalAreaUpdate();
void onGeoImageCreate();
void onMapObjectClicked(const SituationMap::Map::RefPointerMapObjectList &mapObjList,
const QPointF &viewportPosition);
void onSoundAreaCreate();
void onSoundAreaUpdate();
void onHitLineCreate();
void onHitLineUpdate();
void onLineOfSightLineCreate();
void onMineCreate();
void onMineUpdate();
void onMineFieldCreate();
void onMineFieldUpdate();
void onNuclearCreate();
void onNuclearUpdate();
void onChargeEffectCreate();
void onChargeEffectUpdate();
void onBoobyTrapCreate();
void onBoobyTrapUpdate();
void onSingleFireCreate();
void onSingleFireUpdate();
void onShowBasePoint();
void onShowPolizei();
void onObjectIndicator();
void onMeasureLength();
void onStartLocationDisplay();
void onStopLocationDisplay();
void onLoadFile();
void onPortal();
void onMovePoint();
#ifdef USE_LOCAL_SERVER
void onStartLocalService();
void onStopLocalService();
void onStartViewshedSvc();
void onStopViewshedSvc();
void onViewshedInteractive();
void onViewshed();
#endif // USE_LOCAL_SERVER
void onSightFan();
void onViewshedSightFan();
void onLoadPlan();
void onOverlayVisiChanged(bool checked);
void onRemovePlan();
void onRemoveAllPlans();
void onToPdf();
void onDump();
void onOverviewMap();
void onBackupOms();
void onRestoreOms();
void onRemove();
void onRemoveAll();
void onToggleGrid();
void onToggleVisibility();
void onToggleVisibilityTechn();
void onToggleVisibilityTact();
void onToggleSelection();
void onTogglePicking();
void onToggleBoundingBox();
void onToggleBB2();
void onMapRotationChanged();
void onSymbolSizeChanged();
void onSymbolOpacityChanged();
void onDrawOrderChanged();
//void onBuildingPERFORMANCE();
void onMouseWindowPositionChanged(int x, int y);
void onMouseMapPositionChanged(const Point3D &mapPos, bool isValid);
void onMouseGeoPositionChanged(const Point3D &geoPos, bool isValid);
void onExtendChanged();
private:
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
@@ -0,0 +1,21 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <qglobal.h>
#ifndef FUNCTIONDIALOG_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define FUNCTIONDIALOG_EXPORT
# else
# if defined(MAKE_SITUATIONMAPFUNCTIONDIALOG_LIB)
# define FUNCTIONDIALOG_EXPORT Q_DECL_EXPORT
# else
# define FUNCTIONDIALOG_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
@@ -0,0 +1,378 @@
/**
* @RDE_HEADER@
* FunctionDialog
* $Id$
* $HeadURL$
* $Author$
*/
#include "simple_esri_layer.h"
#include <arcgis/Map.h>
#include <MapImpl/map_impl.h>
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/LayerListModel.h>
#include <arcgis/ArcGISTiledLayer.h>
// for overview
#include <QDebug>
#include <QDialog>
#include <QLabel>
#include <QCheckBox>
#include <QPushButton>
#include <QSlider>
#include <QGridLayout>
#include <QStandardItemModel>
namespace SituationMap
{
Esri::ArcGISRuntime::Layer *getEsriBasemap_Layer(Esri::ArcGISRuntime::Map *map,
const QString &layername)
{
if (!(map &&
map->basemap() &&
map->basemap()->baseLayers())
)
return nullptr;
Esri::ArcGISRuntime::Layer *layerFound = nullptr;
const Esri::ArcGISRuntime::LayerListModel *model = map->basemap()->baseLayers();
for (int i = 0; i < model->size(); ++i)
{
const Esri::ArcGISRuntime::Layer *curLayer = model->at(i);
const QString &curName = curLayer->name();
if (QString::compare(layername, curName) == 0)
{
layerFound = model->at(i);
break;
}
} // for
return layerFound;
}
Esri::ArcGISRuntime::ArcGISTiledLayer *getEsriBasemap_ArcGISTiledLayer(Esri::ArcGISRuntime::Map *map,
const QString &layername)
{
Esri::ArcGISRuntime::Layer *layerFound = getEsriBasemap_Layer(map, layername);
if (! layerFound)
return nullptr;
if (layerFound->layerType() == Esri::ArcGISRuntime::LayerType::ArcGISTiledLayer)
{
Esri::ArcGISRuntime::ArcGISTiledLayer *layer2 =
static_cast<Esri::ArcGISRuntime::ArcGISTiledLayer *>(layerFound);
return layer2;
}
return nullptr;
}
class SimpleEsriLayer::Private
{
public:
Private()
{
}
~Private()
{
}
// ESRI data:
Esri::ArcGISRuntime::Map *m_map = nullptr;
Esri::ArcGISRuntime::MapGraphicsView *m_mapView = nullptr;
// OWN data:
QGridLayout *m_layout = nullptr;
}; // class SimpleEsriLayer::Private
//-----------------------------------------------------------------------------
SimpleEsriLayer::SimpleEsriLayer(QWidget *parent, Esri::ArcGISRuntime::MapGraphicsView *mapView)
: QWidget(parent)
, d(new Private)
{
d->m_mapView = mapView;
if (d->m_mapView)
d->m_map = d->m_mapView->map();
d->m_layout = new QGridLayout(this);
updatePanel();
}
SimpleEsriLayer::~SimpleEsriLayer()
{
delete d;
}
bool SimpleEsriLayer::updatePanel()
{
for (int row = 0; row < d->m_layout->rowCount(); ++row)
{
for (int col = 0; col < d->m_layout->columnCount(); ++col)
{
QLayoutItem *item = d->m_layout->itemAtPosition(row, col);
if (item)
{
QWidget *w = item->widget();
delete w;
}
}
}
delete d->m_layout;
d->m_layout = new QGridLayout(this);
if (d->m_map)
{
// Header
int row = 0;
int col = 0;
for (const char *name : { "OnOff", "Opac", "Brightness", "Contrast", "Gamma" })
d->m_layout->addWidget(new QLabel(QString::fromLatin1(name)), row, col++);
row = d->m_layout->rowCount();
// GraphicalOverlay
Esri::ArcGISRuntime::GraphicsOverlayListModel *graphicsOverlayListModel = d->m_mapView->graphicsOverlays();
if (graphicsOverlayListModel)
{
int graphicsOverlaySize = graphicsOverlayListModel->size();
// qDebug() << LOGFUNCTION << " graphicsOverlayListModel -> size=" << graphicsOverlaySize;
row += graphicsOverlaySize;
for (int i = 0; i < graphicsOverlaySize; ++i)
{
--row;
col = 0;
// qDebug() << LOGFUNCTION << " graphicsOverlayListModel handle layer i='" << i;
Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = graphicsOverlayListModel->at(i);
if (! gOverlay)
{
qWarning() << LOGFUNCTION << " gOverlay is nil";
return false;
}
const QString &layername = gOverlay->objectName();
QCheckBox *cbOnOff = new QCheckBox(layername);
cbOnOff->setChecked(gOverlay->isVisible());
connect(cbOnOff, &QCheckBox::stateChanged, this, &SimpleEsriLayer::layerOnOff);
d->m_layout->addWidget(cbOnOff, row, col++);
SimpleEsriLayerSlider *sl_opac = new SimpleEsriLayerSlider(layername, 0, 100);
sl_opac->setValue(gOverlay->opacity() * 100);
connect(sl_opac, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::opacity);
d->m_layout->addWidget(sl_opac, row, col++);
} // for
} // if (graphicsOverlayListModel)
// BasemapLayer
row = d->m_layout->rowCount();
Esri::ArcGISRuntime::LayerListModel *baseLayerModel = d->m_map->basemap()->baseLayers();
if (baseLayerModel)
{
int baseLayerModelSize = baseLayerModel->size();
qWarning() << LOGFUNCTION << " baseLayerModel -> size=" << baseLayerModelSize;
row += baseLayerModelSize;
for (int i = 0; i < baseLayerModelSize; ++i)
{
col = 0;
--row;
qWarning() << LOGFUNCTION << " baseLayerModel handle layer i=" << i;
Esri::ArcGISRuntime::Layer *layer = baseLayerModel->at(i);
if (! layer)
{
qWarning() << LOGFUNCTION << " layer is nil";
return false;
}
const QString &layername = layer->name();
QCheckBox *cbOnOff = new QCheckBox(layername);
cbOnOff->setChecked(layer->isVisible());
// QString tooltip = layer->url();
// if (!l->serviceDescription().isEmpty())
// tooltip += QLatin1String(" ; ") % l->serviceDescription();
// if (!tooltip.isEmpty())
// cbOnOff->setToolTip(tooltip);
connect(cbOnOff, &QCheckBox::stateChanged, this, &SimpleEsriLayer::layerOnOff);
d->m_layout->addWidget(cbOnOff, row, col++);
SimpleEsriLayerSlider *sl_opac = new SimpleEsriLayerSlider(layername, 0, 100);
sl_opac->setValue(layer->opacity() * 100);
connect(sl_opac, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::opacity);
d->m_layout->addWidget(sl_opac, row, col++);
if (layer->layerType() == Esri::ArcGISRuntime::LayerType::ArcGISTiledLayer
// || layer->type() == EsriRuntimeQt::LayerType::ArcGISTiledMapService
)
{
Esri::ArcGISRuntime::ArcGISTiledLayer *realLayer =
dynamic_cast<Esri::ArcGISRuntime::ArcGISTiledLayer *>(layer);
if (realLayer)
{
float brightness = realLayer->brightness();
float contrast = realLayer->contrast();
float gamma = realLayer->gamma();
qWarning() << " brightness=" << brightness; // QuartzBeta1 -> not supported
qWarning() << " contrast=" << contrast; // QuartzBeta1 -> not supported
qWarning() << " gamma=" << gamma; // QuartzBeta1 -> not supported
SimpleEsriLayerSlider *sl_brightness = new SimpleEsriLayerSlider(layername, -100, 100);
sl_brightness->setValue(brightness * 100);
connect(sl_brightness, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::brightness);
d->m_layout->addWidget(sl_brightness, row, col++);
SimpleEsriLayerSlider *sl_contrast = new SimpleEsriLayerSlider(layername, -100, 100);
sl_contrast->setValue(contrast * 100);
connect(sl_contrast, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::contrast);
d->m_layout->addWidget(sl_contrast, row, col++);
SimpleEsriLayerSlider *sl_gamma = new SimpleEsriLayerSlider(layername, -100, 100);
sl_gamma->setValue(gamma * 100);
connect(sl_gamma, &SimpleEsriLayerSlider::valueChanged, this, &SimpleEsriLayer::gamma);
d->m_layout->addWidget(sl_gamma, row, col++);
} // ir (realLayer)
} // if (layer->type() == Es
} // for
} // if (baseLayerModel)
QPushButton *pbMaximizeExtent = new QPushButton(QLatin1String("maximize scale of 1st basemap"));
d->m_layout->addWidget(pbMaximizeExtent, 9999, 0);
connect(pbMaximizeExtent, &QPushButton::clicked, this, [this]()
{
if (d->m_map)
{
d->m_map->setMinScale(500000000); // 5e+08
d->m_map->setMaxScale(100);
}
});
}
else
qWarning() << LOGFUNCTION << " no map !!! ";
return true;
}
void SimpleEsriLayer::layerOnOff()
{
QCheckBox *cb = qobject_cast<QCheckBox *>(sender());
if (!cb)
return;
QString layername = cb->text();
qDebug() << LOGFUNCTION << " layername=" << layername;
Esri::ArcGISRuntime::Layer *l = getEsriBasemap_Layer(d->m_map, layername);
if (l)
l->setVisible(cb->isChecked());
else
qDebug() << LOGFUNCTION << " no layer!!!, layername=" << layername;
}
void SimpleEsriLayer::opacity(int)
{
SimpleEsriLayerSlider *sl = qobject_cast<SimpleEsriLayerSlider *>(sender());
if (!sl)
return;
QString layername = sl->m_layername;
double value = sl->value() / 100.0;
Esri::ArcGISRuntime::Layer *l = getEsriBasemap_Layer(d->m_map, layername);
if (l)
{
qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value;
l->setOpacity(value);
}
else
qWarning() << LOGFUNCTION << " no layer!!!, layername=" << layername;
}
void SimpleEsriLayer::brightness(int)
{
SimpleEsriLayerSlider *sl = qobject_cast<SimpleEsriLayerSlider *>(sender());
if (!sl)
return;
QString layername = sl->m_layername;
Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername);
double value = sl->value() / 100.0;
// double value = sl->value();
qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value;
l->setBrightness(value);
}
void SimpleEsriLayer::contrast(int)
{
SimpleEsriLayerSlider *sl = qobject_cast<SimpleEsriLayerSlider *>(sender());
if (!sl)
return;
QString layername = sl->m_layername;
Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername);
double value = sl->value() / 100.0;
// double value = sl->value();
qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value;
l->setContrast(value);
}
void SimpleEsriLayer::gamma(int)
{
SimpleEsriLayerSlider *sl = qobject_cast<SimpleEsriLayerSlider *>(sender());
if (!sl)
return;
QString layername = sl->m_layername;
Esri::ArcGISRuntime::ArcGISTiledLayer *l = getEsriBasemap_ArcGISTiledLayer(d->m_map, layername);
double value = sl->value() / 100.0;
// double value = sl->value();
qDebug() << LOGFUNCTION << " layername=" << layername << " set to val=" << value;
l->setGamma(value);
}
} // namespace SituationMap
+79
View File
@@ -0,0 +1,79 @@
/**
* @RDE_HEADER@
* FunctionDialog
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <QObject>
#include <QSlider>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/MapGraphicsView.h>
#include <arcgis/GraphicsOverlay.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
class SimpleEsriLayerSlider : public QSlider
{
Q_OBJECT
public:
SimpleEsriLayerSlider(const QString &layername, int min, int max)
: QSlider(), m_layername(layername)
{
this->setOrientation(Qt::Horizontal);
setMinimum(min);
setMaximum(max);
}
public:
QString m_layername;
};
class SimpleEsriLayer : public QWidget
{
Q_OBJECT
public:
SimpleEsriLayer(QWidget *parent, Esri::ArcGISRuntime::MapGraphicsView *mapView);
virtual ~SimpleEsriLayer();
SimpleEsriLayer(const SimpleEsriLayer &other) = delete; // No copy
SimpleEsriLayer &operator=(const SimpleEsriLayer &other) = delete; // No copy
bool updatePanel();
private slots:
void layerOnOff();
void opacity(int);
void brightness(int);
void contrast(int);
void gamma(int);
private :
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
//----------------------------------------------------------------------------
// Q_DECLARE_METATYPE(SituationMap::GeometryType);
+156
View File
@@ -0,0 +1,156 @@
/* XPM */
static const char * stateIconA_xpm[] = {
"24 24 129 2",
" c None",
". c #4CE146",
"+ c #9E8E3A",
"@ c #4CE045",
"# c #BDAB46",
"$ c #A3912F",
"% c #4BDF44",
"& c #32942E",
"* c #B8C14A",
"= c #CABF35",
"- c #4ADE43",
"; c #F2DA55",
"> c #A7973C",
", c #CDC74E",
"' c #E1D132",
") c #347D25",
"! c #48DD42",
"~ c #E7CF4C",
"{ c #F7D519",
"] c #F8DB3E",
"^ c #9D8E38",
"/ c #E3CC50",
"( c #F6D629",
"_ c #56821F",
": c #37AA32",
"< c #47DB41",
"[ c #DAC54B",
"} c #F6D51F",
"| c #F5D210",
"1 c #F5D211",
"2 c #F5DA3F",
"3 c #819D38",
"4 c #E8D14F",
"5 c #F6D522",
"6 c #A48D0C",
"7 c #46DA40",
"8 c #C3B445",
"9 c #F4D425",
"0 c #F3D00E",
"a c #C9BF13",
"b c #F3D00F",
"c c #F2D840",
"d c #8FAA3D",
"e c #E7D54D",
"f c #F4D31B",
"g c #D4B50C",
"h c #45D93E",
"i c #AC9B3C",
"j c #F3D52C",
"k c #EBCC0D",
"l c #7D9516",
"m c #CCAE0A",
"n c #F1CE0C",
"o c #F1CE0E",
"p c #F4D738",
"q c #F1D015",
"r c #DFC70E",
"s c #43D73D",
"t c #8E7F31",
"u c #F0D434",
"v c #DBC50D",
"w c #588019",
"x c #D6B709",
"y c #EFCC0A",
"z c #EDCB0A",
"A c #5B8F1F",
"B c #42D63B",
"C c #ECD23C",
"D c #C9BA0D",
"E c #3A7F1F",
"F c #DEBD07",
"G c #EDCA08",
"H c #A39309",
"I c #2E972A",
"J c #40D43A",
"K c #E7CF41",
"L c #C2A60A",
"M c #27701E",
"N c #33A92E",
"O c #C1A405",
"P c #38BB32",
"Q c #DCC543",
"R c #9D8709",
"S c #37B931",
"T c #3FD338",
"U c #24711E",
"V c #C2B33F",
"W c #3DD137",
"X c #C8B54A",
"Y c #83A134",
"Z c #3D6517",
"` c #3CD036",
" . c #E4CA37",
".. c #EDD035",
"+. c #29731E",
"@. c #F6D726",
"#. c #F9D821",
"$. c #3ACF34",
"%. c #39CD33",
"&. c #8F8132",
"*. c #F6D414",
"=. c #F6D311",
"-. c #AD9B39",
";. c #38CC32",
">. c #C9B337",
",. c #D9BF33",
"'. c #37CB30",
"). c #E9CB29",
"!. c #F1CD0C",
"~. c #EFD022",
"{. c #36CA2F",
"]. c #EFCD12",
"^. c #EECB09",
"/. c #EECC0C",
"(. c #8C7E2F",
"_. c #35C92E",
":. c #B29F34",
"<. c #EBC806",
"[. c #C4AD31",
"}. c #D7BD29",
"|. c #E8C503",
"1. c #E4C722",
"2. c #E8C714",
"3. c #E7C402",
"4. c #E8C508",
"5. c #97872E",
"6. c #E5C201",
"7. c #E5C200",
"8. c #B8A330",
" ",
" . . . . . . . . + ",
" @ @ @ @ # $ ",
" % & * = ",
" - ; > , ' ) ",
" ! ~ { ] ^ / ( _ : ",
" < [ } | 1 2 3 4 5 6 < ",
" 7 8 9 0 a 0 b c d e f g 7 ",
" h i j k l m n o p q r h ",
" s t u v w x y y z A s ",
" B C D E F G H I B ",
" J K L M N O J ",
" P Q R S T ",
" U V W # X W W ",
" Y Z ` ... ` ` ",
" +. @.#. $. $. ",
" %. &.*.=.-. %. ",
" ;. >.0 0 ,. ;. ",
" '. ).!.!.~. '. ",
" {. ].^.^./.(. {. ",
" _. :.<.<.<.<.[. _. ",
" }.|.|.|.|.1. ",
" 2.3.3.3.3.4. ",
" 5.6.7.7.7.7.7.8. "};
+216
View File
@@ -0,0 +1,216 @@
/* XPM */
static const char * stateIconB_xpm[] = {
"24 24 189 2",
" c None",
". c #4A8448",
"+ c #69BC66",
"@ c #75D272",
"# c #7ADA76",
"$ c #77D673",
"% c #6CC269",
"& c #508E4D",
"* c #61AE5E",
"= c #7EE57A",
"- c #73E76E",
"; c #64E55F",
"> c #5BE355",
", c #57E351",
"' c #59E353",
") c #62E55D",
"! c #71E76C",
"~ c #7EE779",
"{ c #68BB65",
"] c #7BE076",
"^ c #69E563",
"/ c #50E149",
"( c #46CE40",
"_ c #34982E",
": c #44C93E",
"< c #4EE047",
"[ c #65E45F",
"} c #7CE377",
"| c #4A8447",
"1 c #51934E",
"2 c #75E56F",
"3 c #51E04B",
"4 c #48D541",
"5 c #46D140",
"6 c #4FE048",
"7 c #71E56B",
"8 c #59A056",
"9 c #6EE368",
"0 c #4BDE44",
"a c #42C73C",
"b c #40BF3A",
"c c #6AE364",
"d c #4C894A",
"e c #6EDF69",
"f c #49DD43",
"g c #40C63B",
"h c #3FC13A",
"i c #6CE068",
"j c #66BD62",
"k c #4BDC45",
"l c #44D03E",
"m c #5EAB5B",
"n c #75D571",
"o c #42CD3D",
"p c #65C062",
"q c #5CDE57",
"r c #46DA40",
"s c #74D670",
"t c #64E05F",
"u c #52DC4C",
"v c #44CF3E",
"w c #5BDD56",
"x c #5AA956",
"y c #46D93F",
"z c #3EC438",
"A c #6DD168",
"B c #4FDB49",
"C c #3CBC36",
"D c #6DD268",
"E c #58A954",
"F c #57C352",
"G c #43D73D",
"H c #389234",
"I c #569F53",
"J c #50DA4A",
"K c #3ABA35",
"L c #3ABB35",
"M c #4FDA49",
"N c #5BA758",
"O c #58C553",
"P c #50D04B",
"Q c #42D63B",
"R c #5CCA57",
"S c #5BCE56",
"T c #50D049",
"U c #46D240",
"V c #40D43A",
"W c #4CD146",
"X c #3ECE38",
"Y c #3DCB38",
"Z c #4AD545",
"` c #41CF3A",
" . c #3FD338",
".. c #42CF3B",
"+. c #43D03C",
"@. c #45CF3E",
"#. c #40D139",
"$. c #41CE3A",
"%. c #3EC738",
"&. c #3DD137",
"*. c #3ABC35",
"=. c #41D23B",
"-. c #BDAB46",
";. c #C8B54A",
">. c #42D23C",
",. c #3ABF35",
"'. c #3BB136",
"). c #3CD036",
"!. c #53A74F",
"~. c #2F812B",
"{. c #64CF60",
"]. c #E4CA37",
"^. c #EDD035",
"/. c #66CF62",
"(. c #30852C",
"_. c #51A44D",
":. c #3DB637",
"<. c #338530",
"[. c #3ACF34",
"}. c #55CF51",
"|. c #33B72E",
"1. c #42CF3D",
"2. c #F6D726",
"3. c #F9D821",
"4. c #3A6E37",
"5. c #44D13E",
"6. c #35BD2F",
"7. c #55CC51",
"8. c #348930",
"9. c #38CA32",
"0. c #3FCF39",
"a. c #57A654",
"b. c #278723",
"c. c #8F8132",
"d. c #F6D414",
"e. c #F6D311",
"f. c #AD9B39",
"g. c #2EA72A",
"h. c #519A4E",
"i. c #42CF3C",
"j. c #2C9928",
"k. c #38CC32",
"l. c #56D351",
"m. c #C9B337",
"n. c #F3D00E",
"o. c #D9BF33",
"p. c #5AD256",
"q. c #2E9F2A",
"r. c #34C02D",
"s. c #38CB31",
"t. c #62D25D",
"u. c #E9CB29",
"v. c #F1CD0C",
"w. c #EFD022",
"x. c #64CD5F",
"y. c #3ACC33",
"z. c #34BF2D",
"A. c #35C62E",
"B. c #3BCB34",
"C. c #67D261",
"D. c #EFCD12",
"E. c #EECB09",
"F. c #EECC0C",
"G. c #8C7E2F",
"H. c #6ACF65",
"I. c #3FCC39",
"J. c #34C42D",
"K. c #39CA32",
"L. c #5BC656",
"M. c #B29F34",
"N. c #EBC806",
"O. c #C4AD31",
"P. c #63CD5E",
"Q. c #3ECB37",
"R. c #34C52D",
"S. c #30B82A",
"T. c #2B8327",
"U. c #D7BD29",
"V. c #E8C503",
"W. c #E4C722",
"X. c #2D9428",
"Y. c #2FB72A",
"Z. c #E8C714",
"`. c #E7C402",
" + c #E8C508",
".+ c #97872E",
"++ c #E5C201",
"@+ c #E5C200",
"#+ c #B8A330",
" . + @ # # $ % & ",
" * = - ; > , , ' ) ! ~ { ",
" ] ^ / ( _ : < [ } | ",
" 1 2 3 4 5 6 7 8 ",
" 9 0 a b 0 c d ",
" e f g h f i ",
" j k l m n = = n m o k p ",
" q r s t u v v u t s r w ",
"x y z A B C C B D z y E ",
"F G H I J K L M N H G O ",
"P Q R Q Q S Q T ",
"U V W X Y Z V U ",
"` . ..+. @.#. .$.",
"%.&. *.=. -.;. >.,. &.%.",
"'.).!. ~.).{. ].^. /.).(. _.).:.",
"<.[.}. |.1. 2.3. 4.5.6. 7.[.8.",
" 9.0.a. b. c.d.e.f. g. h.i.9. ",
" j.k.l. m.n.n.o. p.k.q. ",
" r.s.t. u.v.v.w. x.y.z. ",
" A.B.C. D.E.E.F.G. H.I.A. ",
" J.K.L. M.N.N.N.N.O. P.Q.R. ",
" S.T. U.V.V.V.V.W. X.Y. ",
" Z.`.`.`.`. + ",
" .+++@+@+@+@+@+#+ "};
+110
View File
@@ -0,0 +1,110 @@
/* XPM */
static const char * stateIconC_xpm[] = {
"24 24 83 1",
" c None",
". c #7FBD7C",
"+ c #8BCF88",
"@ c #84C582",
"# c #588456",
"$ c #63BE5F",
"% c #57B953",
"& c #73BF70",
"* c #56B752",
"= c #499B45",
"- c #699F66",
"; c #59B755",
"> c #54B550",
", c #52B14E",
"' c #6ABC67",
") c #52B34E",
"! c #377334",
"~ c #6FAF6C",
"{ c #50B24C",
"] c #4FB14B",
"^ c #48A144",
"/ c #4C734A",
"( c #5DB559",
"_ c #4DAE49",
": c #4CAD48",
"< c #6BB569",
"[ c #4AAC46",
"} c #377F35",
"| c #5E915C",
"1 c #50AD4C",
"2 c #48A944",
"3 c #44A040",
"4 c #61B05E",
"5 c #45A641",
"6 c #527F50",
"7 c #7BBF78",
"8 c #78BA75",
"9 c #639C60",
"0 c #46A542",
"a c #42A33E",
"b c #368633",
"c c #389035",
"d c #3FA13B",
"e c #43A33F",
"f c #619B5E",
"g c #56AC53",
"h c #3D9D39",
"i c #3B9C37",
"j c #3C9E38",
"k c #54A951",
"l c #64A761",
"m c #3D9E39",
"n c #2A6B26",
"o c #3A9B35",
"p c #3B9B36",
"q c #62A55F",
"r c #4B7749",
"s c #48A243",
"t c #33892F",
"u c #328B2F",
"v c #379833",
"w c #459F41",
"x c #4F7E4D",
"y c #5CA659",
"z c #369632",
"A c #349530",
"B c #349630",
"C c #59A955",
"D c #3A9936",
"E c #276C25",
"F c #256C22",
"G c #32932E",
"H c #2F892B",
"I c #2C8728",
"J c #2F912B",
"K c #2F902B",
"L c #2D8E29",
"M c #2D8F29",
"N c #257322",
"O c #226E1F",
"P c #2B8D27",
"Q c #298826",
"R c #288424",
" ",
" ",
" .+++@ ",
" #$%%% ",
" &***= ",
" -;>>, ",
" ')))! ",
" ~{]]^ ",
" /(__: ",
" <[[[} ",
" |1223 ",
" 4555 ",
" 67778 90aab ",
" cddef gddh ",
" ijjk lmjj ",
" noopq rsoot ",
" uvvwxyvvz ",
" ABBCDBBE ",
" FGGGGGH ",
" IJJJK ",
" LMMN ",
" OPQ ",
" R ",
" "};
+445
View File
@@ -0,0 +1,445 @@
"id";"name";"sym_klassifiz_id";"name";"symbol_id_code";"id";"name";"def_symbol_id"
"611";"ADMINISTRATIVE FINANCE";"331";"FINANCE";"S*G*USAF--*****";"(null)";"(null)";"(null)"
"609";"ADMINISTRATIVE JUDGE ADVOCATE GENERAL (JAG)";"325";"JUDGEADVOCATEGENERAL(JAG)";"S*G*USAJ--*****";"(null)";"(null)";"(null)"
"606";"ADMINISTRATIVE LABOUR";"355";"LABOUR";"S*G*USAL--*****";"(null)";"(null)";"(null)"
"607";"ADMINISTRATIVE MORAL, WELFARE, RECREATION (MWR)";"358";"MORAL,WELFARE,RECREATION(MWR)";"S*G*USAW--*****";"(null)";"(null)";"(null)"
"613";"ADMINISTRATIVE MORTUARY/GRAVE REGISTRY";"337";"MORTUARY/GRAVESREGISTRY";"S*G*USAM--*****";"(null)";"(null)";"(null)"
"612";"ADMINISTRATIVE PERSONNEL SERVICES";"334";"PERSONNELSERVICES";"S*G*USAS--*****";"(null)";"(null)";"(null)"
"610";"ADMINISTRATIVE POSTAL";"328";"POSTAL";"S*G*USAO--*****";"(null)";"(null)";"(null)"
"615";"ADMINISTRATIVE PUPLIC AFFAIRS";"343";"PUBLICAFFAIRS";"S*G*USAP--*****";"(null)";"(null)";"(null)"
"608";"ADMINISTRATIVE QUARTERMASTER (";"361";"QUARTERMASTER(SUPPLY)";"S*G*USAQ--*****";"(null)";"(null)";"(null)"
"614";"ADMINISTRATIVE RELIGIOUS/CHAPLAIN";"340";"RELIGIOUS/CHAPLAIN";"S*G*USAR--*****";"(null)";"(null)";"(null)"
"605";"ADMINISTRATIVE REPLACEMENT HOLDING UNIT (RHV)";"352";"REPLACEMENTHOLDINGUNIT(RHU)";"S*G*USAX--*****";"(null)";"(null)";"(null)"
"364";"AIR DEFENCE";"79";"AIRDEFENCE";"S*G*UCD---*****";"(null)";"(null)";"(null)"
"664";"AIR DEFENCE (AD) MISSILE LAUNC";"471";"AIRDEFENCE(AD)MISSILELAUNCH";"S*G*EWMA--*****";"(null)";"(null)";"(null)"
"379";"AIR DEFENCE COMPOSITE";"90";"COMPOSITE";"S*G*UCDC--*****";"(null)";"(null)";"(null)"
"367";"Air Defence COY";"79";"AIRDEFENCE";"S*G*UCD---*****";"(null)";"(null)";"(null)"
"683";"AIR DEFENCE GUN";"527";"AIRDEFENCEGUN";"S*G*EWA---*****";"(null)";"(null)";"(null)"
"686";"AIR DEFENCE GUN HEAVY";"530";"AIRDEFENCEGUNHEAVY";"S*G*EWAH--*****";"(null)";"(null)";"(null)"
"684";"AIR DEFENCE GUN LIGHT";"528";"AIRDEFENCEGUNLIGHT";"S*G*EWAL--*****";"(null)";"(null)";"(null)"
"685";"AIR DEFENCE GUN MEDIUM";"529";"AIRDEFENCEGUNMEDIUM";"S*G*EWAM--*****";"44";"Soldier Air Defence";"685"
"378";"AIR DEFENCE GUN UNIT";"89";"GUNUNIT";"S*G*UCDG--*****";"(null)";"(null)";"(null)"
"377";"AIR DEFENCE H/MAD";"88";"H/MAD";"S*G*UCDH--*****";"(null)";"(null)";"(null)"
"372";"AIR DEFENCE MISSILE";"83";"AIRDEFENCEMISSILE";"S*G*UCDM--*****";"(null)";"(null)";"(null)"
"376";"AIR DEFENCE MISSILE HEAVY";"87";"AIRDEFENCEMISSILEHEAVY";"S*G*UCDMH-*****";"(null)";"(null)";"(null)"
"373";"AIR DEFENCE MISSILE LIGHT";"84";"AIRDEFENCEMISSILELIGHT";"S*G*UCDML-*****";"229";"Legatus Wiesel";"373"
"375";"AIR DEFENCE MISSILE MEDIUM";"86";"AIRDEFENCEMISSILEMEDIUM";"S*G*UCDMM-*****";"(null)";"(null)";"(null)"
"374";"AIR DEFENCE MISSILE MOTORIZED";"85";"AIRDEFENCEMISSILEMOTORIZED";"S*G*UCDMLA*****";"(null)";"(null)";"(null)"
"369";"AIR DEFENCE SHORT RANGE";"80";"SHORTRANGE";"S*G*UCDS--*****";"(null)";"(null)";"(null)"
"371";"AIR DEFENCE SHORT RANGE GUN";"82";"GUN";"S*G*UCDS--*****";"(null)";"(null)";"(null)"
"370";"AIR DEFENCE SHORT RANGE MISSILE";"81";"MISSILE";"S*G*UCDS--*****";"(null)";"(null)";"(null)"
"380";"AIR DEFENCE TARGETING UNIT";"91";"TARGETINGUNIT";"S*G*UCDT--*****";"(null)";"(null)";"(null)"
"381";"AIR DEFENCE THEATRE MISSILE DEFENCE UNIT";"92";"THEATREMISSILEDEFENCEUNIT";"S*G*UCDO--*****";"(null)";"(null)";"(null)"
"781";"AIRPORT/AIRBASE";"605";"AIRPORT/AIRBASE";"S*G*IBA---H****";"(null)";"(null)";"(null)"
"342";"AIR TRACK";"6";"AIRTRACK";"S*A*------*****";"(null)";"(null)";"(null)"
"777";"AMMUNITION AND EXPLOSIVES PRODUCTION";"595";"AMMUNITIONANDEXPLOSIVESPRODUCTION";"S*G*IME---H****";"(null)";"(null)";"(null)"
"788";"AMPHIBIOUS WARFARE SHIP";"620";"AMPHIBIOUSWARFARESHIP";"S*S*CA----*****";"(null)";"(null)";"(null)"
"414";"ANTI ARMOUR";"110";"ANTIARMOUR";"S*G*UCAA--*****";"(null)";"(null)";"(null)"
"422";"ANTI ARMOUR AIR ASSAULT";"114";"ANTIARMOURAIRASSAULT";"S*G*UCAAS-*****";"(null)";"(null)";"(null)"
"421";"ANTI ARMOUR AIRBORNE";"113";"ANTIARMOURAIRBORNE";"S*G*UCAAM-*****";"(null)";"(null)";"(null)"
"424";"ANTI ARMOUR ARMOURED";"117";"ANTIARMOURARMOURED";"S*G*UCAAA-*****";"(null)";"(null)";"(null)"
"427";"ANTI ARMOUR ARMOURED AIR ASSAU";"120";"ANTIARMOURARMOUREDAIRASSAULT";"S*G*UCAAAS*****";"(null)";"(null)";"(null)"
"425";"ANTI ARMOUR ARMOURED TRACKED";"118";"ANTIARMOURARMOUREDTRACKED";"S*G*UCAAAT*****";"(null)";"(null)";"(null)"
"426";"ANTI ARMOUR ARMOURED WHEELED";"119";"ANTIARMOURARMOUREDWHEELED";"S*G*UCAAAW*****";"(null)";"(null)";"(null)"
"419";"ANTI ARMOUR DISMOUNTED";"111";"ANTIARMOURDISMOUNTED";"S*G*UCAAD-*****";"(null)";"(null)";"(null)"
"420";"ANTI ARMOUR LIGHT";"112";"ANTIARMOURLIGHT";"S*G*UCAAL-*****";"(null)";"(null)";"(null)"
"428";"ANTI ARMOUR MOTORIZED";"121";"ANTIARMOURMOTORIZED";"S*G*UCAAO-*****";"(null)";"(null)";"(null)"
"429";"ANTI ARMOUR MOTORIZED AIR ASSA";"122";"ANTIARMOURMOTORIZEDAIRASSAULT";"S*G*UCAAOS*****";"(null)";"(null)";"(null)"
"423";"ANTI ARMOUR MOUNTAIN";"115";"ANTIARMOURMOUNTAIN";"S*G*UCAAU-*****";"(null)";"(null)";"(null)"
"718";"ANTITANK GUN";"515";"ANTITANKGUN";"S*G*EWG---*****";"130";"Soldier AT (RPG7)";"718"
"722";"ANTITANK GUN HEAVY";"519";"ANTITANKGUNHEAVY";"S*G*EWGH--*****";"(null)";"(null)";"(null)"
"720";"ANTITANK GUN LIGHT";"517";"ANTITANKGUNLIGHT";"S*G*EWGL--*****";"(null)";"(null)";"(null)"
"721";"ANTITANK GUN MEDIUM";"518";"ANTITANKGUNMEDIUM";"S*G*EWGM--*****";"(null)";"(null)";"(null)"
"719";"ANTITANK GUN RECOILLESS";"516";"ANTITANKGUNRECOILLESS";"S*G*EWGR--*****";"(null)";"(null)";"(null)"
"695";"ANTITANK ROCKET LAUNCHER";"492";"ANTITANKROCKETLAUNCHER";"S*G*EWT---*****";"(null)";"(null)";"(null)"
"698";"ANTITANK ROCKET LAUNCHER HEAVY";"495";"ANTITANKROCKETLAUNCHERHEAVY";"S*G*EWTH--*****";"(null)";"(null)";"(null)"
"696";"ANTITANK ROCKET LAUNCHER LIGHT";"493";"ANTITANKROCKETLAUNCHERLIGHT";"S*G*EWTL--*****";"(null)";"(null)";"(null)"
"697";"ANTITANK ROCKET LAUNCHER MEDIU";"494";"ANTITANKROCKETLAUNCHERMEDIUM";"S*G*EWTM--*****";"(null)";"(null)";"(null)"
"392";"ARMOUR";"93";"ARMOUR";"S*G*UCA---*****";"(null)";"(null)";"(null)"
"752";"ARMOURED CARRIER WITH SCATTERA";"561";"ARMOUREDCARRIERWITHSCATTERABLEMINES";"S*G*EVEMSM*****";"(null)";"(null)";"(null)"
"734";"ARMOURED INFANTRY";"542";"ARMOUREDINFANTRY";"S*G*EVAI--*****";"(null)";"(null)";"(null)"
"732";"ARMOURED PERSONNEL CARRIER";"540";"ARMOUREDPERSONNELCARRIER";"S*G*EVAA--*****";"(null)";"(null)";"(null)"
"733";"ARMOURED PERSONNEL CARRIER REC";"541";"ARMOUREDPERSONNELCARRIERRECOVERY";"S*G*EVAAR-*****";"(null)";"(null)";"(null)"
"724";"ARMOURED VEHICLE";"532";"ARMOUREDVEHICLE";"S*G*EVA---*****";"231";"Marder";"724"
"750";"ARMOURED VEHICLE MOUNTED";"558";"ARMOUREDVEHICLEMOUNTED";"S*G*EVEMA-*****";"(null)";"(null)";"(null)"
"397";"ARMOUR TRACK";"94";"ARMOURTRACK";"S*G*UCAT--*****";"(null)";"(null)";"(null)"
"398";"ARMOUR TRACK AIRBORNE";"95";"ARMOURTRACKAIRBORNE";"S*G*UCATA-*****";"(null)";"(null)";"(null)"
"399";"ARMOUR TRACK AMPHIBIOUS";"96";"ARMOURTRACKAMPHIBIOUS";"S*G*UCATW-*****";"(null)";"(null)";"(null)"
"400";"ARMOUR TRACK AMPHIBIOUS RECOVE";"97";"ARMOURTRACKAMPHIBIOUSRECOVERY";"S*G*UCATWR*****";"(null)";"(null)";"(null)"
"403";"ARMOUR TRACK HEAVY";"100";"ARMOURTRACK,HEAVY";"S*G*UCATH-*****";"(null)";"(null)";"(null)"
"401";"ARMOUR TRACK LIGHT";"98";"ARMOURTRACK,LIGHT";"S*G*UCATL-*****";"(null)";"(null)";"(null)"
"402";"ARMOUR TRACK MEDIUM";"99";"ARMOURTRACK,MEDIUM";"S*G*UCATM-*****";"(null)";"(null)";"(null)"
"404";"ARMOUR TRACK RECOVERY";"101";"ARMOURTRACK,RECOVERY";"S*G*UCATR-*****";"(null)";"(null)";"(null)"
"405";"ARMOUR WHEELED";"102";"ARMOUR,WHEELED";"S*G*UCAW--*****";"(null)";"(null)";"(null)"
"406";"ARMOUR WHEELED AIR ASSAULT";"103";"ARMOUR,WHEELEDAIRASSAULT";"S*G*UCAWS-*****";"(null)";"(null)";"(null)"
"407";"ARMOUR WHEELED AIRBORNE";"104";"ARMOUR,WHEELEDAIRBORNE";"S*G*UCAWA-*****";"227";"BMP3 IFV";"407"
"408";"ARMOUR WHEELED AMPHIBIOUS";"105";"ARMOUR,WHEELEDAMPHIBIOUS";"S*G*UCAWA-*****";"(null)";"(null)";"(null)"
"409";"ARMOUR WHEELED AMPHIBIOUS RECO";"1325";"ARMOURWHEELEDAMPHIBIOUSRECOVERY";"S*G*UCAWWR*****";"(null)";"(null)";"(null)"
"412";"ARMOUR WHEELED HEAVY";"108";"ARMOUR,WHEELEDHEAVY";"S*G*UCAWH-*****";"(null)";"(null)";"(null)"
"410";"ARMOUR WHEELED LIGHT";"106";"ARMOUR,WHEELEDLIGHT";"S*G*UCAWL-*****";"(null)";"(null)";"(null)"
"411";"ARMOUR WHEELED MEDIUM";"107";"ARMOUR,WHEELEDMEDIUM";"S*G*UCAWM-*****";"(null)";"(null)";"(null)"
"413";"ARMOUR WHEELED RECOVERY";"109";"ARMOUR,WHEELEDRECOVERY";"S*G*UCAWR-*****";"10";"Armored Recovery Vehicle";"413"
"789";"ASSAULT VESSEL";"621";"ASSAULTVESSEL";"S*S*CALA--*****";"(null)";"(null)";"(null)"
"776";"ATOMIC ENERGY PRODUCTION";"593";"ATOMICENERGYPRODUCTION";"S*G*IMF---H****";"(null)";"(null)";"(null)"
"430";"AVIATION";"123";"AVIATION";"S*G*UCV---*****";"(null)";"(null)";"(null)"
"431";"AVIATION FIXED WING";"124";"FIXEDWING";"S*G*UCVF--*****";"(null)";"(null)";"(null)"
"433";"AVIATION FIXED WING ATTACK";"126";"ATTACKFIXEDWING";"S*G*UCVFA-*****";"(null)";"(null)";"(null)"
"436";"AVIATION FIXED WING RECON";"129";"RECONFIXEDWING";"S*G*UCVFR-*****";"(null)";"(null)";"(null)"
"432";"AVIATION FIXED WING UTILITY";"125";"UTILITYFIXEDWING";"S*G*UCVFU-*****";"(null)";"(null)";"(null)"
"435";"AVIATION FORWARD AIR CONTROLLER (FAC)";"128";"FORWARDAIRCONTROLLER(FAC)";"S*G*UCVUFC*****";"(null)";"(null)";"(null)"
"437";"AVIATION ROTARY WING";"130";"ROTARYWING";"S*G*UCVR--*****";"(null)";"(null)";"(null)"
"440";"AVIATION ROTARY WING C2";"138";"C2ROTARYWING";"S*G*UCVRUC*****";"(null)";"(null)";"(null)"
"441";"AVIATION ROTARY WING MEDEVAC";"139";"MEDEVACROTARYWING";"S*G*UCVRUE*****";"(null)";"(null)";"(null)"
"439";"AVIATION ROTARY WING UTILITY";"134";"UTILITYROTARYWING";"S*G*UCVRU-*****";"(null)";"(null)";"(null)"
"434";"AVIATION TACTICAL AIR CONTROL";"127";"TACTICALAIRCONTROLPARTY(TACP)";"S*G*UCVUTP*****";"(null)";"(null)";"(null)"
"445";"AVIATION UAV CONTROL";"1326";"AVIATIONUAVCONTROL";"S*G*UCVU--*****";"(null)";"(null)";"(null)"
"446";"AVIATION UAV LAUNCHER";"1327";"AVIATIONUAVLAUNCHER";"S*G*UCVU--*****";"(null)";"(null)";"(null)"
"447";"AVIATION UAV RECOVERY";"1337";"AVIATIONUAVRECOVERY";"S*G*UCVU--*****";"(null)";"(null)";"(null)"
"444";"AVIATION UNMANNED AERIAL VEHIC";"145";"UNMANNEDAERIALVEHICLEFIXEDWING";"S*G*UCVUF-*****";"(null)";"(null)";"(null)"
"448";"AVIATION UNMANNED AERIAL VEHICLE ROTARY WING";"146";"UNMANNEDAERIALVEHICLEROTARYWING";"S*G*UCVUR-*****";"(null)";"(null)";"(null)"
"443";"AVIATION UNMANNED AVIATION VEH";"144";"UNMANNEDAERIALVEHICLE";"S*G*UCVU--*****";"(null)";"(null)";"(null)"
"442";"AVIATION VERTICAL/SHORT TAKEOF";"143";"VERTICAL/SHORTTAKEOFFANDLANDING(V/STOL)";"S*G*UCVV--*****";"(null)";"(null)";"(null)"
"769";"BIOLOGICAL";"581";"BIOLOGICAL";"S*G*IRNB--H****";"(null)";"(null)";"(null)"
"745";"BRIDGE";"553";"BRIDGE";"S*G*EVEB--*****";"(null)";"(null)";"(null)"
"778";"BRIDGE";"599";"BRIDGE";"S*G*IMNB--H****";"(null)";"(null)";"(null)"
"739";"BUS";"547";"BUS";"S*G*EVUB--*****";"(null)";"(null)";"(null)"
"735";"C2V/ACV";"543";"C2V/ACV";"S*G*EVAC--*****";"(null)";"(null)";"(null)"
"548";"CBRN";"256";"COMBATSUPPORTCBRN";"S*G*UUA---*****";"(null)";"(null)";"(null)"
"768";"CBRN";"580";"CBRN";"S*G*IRN---H****";"(null)";"(null)";"(null)"
"560";"CBRN BIOLOGICAL";"268";"BIOLOGICAL";"S*G*UUAB--*****";"(null)";"(null)";"(null)"
"561";"CBRN BIOLOGICAL RECON EQUIPPED";"269";"RECONEQUIPPED";"S*G*UUABR-*****";"(null)";"(null)";"(null)"
"549";"CBRN CHEMICAL";"257";"CHEMICAL";"S*G*UUAC--*****";"(null)";"(null)";"(null)"
"556";"CBRN CHEMICAL RECON";"264";"CHEMICALRECON";"S*G*UUACR-*****";"(null)";"(null)";"(null)"
"553";"CBRN CHEMICAL SMOKE";"261";"SMOKE";"S*G*UUACS-*****";"(null)";"(null)";"(null)"
"555";"CBRN CHEMICAL SMOKE ARMOUR";"263";"ARMOURSMOKE";"S*G*UUACSA*****";"(null)";"(null)";"(null)"
"550";"CBRN CHEMICAL SMOKE/DECON";"258";"SMOKE/DECON";"S*G*UUACC-*****";"(null)";"(null)";"(null)"
"551";"CBRN CHEMICAL SMOKE/DECON MECHANIZED";"259";"MECHANIZEDSMOKE/DECON";"S*G*UUACCK*****";"(null)";"(null)";"(null)"
"552";"CBRN CHEMICAL SMOKE/DECON MOTO";"260";"MOTORIZEDSMOKE/DECON";"S*G*UUACCM*****";"(null)";"(null)";"(null)"
"554";"CBRN CHEMICAL SMOKE MOTORIZED";"262";"MOTORIZEDSMOKE";"S*G*UUACSM*****";"(null)";"(null)";"(null)"
"557";"CBRN CHEMICAL WHEELED ARMOURED VEHICLE";"265";"CHEMICALWHEELEDARMOUREDVEHICLE";"S*G*UUACRW*****";"(null)";"(null)";"(null)"
"558";"CBRN CHEMICAL WHEELED ARMOURED VEHICLE RECONNAISAN";"266";"CHEMICALWHEELEDARMOUREDVEHICLERECONNAISSANCESURVEI";"S*G*UUACRS*****";"(null)";"(null)";"(null)"
"562";"CBRN DECON";"270";"DECONTAMINATION";"S*G*UUAD--*****";"(null)";"(null)";"(null)"
"564";"CBRN DECON EQUIPMENT";"1330";"CBRNDECONEQUIPMENT";"S*G*UUADE-*****";"(null)";"(null)";"(null)"
"565";"CBRN DECON EQUIPMENT/TROOP";"1331";"CBRNDECONEQUIPMENT/TROOP";"S*G*UUADET*****";"(null)";"(null)";"(null)"
"563";"CBRN DECON TROOP";"1329";"CBRNDECONTROOP";"S*G*UUADT-*****";"(null)";"(null)";"(null)"
"760";"CBRN EQUIPMENT";"571";"CBRNEQUIPMENT";"S*G*EXN---*****";"(null)";"(null)";"(null)"
"566";"CBRN LABORATORY";"1332";"CBRNLABORATORY";"S*G*UUAL--*****";"(null)";"(null)";"(null)"
"559";"CBRN NUCLEAR";"267";"NUCLEAR";"S*G*UUAN--*****";"(null)";"(null)";"(null)"
"770";"CHEMICAL";"582";"CHEMICAL";"S*G*IRNC--H****";"(null)";"(null)";"(null)"
"356";"CIVIL FIXED WING";"73";"FIXEDWING";"S*A*CF----*****";"(null)";"(null)";"(null)"
"755";"CIVILIAN VEHICLE";"565";"CIVILIANVEHICLE";"S*G*EVC---*****";"(null)";"(null)";"(null)"
"357";"CIVIL ROTARY WING";"74";"ROTARYWING";"S*A*CH----*****";"(null)";"(null)";"(null)"
"604";"COMBAT SERVICE SUPPORT";"321";"COMBATSERVICESUPPORT";"S*G*US----*****";"(null)";"(null)";"(null)"
"736";"COMBAT SERVICE SUPPORT VEHICLE";"544";"COMBATSERVICESUPPORTVEHICLE";"S*G*EVAS--*****";"(null)";"(null)";"(null)"
"547";"COMBAT SUPPORT";"255";"COMBATSUPPORT";"S*G*UU----*****";"(null)";"(null)";"(null)"
"748";"CONSTRUCTION VEHICLE";"556";"CONSTRUCTIONVEHICLE";"S*G*EVEC--*****";"(null)";"(null)";"(null)"
"742";"CROSS-COUNTRY TRUCK";"550";"CROSS-COUNTRYTRUCK";"S*G*EVUX--*****";"212";"TATRA Tanker Fuel";"742"
"742";"CROSS-COUNTRY TRUCK";"550";"CROSS-COUNTRYTRUCK";"S*G*EVUX--*****";"50";"Truck 2t off-road";"742"
"772";"DECON";"585";"DECON";"S*G*IPD---H****";"(null)";"(null)";"(null)"
"676";"DIRECT FIRE GUN";"520";"DIRECTFIREGUN";"S*G*EWD---*****";"(null)";"(null)";"(null)"
"681";"DIRECT FIRE GUN HEAVY";"525";"DIRECTFIREGUNHEAVY";"S*G*EWDH--*****";"(null)";"(null)";"(null)"
"682";"DIRECT FIRE GUN HEAVY SELF-PRO";"526";"DIRECTFIREGUNHEAVYSELF-PROPELLED";"S*G*EWDHS-*****";"(null)";"(null)";"(null)"
"677";"DIRECT FIRE GUN LIGHT";"521";"DIRECTFIREGUNLIGHT";"S*G*EWDL--*****";"(null)";"(null)";"(null)"
"678";"DIRECT FIRE GUN LIGHT SELF PRO";"522";"DIRECTFIREGUNLIGHTSELF-PROPELLED";"S*G*EWDLS-*****";"(null)";"(null)";"(null)"
"679";"DIRECT FIRE GUN MEDIUM";"523";"DIRECTFIREGUNMEDIUM";"S*G*EWDM--*****";"(null)";"(null)";"(null)"
"680";"DIRECT FIRE GUN MEDIUM SELF-PR";"524";"DIRECTFIREGUNMEDIUMSELF-PROPELLED";"S*G*EWDMS-*****";"(null)";"(null)";"(null)"
"753";"DOZER";"562";"DOZER";"S*G*EVED--*****";"215";"CAT Excavator";"753"
"746";"EARTHMOVER";"554";"EARTHMOVER";"S*G*EVEE--*****";"(null)";"(null)";"(null)"
"774";"ELCECTRIC POWER FACILITY";"590";"ELECTRICPOWERFACILITY";"S*G*IUE---H****";"(null)";"(null)";"(null)"
"758";"EMPLACED SENSOR";"568";"EMPLACEDSENSOR";"S*G*ESE---*****";"(null)";"(null)";"(null)"
"467";"ENGINEER";"159";"ENGINEER";"S*G*UCE---*****";"(null)";"(null)";"(null)"
"472";"ENGINEER COMBAT";"160";"ENGINEERCOMBAT";"S*G*UCEC--*****";"(null)";"(null)";"(null)"
"473";"ENGINEER COMBAT AIR ASSAULT";"161";"ENGINEERCOMBATAIRASSAULT";"S*G*UCECS-*****";"(null)";"(null)";"(null)"
"478";"ENGINEER COMBAT AIRBORNE";"162";"ENGINEERCOMBATAIRBORNE";"S*G*UCECA-*****";"(null)";"(null)";"(null)"
"475";"ENGINEER COMBAT BRIDGING";"171";"ENGINEERCOMBATBRIDGING";"S*G*UCEC--*****";"(null)";"(null)";"(null)"
"481";"ENGINEER COMBAT HEAVY";"166";"ENGINEERCOMBATHEAVY";"S*G*UCECH-*****";"(null)";"(null)";"(null)"
"479";"ENGINEER COMBAT LIGHT";"164";"ENGINEERCOMBATLIGHT";"S*G*UCECL-*****";"(null)";"(null)";"(null)"
"482";"ENGINEER COMBAT MECH (TRACK)";"167";"ENGINEERCOMBATMECHANIZED(TRACK)";"S*G*UCECT-*****";"(null)";"(null)";"(null)"
"480";"ENGINEER COMBAT MEDIUM";"165";"ENGINEERCOMBATMEDIUM";"S*G*UCECM-*****";"(null)";"(null)";"(null)"
"476";"ENGINEER COMBAT MINE CLEARING";"172";"ENGINEERCOMBATMINECLEARING";"S*G*UCEC--*****";"(null)";"(null)";"(null)"
"477";"ENGINEER COMBAT MINE LAYING";"173";"ENGINEERCOMBATMINELAYING";"S*G*UCEC--*****";"(null)";"(null)";"(null)"
"483";"ENGINEER COMBAT MOTORIZED";"168";"ENGINEERCOMBATMOTORIZED";"S*G*UCECW-*****";"(null)";"(null)";"(null)"
"484";"ENGINEER COMBAT MOUNTAIN";"169";"ENGINEERCOMBATMOUNTAIN";"S*G*UCECO-*****";"(null)";"(null)";"(null)"
"474";"ENGINEER COMBAT RECON";"170";"ENGINEERCOMBATRECON";"S*G*UCECR-*****";"(null)";"(null)";"(null)"
"485";"ENGINEER CONSTRUCTION";"175";"ENGINEERCONSTRUCTION";"S*G*UCEN--*****";"(null)";"(null)";"(null)"
"744";"ENGINEER VEHICLE";"552";"ENGINEERVEHICLE";"S*G*EVE---*****";"(null)";"(null)";"(null)"
"657";"ENVIRONMENTAL PROTECTION";"463";"ENVIRONMENTALPROTECTION";"S*G*USXEP-*****";"(null)";"(null)";"(null)"
"576";"EW CORPS";"280";"CORPS";"S*G*UUMSEC*****";"(null)";"(null)";"(null)"
"575";"EW THEATRE";"279";"THEATRE";"S*G*UUMSET*****";"(null)";"(null)";"(null)"
"602";"EXPLOSIVE ORDNANCE DISPOSAL";"317";"EXPLOSIVEORDINANCEDISPOSAL";"S*G*UUE---*****";"(null)";"(null)";"(null)"
"490";"FIELD ARTELLERY HOWITZER/GUN AIRBORNE";"181";"AIRBORNE";"S*G*UCFHA-*****";"(null)";"(null)";"(null)"
"495";"FIELD ARTELLERY HOWITZER/GUN AMPHIBIOUS";"187";"AMPHIBIOUS";"S*G*UCFHX-*****";"(null)";"(null)";"(null)"
"494";"FIELD ARTELLERY HOWITZER/GUN HEAVY";"186";"HEAVY";"S*G*UCFHH-*****";"(null)";"(null)";"(null)"
"492";"FIELD ARTELLERY HOWITZER/GUN LIGHT";"184";"LIGHT";"S*G*UCFHL-*****";"(null)";"(null)";"(null)"
"493";"FIELD ARTELLERY HOWITZER/GUN MEDIUM";"185";"MEDIUM";"S*G*UCFHM-*****";"(null)";"(null)";"(null)"
"491";"FIELD ARTELLERY HOWITZER/GUN MOUNTAIN";"183";"MOUNTAIN";"S*G*UCFHO-*****";"(null)";"(null)";"(null)"
"496";"FIELD ARTELLERY ROCKET";"188";"ROCKET";"S*G*UCFR--*****";"(null)";"(null)";"(null)"
"498";"FIELD ARTILEERY ROCKET SINGLE";"190";"SINGLEROCKETSELFPROPELLED";"S*G*UCFRSS*****";"(null)";"(null)";"(null)"
"486";"FIELD ARTILLERY";"177";"FIELDARTILLERY";"S*G*UCF---*****";"(null)";"(null)";"(null)"
"521";"FIELD ARTILLERY ARTILLERY SURV";"214";"ARTILLERYSURVEY";"S*G*UCFS--*****";"(null)";"(null)";"(null)"
"523";"FIELD ARTILLERY FIRE DIRECTION CENTRE";"224";"FIREDIRECTIONCENTRE";"S*G*UCF---*****";"(null)";"(null)";"(null)"
"487";"FIELD ARTILLERY HOWITZER/GUN";"178";"HOWITZER/GUN";"S*G*UCFH--*****";"(null)";"(null)";"(null)"
"489";"FIELD ARTILLERY HOWITZER/GUN AIR ASSAULT";"180";"AIRASSAULT";"S*G*UCFHS-*****";"(null)";"(null)";"(null)"
"488";"FIELD ARTILLERY HOWITZER/GUN SELF-PROPELLED";"179";"SELFPROPELLED";"S*G*UCFHE-*****";"(null)";"(null)";"(null)"
"522";"FIELD ARTILLERY METEOROLOGICAL";"219";"METEOROLOGICAL";"S*G*UCFO--*****";"(null)";"(null)";"(null)"
"513";"FIELD ARTILLERY MORTAR";"205";"MORTAR";"S*G*UCFM--*****";"(null)";"(null)";"(null)"
"514";"FIELD ARTILLERY MORTAR SP TRAC";"206";"SELFPROPELLED(SP)TRACKEDMORTAR";"S*G*UCFMS-*****";"(null)";"(null)";"(null)"
"515";"FIELD ARTILLERY MORTAR SP WHEE";"207";"SPWHEELEDMORTAR";"S*G*UCFMSW*****";"(null)";"(null)";"(null)"
"516";"FIELD ARTILLERY MORTAR TOWED";"208";"TOWEDMORTAR";"S*G*UCFMT-*****";"(null)";"(null)";"(null)"
"518";"FIELD ARTILLERY MORTAR TOWED A";"210";"TOWEDAIRASSAULTMORTAR";"S*G*UCFMTS*****";"(null)";"(null)";"(null)"
"517";"FIELD ARTILLERY MORTAR TOWED A";"209";"TOWEDAIRBORNEMORTAR";"S*G*UCFMTA*****";"(null)";"(null)";"(null)"
"519";"FIELD ARTILLERY MORTAR TOWED M";"212";"TOWEDMOUNTAINMORTAR";"S*G*UCFMTO*****";"(null)";"(null)";"(null)"
"524";"FIELD ARTILLERY OBERSERVER";"225";"OBERSERVER";"S*G*UCF---*****";"(null)";"(null)";"(null)"
"502";"FIELD ARTILLERY ROCKET MULTI R";"194";"MULTIROCKETSELFPROPELLED";"S*G*UCFRMS*****";"(null)";"(null)";"(null)"
"504";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI";"196";"MULTIROCKETTOWED";"S*G*UCFRMT*****";"(null)";"(null)";"(null)"
"503";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI";"195";"MULTIROCKETTRUCK";"S*G*UCFRMR*****";"(null)";"(null)";"(null)"
"501";"FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCHER";"193";"MULTIROCKETLAUNCHER";"S*G*UCFRM-*****";"(null)";"(null)";"(null)"
"497";"FIELD ARTILLERY ROCKET SINGLE";"189";"SINGLEROCKETLAUNCHER";"S*G*UCFRS-*****";"(null)";"(null)";"(null)"
"499";"FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING";"191";"SINGLEROCKETTRUCK";"S*G*UCFRSR*****";"(null)";"(null)";"(null)"
"500";"FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING";"192";"SINGLEROCKETTOWED";"S*G*UCFRST*****";"(null)";"(null)";"(null)"
"506";"FIELD ARTILLERY TARGET ACQUISI";"198";"RADAR";"S*G*UCFTR-*****";"(null)";"(null)";"(null)"
"505";"FIELD ARTILLERY TARGET ACQUISITION";"197";"TARGETACQUISITION";"S*G*UCFT--*****";"(null)";"(null)";"(null)"
"510";"FIELD ARTILLERY TARGET ACQUISITION DISMOUNTED";"202";"TARGETACQUISITIONDISMOUNTED";"S*G*UCFTCD*****";"(null)";"(null)";"(null)"
"508";"FIELD ARTILLERY TARGET ACQUISITION FLASH (OPTICAL)";"200";"FLASH(OPTICAL)";"S*G*UCFTF-*****";"(null)";"(null)";"(null)"
"509";"FIELD ARTILLERY TARGET ACQUISITION MOUNTED";"201";"TARGETACQUISITIONMOUNTED";"S*G*UCFTC-*****";"(null)";"(null)";"(null)"
"512";"FIELD ARTILLERY TARGET ACQUISITION NAVAL GUNFIRE";"204";"TARGETACQUISITIONNAVALGUNFIRE";"S*G*UCFTA-*****";"(null)";"(null)";"(null)"
"507";"FIELD ARTILLERY TARGET ACQUISITION SOUND";"199";"SOUND";"S*G*UCFTS-*****";"(null)";"(null)";"(null)"
"511";"FIELD ARTILLERY TARGET ACQUISITION TRACKED";"203";"TARGETACQUISITIONTRACKED";"S*G*UCFTCM*****";"(null)";"(null)";"(null)"
"345";"FIXED WING DRONE (RPV/UAE)";"36";"DRONE(RPV/UAV)";"S*A*MFQ---*****";"(null)";"(null)";"(null)"
"761";"FLAME THROWER";"572";"FLAMETHROWER";"S*G*EXF---*****";"(null)";"(null)";"(null)"
"660";"GENERAL HEADQUATERS AND SERVICE";"466";"GENERALHEADQUARTERSANDSERVICE";"S*G*UH----*****";"(null)";"(null)";"(null)"
"779";"GOVERNMENT LEADERSHIP";"603";"GOVERNMENTLEADERSHIP";"S*G*IG----H****";"(null)";"(null)";"(null)"
"703";"GRENADE LAUNCHER";"500";"GRENADELAUNCHER";"S*G*EWZ---*****";"(null)";"(null)";"(null)"
"706";"GRENADE LAUNCHER HEAVY";"503";"GRENADELAUNCHERHEAVY";"S*G*EWZH--*****";"(null)";"(null)";"(null)"
"704";"GRENADE LAUNCHER LIGHT";"501";"GRENADELAUNCHERLIGHT";"S*G*EWZL--*****";"(null)";"(null)";"(null)"
"705";"GRENADE LAUNCHER MEDIUM";"502";"GRENADELAUNCHERMEDIUM";"S*G*EWZM--*****";"(null)";"(null)";"(null)"
"662";"GROUND TRACK EQUIPMENT";"468";"GROUNDTRACKEQUIPMENT";"S*G*E-----*****";"(null)";"(null)";"(null)"
"358";"GROUND TRACK UNIT";"77";"UNIT";"S*G*U-----*****";"217";"Soldier (Role Player)";"358"
"358";"GROUND TRACK UNIT";"77";"UNIT";"S*G*U-----*****";"106";"Soldier";"358"
"363";"GROUND TRACK UNIT COMBAT";"78";"COMBAT";"S*G*UC----*****";"(null)";"(null)";"(null)"
"520";"GROUND TRACK UNIT COMBAT FIELD ARTILLERY MORTAR AM";"213";"AMPHIBIOUSMORTAR";"S*G*UCFML-*****";"(null)";"(null)";"(null)"
"659";"GROUND TRACK UNIT GENERAL HEADQUATERS";"465";"UNITGENERALHEADQUARTERS";"S*G*UH----*****";"(null)";"(null)";"(null)"
"723";"GROUND VEHICLE";"531";"GROUNDVEHICLE";"S*G*EV----*****";"(null)";"(null)";"(null)"
"702";"HEAVY MACHINE GUN";"499";"HEAVYMACHINEGUN";"S*G*EWRH--*****";"129";"Soldier HMG";"702"
"785";"HOSPITAL";"609";"HOSPITAL";"S*G*IXH---H****";"(null)";"(null)";"(null)"
"711";"HOWITZER";"508";"HOWITZER";"S*G*EWH---*****";"(null)";"(null)";"(null)"
"716";"HOWITZER HEAVY";"513";"HOWITZERHEAVY";"S*G*EWHH--*****";"(null)";"(null)";"(null)"
"717";"HOWITZER HEAVY SELF-PROPELLED";"514";"HOWITZERHEAVYSELF-PROPELLED";"S*G*EWHHS-*****";"(null)";"(null)";"(null)"
"712";"HOWITZER LIGHT";"509";"HOWITZERLIGHT";"S*G*EWHL--*****";"(null)";"(null)";"(null)"
"713";"HOWITZER LIGHT SELF-PROPELLED";"510";"HOWITZERLIGHTSELF-PROPELLED";"S*G*EWHLS-*****";"(null)";"(null)";"(null)"
"714";"HOWITZER MEDIUM";"511";"HOWITZERMEDIUM";"S*G*EWHM--*****";"(null)";"(null)";"(null)"
"715";"HOWITZER MEDIUM SELF-PROPELLED";"512";"HOWITZERMEDIUMSELF-PROPELLED";"S*G*EWHMS-*****";"(null)";"(null)";"(null)"
"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"161";"TM";"449"
"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"226";"TEST PLAYER";"449"
"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"232";"0Soldier";"449"
"449";"INFANTRY";"149";"INFANTRY";"S*G*UCI---*****";"128";"TM Leader";"449"
"463";"INFANTRY AIR ASSAULT";"154";"INFANTRYAIRASSAULT";"S*G*UCIS--*****";"(null)";"(null)";"(null)"
"462";"INFANTRY AIRBORNE";"153";"INFANTRYAIRBORNE";"S*G*UCIA--*****";"(null)";"(null)";"(null)"
"466";"INFANTRY FIGHTING VEHICLE";"157";"INFANTRYFIGHTINGVEHICLE";"S*G*UCII--*****";"(null)";"(null)";"(null)"
"454";"INFANTRY LIGHT";"150";"INFANTRYLIGHT";"S*G*UCIL--*****";"(null)";"(null)";"(null)"
"464";"INFANTRY MECHANIZED";"155";"INFANTRYMECHANIZED";"S*G*UCIZ--*****";"(null)";"(null)";"(null)"
"456";"INFANTRY MOTORIZED";"151";"INFANTRYMOTORIZED";"S*G*UCIM--*****";"(null)";"(null)";"(null)"
"461";"INFANTRY MOUNTAIN";"152";"INFANTRYMOUNTAIN";"S*G*UCIO--*****";"(null)";"(null)";"(null)"
"465";"INFANTRY NAVAL";"156";"INFANTRYNAVAL";"S*G*UCIN--*****";"(null)";"(null)";"(null)"
"600";"INFORMATION OPERATIONS";"315";"INFORMATIONWARFAREUNIT";"S*G*UUI---*****";"(null)";"(null)";"(null)"
"765";"INSTALLATION";"576";"INSTALLATION";"S*G*I-----H****";"225";"MEU";"765"
"765";"INSTALLATION";"576";"INSTALLATION";"S*G*I-----H****";"224";"Building";"765"
"666";"INTERMEDIATE RANGE AD MISSILE";"473";"INTERMEDIATERANGEADMISSILELAUNCH";"S*G*EWMAI-*****";"(null)";"(null)";"(null)"
"670";"INTERMEDIATE RANGE SS MISSILE";"478";"INTERMEDIATERANGESSMISSILELAUNCH";"S*G*EWMSI-*****";"(null)";"(null)";"(null)"
"382";"INTERNAL SECURITY FORCES";"246";"INTERNALSECURITYFORCES";"S*G*UCS---*****";"(null)";"(null)";"(null)"
"390";"INTERNAL SECURITY FORCES AVIAT";"254";"AVIATION";"S*G*UCSA--*****";"(null)";"(null)";"(null)"
"387";"INTERNAL SECURITY FORCES GROUN";"251";"MECHANIZEDGROUND";"S*G*UCSGA-*****";"(null)";"(null)";"(null)"
"384";"INTERNAL SECURITY FORCES GROUND";"248";"GROUND";"S*G*UCSG--*****";"(null)";"(null)";"(null)"
"385";"INTERNAL SECURITY FORCES GROUND DISMOUNTED";"249";"DISMOUNTEDGROUND";"S*G*UCSGD-*****";"(null)";"(null)";"(null)"
"386";"INTERNAL SECURITY FORCES GROUND MOTORIZED";"250";"MOTORIZEDGROUND";"S*G*UCSGM-*****";"(null)";"(null)";"(null)"
"389";"INTERNAL SECURITY FORCES RAILROAD";"253";"RAILROAD";"S*G*UCSR--*****";"(null)";"(null)";"(null)"
"383";"INTERNAL SECURITY FORCES RIVERINE";"247";"RIVERINE";"S*G*UCSW--*****";"(null)";"(null)";"(null)"
"388";"INTERNAL SECURITY FORCES WHEEL";"252";"WHEELEDMECHANIZED";"S*G*UCSM--*****";"(null)";"(null)";"(null)"
"791";"LANDING CRAFT";"623";"LANDINGCRAFT";"S*S*CALC--*****";"(null)";"(null)";"(null)"
"790";"LANDING SHIP";"622";"LANDINGSHIP";"S*S*CALS--*****";"(null)";"(null)";"(null)"
"601";"LANDING SUPPORT";"316";"LANDINGSUPPORT";"S*G*UUX---*****";"(null)";"(null)";"(null)"
"762";"LAND MINES";"573";"LANDMINES";"S*G*EXM---*****";"(null)";"(null)";"(null)"
"763";"LAND MINES, LETHAL";"574";"LANDMINES,LETHAL";"S*G*EXMC--*****";"(null)";"(null)";"(null)"
"759";"LASER";"570";"LASER";"S*G*EXL---*****";"(null)";"(null)";"(null)"
"584";"LAW ENFORCEMENT UNIT";"292";"LAWENFORCEMENTUNIT";"S*G*UUL---*****";"(null)";"(null)";"(null)"
"587";"LAW ENFORCEMENT UNIT CIVILIAN LAW ENFORCEMENT";"295";"CIVILIANLAWENFORCEMENT";"S*G*UULC--*****";"(null)";"(null)";"(null)"
"588";"LAW ENFORCEMENT UNIT CRIMINAL INVESTIGATION DIVISI";"297";"CENTRALINTELLIGENCEDIVISION(CID)";"S*G*UULD--*****";"(null)";"(null)";"(null)"
"586";"LAW ENFORCEMENT UNIT MILITARY POLICE";"294";"MILITARYPOLICE";"S*G*UULM--*****";"(null)";"(null)";"(null)"
"585";"LAW ENFORCEMENT UNIT SHORE PATROL";"293";"SHOREPATROL";"S*G*UULS--*****";"(null)";"(null)";"(null)"
"794";"LEISURE CRAFT";"665";"LEISURECRAFT";"S*S*XR----*****";"(null)";"(null)";"(null)"
"764";"LESS THAN LETHAL";"575";"LESSTHANLETHAL";"S*G*EXML--*****";"(null)";"(null)";"(null)"
"737";"LIGHT ARMOURED VEHICLE";"545";"LIGHTARMOUREDVEHICLE";"S*G*EVAL--*****";"(null)";"(null)";"(null)"
"701";"LIGHT MACHINE GUN";"498";"LIGHTMACHINEGUN";"S*G*EWRL--*****";"131";"Soldier LMG";"701"
"741";"LIMITED CROSS-COUNTRY TRUCK";"549";"LIMITEDCROSS-COUNTRYTRUCK";"S*G*EVUL--*****";"(null)";"(null)";"(null)"
"667";"LONG RANGE AD MISSILE LAUNCH";"474";"LONGRANGEADMISSILELAUNCH";"S*G*EWMAL-*****";"(null)";"(null)";"(null)"
"671";"LONG RANGE SS MISSILE LAUNCH";"479";"LONGRANGESSMISSILELAUNCH";"S*G*EWMSL-*****";"(null)";"(null)";"(null)"
"648";"MAINTENANCE";"442";"MAINTENANCE";"S*G*USX---*****";"(null)";"(null)";"(null)"
"654";"MAINTENANCE BATTLE DAMAGE REPA";"460";"BATTLEDAMAGEREPAIR";"S*G*USXBDR*****";"(null)";"(null)";"(null)"
"653";"MAINTENANCE ELECTRO-OPTICAL";"457";"ELECTRO-OPTICAL";"S*G*USXE--*****";"(null)";"(null)";"(null)"
"649";"MAINTENANCE HEAVY";"445";"MAINTENANCEHEAVY";"S*G*USXH--*****";"(null)";"(null)";"(null)"
"651";"MAINTENANCE ORDINANCE";"451";"ORDINANCE";"S*G*USXO--*****";"(null)";"(null)";"(null)"
"652";"MAINTENANCE ORDINANCE MISSILE";"454";"ORDINANCEMISSILE";"S*G*USXOM-*****";"(null)";"(null)";"(null)"
"655";"MAINTENANCE PREVENTIVE MAINTEN";"461";"PREVENTIVEMAINTANANCE";"S*G*USXPM-*****";"(null)";"(null)";"(null)"
"650";"MAINTENANCE RECOVERY";"448";"MAINTENANCERECOVERY";"S*G*USXR--*****";"(null)";"(null)";"(null)"
"623";"MEDICAL DENTAL";"373";"MEDICALDENTAL";"S*G*USMD--*****";"(null)";"(null)";"(null)"
"784";"MEDICAL FACILITY";"608";"MEDICALFACILITY";"S*G*IX----H****";"(null)";"(null)";"(null)"
"624";"MEDICAL PSYCHOLOGICAL";"376";"MEDICALPSYCHOLOGICAL";"S*G*USMP--*****";"(null)";"(null)";"(null)"
"621";"MEDICAL TREATMENT FACILITY";"367";"MEDICALTREATMENTFACILITY";"S*G*USMM--*****";"(null)";"(null)";"(null)"
"622";"MEDICAL VETERINARY";"370";"MEDICALVETERINARY";"S*G*USMV--*****";"(null)";"(null)";"(null)"
"343";"MILITARY";"7";"MILITARY";"S*A*M-----*****";"(null)";"(null)";"(null)"
"780";"MILITARY BASE/FACILITY";"604";"MILITARYBASE/FACILITY";"S*G*IB----H****";"(null)";"(null)";"(null)"
"344";"MILITARY FIXED WING";"8";"FIXEDWING";"S*A*MF----*****";"(null)";"(null)";"(null)"
"567";"MILITARY INTELLIGENCE";"271";"MILITARYINTELLIGENCE";"S*G*UUM---*****";"(null)";"(null)";"(null)"
"568";"MILITARY INTELLIGENCE AERIAL EXPLOITATION";"272";"AERIALEXPLOITATION";"S*G*UUMA--*****";"(null)";"(null)";"(null)"
"577";"MILITARY INTELLIGENCE COUNTER INTELLIGENCE";"281";"COUNTERINTELLIGENCE";"S*G*UUMC--*****";"(null)";"(null)";"(null)"
"573";"MILITARY INTELLIGENCE ELECTRON";"277";"INTERCEPT";"S*G*UUMSEI*****";"(null)";"(null)";"(null)"
"572";"MILITARY INTELLIGENCE ELECTRON";"276";"DIRECTIONFINDING";"S*G*UUMSED*****";"(null)";"(null)";"(null)"
"571";"MILITARY INTELLIGENCE ELECTRON";"275";"ARMOUREDWHEELEDVEHICLE";"S*G*UUMSEA*****";"(null)";"(null)";"(null)"
"570";"MILITARY INTELLIGENCE ELECTRONIC WARFARE";"274";"ELECTRONICWARFARE";"S*G*UUMSE-*****";"(null)";"(null)";"(null)"
"574";"MILITARY INTELLIGENCE ELECTRONIC WARFARE JAMMING";"278";"JAMMING";"S*G*UUMSEJ*****";"(null)";"(null)";"(null)"
"582";"MILITARY INTELLIGENCE INTERROGATION";"290";"INTERROGATION";"S*G*UUMQ--*****";"(null)";"(null)";"(null)"
"583";"MILITARY INTELLIGENCE JOINT INTELLIGENCE CENTRE";"291";"JOINTINTELLIGENCECENTRE";"S*G*UUMJ--*****";"(null)";"(null)";"(null)"
"581";"MILITARY INTELLIGENCE OPERATIONS";"288";"OPERATIONS";"S*G*UUMO--*****";"(null)";"(null)";"(null)"
"569";"MILITARY INTELLIGENCE SIGNALS";"273";"SIGNALINTELLIGENCE(SIGINT)";"S*G*UUMS--*****";"(null)";"(null)";"(null)"
"579";"MILITARY INTELLIGENCE SURVEILL";"283";"GROUNDSURVEILLANCERADAR";"S*G*UUMRG-*****";"(null)";"(null)";"(null)"
"578";"MILITARY INTELLIGENCE SURVEILLANCE";"282";"SURVEILLANCE";"S*G*UUMR--*****";"(null)";"(null)";"(null)"
"580";"MILITARY INTELLIGENCE SURVEILLANCE SENSOR";"284";"SENSOR";"S*G*UUMRS-*****";"(null)";"(null)";"(null)"
"346";"MILITARY ROTARY WING";"39";"ROTARYWING";"S*A*MH----*****";"195";"Apache";"346"
"766";"MINE";"578";"MINE";"S*G*IRM---H****";"(null)";"(null)";"(null)"
"749";"MINE LAYING VEHICLE";"557";"MINELAYINGVEHICLE";"S*G*EVEM--*****";"4";"Trailer, Minelaying";"749"
"675";"MISSILE LAUNCHER AT HEAVY";"483";"MISSILELAUNCHERATHEAVY";"S*G*EWMTH-*****";"(null)";"(null)";"(null)"
"674";"MISSILE LAUNCHER AT MEDIUM";"482";"MISSILELAUNCHERATMEDIUM";"S*G*EWMTM-*****";"(null)";"(null)";"(null)"
"663";"MISSILE LAUNCHERS";"470";"MISSILELAUNCHERS";"S*G*EWM---*****";"(null)";"(null)";"(null)"
"672";"MISSILE LAUNCHERS ANTI TANK (A";"480";"MISSILELAUNCHERSANTITANK(AT)";"S*G*EWMT--*****";"(null)";"(null)";"(null)"
"673";"MISSILE LAUNCHERS AT LIGHT";"481";"MISSILELAUNCHERSATLIGHT";"S*G*EWMTL-*****";"(null)";"(null)";"(null)"
"544";"MISSILE (SURF-SURF)";"243";"MISSILE(SURFACE-SURFACE)";"S*G*UCM---*****";"(null)";"(null)";"(null)"
"546";"MISSILE (SURF-SURF) STRATEGIC";"245";"MISSILE(SURFACE-SURFACE)STRATEGIC";"S*G*UCMS--*****";"(null)";"(null)";"(null)"
"545";"MISSILE (SURF-SURF) TACTICAL";"244";"MISSILE(SURFACE-SURFACE)TACTICAL";"S*G*UCMT--*****";"(null)";"(null)";"(null)"
"707";"MORTAR";"504";"MORTAR";"S*G*EWO---*****";"209";"Soldier Mortar";"707"
"710";"MORTAR HEAVY";"507";"MORTARHEAVY";"S*G*EWOH--*****";"(null)";"(null)";"(null)"
"708";"MORTAR LIGHT";"505";"MORTARLIGHT";"S*G*EWOL--*****";"(null)";"(null)";"(null)"
"709";"MORTAR MEDIUM";"506";"MORTARMEDIUM";"S*G*EWOM--*****";"(null)";"(null)";"(null)"
"747";"MULTIFUNCTIONAL EARTHMOVER/DIG";"555";"MULTIFUNCTIONALEARTHMOVER/DIGGER";"S*G*EVEE--*****";"(null)";"(null)";"(null)"
"691";"MULTIPLE ROCKET LAUNCHER";"488";"MULTIPLEROCKETLAUNCHER";"S*G*EWX---*****";"(null)";"(null)";"(null)"
"694";"MULTIPLE ROCKET LAUNCHER HEAVY";"491";"MULTIPLEROCKETLAUNCHERHEAVY";"S*G*EWXH--*****";"(null)";"(null)";"(null)"
"692";"MULTIPLE ROCKET LAUNCHER LIGHT";"489";"MULTIPLEROCKETLAUNCHERLIGHT";"S*G*EWXL--*****";"(null)";"(null)";"(null)"
"693";"MULTIPLE ROCKET LAUNCHER MEDIU";"490";"MULTIPLEROCKETLAUNCHERMEDIUM";"S*G*EWXM--*****";"(null)";"(null)";"(null)"
"771";"NUCLEAR";"583";"NUCLEAR";"S*G*IRNN--H****";"(null)";"(null)";"(null)"
"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"113";"O/C Staff";"188"
"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"180";"O/C TM";"188"
"188";"O/C Force";"5";"SPACESTATION";"S*P*T-----*****";"114";"O/C Force";"188"
"807";"O/C Force CE";"5";"SPACESTATION";"S*P*T-----*****";"(null)";"(null)";"(null)"
"793";"OILER/TANKER";"655";"OILER/TANKER";"S*S*XMO---*****";"(null)";"(null)";"(null)"
"792";"PATROL";"630";"PATROL";"S*S*CP----*****";"(null)";"(null)";"(null)"
"767";"PETROLEUM/GAS/OIL";"579";"PETROLEUM/GAS/OIL";"S*G*IRP---H****";"(null)";"(null)";"(null)"
"656";"PIPELINE";"462";"PIPELINE";"S*G*USXP--*****";"(null)";"(null)";"(null)"
"775";"PUBLIC WATER SERVICES";"591";"PUBLICWATERSERVICES";"S*G*IUP---H****";"(null)";"(null)";"(null)"
"757";"RADAR";"567";"RADAR";"S*G*ESR---*****";"(null)";"(null)";"(null)"
"525";"RECONNAISSANCE";"226";"RECONNAISSANCE";"S*G*UCR---*****";"(null)";"(null)";"(null)"
"535";"RECONNAISSANCE AIR ASSAULT";"234";"RECONNAISSANCEAIRASSAULT";"S*G*UCRS--*****";"(null)";"(null)";"(null)"
"536";"RECONNAISSANCE AIRBORNE";"235";"RECONNAISSANCEAIRBORNE";"S*G*UCRA--*****";"(null)";"(null)";"(null)"
"539";"RECONNAISSANCE AMPHIBIOUS";"238";"RECONNAISSANCEMARINE";"S*G*UCRR--*****";"(null)";"(null)";"(null)"
"540";"RECONNAISSANCE AMPHIBIOUS DIVI";"239";"RECONNAISSANCEMARINEDIVISION";"S*G*UCRRD-*****";"(null)";"(null)";"(null)"
"541";"RECONNAISSANCE AMPHIBIOUS FORC";"240";"RECONNAISSANCEMARINEFORCE";"S*G*UCRRF-*****";"(null)";"(null)";"(null)"
"530";"RECONNAISSANCE CAVALRY";"228";"RECONNAISSANCECAVALRY";"S*G*UCRV--*****";"(null)";"(null)";"(null)"
"534";"RECONNAISSANCE CAVALRY AIR";"232";"RECONNAISSANCECAVALRYAIR";"S*G*UCRVO-*****";"(null)";"(null)";"(null)"
"531";"RECONNAISSANCE CAVALRY ARMOURE";"229";"RECONNAISSANCECAVALRYARMOURED";"S*G*UCRVA-*****";"(null)";"(null)";"(null)"
"533";"RECONNAISSANCE CAVALRY GROUND";"231";"RECONNAISSANCECAVALRYGROUND";"S*G*UCRVG-*****";"(null)";"(null)";"(null)"
"532";"RECONNAISSANCE CAVALRY MOTORIZ";"230";"RECONNAISSANCECAVALRYMOTORIZED";"S*G*UCRVM-*****";"(null)";"(null)";"(null)"
"538";"RECONNAISSANCE LIGHT";"237";"RECONNAISSANCELIGHT";"S*G*UCRLL--*****";"(null)";"(null)";"(null)"
"542";"RECONNAISSANCE MARINE LIGHT AR";"241";"RECONNAISSANCEMARINELIGHTARMOUREDRECONNAISSANCE(LA";"S*G*UCRRL-*****";"(null)";"(null)";"(null)"
"537";"RECONNAISSANCE MOUNTAIN";"236";"RECONNAISSANCEMOUNTAIN";"S*G*UCRO--*****";"(null)";"(null)";"(null)"
"543";"RECONNAISSANCE SURVEILLANCE LO";"242";"RECONNAISSANCELONGRANGESURVEILLANCE(LRS)";"S*G*UCRX--*****";"(null)";"(null)";"(null)"
"700";"RIFLE";"497";"RIFLE";"S*G*EWRR--*****";"210";"Soldier Assault Rifle";"700"
"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"205";"Soldier Tgt";"699"
"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"216";"Soldier M203";"699"
"699";"RIFLE/AUTOMATIC WEAPON";"496";"RIFLE/AUTOMATICWEAPON";"S*G*EWR---*****";"153";"Soldier MP";"699"
"438";"ROTARY WING ATTACK";"131";"ATTACKROTARYWING";"S*G*UCVRA-*****";"(null)";"(null)";"(null)"
"347";"ROTARY WING ATTACK";"40";"ATTACK";"S*A*MHA---*****";"(null)";"(null)";"(null)"
"352";"ROTARY WING DRONE (RPV/UAV)";"49";"DRONE(RPV/UAV)";"S*A*MHQ---*****";"(null)";"(null)";"(null)"
"348";"ROTARY WING MEDEVAC";"55";"MEDEVAC";"S*A*MHO---*****";"(null)";"(null)";"(null)"
"351";"ROTARY WING RECCE";"48";"RECONNAISSANCE";"S*A*MHR---*****";"(null)";"(null)";"(null)"
"349";"ROTARY WING SOF";"56";"SPECIALOPERATIONSFORCE(SOF)";"S*A*MHM---*****";"(null)";"(null)";"(null)"
"350";"ROTARY WING UTILITY";"42";"UTILITY";"S*A*MHU---*****";"(null)";"(null)";"(null)"
"782";"SEAPORT/NAVAL BASE";"606";"SEAPORT/NAVALBASE";"S*G*IBN---H****";"(null)";"(null)";"(null)"
"786";"SEA SURFACE INSTALLATION, OIL RIG / PLATFORM";"611";"SEASURFACEINSTALLATION,OILRIG/PLATFORM";"S*G*IRR---H****";"(null)";"(null)";"(null)"
"787";"SEA SURFACE TRACK";"612";"SEASURFACETRACK";"S*S*------*****";"(null)";"(null)";"(null)"
"740";"SEMI";"548";"SEMI";"S*G*EVUS--*****";"(null)";"(null)";"(null)"
"756";"SENSOR";"566";"SENSOR";"S*G*ES----*****";"(null)";"(null)";"(null)"
"616";"SERVICE SUPPORT MEDICAL";"364";"MEDICAL";"S*G*USM---*****";"(null)";"(null)";"(null)"
"641";"SERVICE SUPPORT TRANSPORTATION";"424";"TRANSPORTATION";"S*G*UST---*****";"53";"TATRA Logistics Carrier";"641"
"665";"SHORT RANGE AD MISSILE LAUNCHE";"472";"SHORTRANGEADMISSILELAUNCHERS";"S*G*EWMAS-*****";"(null)";"(null)";"(null)"
"669";"SHORT RANGE SS MISSILE LAUNCH";"477";"SHORTRANGESSMISSILELAUNCH";"S*G*EWMSS-*****";"(null)";"(null)";"(null)"
"593";"SIGNALS UNIT FORWARD COMMUNICATIONS";"303";"FORWARDCOMMUNICATIONS";"S*G*UUSF--*****";"(null)";"(null)";"(null)"
"597";"SIGNALS UNIT SIGNAL SUPPORT";"312";"SIGNALSUPPORT";"S*G*UUSS--*****";"(null)";"(null)";"(null)"
"589";"SIGNAL UNIT";"298";"SIGNALUNIT";"S*G*UUS---*****";"(null)";"(null)";"(null)"
"590";"SIGNAL UNIT AREA";"299";"AREA";"S*G*UUSA--*****";"(null)";"(null)";"(null)"
"592";"SIGNAL UNIT COMMAND OPERATIONS";"302";"COMMANDOPERATIONS";"S*G*UUSO--*****";"(null)";"(null)";"(null)"
"591";"SIGNAL UNIT COMMUNICATION CONF";"300";"COMMUNICATIONCONFIGUREDPACKAGE";"S*G*UUSC--*****";"(null)";"(null)";"(null)"
"599";"SIGNAL UNIT ELECTRONIC RANGING";"314";"ELECTRONICRANGING";"S*G*UUSX--*****";"(null)";"(null)";"(null)"
"594";"SIGNAL UNIT RADIO UNIT";"308";"RADIOUNIT";"S*G*UUSR--*****";"(null)";"(null)";"(null)"
"596";"SIGNAL UNIT RADIO UNIT RELAY";"311";"RELAY";"S*G*UUSRW-*****";"(null)";"(null)";"(null)"
"595";"SIGNAL UNIT RADIO UNIT TACTICA";"309";"TACTICALSATELLITE";"S*G*UUSRS-*****";"(null)";"(null)";"(null)"
"598";"SIGNAL UNIT TELEPHONE SWITCH";"313";"TELEPHONESWITCH";"S*G*UUSW--*****";"(null)";"(null)";"(null)"
"687";"SINGLE ROCKET LAUNCHER";"484";"SINGLEROCKETLAUNCHER";"S*G*EWS---*****";"(null)";"(null)";"(null)"
"690";"SINGLE ROCKET LAUNCHER HEAVY";"487";"SINGLEROCKETLAUNCHERHEAVY";"S*G*EWSH--*****";"(null)";"(null)";"(null)"
"688";"SINGLE ROCKET LAUNCHER LIGHT";"485";"SINGLEROCKETLAUNCHERLIGHT";"S*G*EWSL--*****";"(null)";"(null)";"(null)"
"689";"SINGLE ROCKET LAUNCHER MEDIUM";"486";"SINGLEROCKETLAUNCHERMEDIUM";"S*G*EWSM--*****";"(null)";"(null)";"(null)"
"796";"SOF AVIATION FIXED WING";"707";"SOFUNITFIXEDWING";"S*F*AF----*****";"(null)";"(null)";"(null)"
"797";"SOF AVIATION FIXED WING ATTACK";"708";"SOFUNITATTACK";"S*F*AFA---*****";"(null)";"(null)";"(null)"
"798";"SOF AVIATION FIXED WING UTILIT";"710";"SOFUNITUTILITY";"S*F*AFU---*****";"(null)";"(null)";"(null)"
"799";"SOF AVIATION FIXED WING UTILIT";"711";"SOFUNITUTILITY(LIGHT)";"S*F*AFUL--*****";"(null)";"(null)";"(null)"
"800";"SOF AVIATION ROTARY WING";"715";"SOFUNITROTARYWING";"S*F*AH----*****";"(null)";"(null)";"(null)"
"803";"SOF UNIT GROUND";"727";"SOFUNITGROUND";"S*F*G-----*****";"(null)";"(null)";"(null)"
"805";"SOF UNIT GROUND RANGER";"729";"SOFUNITRANGER";"S*F*GSR---*****";"(null)";"(null)";"(null)"
"804";"SOF UNIT GROUND SPECIAL FORCES";"728";"SOFUNITSPECIALFORCES";"S*F*GS----*****";"(null)";"(null)";"(null)"
"801";"SOF UNIT NAVAL";"722";"SOFUNITSOFUNITNAVAL";"S*F*SN----*****";"(null)";"(null)";"(null)"
"802";"SOF UNIT NAVAL SPECIAL BOAT";"725";"SOFUNITSPECIALBOAT";"S*F*SNB---*****";"(null)";"(null)";"(null)"
"806";"SOF UNIT PSYCHOLOGICAL OPERATI";"730";"SOFUNITPSYCHOLOGICALOPERATIONS(PSYOPS)";"S*F*GSP---*****";"(null)";"(null)";"(null)"
"658";"SPECIAL C2 HEADQUATERS COMPONE";"464";"SPECIALC2HEADQUARTERSCOMPONENT";"S*G*UH----*****";"(null)";"(null)";"(null)"
"795";"SPECIAL OPERATIONS FORCE (SOF)";"705";"SPECIALOPERATIONSFORCE(SOF)UNIT";"S*F*------*****";"(null)";"(null)";"(null)"
"625";"SUPPLY";"379";"SUPPLY";"S*G*USS---*****";"(null)";"(null)";"(null)"
"633";"SUPPLY CLASS I";"382";"SUPPLYCLASSI";"S*G*USS1--*****";"(null)";"(null)";"(null)"
"634";"SUPPLY CLASS II";"385";"SUPPLYCLASSII";"S*G*USS2--*****";"(null)";"(null)";"(null)"
"635";"SUPPLY CLASS III";"388";"SUPPLYCLASSIII";"S*G*USS3--*****";"(null)";"(null)";"(null)"
"636";"SUPPLY CLASS III AVIATION";"391";"SUPPLYCLASSIIIAVIATION";"S*G*USS3A-*****";"(null)";"(null)";"(null)"
"637";"SUPPLY CLASS IV";"394";"SUPPLYCLASSIV";"S*G*USS4--*****";"(null)";"(null)";"(null)"
"627";"SUPPLY CLASS IX";"409";"SUPPLYCLASSIX";"S*G*USS9--*****";"(null)";"(null)";"(null)"
"638";"SUPPLY CLASS V";"397";"SUPPLYCLASSV";"S*G*USS5--*****";"(null)";"(null)";"(null)"
"639";"SUPPLY CLASS VI";"400";"SUPPLYCLASSVI";"S*G*USS6--*****";"(null)";"(null)";"(null)"
"640";"SUPPLY CLASS VII";"403";"SUPPLYCLASSVII";"S*G*USS7--*****";"(null)";"(null)";"(null)"
"626";"SUPPLY CLASS VIII";"406";"SUPPLYCLASSVIII";"S*G*USS8--*****";"(null)";"(null)";"(null)"
"628";"SUPPLY CLASS X";"412";"SUPPLYCLASSX";"S*G*USSX--*****";"(null)";"(null)";"(null)"
"629";"SUPPLY LAUNDRY/BATH";"415";"SUPPLYLAUNDRY/BATH";"S*G*USSL--*****";"(null)";"(null)";"(null)"
"632";"SUPPLY MATERIEL (STOCK)";"1335";"SUPPLYMATERIEL(STOCK)";"S*G*US**--*****";"(null)";"(null)";"(null)"
"630";"SUPPLY WATER";"418";"SUPPLYWATER";"S*G*USSW--*****";"(null)";"(null)";"(null)"
"631";"SUPPLY WATER PURIFICATION";"421";"SUPPLYWATERPURIFICATION";"S*G*USSWP-*****";"(null)";"(null)";"(null)"
"668";"SURFACE- SURFACE (SS) MISSILE";"476";"SURFACE-SURFACE(SS)MISSILELAUNCHER";"S*G*EWMS--*****";"(null)";"(null)";"(null)"
"355";"SURFACE TO AIR MISSILE";"65";"SURFACETOAIRMISSILE(SAM)";"S*A*WMSA--*****";"(null)";"(null)";"(null)"
"354";"SURFACE TO SURFACE MISSILE";"64";"SURFACETOSURFACEMISSILE(SSM)";"S*A*WMSS--*****";"(null)";"(null)";"(null)"
"391";"SURVEILLANCE";"1334";"SURVEILLANCE";"S*G*F-S---*****";"(null)";"(null)";"(null)"
"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"233";"0Vehicle";"725"
"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"43";"MBT";"725"
"725";"TANK";"533";"TANK";"S*G*EVAT--*****";"230";"LEOPARD 2A5";"725"
"730";"TANK HEAVY";"538";"TANKHEAVY";"S*G*EVATH-*****";"(null)";"(null)";"(null)"
"731";"TANK HEAVY RECOVERY";"539";"TANKHEAVYRECOVERY";"S*G*EVATY-*****";"(null)";"(null)";"(null)"
"726";"TANK LIGHT";"534";"TANKLIGHT";"S*G*EVATL-*****";"(null)";"(null)";"(null)"
"727";"TANK LIGHT RECOVERY";"535";"TANKLIGHTRECOVERY";"S*G*EVATW-*****";"110";"BMP3 Recovery";"727"
"728";"TANK MEDIUM";"536";"TANKMEDIUM";"S*G*EVATM-*****";"(null)";"(null)";"(null)"
"729";"TANK MEDIUM RECOVERY";"537";"TANKMEDIUMRECOVERY";"S*G*EVATX-*****";"(null)";"(null)";"(null)"
"773";"TELECOMMUNICATIONS FACILITY";"589";"TELECOMMUNICATIONSFACILITY";"S*G*IUT---H****";"(null)";"(null)";"(null)"
"603";"TOPOGRAPHIC";"318";"TOPOGRAPHIC";"S*G*UUT---*****";"(null)";"(null)";"(null)"
"751";"TRAILER MOUNTED";"559";"TRAILERMOUNTED";"S*G*EVEMT-*****";"(null)";"(null)";"(null)"
"754";"TRAIN LOCOMOTIVE";"564";"TRAINLOCOMOTIVE";"S*G*EVST--*****";"(null)";"(null)";"(null)"
"645";"TRANSPORTATION APOD/APOE";"436";"APOD/APOE";"S*G*USTA--*****";"(null)";"(null)";"(null)"
"647";"TRANSPORTATION INTERMODAL";"1328";"TRANSPORTATIONINTERMODAL";"S*G*UST---*****";"(null)";"(null)";"(null)"
"646";"TRANSPORTATION MISSILE";"439";"MISSILE";"S*G*USTI--*****";"(null)";"(null)";"(null)"
"642";"TRANSPORTATION MOVEMENT CONTRO";"427";"MOVEMENTCONTROLCENTRE(MCC)";"S*G*USTM--*****";"(null)";"(null)";"(null)"
"643";"TRANSPORTATION RAILHEAD";"430";"RAILHEAD";"S*G*USTR--*****";"(null)";"(null)";"(null)"
"644";"TRANSPORTATION SPOD/SPOE";"433";"SPOD/SPOE";"S*G*USTS--*****";"(null)";"(null)";"(null)"
"783";"TRANSPORT FACILITY";"607";"TRANSPORTFACILITY";"S*G*IT----H****";"(null)";"(null)";"(null)"
"661";"UNITS GENERAL LIAISON";"467";"UNITGENERALLIAISON";"S*G*UHGL--*****";"(null)";"(null)";"(null)"
"738";"UTILITY VEHICLE";"546";"UTILITYVEHICLE";"S*G*EVU---*****";"54";"HMMWV Utility";"738"
"738";"UTILITY VEHICLE";"546";"UTILITYVEHICLE";"S*G*EVU---*****";"221";"Vehicle Tgt";"738"
"743";"WATER CRAFT";"551";"WATERCRAFT";"S*G*EVUR--*****";"213";"TATRA Tanker Water";"743"
"353";"WEAPON";"61";"WEAPON";"S*A*W-----*****";"(null)";"(null)";"(null)"
1 id name sym_klassifiz_id name symbol_id_code id name def_symbol_id
2 611 ADMINISTRATIVE FINANCE 331 FINANCE S*G*USAF--***** (null) (null) (null)
3 609 ADMINISTRATIVE JUDGE ADVOCATE GENERAL (JAG) 325 JUDGEADVOCATEGENERAL(JAG) S*G*USAJ--***** (null) (null) (null)
4 606 ADMINISTRATIVE LABOUR 355 LABOUR S*G*USAL--***** (null) (null) (null)
5 607 ADMINISTRATIVE MORAL, WELFARE, RECREATION (MWR) 358 MORAL,WELFARE,RECREATION(MWR) S*G*USAW--***** (null) (null) (null)
6 613 ADMINISTRATIVE MORTUARY/GRAVE REGISTRY 337 MORTUARY/GRAVESREGISTRY S*G*USAM--***** (null) (null) (null)
7 612 ADMINISTRATIVE PERSONNEL SERVICES 334 PERSONNELSERVICES S*G*USAS--***** (null) (null) (null)
8 610 ADMINISTRATIVE POSTAL 328 POSTAL S*G*USAO--***** (null) (null) (null)
9 615 ADMINISTRATIVE PUPLIC AFFAIRS 343 PUBLICAFFAIRS S*G*USAP--***** (null) (null) (null)
10 608 ADMINISTRATIVE QUARTERMASTER ( 361 QUARTERMASTER(SUPPLY) S*G*USAQ--***** (null) (null) (null)
11 614 ADMINISTRATIVE RELIGIOUS/CHAPLAIN 340 RELIGIOUS/CHAPLAIN S*G*USAR--***** (null) (null) (null)
12 605 ADMINISTRATIVE REPLACEMENT HOLDING UNIT (RHV) 352 REPLACEMENTHOLDINGUNIT(RHU) S*G*USAX--***** (null) (null) (null)
13 364 AIR DEFENCE 79 AIRDEFENCE S*G*UCD---***** (null) (null) (null)
14 664 AIR DEFENCE (AD) MISSILE LAUNC 471 AIRDEFENCE(AD)MISSILELAUNCH S*G*EWMA--***** (null) (null) (null)
15 379 AIR DEFENCE COMPOSITE 90 COMPOSITE S*G*UCDC--***** (null) (null) (null)
16 367 Air Defence COY 79 AIRDEFENCE S*G*UCD---***** (null) (null) (null)
17 683 AIR DEFENCE GUN 527 AIRDEFENCEGUN S*G*EWA---***** (null) (null) (null)
18 686 AIR DEFENCE GUN HEAVY 530 AIRDEFENCEGUNHEAVY S*G*EWAH--***** (null) (null) (null)
19 684 AIR DEFENCE GUN LIGHT 528 AIRDEFENCEGUNLIGHT S*G*EWAL--***** (null) (null) (null)
20 685 AIR DEFENCE GUN MEDIUM 529 AIRDEFENCEGUNMEDIUM S*G*EWAM--***** 44 Soldier Air Defence 685
21 378 AIR DEFENCE GUN UNIT 89 GUNUNIT S*G*UCDG--***** (null) (null) (null)
22 377 AIR DEFENCE H/MAD 88 H/MAD S*G*UCDH--***** (null) (null) (null)
23 372 AIR DEFENCE MISSILE 83 AIRDEFENCEMISSILE S*G*UCDM--***** (null) (null) (null)
24 376 AIR DEFENCE MISSILE HEAVY 87 AIRDEFENCEMISSILEHEAVY S*G*UCDMH-***** (null) (null) (null)
25 373 AIR DEFENCE MISSILE LIGHT 84 AIRDEFENCEMISSILELIGHT S*G*UCDML-***** 229 Legatus Wiesel 373
26 375 AIR DEFENCE MISSILE MEDIUM 86 AIRDEFENCEMISSILEMEDIUM S*G*UCDMM-***** (null) (null) (null)
27 374 AIR DEFENCE MISSILE MOTORIZED 85 AIRDEFENCEMISSILEMOTORIZED S*G*UCDMLA***** (null) (null) (null)
28 369 AIR DEFENCE SHORT RANGE 80 SHORTRANGE S*G*UCDS--***** (null) (null) (null)
29 371 AIR DEFENCE SHORT RANGE GUN 82 GUN S*G*UCDS--***** (null) (null) (null)
30 370 AIR DEFENCE SHORT RANGE MISSILE 81 MISSILE S*G*UCDS--***** (null) (null) (null)
31 380 AIR DEFENCE TARGETING UNIT 91 TARGETINGUNIT S*G*UCDT--***** (null) (null) (null)
32 381 AIR DEFENCE THEATRE MISSILE DEFENCE UNIT 92 THEATREMISSILEDEFENCEUNIT S*G*UCDO--***** (null) (null) (null)
33 781 AIRPORT/AIRBASE 605 AIRPORT/AIRBASE S*G*IBA---H**** (null) (null) (null)
34 342 AIR TRACK 6 AIRTRACK S*A*------***** (null) (null) (null)
35 777 AMMUNITION AND EXPLOSIVES PRODUCTION 595 AMMUNITIONANDEXPLOSIVESPRODUCTION S*G*IME---H**** (null) (null) (null)
36 788 AMPHIBIOUS WARFARE SHIP 620 AMPHIBIOUSWARFARESHIP S*S*CA----***** (null) (null) (null)
37 414 ANTI ARMOUR 110 ANTIARMOUR S*G*UCAA--***** (null) (null) (null)
38 422 ANTI ARMOUR AIR ASSAULT 114 ANTIARMOURAIRASSAULT S*G*UCAAS-***** (null) (null) (null)
39 421 ANTI ARMOUR AIRBORNE 113 ANTIARMOURAIRBORNE S*G*UCAAM-***** (null) (null) (null)
40 424 ANTI ARMOUR ARMOURED 117 ANTIARMOURARMOURED S*G*UCAAA-***** (null) (null) (null)
41 427 ANTI ARMOUR ARMOURED AIR ASSAU 120 ANTIARMOURARMOUREDAIRASSAULT S*G*UCAAAS***** (null) (null) (null)
42 425 ANTI ARMOUR ARMOURED TRACKED 118 ANTIARMOURARMOUREDTRACKED S*G*UCAAAT***** (null) (null) (null)
43 426 ANTI ARMOUR ARMOURED WHEELED 119 ANTIARMOURARMOUREDWHEELED S*G*UCAAAW***** (null) (null) (null)
44 419 ANTI ARMOUR DISMOUNTED 111 ANTIARMOURDISMOUNTED S*G*UCAAD-***** (null) (null) (null)
45 420 ANTI ARMOUR LIGHT 112 ANTIARMOURLIGHT S*G*UCAAL-***** (null) (null) (null)
46 428 ANTI ARMOUR MOTORIZED 121 ANTIARMOURMOTORIZED S*G*UCAAO-***** (null) (null) (null)
47 429 ANTI ARMOUR MOTORIZED AIR ASSA 122 ANTIARMOURMOTORIZEDAIRASSAULT S*G*UCAAOS***** (null) (null) (null)
48 423 ANTI ARMOUR MOUNTAIN 115 ANTIARMOURMOUNTAIN S*G*UCAAU-***** (null) (null) (null)
49 718 ANTITANK GUN 515 ANTITANKGUN S*G*EWG---***** 130 Soldier AT (RPG7) 718
50 722 ANTITANK GUN HEAVY 519 ANTITANKGUNHEAVY S*G*EWGH--***** (null) (null) (null)
51 720 ANTITANK GUN LIGHT 517 ANTITANKGUNLIGHT S*G*EWGL--***** (null) (null) (null)
52 721 ANTITANK GUN MEDIUM 518 ANTITANKGUNMEDIUM S*G*EWGM--***** (null) (null) (null)
53 719 ANTITANK GUN RECOILLESS 516 ANTITANKGUNRECOILLESS S*G*EWGR--***** (null) (null) (null)
54 695 ANTITANK ROCKET LAUNCHER 492 ANTITANKROCKETLAUNCHER S*G*EWT---***** (null) (null) (null)
55 698 ANTITANK ROCKET LAUNCHER HEAVY 495 ANTITANKROCKETLAUNCHERHEAVY S*G*EWTH--***** (null) (null) (null)
56 696 ANTITANK ROCKET LAUNCHER LIGHT 493 ANTITANKROCKETLAUNCHERLIGHT S*G*EWTL--***** (null) (null) (null)
57 697 ANTITANK ROCKET LAUNCHER MEDIU 494 ANTITANKROCKETLAUNCHERMEDIUM S*G*EWTM--***** (null) (null) (null)
58 392 ARMOUR 93 ARMOUR S*G*UCA---***** (null) (null) (null)
59 752 ARMOURED CARRIER WITH SCATTERA 561 ARMOUREDCARRIERWITHSCATTERABLEMINES S*G*EVEMSM***** (null) (null) (null)
60 734 ARMOURED INFANTRY 542 ARMOUREDINFANTRY S*G*EVAI--***** (null) (null) (null)
61 732 ARMOURED PERSONNEL CARRIER 540 ARMOUREDPERSONNELCARRIER S*G*EVAA--***** (null) (null) (null)
62 733 ARMOURED PERSONNEL CARRIER REC 541 ARMOUREDPERSONNELCARRIERRECOVERY S*G*EVAAR-***** (null) (null) (null)
63 724 ARMOURED VEHICLE 532 ARMOUREDVEHICLE S*G*EVA---***** 231 Marder 724
64 750 ARMOURED VEHICLE MOUNTED 558 ARMOUREDVEHICLEMOUNTED S*G*EVEMA-***** (null) (null) (null)
65 397 ARMOUR TRACK 94 ARMOURTRACK S*G*UCAT--***** (null) (null) (null)
66 398 ARMOUR TRACK AIRBORNE 95 ARMOURTRACKAIRBORNE S*G*UCATA-***** (null) (null) (null)
67 399 ARMOUR TRACK AMPHIBIOUS 96 ARMOURTRACKAMPHIBIOUS S*G*UCATW-***** (null) (null) (null)
68 400 ARMOUR TRACK AMPHIBIOUS RECOVE 97 ARMOURTRACKAMPHIBIOUSRECOVERY S*G*UCATWR***** (null) (null) (null)
69 403 ARMOUR TRACK HEAVY 100 ARMOURTRACK,HEAVY S*G*UCATH-***** (null) (null) (null)
70 401 ARMOUR TRACK LIGHT 98 ARMOURTRACK,LIGHT S*G*UCATL-***** (null) (null) (null)
71 402 ARMOUR TRACK MEDIUM 99 ARMOURTRACK,MEDIUM S*G*UCATM-***** (null) (null) (null)
72 404 ARMOUR TRACK RECOVERY 101 ARMOURTRACK,RECOVERY S*G*UCATR-***** (null) (null) (null)
73 405 ARMOUR WHEELED 102 ARMOUR,WHEELED S*G*UCAW--***** (null) (null) (null)
74 406 ARMOUR WHEELED AIR ASSAULT 103 ARMOUR,WHEELEDAIRASSAULT S*G*UCAWS-***** (null) (null) (null)
75 407 ARMOUR WHEELED AIRBORNE 104 ARMOUR,WHEELEDAIRBORNE S*G*UCAWA-***** 227 BMP3 IFV 407
76 408 ARMOUR WHEELED AMPHIBIOUS 105 ARMOUR,WHEELEDAMPHIBIOUS S*G*UCAWA-***** (null) (null) (null)
77 409 ARMOUR WHEELED AMPHIBIOUS RECO 1325 ARMOURWHEELEDAMPHIBIOUSRECOVERY S*G*UCAWWR***** (null) (null) (null)
78 412 ARMOUR WHEELED HEAVY 108 ARMOUR,WHEELEDHEAVY S*G*UCAWH-***** (null) (null) (null)
79 410 ARMOUR WHEELED LIGHT 106 ARMOUR,WHEELEDLIGHT S*G*UCAWL-***** (null) (null) (null)
80 411 ARMOUR WHEELED MEDIUM 107 ARMOUR,WHEELEDMEDIUM S*G*UCAWM-***** (null) (null) (null)
81 413 ARMOUR WHEELED RECOVERY 109 ARMOUR,WHEELEDRECOVERY S*G*UCAWR-***** 10 Armored Recovery Vehicle 413
82 789 ASSAULT VESSEL 621 ASSAULTVESSEL S*S*CALA--***** (null) (null) (null)
83 776 ATOMIC ENERGY PRODUCTION 593 ATOMICENERGYPRODUCTION S*G*IMF---H**** (null) (null) (null)
84 430 AVIATION 123 AVIATION S*G*UCV---***** (null) (null) (null)
85 431 AVIATION FIXED WING 124 FIXEDWING S*G*UCVF--***** (null) (null) (null)
86 433 AVIATION FIXED WING ATTACK 126 ATTACKFIXEDWING S*G*UCVFA-***** (null) (null) (null)
87 436 AVIATION FIXED WING RECON 129 RECONFIXEDWING S*G*UCVFR-***** (null) (null) (null)
88 432 AVIATION FIXED WING UTILITY 125 UTILITYFIXEDWING S*G*UCVFU-***** (null) (null) (null)
89 435 AVIATION FORWARD AIR CONTROLLER (FAC) 128 FORWARDAIRCONTROLLER(FAC) S*G*UCVUFC***** (null) (null) (null)
90 437 AVIATION ROTARY WING 130 ROTARYWING S*G*UCVR--***** (null) (null) (null)
91 440 AVIATION ROTARY WING C2 138 C2ROTARYWING S*G*UCVRUC***** (null) (null) (null)
92 441 AVIATION ROTARY WING MEDEVAC 139 MEDEVACROTARYWING S*G*UCVRUE***** (null) (null) (null)
93 439 AVIATION ROTARY WING UTILITY 134 UTILITYROTARYWING S*G*UCVRU-***** (null) (null) (null)
94 434 AVIATION TACTICAL AIR CONTROL 127 TACTICALAIRCONTROLPARTY(TACP) S*G*UCVUTP***** (null) (null) (null)
95 445 AVIATION UAV CONTROL 1326 AVIATIONUAVCONTROL S*G*UCVU--***** (null) (null) (null)
96 446 AVIATION UAV LAUNCHER 1327 AVIATIONUAVLAUNCHER S*G*UCVU--***** (null) (null) (null)
97 447 AVIATION UAV RECOVERY 1337 AVIATIONUAVRECOVERY S*G*UCVU--***** (null) (null) (null)
98 444 AVIATION UNMANNED AERIAL VEHIC 145 UNMANNEDAERIALVEHICLEFIXEDWING S*G*UCVUF-***** (null) (null) (null)
99 448 AVIATION UNMANNED AERIAL VEHICLE ROTARY WING 146 UNMANNEDAERIALVEHICLEROTARYWING S*G*UCVUR-***** (null) (null) (null)
100 443 AVIATION UNMANNED AVIATION VEH 144 UNMANNEDAERIALVEHICLE S*G*UCVU--***** (null) (null) (null)
101 442 AVIATION VERTICAL/SHORT TAKEOF 143 VERTICAL/SHORTTAKEOFFANDLANDING(V/STOL) S*G*UCVV--***** (null) (null) (null)
102 769 BIOLOGICAL 581 BIOLOGICAL S*G*IRNB--H**** (null) (null) (null)
103 745 BRIDGE 553 BRIDGE S*G*EVEB--***** (null) (null) (null)
104 778 BRIDGE 599 BRIDGE S*G*IMNB--H**** (null) (null) (null)
105 739 BUS 547 BUS S*G*EVUB--***** (null) (null) (null)
106 735 C2V/ACV 543 C2V/ACV S*G*EVAC--***** (null) (null) (null)
107 548 CBRN 256 COMBATSUPPORTCBRN S*G*UUA---***** (null) (null) (null)
108 768 CBRN 580 CBRN S*G*IRN---H**** (null) (null) (null)
109 560 CBRN BIOLOGICAL 268 BIOLOGICAL S*G*UUAB--***** (null) (null) (null)
110 561 CBRN BIOLOGICAL RECON EQUIPPED 269 RECONEQUIPPED S*G*UUABR-***** (null) (null) (null)
111 549 CBRN CHEMICAL 257 CHEMICAL S*G*UUAC--***** (null) (null) (null)
112 556 CBRN CHEMICAL RECON 264 CHEMICALRECON S*G*UUACR-***** (null) (null) (null)
113 553 CBRN CHEMICAL SMOKE 261 SMOKE S*G*UUACS-***** (null) (null) (null)
114 555 CBRN CHEMICAL SMOKE ARMOUR 263 ARMOURSMOKE S*G*UUACSA***** (null) (null) (null)
115 550 CBRN CHEMICAL SMOKE/DECON 258 SMOKE/DECON S*G*UUACC-***** (null) (null) (null)
116 551 CBRN CHEMICAL SMOKE/DECON MECHANIZED 259 MECHANIZEDSMOKE/DECON S*G*UUACCK***** (null) (null) (null)
117 552 CBRN CHEMICAL SMOKE/DECON MOTO 260 MOTORIZEDSMOKE/DECON S*G*UUACCM***** (null) (null) (null)
118 554 CBRN CHEMICAL SMOKE MOTORIZED 262 MOTORIZEDSMOKE S*G*UUACSM***** (null) (null) (null)
119 557 CBRN CHEMICAL WHEELED ARMOURED VEHICLE 265 CHEMICALWHEELEDARMOUREDVEHICLE S*G*UUACRW***** (null) (null) (null)
120 558 CBRN CHEMICAL WHEELED ARMOURED VEHICLE RECONNAISAN 266 CHEMICALWHEELEDARMOUREDVEHICLERECONNAISSANCESURVEI S*G*UUACRS***** (null) (null) (null)
121 562 CBRN DECON 270 DECONTAMINATION S*G*UUAD--***** (null) (null) (null)
122 564 CBRN DECON EQUIPMENT 1330 CBRNDECONEQUIPMENT S*G*UUADE-***** (null) (null) (null)
123 565 CBRN DECON EQUIPMENT/TROOP 1331 CBRNDECONEQUIPMENT/TROOP S*G*UUADET***** (null) (null) (null)
124 563 CBRN DECON TROOP 1329 CBRNDECONTROOP S*G*UUADT-***** (null) (null) (null)
125 760 CBRN EQUIPMENT 571 CBRNEQUIPMENT S*G*EXN---***** (null) (null) (null)
126 566 CBRN LABORATORY 1332 CBRNLABORATORY S*G*UUAL--***** (null) (null) (null)
127 559 CBRN NUCLEAR 267 NUCLEAR S*G*UUAN--***** (null) (null) (null)
128 770 CHEMICAL 582 CHEMICAL S*G*IRNC--H**** (null) (null) (null)
129 356 CIVIL FIXED WING 73 FIXEDWING S*A*CF----***** (null) (null) (null)
130 755 CIVILIAN VEHICLE 565 CIVILIANVEHICLE S*G*EVC---***** (null) (null) (null)
131 357 CIVIL ROTARY WING 74 ROTARYWING S*A*CH----***** (null) (null) (null)
132 604 COMBAT SERVICE SUPPORT 321 COMBATSERVICESUPPORT S*G*US----***** (null) (null) (null)
133 736 COMBAT SERVICE SUPPORT VEHICLE 544 COMBATSERVICESUPPORTVEHICLE S*G*EVAS--***** (null) (null) (null)
134 547 COMBAT SUPPORT 255 COMBATSUPPORT S*G*UU----***** (null) (null) (null)
135 748 CONSTRUCTION VEHICLE 556 CONSTRUCTIONVEHICLE S*G*EVEC--***** (null) (null) (null)
136 742 CROSS-COUNTRY TRUCK 550 CROSS-COUNTRYTRUCK S*G*EVUX--***** 212 TATRA Tanker Fuel 742
137 742 CROSS-COUNTRY TRUCK 550 CROSS-COUNTRYTRUCK S*G*EVUX--***** 50 Truck 2t off-road 742
138 772 DECON 585 DECON S*G*IPD---H**** (null) (null) (null)
139 676 DIRECT FIRE GUN 520 DIRECTFIREGUN S*G*EWD---***** (null) (null) (null)
140 681 DIRECT FIRE GUN HEAVY 525 DIRECTFIREGUNHEAVY S*G*EWDH--***** (null) (null) (null)
141 682 DIRECT FIRE GUN HEAVY SELF-PRO 526 DIRECTFIREGUNHEAVYSELF-PROPELLED S*G*EWDHS-***** (null) (null) (null)
142 677 DIRECT FIRE GUN LIGHT 521 DIRECTFIREGUNLIGHT S*G*EWDL--***** (null) (null) (null)
143 678 DIRECT FIRE GUN LIGHT SELF PRO 522 DIRECTFIREGUNLIGHTSELF-PROPELLED S*G*EWDLS-***** (null) (null) (null)
144 679 DIRECT FIRE GUN MEDIUM 523 DIRECTFIREGUNMEDIUM S*G*EWDM--***** (null) (null) (null)
145 680 DIRECT FIRE GUN MEDIUM SELF-PR 524 DIRECTFIREGUNMEDIUMSELF-PROPELLED S*G*EWDMS-***** (null) (null) (null)
146 753 DOZER 562 DOZER S*G*EVED--***** 215 CAT Excavator 753
147 746 EARTHMOVER 554 EARTHMOVER S*G*EVEE--***** (null) (null) (null)
148 774 ELCECTRIC POWER FACILITY 590 ELECTRICPOWERFACILITY S*G*IUE---H**** (null) (null) (null)
149 758 EMPLACED SENSOR 568 EMPLACEDSENSOR S*G*ESE---***** (null) (null) (null)
150 467 ENGINEER 159 ENGINEER S*G*UCE---***** (null) (null) (null)
151 472 ENGINEER COMBAT 160 ENGINEERCOMBAT S*G*UCEC--***** (null) (null) (null)
152 473 ENGINEER COMBAT AIR ASSAULT 161 ENGINEERCOMBATAIRASSAULT S*G*UCECS-***** (null) (null) (null)
153 478 ENGINEER COMBAT AIRBORNE 162 ENGINEERCOMBATAIRBORNE S*G*UCECA-***** (null) (null) (null)
154 475 ENGINEER COMBAT BRIDGING 171 ENGINEERCOMBATBRIDGING S*G*UCEC--***** (null) (null) (null)
155 481 ENGINEER COMBAT HEAVY 166 ENGINEERCOMBATHEAVY S*G*UCECH-***** (null) (null) (null)
156 479 ENGINEER COMBAT LIGHT 164 ENGINEERCOMBATLIGHT S*G*UCECL-***** (null) (null) (null)
157 482 ENGINEER COMBAT MECH (TRACK) 167 ENGINEERCOMBATMECHANIZED(TRACK) S*G*UCECT-***** (null) (null) (null)
158 480 ENGINEER COMBAT MEDIUM 165 ENGINEERCOMBATMEDIUM S*G*UCECM-***** (null) (null) (null)
159 476 ENGINEER COMBAT MINE CLEARING 172 ENGINEERCOMBATMINECLEARING S*G*UCEC--***** (null) (null) (null)
160 477 ENGINEER COMBAT MINE LAYING 173 ENGINEERCOMBATMINELAYING S*G*UCEC--***** (null) (null) (null)
161 483 ENGINEER COMBAT MOTORIZED 168 ENGINEERCOMBATMOTORIZED S*G*UCECW-***** (null) (null) (null)
162 484 ENGINEER COMBAT MOUNTAIN 169 ENGINEERCOMBATMOUNTAIN S*G*UCECO-***** (null) (null) (null)
163 474 ENGINEER COMBAT RECON 170 ENGINEERCOMBATRECON S*G*UCECR-***** (null) (null) (null)
164 485 ENGINEER CONSTRUCTION 175 ENGINEERCONSTRUCTION S*G*UCEN--***** (null) (null) (null)
165 744 ENGINEER VEHICLE 552 ENGINEERVEHICLE S*G*EVE---***** (null) (null) (null)
166 657 ENVIRONMENTAL PROTECTION 463 ENVIRONMENTALPROTECTION S*G*USXEP-***** (null) (null) (null)
167 576 EW CORPS 280 CORPS S*G*UUMSEC***** (null) (null) (null)
168 575 EW THEATRE 279 THEATRE S*G*UUMSET***** (null) (null) (null)
169 602 EXPLOSIVE ORDNANCE DISPOSAL 317 EXPLOSIVEORDINANCEDISPOSAL S*G*UUE---***** (null) (null) (null)
170 490 FIELD ARTELLERY HOWITZER/GUN AIRBORNE 181 AIRBORNE S*G*UCFHA-***** (null) (null) (null)
171 495 FIELD ARTELLERY HOWITZER/GUN AMPHIBIOUS 187 AMPHIBIOUS S*G*UCFHX-***** (null) (null) (null)
172 494 FIELD ARTELLERY HOWITZER/GUN HEAVY 186 HEAVY S*G*UCFHH-***** (null) (null) (null)
173 492 FIELD ARTELLERY HOWITZER/GUN LIGHT 184 LIGHT S*G*UCFHL-***** (null) (null) (null)
174 493 FIELD ARTELLERY HOWITZER/GUN MEDIUM 185 MEDIUM S*G*UCFHM-***** (null) (null) (null)
175 491 FIELD ARTELLERY HOWITZER/GUN MOUNTAIN 183 MOUNTAIN S*G*UCFHO-***** (null) (null) (null)
176 496 FIELD ARTELLERY ROCKET 188 ROCKET S*G*UCFR--***** (null) (null) (null)
177 498 FIELD ARTILEERY ROCKET SINGLE 190 SINGLEROCKETSELFPROPELLED S*G*UCFRSS***** (null) (null) (null)
178 486 FIELD ARTILLERY 177 FIELDARTILLERY S*G*UCF---***** (null) (null) (null)
179 521 FIELD ARTILLERY ARTILLERY SURV 214 ARTILLERYSURVEY S*G*UCFS--***** (null) (null) (null)
180 523 FIELD ARTILLERY FIRE DIRECTION CENTRE 224 FIREDIRECTIONCENTRE S*G*UCF---***** (null) (null) (null)
181 487 FIELD ARTILLERY HOWITZER/GUN 178 HOWITZER/GUN S*G*UCFH--***** (null) (null) (null)
182 489 FIELD ARTILLERY HOWITZER/GUN AIR ASSAULT 180 AIRASSAULT S*G*UCFHS-***** (null) (null) (null)
183 488 FIELD ARTILLERY HOWITZER/GUN SELF-PROPELLED 179 SELFPROPELLED S*G*UCFHE-***** (null) (null) (null)
184 522 FIELD ARTILLERY METEOROLOGICAL 219 METEOROLOGICAL S*G*UCFO--***** (null) (null) (null)
185 513 FIELD ARTILLERY MORTAR 205 MORTAR S*G*UCFM--***** (null) (null) (null)
186 514 FIELD ARTILLERY MORTAR SP TRAC 206 SELFPROPELLED(SP)TRACKEDMORTAR S*G*UCFMS-***** (null) (null) (null)
187 515 FIELD ARTILLERY MORTAR SP WHEE 207 SPWHEELEDMORTAR S*G*UCFMSW***** (null) (null) (null)
188 516 FIELD ARTILLERY MORTAR TOWED 208 TOWEDMORTAR S*G*UCFMT-***** (null) (null) (null)
189 518 FIELD ARTILLERY MORTAR TOWED A 210 TOWEDAIRASSAULTMORTAR S*G*UCFMTS***** (null) (null) (null)
190 517 FIELD ARTILLERY MORTAR TOWED A 209 TOWEDAIRBORNEMORTAR S*G*UCFMTA***** (null) (null) (null)
191 519 FIELD ARTILLERY MORTAR TOWED M 212 TOWEDMOUNTAINMORTAR S*G*UCFMTO***** (null) (null) (null)
192 524 FIELD ARTILLERY OBERSERVER 225 OBERSERVER S*G*UCF---***** (null) (null) (null)
193 502 FIELD ARTILLERY ROCKET MULTI R 194 MULTIROCKETSELFPROPELLED S*G*UCFRMS***** (null) (null) (null)
194 504 FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI 196 MULTIROCKETTOWED S*G*UCFRMT***** (null) (null) (null)
195 503 FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCER MULTI 195 MULTIROCKETTRUCK S*G*UCFRMR***** (null) (null) (null)
196 501 FIELD ARTILLERY ROCKET MULTI ROCKET LAUNCHER 193 MULTIROCKETLAUNCHER S*G*UCFRM-***** (null) (null) (null)
197 497 FIELD ARTILLERY ROCKET SINGLE 189 SINGLEROCKETLAUNCHER S*G*UCFRS-***** (null) (null) (null)
198 499 FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING 191 SINGLEROCKETTRUCK S*G*UCFRSR***** (null) (null) (null)
199 500 FIELD ARTILLERY ROCKET SINGLE ROCKET LAUNCHER SING 192 SINGLEROCKETTOWED S*G*UCFRST***** (null) (null) (null)
200 506 FIELD ARTILLERY TARGET ACQUISI 198 RADAR S*G*UCFTR-***** (null) (null) (null)
201 505 FIELD ARTILLERY TARGET ACQUISITION 197 TARGETACQUISITION S*G*UCFT--***** (null) (null) (null)
202 510 FIELD ARTILLERY TARGET ACQUISITION DISMOUNTED 202 TARGETACQUISITIONDISMOUNTED S*G*UCFTCD***** (null) (null) (null)
203 508 FIELD ARTILLERY TARGET ACQUISITION FLASH (OPTICAL) 200 FLASH(OPTICAL) S*G*UCFTF-***** (null) (null) (null)
204 509 FIELD ARTILLERY TARGET ACQUISITION MOUNTED 201 TARGETACQUISITIONMOUNTED S*G*UCFTC-***** (null) (null) (null)
205 512 FIELD ARTILLERY TARGET ACQUISITION NAVAL GUNFIRE 204 TARGETACQUISITIONNAVALGUNFIRE S*G*UCFTA-***** (null) (null) (null)
206 507 FIELD ARTILLERY TARGET ACQUISITION SOUND 199 SOUND S*G*UCFTS-***** (null) (null) (null)
207 511 FIELD ARTILLERY TARGET ACQUISITION TRACKED 203 TARGETACQUISITIONTRACKED S*G*UCFTCM***** (null) (null) (null)
208 345 FIXED WING DRONE (RPV/UAE) 36 DRONE(RPV/UAV) S*A*MFQ---***** (null) (null) (null)
209 761 FLAME THROWER 572 FLAMETHROWER S*G*EXF---***** (null) (null) (null)
210 660 GENERAL HEADQUATERS AND SERVICE 466 GENERALHEADQUARTERSANDSERVICE S*G*UH----***** (null) (null) (null)
211 779 GOVERNMENT LEADERSHIP 603 GOVERNMENTLEADERSHIP S*G*IG----H**** (null) (null) (null)
212 703 GRENADE LAUNCHER 500 GRENADELAUNCHER S*G*EWZ---***** (null) (null) (null)
213 706 GRENADE LAUNCHER HEAVY 503 GRENADELAUNCHERHEAVY S*G*EWZH--***** (null) (null) (null)
214 704 GRENADE LAUNCHER LIGHT 501 GRENADELAUNCHERLIGHT S*G*EWZL--***** (null) (null) (null)
215 705 GRENADE LAUNCHER MEDIUM 502 GRENADELAUNCHERMEDIUM S*G*EWZM--***** (null) (null) (null)
216 662 GROUND TRACK EQUIPMENT 468 GROUNDTRACKEQUIPMENT S*G*E-----***** (null) (null) (null)
217 358 GROUND TRACK UNIT 77 UNIT S*G*U-----***** 217 Soldier (Role Player) 358
218 358 GROUND TRACK UNIT 77 UNIT S*G*U-----***** 106 Soldier 358
219 363 GROUND TRACK UNIT COMBAT 78 COMBAT S*G*UC----***** (null) (null) (null)
220 520 GROUND TRACK UNIT COMBAT FIELD ARTILLERY MORTAR AM 213 AMPHIBIOUSMORTAR S*G*UCFML-***** (null) (null) (null)
221 659 GROUND TRACK UNIT GENERAL HEADQUATERS 465 UNITGENERALHEADQUARTERS S*G*UH----***** (null) (null) (null)
222 723 GROUND VEHICLE 531 GROUNDVEHICLE S*G*EV----***** (null) (null) (null)
223 702 HEAVY MACHINE GUN 499 HEAVYMACHINEGUN S*G*EWRH--***** 129 Soldier HMG 702
224 785 HOSPITAL 609 HOSPITAL S*G*IXH---H**** (null) (null) (null)
225 711 HOWITZER 508 HOWITZER S*G*EWH---***** (null) (null) (null)
226 716 HOWITZER HEAVY 513 HOWITZERHEAVY S*G*EWHH--***** (null) (null) (null)
227 717 HOWITZER HEAVY SELF-PROPELLED 514 HOWITZERHEAVYSELF-PROPELLED S*G*EWHHS-***** (null) (null) (null)
228 712 HOWITZER LIGHT 509 HOWITZERLIGHT S*G*EWHL--***** (null) (null) (null)
229 713 HOWITZER LIGHT SELF-PROPELLED 510 HOWITZERLIGHTSELF-PROPELLED S*G*EWHLS-***** (null) (null) (null)
230 714 HOWITZER MEDIUM 511 HOWITZERMEDIUM S*G*EWHM--***** (null) (null) (null)
231 715 HOWITZER MEDIUM SELF-PROPELLED 512 HOWITZERMEDIUMSELF-PROPELLED S*G*EWHMS-***** (null) (null) (null)
232 449 INFANTRY 149 INFANTRY S*G*UCI---***** 161 TM 449
233 449 INFANTRY 149 INFANTRY S*G*UCI---***** 226 TEST PLAYER 449
234 449 INFANTRY 149 INFANTRY S*G*UCI---***** 232 0Soldier 449
235 449 INFANTRY 149 INFANTRY S*G*UCI---***** 128 TM Leader 449
236 463 INFANTRY AIR ASSAULT 154 INFANTRYAIRASSAULT S*G*UCIS--***** (null) (null) (null)
237 462 INFANTRY AIRBORNE 153 INFANTRYAIRBORNE S*G*UCIA--***** (null) (null) (null)
238 466 INFANTRY FIGHTING VEHICLE 157 INFANTRYFIGHTINGVEHICLE S*G*UCII--***** (null) (null) (null)
239 454 INFANTRY LIGHT 150 INFANTRYLIGHT S*G*UCIL--***** (null) (null) (null)
240 464 INFANTRY MECHANIZED 155 INFANTRYMECHANIZED S*G*UCIZ--***** (null) (null) (null)
241 456 INFANTRY MOTORIZED 151 INFANTRYMOTORIZED S*G*UCIM--***** (null) (null) (null)
242 461 INFANTRY MOUNTAIN 152 INFANTRYMOUNTAIN S*G*UCIO--***** (null) (null) (null)
243 465 INFANTRY NAVAL 156 INFANTRYNAVAL S*G*UCIN--***** (null) (null) (null)
244 600 INFORMATION OPERATIONS 315 INFORMATIONWARFAREUNIT S*G*UUI---***** (null) (null) (null)
245 765 INSTALLATION 576 INSTALLATION S*G*I-----H**** 225 MEU 765
246 765 INSTALLATION 576 INSTALLATION S*G*I-----H**** 224 Building 765
247 666 INTERMEDIATE RANGE AD MISSILE 473 INTERMEDIATERANGEADMISSILELAUNCH S*G*EWMAI-***** (null) (null) (null)
248 670 INTERMEDIATE RANGE SS MISSILE 478 INTERMEDIATERANGESSMISSILELAUNCH S*G*EWMSI-***** (null) (null) (null)
249 382 INTERNAL SECURITY FORCES 246 INTERNALSECURITYFORCES S*G*UCS---***** (null) (null) (null)
250 390 INTERNAL SECURITY FORCES AVIAT 254 AVIATION S*G*UCSA--***** (null) (null) (null)
251 387 INTERNAL SECURITY FORCES GROUN 251 MECHANIZEDGROUND S*G*UCSGA-***** (null) (null) (null)
252 384 INTERNAL SECURITY FORCES GROUND 248 GROUND S*G*UCSG--***** (null) (null) (null)
253 385 INTERNAL SECURITY FORCES GROUND DISMOUNTED 249 DISMOUNTEDGROUND S*G*UCSGD-***** (null) (null) (null)
254 386 INTERNAL SECURITY FORCES GROUND MOTORIZED 250 MOTORIZEDGROUND S*G*UCSGM-***** (null) (null) (null)
255 389 INTERNAL SECURITY FORCES RAILROAD 253 RAILROAD S*G*UCSR--***** (null) (null) (null)
256 383 INTERNAL SECURITY FORCES RIVERINE 247 RIVERINE S*G*UCSW--***** (null) (null) (null)
257 388 INTERNAL SECURITY FORCES WHEEL 252 WHEELEDMECHANIZED S*G*UCSM--***** (null) (null) (null)
258 791 LANDING CRAFT 623 LANDINGCRAFT S*S*CALC--***** (null) (null) (null)
259 790 LANDING SHIP 622 LANDINGSHIP S*S*CALS--***** (null) (null) (null)
260 601 LANDING SUPPORT 316 LANDINGSUPPORT S*G*UUX---***** (null) (null) (null)
261 762 LAND MINES 573 LANDMINES S*G*EXM---***** (null) (null) (null)
262 763 LAND MINES, LETHAL 574 LANDMINES,LETHAL S*G*EXMC--***** (null) (null) (null)
263 759 LASER 570 LASER S*G*EXL---***** (null) (null) (null)
264 584 LAW ENFORCEMENT UNIT 292 LAWENFORCEMENTUNIT S*G*UUL---***** (null) (null) (null)
265 587 LAW ENFORCEMENT UNIT CIVILIAN LAW ENFORCEMENT 295 CIVILIANLAWENFORCEMENT S*G*UULC--***** (null) (null) (null)
266 588 LAW ENFORCEMENT UNIT CRIMINAL INVESTIGATION DIVISI 297 CENTRALINTELLIGENCEDIVISION(CID) S*G*UULD--***** (null) (null) (null)
267 586 LAW ENFORCEMENT UNIT MILITARY POLICE 294 MILITARYPOLICE S*G*UULM--***** (null) (null) (null)
268 585 LAW ENFORCEMENT UNIT SHORE PATROL 293 SHOREPATROL S*G*UULS--***** (null) (null) (null)
269 794 LEISURE CRAFT 665 LEISURECRAFT S*S*XR----***** (null) (null) (null)
270 764 LESS THAN LETHAL 575 LESSTHANLETHAL S*G*EXML--***** (null) (null) (null)
271 737 LIGHT ARMOURED VEHICLE 545 LIGHTARMOUREDVEHICLE S*G*EVAL--***** (null) (null) (null)
272 701 LIGHT MACHINE GUN 498 LIGHTMACHINEGUN S*G*EWRL--***** 131 Soldier LMG 701
273 741 LIMITED CROSS-COUNTRY TRUCK 549 LIMITEDCROSS-COUNTRYTRUCK S*G*EVUL--***** (null) (null) (null)
274 667 LONG RANGE AD MISSILE LAUNCH 474 LONGRANGEADMISSILELAUNCH S*G*EWMAL-***** (null) (null) (null)
275 671 LONG RANGE SS MISSILE LAUNCH 479 LONGRANGESSMISSILELAUNCH S*G*EWMSL-***** (null) (null) (null)
276 648 MAINTENANCE 442 MAINTENANCE S*G*USX---***** (null) (null) (null)
277 654 MAINTENANCE BATTLE DAMAGE REPA 460 BATTLEDAMAGEREPAIR S*G*USXBDR***** (null) (null) (null)
278 653 MAINTENANCE ELECTRO-OPTICAL 457 ELECTRO-OPTICAL S*G*USXE--***** (null) (null) (null)
279 649 MAINTENANCE HEAVY 445 MAINTENANCEHEAVY S*G*USXH--***** (null) (null) (null)
280 651 MAINTENANCE ORDINANCE 451 ORDINANCE S*G*USXO--***** (null) (null) (null)
281 652 MAINTENANCE ORDINANCE MISSILE 454 ORDINANCEMISSILE S*G*USXOM-***** (null) (null) (null)
282 655 MAINTENANCE PREVENTIVE MAINTEN 461 PREVENTIVEMAINTANANCE S*G*USXPM-***** (null) (null) (null)
283 650 MAINTENANCE RECOVERY 448 MAINTENANCERECOVERY S*G*USXR--***** (null) (null) (null)
284 623 MEDICAL DENTAL 373 MEDICALDENTAL S*G*USMD--***** (null) (null) (null)
285 784 MEDICAL FACILITY 608 MEDICALFACILITY S*G*IX----H**** (null) (null) (null)
286 624 MEDICAL PSYCHOLOGICAL 376 MEDICALPSYCHOLOGICAL S*G*USMP--***** (null) (null) (null)
287 621 MEDICAL TREATMENT FACILITY 367 MEDICALTREATMENTFACILITY S*G*USMM--***** (null) (null) (null)
288 622 MEDICAL VETERINARY 370 MEDICALVETERINARY S*G*USMV--***** (null) (null) (null)
289 343 MILITARY 7 MILITARY S*A*M-----***** (null) (null) (null)
290 780 MILITARY BASE/FACILITY 604 MILITARYBASE/FACILITY S*G*IB----H**** (null) (null) (null)
291 344 MILITARY FIXED WING 8 FIXEDWING S*A*MF----***** (null) (null) (null)
292 567 MILITARY INTELLIGENCE 271 MILITARYINTELLIGENCE S*G*UUM---***** (null) (null) (null)
293 568 MILITARY INTELLIGENCE AERIAL EXPLOITATION 272 AERIALEXPLOITATION S*G*UUMA--***** (null) (null) (null)
294 577 MILITARY INTELLIGENCE COUNTER INTELLIGENCE 281 COUNTERINTELLIGENCE S*G*UUMC--***** (null) (null) (null)
295 573 MILITARY INTELLIGENCE ELECTRON 277 INTERCEPT S*G*UUMSEI***** (null) (null) (null)
296 572 MILITARY INTELLIGENCE ELECTRON 276 DIRECTIONFINDING S*G*UUMSED***** (null) (null) (null)
297 571 MILITARY INTELLIGENCE ELECTRON 275 ARMOUREDWHEELEDVEHICLE S*G*UUMSEA***** (null) (null) (null)
298 570 MILITARY INTELLIGENCE ELECTRONIC WARFARE 274 ELECTRONICWARFARE S*G*UUMSE-***** (null) (null) (null)
299 574 MILITARY INTELLIGENCE ELECTRONIC WARFARE JAMMING 278 JAMMING S*G*UUMSEJ***** (null) (null) (null)
300 582 MILITARY INTELLIGENCE INTERROGATION 290 INTERROGATION S*G*UUMQ--***** (null) (null) (null)
301 583 MILITARY INTELLIGENCE JOINT INTELLIGENCE CENTRE 291 JOINTINTELLIGENCECENTRE S*G*UUMJ--***** (null) (null) (null)
302 581 MILITARY INTELLIGENCE OPERATIONS 288 OPERATIONS S*G*UUMO--***** (null) (null) (null)
303 569 MILITARY INTELLIGENCE SIGNALS 273 SIGNALINTELLIGENCE(SIGINT) S*G*UUMS--***** (null) (null) (null)
304 579 MILITARY INTELLIGENCE SURVEILL 283 GROUNDSURVEILLANCERADAR S*G*UUMRG-***** (null) (null) (null)
305 578 MILITARY INTELLIGENCE SURVEILLANCE 282 SURVEILLANCE S*G*UUMR--***** (null) (null) (null)
306 580 MILITARY INTELLIGENCE SURVEILLANCE SENSOR 284 SENSOR S*G*UUMRS-***** (null) (null) (null)
307 346 MILITARY ROTARY WING 39 ROTARYWING S*A*MH----***** 195 Apache 346
308 766 MINE 578 MINE S*G*IRM---H**** (null) (null) (null)
309 749 MINE LAYING VEHICLE 557 MINELAYINGVEHICLE S*G*EVEM--***** 4 Trailer, Minelaying 749
310 675 MISSILE LAUNCHER AT HEAVY 483 MISSILELAUNCHERATHEAVY S*G*EWMTH-***** (null) (null) (null)
311 674 MISSILE LAUNCHER AT MEDIUM 482 MISSILELAUNCHERATMEDIUM S*G*EWMTM-***** (null) (null) (null)
312 663 MISSILE LAUNCHERS 470 MISSILELAUNCHERS S*G*EWM---***** (null) (null) (null)
313 672 MISSILE LAUNCHERS ANTI TANK (A 480 MISSILELAUNCHERSANTITANK(AT) S*G*EWMT--***** (null) (null) (null)
314 673 MISSILE LAUNCHERS AT LIGHT 481 MISSILELAUNCHERSATLIGHT S*G*EWMTL-***** (null) (null) (null)
315 544 MISSILE (SURF-SURF) 243 MISSILE(SURFACE-SURFACE) S*G*UCM---***** (null) (null) (null)
316 546 MISSILE (SURF-SURF) STRATEGIC 245 MISSILE(SURFACE-SURFACE)STRATEGIC S*G*UCMS--***** (null) (null) (null)
317 545 MISSILE (SURF-SURF) TACTICAL 244 MISSILE(SURFACE-SURFACE)TACTICAL S*G*UCMT--***** (null) (null) (null)
318 707 MORTAR 504 MORTAR S*G*EWO---***** 209 Soldier Mortar 707
319 710 MORTAR HEAVY 507 MORTARHEAVY S*G*EWOH--***** (null) (null) (null)
320 708 MORTAR LIGHT 505 MORTARLIGHT S*G*EWOL--***** (null) (null) (null)
321 709 MORTAR MEDIUM 506 MORTARMEDIUM S*G*EWOM--***** (null) (null) (null)
322 747 MULTIFUNCTIONAL EARTHMOVER/DIG 555 MULTIFUNCTIONALEARTHMOVER/DIGGER S*G*EVEE--***** (null) (null) (null)
323 691 MULTIPLE ROCKET LAUNCHER 488 MULTIPLEROCKETLAUNCHER S*G*EWX---***** (null) (null) (null)
324 694 MULTIPLE ROCKET LAUNCHER HEAVY 491 MULTIPLEROCKETLAUNCHERHEAVY S*G*EWXH--***** (null) (null) (null)
325 692 MULTIPLE ROCKET LAUNCHER LIGHT 489 MULTIPLEROCKETLAUNCHERLIGHT S*G*EWXL--***** (null) (null) (null)
326 693 MULTIPLE ROCKET LAUNCHER MEDIU 490 MULTIPLEROCKETLAUNCHERMEDIUM S*G*EWXM--***** (null) (null) (null)
327 771 NUCLEAR 583 NUCLEAR S*G*IRNN--H**** (null) (null) (null)
328 188 O/C Force 5 SPACESTATION S*P*T-----***** 113 O/C Staff 188
329 188 O/C Force 5 SPACESTATION S*P*T-----***** 180 O/C TM 188
330 188 O/C Force 5 SPACESTATION S*P*T-----***** 114 O/C Force 188
331 807 O/C Force CE 5 SPACESTATION S*P*T-----***** (null) (null) (null)
332 793 OILER/TANKER 655 OILER/TANKER S*S*XMO---***** (null) (null) (null)
333 792 PATROL 630 PATROL S*S*CP----***** (null) (null) (null)
334 767 PETROLEUM/GAS/OIL 579 PETROLEUM/GAS/OIL S*G*IRP---H**** (null) (null) (null)
335 656 PIPELINE 462 PIPELINE S*G*USXP--***** (null) (null) (null)
336 775 PUBLIC WATER SERVICES 591 PUBLICWATERSERVICES S*G*IUP---H**** (null) (null) (null)
337 757 RADAR 567 RADAR S*G*ESR---***** (null) (null) (null)
338 525 RECONNAISSANCE 226 RECONNAISSANCE S*G*UCR---***** (null) (null) (null)
339 535 RECONNAISSANCE AIR ASSAULT 234 RECONNAISSANCEAIRASSAULT S*G*UCRS--***** (null) (null) (null)
340 536 RECONNAISSANCE AIRBORNE 235 RECONNAISSANCEAIRBORNE S*G*UCRA--***** (null) (null) (null)
341 539 RECONNAISSANCE AMPHIBIOUS 238 RECONNAISSANCEMARINE S*G*UCRR--***** (null) (null) (null)
342 540 RECONNAISSANCE AMPHIBIOUS DIVI 239 RECONNAISSANCEMARINEDIVISION S*G*UCRRD-***** (null) (null) (null)
343 541 RECONNAISSANCE AMPHIBIOUS FORC 240 RECONNAISSANCEMARINEFORCE S*G*UCRRF-***** (null) (null) (null)
344 530 RECONNAISSANCE CAVALRY 228 RECONNAISSANCECAVALRY S*G*UCRV--***** (null) (null) (null)
345 534 RECONNAISSANCE CAVALRY AIR 232 RECONNAISSANCECAVALRYAIR S*G*UCRVO-***** (null) (null) (null)
346 531 RECONNAISSANCE CAVALRY ARMOURE 229 RECONNAISSANCECAVALRYARMOURED S*G*UCRVA-***** (null) (null) (null)
347 533 RECONNAISSANCE CAVALRY GROUND 231 RECONNAISSANCECAVALRYGROUND S*G*UCRVG-***** (null) (null) (null)
348 532 RECONNAISSANCE CAVALRY MOTORIZ 230 RECONNAISSANCECAVALRYMOTORIZED S*G*UCRVM-***** (null) (null) (null)
349 538 RECONNAISSANCE LIGHT 237 RECONNAISSANCELIGHT S*G*UCRLL--***** (null) (null) (null)
350 542 RECONNAISSANCE MARINE LIGHT AR 241 RECONNAISSANCEMARINELIGHTARMOUREDRECONNAISSANCE(LA S*G*UCRRL-***** (null) (null) (null)
351 537 RECONNAISSANCE MOUNTAIN 236 RECONNAISSANCEMOUNTAIN S*G*UCRO--***** (null) (null) (null)
352 543 RECONNAISSANCE SURVEILLANCE LO 242 RECONNAISSANCELONGRANGESURVEILLANCE(LRS) S*G*UCRX--***** (null) (null) (null)
353 700 RIFLE 497 RIFLE S*G*EWRR--***** 210 Soldier Assault Rifle 700
354 699 RIFLE/AUTOMATIC WEAPON 496 RIFLE/AUTOMATICWEAPON S*G*EWR---***** 205 Soldier Tgt 699
355 699 RIFLE/AUTOMATIC WEAPON 496 RIFLE/AUTOMATICWEAPON S*G*EWR---***** 216 Soldier M203 699
356 699 RIFLE/AUTOMATIC WEAPON 496 RIFLE/AUTOMATICWEAPON S*G*EWR---***** 153 Soldier MP 699
357 438 ROTARY WING ATTACK 131 ATTACKROTARYWING S*G*UCVRA-***** (null) (null) (null)
358 347 ROTARY WING ATTACK 40 ATTACK S*A*MHA---***** (null) (null) (null)
359 352 ROTARY WING DRONE (RPV/UAV) 49 DRONE(RPV/UAV) S*A*MHQ---***** (null) (null) (null)
360 348 ROTARY WING MEDEVAC 55 MEDEVAC S*A*MHO---***** (null) (null) (null)
361 351 ROTARY WING RECCE 48 RECONNAISSANCE S*A*MHR---***** (null) (null) (null)
362 349 ROTARY WING SOF 56 SPECIALOPERATIONSFORCE(SOF) S*A*MHM---***** (null) (null) (null)
363 350 ROTARY WING UTILITY 42 UTILITY S*A*MHU---***** (null) (null) (null)
364 782 SEAPORT/NAVAL BASE 606 SEAPORT/NAVALBASE S*G*IBN---H**** (null) (null) (null)
365 786 SEA SURFACE INSTALLATION, OIL RIG / PLATFORM 611 SEASURFACEINSTALLATION,OILRIG/PLATFORM S*G*IRR---H**** (null) (null) (null)
366 787 SEA SURFACE TRACK 612 SEASURFACETRACK S*S*------***** (null) (null) (null)
367 740 SEMI 548 SEMI S*G*EVUS--***** (null) (null) (null)
368 756 SENSOR 566 SENSOR S*G*ES----***** (null) (null) (null)
369 616 SERVICE SUPPORT MEDICAL 364 MEDICAL S*G*USM---***** (null) (null) (null)
370 641 SERVICE SUPPORT TRANSPORTATION 424 TRANSPORTATION S*G*UST---***** 53 TATRA Logistics Carrier 641
371 665 SHORT RANGE AD MISSILE LAUNCHE 472 SHORTRANGEADMISSILELAUNCHERS S*G*EWMAS-***** (null) (null) (null)
372 669 SHORT RANGE SS MISSILE LAUNCH 477 SHORTRANGESSMISSILELAUNCH S*G*EWMSS-***** (null) (null) (null)
373 593 SIGNALS UNIT FORWARD COMMUNICATIONS 303 FORWARDCOMMUNICATIONS S*G*UUSF--***** (null) (null) (null)
374 597 SIGNALS UNIT SIGNAL SUPPORT 312 SIGNALSUPPORT S*G*UUSS--***** (null) (null) (null)
375 589 SIGNAL UNIT 298 SIGNALUNIT S*G*UUS---***** (null) (null) (null)
376 590 SIGNAL UNIT AREA 299 AREA S*G*UUSA--***** (null) (null) (null)
377 592 SIGNAL UNIT COMMAND OPERATIONS 302 COMMANDOPERATIONS S*G*UUSO--***** (null) (null) (null)
378 591 SIGNAL UNIT COMMUNICATION CONF 300 COMMUNICATIONCONFIGUREDPACKAGE S*G*UUSC--***** (null) (null) (null)
379 599 SIGNAL UNIT ELECTRONIC RANGING 314 ELECTRONICRANGING S*G*UUSX--***** (null) (null) (null)
380 594 SIGNAL UNIT RADIO UNIT 308 RADIOUNIT S*G*UUSR--***** (null) (null) (null)
381 596 SIGNAL UNIT RADIO UNIT RELAY 311 RELAY S*G*UUSRW-***** (null) (null) (null)
382 595 SIGNAL UNIT RADIO UNIT TACTICA 309 TACTICALSATELLITE S*G*UUSRS-***** (null) (null) (null)
383 598 SIGNAL UNIT TELEPHONE SWITCH 313 TELEPHONESWITCH S*G*UUSW--***** (null) (null) (null)
384 687 SINGLE ROCKET LAUNCHER 484 SINGLEROCKETLAUNCHER S*G*EWS---***** (null) (null) (null)
385 690 SINGLE ROCKET LAUNCHER HEAVY 487 SINGLEROCKETLAUNCHERHEAVY S*G*EWSH--***** (null) (null) (null)
386 688 SINGLE ROCKET LAUNCHER LIGHT 485 SINGLEROCKETLAUNCHERLIGHT S*G*EWSL--***** (null) (null) (null)
387 689 SINGLE ROCKET LAUNCHER MEDIUM 486 SINGLEROCKETLAUNCHERMEDIUM S*G*EWSM--***** (null) (null) (null)
388 796 SOF AVIATION FIXED WING 707 SOFUNITFIXEDWING S*F*AF----***** (null) (null) (null)
389 797 SOF AVIATION FIXED WING ATTACK 708 SOFUNITATTACK S*F*AFA---***** (null) (null) (null)
390 798 SOF AVIATION FIXED WING UTILIT 710 SOFUNITUTILITY S*F*AFU---***** (null) (null) (null)
391 799 SOF AVIATION FIXED WING UTILIT 711 SOFUNITUTILITY(LIGHT) S*F*AFUL--***** (null) (null) (null)
392 800 SOF AVIATION ROTARY WING 715 SOFUNITROTARYWING S*F*AH----***** (null) (null) (null)
393 803 SOF UNIT GROUND 727 SOFUNITGROUND S*F*G-----***** (null) (null) (null)
394 805 SOF UNIT GROUND RANGER 729 SOFUNITRANGER S*F*GSR---***** (null) (null) (null)
395 804 SOF UNIT GROUND SPECIAL FORCES 728 SOFUNITSPECIALFORCES S*F*GS----***** (null) (null) (null)
396 801 SOF UNIT NAVAL 722 SOFUNITSOFUNITNAVAL S*F*SN----***** (null) (null) (null)
397 802 SOF UNIT NAVAL SPECIAL BOAT 725 SOFUNITSPECIALBOAT S*F*SNB---***** (null) (null) (null)
398 806 SOF UNIT PSYCHOLOGICAL OPERATI 730 SOFUNITPSYCHOLOGICALOPERATIONS(PSYOPS) S*F*GSP---***** (null) (null) (null)
399 658 SPECIAL C2 HEADQUATERS COMPONE 464 SPECIALC2HEADQUARTERSCOMPONENT S*G*UH----***** (null) (null) (null)
400 795 SPECIAL OPERATIONS FORCE (SOF) 705 SPECIALOPERATIONSFORCE(SOF)UNIT S*F*------***** (null) (null) (null)
401 625 SUPPLY 379 SUPPLY S*G*USS---***** (null) (null) (null)
402 633 SUPPLY CLASS I 382 SUPPLYCLASSI S*G*USS1--***** (null) (null) (null)
403 634 SUPPLY CLASS II 385 SUPPLYCLASSII S*G*USS2--***** (null) (null) (null)
404 635 SUPPLY CLASS III 388 SUPPLYCLASSIII S*G*USS3--***** (null) (null) (null)
405 636 SUPPLY CLASS III AVIATION 391 SUPPLYCLASSIIIAVIATION S*G*USS3A-***** (null) (null) (null)
406 637 SUPPLY CLASS IV 394 SUPPLYCLASSIV S*G*USS4--***** (null) (null) (null)
407 627 SUPPLY CLASS IX 409 SUPPLYCLASSIX S*G*USS9--***** (null) (null) (null)
408 638 SUPPLY CLASS V 397 SUPPLYCLASSV S*G*USS5--***** (null) (null) (null)
409 639 SUPPLY CLASS VI 400 SUPPLYCLASSVI S*G*USS6--***** (null) (null) (null)
410 640 SUPPLY CLASS VII 403 SUPPLYCLASSVII S*G*USS7--***** (null) (null) (null)
411 626 SUPPLY CLASS VIII 406 SUPPLYCLASSVIII S*G*USS8--***** (null) (null) (null)
412 628 SUPPLY CLASS X 412 SUPPLYCLASSX S*G*USSX--***** (null) (null) (null)
413 629 SUPPLY LAUNDRY/BATH 415 SUPPLYLAUNDRY/BATH S*G*USSL--***** (null) (null) (null)
414 632 SUPPLY MATERIEL (STOCK) 1335 SUPPLYMATERIEL(STOCK) S*G*US**--***** (null) (null) (null)
415 630 SUPPLY WATER 418 SUPPLYWATER S*G*USSW--***** (null) (null) (null)
416 631 SUPPLY WATER PURIFICATION 421 SUPPLYWATERPURIFICATION S*G*USSWP-***** (null) (null) (null)
417 668 SURFACE- SURFACE (SS) MISSILE 476 SURFACE-SURFACE(SS)MISSILELAUNCHER S*G*EWMS--***** (null) (null) (null)
418 355 SURFACE TO AIR MISSILE 65 SURFACETOAIRMISSILE(SAM) S*A*WMSA--***** (null) (null) (null)
419 354 SURFACE TO SURFACE MISSILE 64 SURFACETOSURFACEMISSILE(SSM) S*A*WMSS--***** (null) (null) (null)
420 391 SURVEILLANCE 1334 SURVEILLANCE S*G*F-S---***** (null) (null) (null)
421 725 TANK 533 TANK S*G*EVAT--***** 233 0Vehicle 725
422 725 TANK 533 TANK S*G*EVAT--***** 43 MBT 725
423 725 TANK 533 TANK S*G*EVAT--***** 230 LEOPARD 2A5 725
424 730 TANK HEAVY 538 TANKHEAVY S*G*EVATH-***** (null) (null) (null)
425 731 TANK HEAVY RECOVERY 539 TANKHEAVYRECOVERY S*G*EVATY-***** (null) (null) (null)
426 726 TANK LIGHT 534 TANKLIGHT S*G*EVATL-***** (null) (null) (null)
427 727 TANK LIGHT RECOVERY 535 TANKLIGHTRECOVERY S*G*EVATW-***** 110 BMP3 Recovery 727
428 728 TANK MEDIUM 536 TANKMEDIUM S*G*EVATM-***** (null) (null) (null)
429 729 TANK MEDIUM RECOVERY 537 TANKMEDIUMRECOVERY S*G*EVATX-***** (null) (null) (null)
430 773 TELECOMMUNICATIONS FACILITY 589 TELECOMMUNICATIONSFACILITY S*G*IUT---H**** (null) (null) (null)
431 603 TOPOGRAPHIC 318 TOPOGRAPHIC S*G*UUT---***** (null) (null) (null)
432 751 TRAILER MOUNTED 559 TRAILERMOUNTED S*G*EVEMT-***** (null) (null) (null)
433 754 TRAIN LOCOMOTIVE 564 TRAINLOCOMOTIVE S*G*EVST--***** (null) (null) (null)
434 645 TRANSPORTATION APOD/APOE 436 APOD/APOE S*G*USTA--***** (null) (null) (null)
435 647 TRANSPORTATION INTERMODAL 1328 TRANSPORTATIONINTERMODAL S*G*UST---***** (null) (null) (null)
436 646 TRANSPORTATION MISSILE 439 MISSILE S*G*USTI--***** (null) (null) (null)
437 642 TRANSPORTATION MOVEMENT CONTRO 427 MOVEMENTCONTROLCENTRE(MCC) S*G*USTM--***** (null) (null) (null)
438 643 TRANSPORTATION RAILHEAD 430 RAILHEAD S*G*USTR--***** (null) (null) (null)
439 644 TRANSPORTATION SPOD/SPOE 433 SPOD/SPOE S*G*USTS--***** (null) (null) (null)
440 783 TRANSPORT FACILITY 607 TRANSPORTFACILITY S*G*IT----H**** (null) (null) (null)
441 661 UNITS GENERAL LIAISON 467 UNITGENERALLIAISON S*G*UHGL--***** (null) (null) (null)
442 738 UTILITY VEHICLE 546 UTILITYVEHICLE S*G*EVU---***** 54 HMMWV Utility 738
443 738 UTILITY VEHICLE 546 UTILITYVEHICLE S*G*EVU---***** 221 Vehicle Tgt 738
444 743 WATER CRAFT 551 WATERCRAFT S*G*EVUR--***** 213 TATRA Tanker Water 743
445 353 WEAPON 61 WEAPON S*A*W-----***** (null) (null) (null)
+42
View File
@@ -0,0 +1,42 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# PlanModel
SET(GeometryModel_SRCS
geometry_model.dtd
geometry_model.cpp
geometry_model.h
geometry_model_export.h
geometry_model_factory.cpp
geometry_model_factory.h
geometry_model_q_dom.cpp
geometry_model_q_dom.h
geometry_node_visitor.cpp
geometry_node_visitor.h
geometry_pixelTextBox.cpp
)
SOURCE_GROUP(GeometryModel FILES ${GeometryModel_SRCS})
NGL_ADD_LIBRARY(WS situationMapGeometryModel SHARED ${GeometryModel_SRCS})
NGL_TARGET_INCLUDE_DIRECTORIES( situationMapGeometryModel
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/..
)
NGL_TARGET_LINK_LIBRARIES(situationMapGeometryModel
PRIVATE
NGLLogger
PUBLIC
situationMapCommon
Qt5::Core
Qt5::Gui
Qt5::Xml
)
NGL_INSTALL_TARGET(situationMapGeometryModel)
+38
View File
@@ -0,0 +1,38 @@
/**
**
** @RDE_HEADER@
** GeometryModel to write and read PlanEditor plans.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 11.11.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "geometry_model.h"
namespace SituationMap
{
GeometryModel::GeometryModel(QObject *parent) : QObject(parent) {;}
GeometryModel::GeometryModel(const GeometryModel &) : QObject() {;}
} // namespace SituationMap
+400
View File
@@ -0,0 +1,400 @@
<!--
DTD fuer Plan-Formate
(c)RDE 2009
Stephan Kuhagen
-->
<!--
The Top-Element of the Plan. Defines global variables in the Attributes.
-->
<!ELEMENT GeometryModelDocument (GeometryModel|MapSettings) >
<!ELEMENT GeometryModel (dictionarySymbol|ellipse|line|polyline|rectangle|spline|symbol|text|node)>
<!ELEMENT MapSettings EMPTY>
<!--
utm_zone: UTM-zone and band (two digits, one char)
utm_grid: UTM-grid-Square (two chars)
utm_east: UTM-easting-value (int)
utm_north: UTM-northing-value (int)
pe_show_utm: bool to indicate, if the PE should display the UTM-grid
pe_map_saturation: saturation in % of the map in the PE
pe_map_brightness: brightness in % of the map in the PE
pe_map_zoom: zoom-value of the map in the PE
pe_map_rotation: The rotation of the map (double)
pe_map_type: The type of the map (Raster, Vector, Contour, Elevation, RasterElevation)
pe_map_centerx: center-x of the map in the PE
pe_map_centery: center-y of the map in the PE
-->
<!ATTLIST MapSettings
utm_zone CDATA #REQUIRED
utm_grid CDATA #REQUIRED
utm_east CDATA #REQUIRED
utm_north CDATA #REQUIRED
pe_show_utm CDATA #REQUIRED
pe_map_saturation CDATA #REQUIRED
pe_map_brightness CDATA #REQUIRED
pe_map_zoom CDATA #REQUIRED
pe_map_rotation CDATA #REQUIRED
pe_map_type CDATA #REQUIRED
pe_map_centerx CDATA #REQUIRED
pe_map_centery CDATA #REQUIRED
pe_symbol_transparency CDATA #IMPLIED
pe_symbol_fill_type CDATA #IMPLIED
pe_symbol_scale CDATA #IMPLIED>
<!--
A node is a grouping element, which can hold any number of primitives or
other nodes, thus creating a tree.
-->
<!ELEMENT node (dictionarySymbol|ellipse|line|polyline|rectangle|spline|symbol|text|node)*>
<!--
The symbology library provides a symbol dictionary. Each symbol is defined by a list of coordinates and one non-ambiguous SIDC code. The SIDC
is a string with 15 character. See symbology library for further details.
-->
<!ELEMENT dictionarySymbol (point+)>
<!ATTLIST dictionarySymbol
GuiDisplayName CDATA #IMPLIED
SIDC CDATA #REQUIRED
SIDC_Norm CDATA #REQUIRED >
<!--
Defines ellipsis and circles. Can have start-angle and extent for sections of
circles/ellipsis. The point defines the center of the primitive. The
Attributes width and height define the metric dimensions, resulting in a
centered bounding box. The primitive can be rotated with attribute
rotate. Start-angle and extent are defined for the axis-parallel bounding-box
and are rotated with the box. Rotation is done around the center-point.
The optional text-tag is used for text directly assigned to this primitive.
-->
<!ELEMENT ellipse (point,text?)>
<!--
width: The metric width of the ellipse.
height: The metric height of the ellipse.
rotate: The bounding rectangle, that defines the ellipse is rotated according
to the angle here (360° for full rotation). Double-Value.
start: Sets the start-angle of an open ellipse before (!) rotation. Double-Value.
extent: Sets the extent of the ellipse. For a full ellipse this must be
360. Double-Value.
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
linewidth: Line-width. Double-Value.
linestyle: An enum defining the line-style
fill: A bool, if true, the primitive should be filled with the given color and pattern.
fill_color: Fill-Color in 32-Bit hex RGBA (#RRGGBBAA)
fill_pattern: An enum defining the fill-pattern.
fill_style: An enum defining the style for a filles ellipse:
- "chord": A straight line between the start-point and end-point
defines the inner region to be filled.
- "pieslice": A line from the start-point to the center and from the
center to the end-point define the inner region to be filled.
-->
<!ATTLIST ellipse
GuiDisplayName CDATA #IMPLIED
rotate CDATA #IMPLIED
width CDATA #REQUIRED
height CDATA #REQUIRED
start CDATA #IMPLIED
extent CDATA #IMPLIED
color CDATA #REQUIRED
linewidth CDATA #REQUIRED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED
fill CDATA #IMPLIED
fill_color CDATA #IMPLIED
fill_pattern (none|dots|hor1|hor2|ver1|ver2|diag1|diag2|solid) #IMPLIED
fill_style (chord|pieslice) #IMPLIED>
<!--
Defines a line with start- and endpoint.
The optional text-tag is used for text directly assigned to this primitive.
-->
<!ELEMENT line (point,point,text?)>
<!--
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
arrows: Enum defining, if and where to add arrows to the line.
arrow_height: The height of the arrow from the arrow-head to the outer points,
parallel to the middle axis of the arrow.
arrow_clamp_height: The height from the the arrow-neck to the outer points,
parallel to the middle axis of the arrow.
arrow_width: The distance between the outer points of the arrow.
arrow_line_width: The line width of the line used to draw the arrow.
linewidth: Line-width. Double-Value.
linestyle: An enum defining the line-style
-->
<!ATTLIST line
GuiDisplayName CDATA #IMPLIED
color CDATA #REQUIRED
arrows (none|start|end|both) #IMPLIED
arrow_height CDATA #IMPLIED
arrow_clamp_height CDATA #IMPLIED
arrow_width CDATA #IMPLIED
arrow_line_width CDATA #IMPLIED
linewidth CDATA #REQUIRED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED>
<!--
Defines a polyline with at least three points.
The optional text-tag is used for text directly assigned to this primitive.
-->
<!ELEMENT polyline (point,point,point+,text?)>
<!--
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
linewidth: Line-width. Double-Value.
linestyle: An enum defining the line-style
arrows: Enum defining, if and where to add arrows to the line. If the
primitive is closed, no arrows should be drawn.
arrow_height: The height of the arrow from the arrow-head to the outer points,
parallel to the middle axis of the arrow.
arrow_clamp_height: The height from the the arrow-neck to the outer points,
parallel to the middle axis of the arrow.
arrow_width: The distance between the outer points of the arrow.
arrow_line_width: The line width of the line used to draw the arrow.
close: A bool. If true, connect the start- and end-point.
fill: A bool, if true, the primitive should be filled with the given color and pattern.
fill_color: Fill-Color in 32-Bit hex RGBA (#RRGGBBAA)
fill_pattern: An enum defining the fill-pattern.
-->
<!ATTLIST polyline
GuiDisplayName CDATA #IMPLIED
color CDATA #REQUIRED
linewidth CDATA #REQUIRED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED
arrows (none|start|end|both) #IMPLIED
arrow_height CDATA #IMPLIED
arrow_clamp_height CDATA #IMPLIED
arrow_width CDATA #IMPLIED
arrow_line_width CDATA #IMPLIED
close CDATA #REQUIRED
fill CDATA #IMPLIED
fill_color CDATA #IMPLIED
fill_pattern (none|dots|hor1|hor2|ver1|ver2|diag1|diag2|solid) #IMPLIED>
<!--
Defines a rectangle with a center-point and width/height.
The optional text-tag is used for text directly assigned to this primitive.
-->
<!ELEMENT rectangle (point,text?)>
<!--
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
width: The metric width of the rectangle.
height: The metric height of the rectangle.
rotate: The rectangle is rotated according to the angle here (360° for full
rotation). Double-Value.
linewidth: Line-width. Double-Value.
linestyle: An enum defining the line-style
rounding: A percentage-value (double) for round edges
fill: A bool, if true, the primitive should be filled with the given color and pattern.
fill_color: Fill-Color in 32-Bit hex RGBA (#RRGGBBAA)
fill_pattern: An enum defining the fill-pattern.
-->
<!ATTLIST rectangle
GuiDisplayName CDATA #IMPLIED
color CDATA #REQUIRED
width CDATA #REQUIRED
height CDATA #REQUIRED
rotate CDATA #IMPLIED
linewidth CDATA #REQUIRED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED
rounding CDATA #IMPLIED
fill CDATA #IMPLIED
fill_color CDATA #IMPLIED
fill_pattern (none|dots|hor1|hor2|ver1|ver2|diag1|diag2|solid) #IMPLIED>
<!--
Defines a spline with at least three points. The first and the last point
define the endpoints, all other points define the curve between them.
The optional text-tag is used for text directly assigned to this primitive.
-->
<!ELEMENT spline (point,point,point+,text?)>
<!--
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
linewidth: Line-width. Double-Value.
linestyle: An enum defining the line-style
arrows: Enum defining, if and where to add arrows to the line. If the
primitive is closed, no arrows should be drawn.
arrow_height: The height of the arrow from the arrow-head to the outer points,
parallel to the middle axis of the arrow.
arrow_clamp_height: The height from the the arrow-neck to the outer points,
parallel to the middle axis of the arrow.
arrow_width: The distance between the outer points of the arrow.
arrow_line_width: The line width of the line used to draw the arrow.
close: A bool. If true, connect the start- and end-point.
fill: A bool. If true, the primitive should be filled with the given color and
pattern. This also means, that the start-point is automatically
connected to the end-point to get a closed shape to fill.
fill_color: Fill-Color in 32-Bit hex RGBA (#RRGGBBAA)
fill_pattern: An enum defining the fill-pattern.
-->
<!ATTLIST spline
GuiDisplayName CDATA #IMPLIED
color CDATA #REQUIRED
linewidth CDATA #REQUIRED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED
arrows (none|start|end|both) #IMPLIED
arrow_height CDATA #IMPLIED
arrow_clamp_height CDATA #IMPLIED
arrow_width CDATA #IMPLIED
arrow_line_width CDATA #IMPLIED
close CDATA #REQUIRED
fill CDATA #IMPLIED
fill_color CDATA #IMPLIED
fill_pattern (none|dots|hor1|hor2|ver1|ver2|diag1|diag2|solid) #IMPLIED>
<!--
A symbol to display in the plan.
position, offsetx (in Pixel), offsety (in Pixel), direction of movement
-->
<!ELEMENT symbol (point)>
<!--
text_w: Text for symbols text-field w.
text_xy: Text for symbols text-field xy.
text_v: Text for symbols text-field v.
text_t: Text for symbols text-field t.
text_z: Text for symbols text-field z.
text_ef: Text for symbols text-field ef.
text_g: Text for symbols text-field g.
text_h: Text for symbols text-field h.
text_m: Text for symbols text-field m.
text_jklnp: Text for symbols text-field jklnp.
symbol_id: ID of the symbol (int).
force_id: Force-ID of the symbol (int).
abc_protection: Flag for abc_protection (bool).
affil: Enum for affiliation of the symbol.
mobility: Enum for the mobility of the symbol.
w_vbg_et: Enum for tactical state of the symbol. Corresponds to enum w_vbg_et
in zskssmal_teilnehmer.h.
rotate: The Symbol is rotated according
to the angle here (360° for full rotation). Double-Value.
-->
<!ATTLIST symbol
GuiDisplayName CDATA #IMPLIED
text_w CDATA #IMPLIED
text_xy CDATA #IMPLIED
text_v CDATA #IMPLIED
text_t CDATA #IMPLIED
text_z CDATA #IMPLIED
text_ef CDATA #IMPLIED
text_g CDATA #IMPLIED
text_h CDATA #IMPLIED
text_m CDATA #IMPLIED
text_jklnp CDATA #IMPLIED
symbol_id CDATA #IMPLIED
symbol_offsetX CDATA #IMPLIED
symbol_offsetY CDATA #IMPLIED
symbol_direction CDATA #IMPLIED
force_id CDATA #IMPLIED
symbol_state CDATA #IMPLIED
symbol_uncertainty CDATA #IMPLIED
damage_force_id CDATA #IMPLIED
abc_protection CDATA #IMPLIED
affil (unknown|friend|neutral|enemy) #REQUIRED
mobility (wheel|wheel_all_terrain|chain|chain_all_terrain|trailer|train) #REQUIRED
w_vbg_et (einsatzbereit|treffer_ohne_wirkung|annaehender_treffer|zeitlicher_ausfall|leichter_treffer|mittlerer_treffer|schwerer_treffer|bewegungsunfaehig|bewegungsunfaehig_sichtb|ausfall_waffenanlage|ausfall_waffenanlage_sichtb|totalausfall) #REQUIRED
rotate CDATA #IMPLIED>
<!--
A one-line or multiline-text with optional background and frame.
If used as a child to another primitive, the point position of the text is
ignored. The alignment attributes define the text-position in relation to the
primitive instead.
-->
<!ELEMENT text (point,text_data)>
<!--
anchor_horizontal: Horizontal position of the text on its point
anchor_vertical: Vertical position of the text on its point
margin_horizontal: Horizontal margin in Pixels between the texts bounding box and the surrounding frame
margin_vertical: Vertical margin in Pixels between the texts bounding box and the surrounding frame
color: Color in 32-Bit hex RGBA (#RRGGBBAA)
font: Font-Type
font_size: Font-Size. Int.
font_italic: A bool. If true, the font should be italic.
font_bold: A bool. If true, the font should be bold.
box: A bool, if true, the primitive should be filled with the given color and pattern.
linewidth: Line-width for the box. Double-Value.
linestyle: An enum defining the line-style for the box.
linecolor: Color in 32-Bit hex RGBA (#RRGGBBAA) for the outline of the box.
fill: A bool, if true, the box should be filled with the given color and pattern.
fill_color: Fill-Color for the box in 32-Bit hex RGBA (#RRGGBBAA)
fill_pattern: An enum defining the fill-pattern for the box.
rotate: The bounding rectangle, that defines the ellipse is rotated according
to the angle here (360° for full rotation). Double-Value.
-->
<!ATTLIST text
GuiDisplayName CDATA #IMPLIED
anchor_horizontal (left|center|right) #REQUIRED
anchor_vertical (top|center|bottom) #REQUIRED
margin_horizontal CDATA #IMPLIED
margin_vertical CDATA #IMPLIED
color CDATA #REQUIRED
font (SansSerif|Serif|Fixed|Decorative) #REQUIRED
font_size CDATA #REQUIRED
font_italic CDATA #IMPLIED
font_bold CDATA #IMPLIED
box CDATA #IMPLIED
linewidth CDATA #IMPLIED
linestyle (solid|dashed|dotted|dashed_dotted) #IMPLIED
linecolor CDATA #IMPLIED
fill CDATA #IMPLIED
fill_color CDATA #IMPLIED
fill_pattern (none|dots|hor1|hor2|ver1|ver2|diag1|diag2|solid) #IMPLIED
rotate CDATA #IMPLIED>
<!--
A tag that contains the actual text-data of a text-tag.
-->
<!ELEMENT text_data (#PCDATA)>
<!--
alignment: If this text contains more than one line, this defines the
alignment of the text in the bounding box of all lines.
-->
<!ATTLIST text_data
alignment (left|center|right) #REQUIRED>
<!--
Defines a point with a double precision x and y-value.
-->
<!ELEMENT point EMPTY>
<!--
x: The x-coordinate of the point.
y: The y-coordinate of the point.
-->
<!ATTLIST point
x CDATA #REQUIRED
y CDATA #REQUIRED>
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,21 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <qglobal.h>
#ifndef GEOMETRYMODEL_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define GEOMETRYMODEL_EXPORT
# else
# if defined(MAKE_SITUATIONMAPGEOMETRYMODEL_LIB)
# define GEOMETRYMODEL_EXPORT Q_DECL_EXPORT
# else
# define GEOMETRYMODEL_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
@@ -0,0 +1,58 @@
/**
**
** @RDE_HEADER@
** GeometryModelFactory to create geometry model
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 20.11.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "geometry_model_factory.h"
#include "geometry_model_q_dom.h"
namespace SituationMap
{
GeometryModelFactory *GeometryModelFactory::singleton()
{
static GeometryModelFactory s_factory;
return &s_factory;
} /* GeometryModelFactory::singleton() */
GeometryModel *GeometryModelFactory::create()
{
return new GeometryModelQDom();
} // GeometryModelFactory::create()
GeometryModel *GeometryModelFactory::clone(GeometryModel *geoModel)
{
GeometryModelQDom * qdomModel = dynamic_cast<GeometryModelQDom*>(geoModel);
if (qdomModel)
return new GeometryModelQDom(*qdomModel);
return nullptr;
} // GeometryModelFactory::clone()
} /* namespace SituationMap */
@@ -0,0 +1,59 @@
/**
**
** @RDE_HEADER@
** GeometryModelFactory to create geometry model
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 20.11.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include "geometry_model_q_dom.h"
namespace SituationMap
{
/**
@brief Factory class to create GeometryModels
@see GeometryModel GeometryModelQDom
*/
class GEOMETRYMODEL_EXPORT GeometryModelFactory
{
public:
static GeometryModelFactory *singleton();
GeometryModelFactory() = default;
~GeometryModelFactory() = default;
GeometryModelFactory(const GeometryModelFactory &) = delete;
GeometryModelFactory operator =(const GeometryModelFactory &) = delete;
GeometryModel *create();
GeometryModel *clone( GeometryModel * );
}; /* GeometryModelFactory */
} /* namespace SituationMap */
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,374 @@
/**
**
** @RDE_HEADER@
** GeometryModel implementation to write and read PlanEditor plans from/to XML and manipulate DOM-tree.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Stephan Kuhagen <kuhagen@rheinmetall-de.com>
** Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** ??.??.200?, SK : First edition for use with Qt4
** 11.11.2015, JT : Splitting interface and implementation
**
** -----------------------------------------------------------------------------------------------
*/
// clazy:excludeall=const-signal-or-slot
#pragma once
#include <QtGui/QColor>
#include <QtXml/QDomDocument>
#include <QtCore/QMultiMap>
#include <QtCore/QObject>
#include <QtCore/QPair>
#include <QtCore/QPointF>
#include <QtCore/QRectF>
#include <QtCore/QStack>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QVariant>
#include "geometry_model.h"
class QTransform;
namespace SituationMap
{
// exported for unit test only
class GEOMETRYMODEL_EXPORT GeometryModelQDom : public GeometryModel, public QDomDocument
{
Q_OBJECT
public:
explicit GeometryModelQDom(QObject *parent = nullptr);
//! Copy-Constructor
GeometryModelQDom(const GeometryModelQDom &other);
~GeometryModelQDom();
GeometryModelQDom & operator = (const GeometryModelQDom &other);
QByteArray toByteArray() const override;
QByteArray toByteArrayDebug() const override;
bool fromByteArray(const QByteArray &content) override;
bool fromDevice(QIODevice *device) override;
bool fromByteArrayDebug(const QByteArray &content) override;
bool isEmpty() const override;
Node getRootNode() const override;
Node getMapDefaultSettingsNode() const override;
void setModelAttribute(GeometryModel::Attribute attribute, const QVariant &value) override;
void setModelEditorAttribute(GeometryModel::Attribute attribute, const QVariant &value) override;
void setPrimitiveAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute, const QVariant &value) override;
void movePrimitiveIntoGroup(const GeometryModel::Node &primitive, const GeometryModel::Node &target) override;
void releaseGroup(const GeometryModel::Node &group) override;
void deleteGroup(const GeometryModel::Node &group) override;
void removePrimitive(const GeometryModel::Node &primitive) override;
void removePrimitivesByUserData(const QList<int> &userDataList) override;
void removeText(GeometryModel::Node &parent) override;
void setTextData(const GeometryModel::Node &textNode, const QString &data, GeometryModel::HorizontalAlign textAlign) override;
void clearModel() override;
void translateNode(const GeometryModel::Node &node, double x, double y) override;
void moveNode(const GeometryModel::Node &node, double x, double y) override;
void scaleNode(const GeometryModel::Node &node, double scaleX, double scaleY) override;
void rotateNode(const GeometryModel::Node &node, double angle) override;
void reorderPrimitives(const QList<GeometryModel::Node> &nodes, GeometryModel::PrimitivOrder order) override;
void merge(const GeometryModel::Node &other, bool skipUndo = false, bool intoNewGroup = false) override;
void merge(GeometryModel *other, bool skipUndo = false, bool intoNewGroup = false) override;
unsigned int modificationCounter() const override;
void resetModificationCounter() override;
void undo(unsigned int steps = 1) override;
void redo(unsigned int steps = 1) override;
void enableUndo(bool enable) override;
void startUndoBlock() override;
void endUndoBlock(const QString &eventDescription = QString()) override;
void abortUndoBlock() override;
void setMaxUndoSteps(unsigned int max) override;
unsigned int maxUndoSteps() const override;
void cleanModel() override;
GeometryModel::Node addPrimitive(GeometryModel::PrimitiveType type
, const QList<QPointF> &points
, const QList<QPair<GeometryModel::Attribute, QVariant> > &attributes
, int userData = -1
, const QString &textData = QString()
, GeometryModel::HorizontalAlign textAlign = HorizontalAlign::Left) override;
bool setPrimitivePoints(const GeometryModel::Node &node, const QList<QPointF> &points) override;
GeometryModel::Node addText(const GeometryModel::Node &parent
, const QString &textData
, GeometryModel::HorizontalAlign textAlign
, const QList<QPair<GeometryModel::Attribute, QVariant> > &attributes
, int userData) override;
GeometryModel::Node getText(const GeometryModel::Node &parent) const override;
QString getTextData(const GeometryModel::Node &textNode) const override;
QList<QPointF> getPrimitivePoints(const GeometryModel::Node &node) const override;
QRectF getBoundingBox(const GeometryModel::Node &node) const override;
void setNodeDisplayName(const Node &node, const QString &name) override;
QString getNodeDisplayName(const Node &node) const override;
QVariant getModelAttribute(Attribute attribute) const override;
QVariant getAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute) const override;
template <class T> T getAttribute(const Node &node, Attribute attribute) const;
bool hasAttribute(const GeometryModel::Node &node, GeometryModel::Attribute attribute) const override;
QVariant::Type attributeType(const GeometryModel::Attribute &attribute) const override;
QList<GeometryModel::Attribute> supportedAttributes(GeometryModel::PrimitiveType type) const override;
GeometryModel::Node createGroup(const QList<GeometryModel::Node> &primitives) override;
GeometryModel::Node isInGroup(const GeometryModel::Node &primitive) const override;
GeometryModel::NodeList getGroupPrimitives(const GeometryModel::Node &node) override;
GeometryModel::Node findPrimitive(int userData) const override;
bool hasUserData(const Node &node) const override;
int getUserData(const GeometryModel::Node &node, bool *ok = nullptr) const override;
void setUserData(const GeometryModel::Node &node, int userData) override;
QMultiMap<int, GeometryModel::Node> userDataMap(const GeometryModel::Node &node = GeometryModel::Node()) override;
GeometryModel::Node copy(const GeometryModel::Node &node, int userData = 0) override;
GeometryModel::PrimitiveType getType(const GeometryModel::Node &node) const override;
unsigned int undoSteps() const override;
unsigned int redoSteps() const override;
QStringList undoHistory() const override { return _undoHistory.toList(); }
QStringList redoHistory() const override { return _redoHistory.toList(); }
void clearUndoStack() override
{
_undoStack.clear();
_undoHistory.clear();
} // clearUndoStack
void clearRedoStack() override
{
_redoStack.clear();
_redoHistory.clear();
} // clearRedoStack
QString attributeToString(const GeometryModel::Attribute &attribute) const override;
bool parseSymbolAttributeString(const QString &sidcAttributes, QMap<QString, QVariant> &attrMap) const override;
QString buildSymbolAttributeString(const QMap<QString, QVariant> &attrMap) const override;
static void cleanupStaticMember();
private:
static void valueFromVariant(const QVariant &v, QPointF &result);
static void valueFromVariant(const QVariant &v, QColor &result);
/**
convert to QString (with non locale-aware conversions)
*/
static QString variant2XmlString(const QVariant &v);
/**
Create a QColor from an attribute-value with alpha-channel (QVariant
ignores Alpha)
\param attColor: The QString-QVariant from an attribute of Type QColor
\return QColor with alpha-channel or invalid QColor, if the value could be parsed.
*/
static QColor colorFromString(const QString &attColor);
/**
Create a QPoint from an attribute-value
\param attPoint: The QString-QVariant from an attribute of Type QPointF
\return QPointF
*/
static QPointF pointFromString(const QString &attPoint);
//! A mapping between all primitives and the attributes they support
static QMultiMap<GeometryModel::PrimitiveType, GeometryModel::Attribute> _primitiveSupportedAttributes;
//! Mappings between all primitive-types and their string/tag-name
static QMap<QString, GeometryModel::PrimitiveType> _primitiveTagsTypes;
static QMap<GeometryModel::PrimitiveType, QString> _primitiveTagsStrings;
//! Default-Values for all supported attributes
static QMap<QPair<GeometryModel::PrimitiveType, GeometryModel::Attribute>, QString> _attributeDefaults;
//! Mapping between Attributes and their QVariant-Type.
static QMap<GeometryModel::Attribute, QVariant::Type> _attributeTypes;
//! Bool to check, if static class variables have been initialized
static bool _staticInitDone;
/**
Init data of the object
*/
void init();
/**
Transform the points of a primitive.
\sa {translateNode(), moveNode(), rotateNode(), scaleNode()}
\param node: The primitive to transform.
\param t: The transformation-matrix.
*/
void transformNode(const QDomNode &node, const QTransform &t);
GeometryModel::PrimitiveType getType(const QDomNode &node) const;
bool hasUserData(const QDomNode &node) const;
int getUserData(const QDomNode &node, bool *okPtr) const;
QRectF getBoundingBox(const QDomNode &node) const;
QList<QPointF> getPrimitivePoints(const QDomNode &node) const;
bool hasAttribute(const QDomNode &node, GeometryModel::Attribute attribute) const;
QVariant getAttribute(const QDomNode &node, GeometryModel::Attribute attribute) const;
void removePrimitive(const QDomNode &primitive);
void translateNode(const QDomNode &node, double x, double y);
void moveNode(const QDomNode &node, double x, double y);
void scaleNode(const QDomNode &node, double scaleX, double scaleY);
void rotateNode(const QDomNode &node, double angle);
void merge(const QDomNode &other, bool skipUndo = false, bool intoNewGroup = false);
QDomNode createGroup(const QList<QDomNode> &primitives);
QMultiMap<int, QDomNode> userDataMap(const QDomNode &node) const;
/**
Wrapper for signal changed(), checks _skipChangedSignal
!!! ATTENTION: Do not use the signal changed directly.
Use emitChangedSignal() instead !!!
\param what: Short description of the change for logging
\param node: the changed node (if this is a Null-Node, the Root-Node of
the plan has changed)
\param att: The attribute, that changed. If att==MaxAttributes, then
multiple attributes are changed
\param coordsChanged: if true, the coordinated of the primitive are changed
*/
void emitChangedSignal(const Node &node, Attribute att, bool coordsChanged);
//! Lock for temporarily blocking changed()-Signals
bool _skipChangedSignal;
//! Undo-Stack
bool _undoEnabled;
unsigned int _maxUndoSteps;
QStack<QDomNode> _undoStack;
QStack<QString> _undoHistory;
QStack<QDomNode> _redoStack;
QStack<QString> _redoHistory;
//! Undo-blocks
QDomNode _undoBlock;
QString _undoHistoryBlock;
bool _insideUndoBlock;
unsigned int _modificationCounter;
//! Push/pop current state to undo-stack, if not locked
void pushUndo(const QString &eventDescription = QString());
GeometryModel::Node popUndo();
}; // GeometryModelQDom
template <class T> T GeometryModelQDom::getAttribute(const Node &node, Attribute attribute) const
{
const QVariant &v = getAttribute(node, attribute);
switch (qMetaTypeId<T>())
{
case QMetaType::QPointF:
case QMetaType::QColor:
{
T result; valueFromVariant(v, result); return result;
} break;
default: break;
}
return v.value<T>();
} // template <class T> T GeometryModelQDom::getAttribute( const Node & node, Attribute attribute ) const
} // namespace SituationMap
@@ -0,0 +1,75 @@
/**
**
** @RDE_HEADER@
** Node visitor for GeometryModel.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 07.07.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#include "geometry_node_visitor.h"
namespace SituationMap
{
GeometryNodeVisitor::GeometryNodeVisitor(GeometryModel *geoModel) : m_geometryModel(geoModel) {;}
bool GeometryNodeVisitor::traverse()
{
if (!m_geometryModel)
return true;
return traverse(m_geometryModel->getRootNode());
} // bool GeometryNodeVisitor::traverse()
bool GeometryNodeVisitor::traverse(GeometryModel::Node node)
{
if (!m_geometryModel)
return true;
if (node.isValid())
{
const GeometryModel::PrimitiveType primitiveType = m_geometryModel->getType(node);
if ((GeometryModel::PrimitiveType::RootNode == primitiveType) || (GeometryModel::PrimitiveType::Node == primitiveType))
{
if (node.hasChildNodes())
{
node = node.firstChildNode();
while (node.isValid())
{
if (!traverse(node))
return false;
node = node.next();
}
}
}
if (!apply(m_geometryModel, node, primitiveType))
return false;
} // if
return true;
}
} // namespace SituationMap
@@ -0,0 +1,75 @@
/**
**
** @RDE_HEADER@
** Node visitor for GeometryModel.
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 07.07.2016, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include "geometry_model.h"
namespace SituationMap
{
/**
GeometryNopdeVisitor implements visitor pattern to traverse geoemtry model tree. The apply function will be
called for each node in geometry model.
*/
class GEOMETRYMODEL_EXPORT GeometryNodeVisitor
{
public:
explicit GeometryNodeVisitor(GeometryModel *geoModel);
virtual ~GeometryNodeVisitor() = default;
GeometryNodeVisitor(const GeometryNodeVisitor &) = delete;
/**
Traversing the complete geometry model
*/
bool traverse();
/**
Traversing the sub-tree of node. Coming in deepth first.
*/
bool traverse(GeometryModel::Node node);
protected:
/**
For each node the apply function will be called. If apply returns false the traversal is aborted. In
other case if return value is true the traversel is continued.
@param geoModel Pointer to geometry model
@param node Node of geometry model
@param nodeType Type of node
@return false to abort traversael otherwise true
*/
virtual bool apply(GeometryModel *geoModel, const GeometryModel::Node &node, GeometryModel::PrimitiveType nodeType) = 0;
GeometryModel *m_geometryModel;
}; // GeometryNodeVisitor
} // namespace SituationMap
@@ -0,0 +1,215 @@
/**
* @RDE_HEADER@
* PixelTextBoxData as Addon to GeometryModel
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLSE, 2023-04-27
*/
#include "geometry_pixelTextBox.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonArray>
#include <QDebug>
namespace SituationMap
{
PixelTextBoxData::PixelTextBoxData(const QString &htmlText, const QColor &backgroundColor)
: m_htmlText(htmlText), m_backgroundColor(backgroundColor)
{
}
void PixelTextBoxData::reset()
{
m_htmlText.clear();
m_backgroundColor = QColor();
m_position = QPoint();
m_size = QSize();
}
bool PixelTextBoxData::isEmpty() const
{
return m_htmlText.isEmpty();
}
QString PixelTextBoxData::toDebugString() const
{
QJsonDocument jDoc(toJson());
return QString::fromStdString(jDoc.toJson(QJsonDocument::JsonFormat::Compact).toStdString());
}
bool PixelTextBoxData::operator==(const PixelTextBoxData &o) const
{
return m_htmlText == o.m_htmlText &&
m_backgroundColor == o.m_backgroundColor &&
m_position == o.m_position &&
m_size == o.m_size;
}
QJsonObject PixelTextBoxData::toJson() const
{
QJsonObject obj;
obj[QStringLiteral("text")] = m_htmlText;
obj[QStringLiteral("backgroundColor")] = m_backgroundColor.name();
obj[QStringLiteral("position")] = QJsonObject({{QStringLiteral("x"), m_position.x()},
{QStringLiteral("y"), m_position.y()},
});
obj[QStringLiteral("size")] = QJsonObject({{QStringLiteral("width"), m_size.width()},
{QStringLiteral("height"), m_size.height()},
});
return obj;
}
bool get(const QJsonObject &obj, const QString &key, int &val)
{
const auto jVal = obj[key];
if (!jVal.isDouble())
{
qCritical() << " isDouble failed";
}
val = jVal.toInt();
return true;
}
PixelTextBoxData PixelTextBoxData::fromJson(const QJsonValue &json)
{
if (json.isUndefined())
return {};
if (!json.isObject())
return {};
PixelTextBoxData ret;
// text
ret.m_htmlText = json[QStringLiteral("text")].toString();
// backgroundColor
QString temp = json[QStringLiteral("backgroundColor")].toString();
ret.m_backgroundColor.setNamedColor(temp);
// position:
{
const auto jVal = json[QStringLiteral("position")];
get(jVal.toObject(), QStringLiteral("x"), ret.m_position.rx());
get(jVal.toObject(), QStringLiteral("y"), ret.m_position.ry());
}
// size:
{
const auto jVal = json[QStringLiteral("size")];
get(jVal.toObject(), QStringLiteral("width"), ret.m_size.rwidth());
get(jVal.toObject(), QStringLiteral("height"), ret.m_size.rheight());
}
return ret;
}
QString PixelTextBoxData::toJsonStr(QJsonDocument::JsonFormat format) const
{
QJsonObject obj = toJson();
QJsonDocument jDoc(obj);
return QString::fromStdString(jDoc.toJson(format).toStdString());
}
PixelTextBoxData PixelTextBoxData::fromJsonStr(const QString &jsonStr)
{
if (jsonStr.isEmpty())
return {};
QJsonParseError parseError;
QJsonDocument jDoc(QJsonDocument::fromJson(jsonStr.toStdString().c_str(), &parseError));
if (parseError.error != QJsonParseError::NoError)
{
qCritical() << " QJsonParseError->errorString: '" << parseError.errorString() << "'"
<< ", offset: " << parseError.offset;
return {};
}
QJsonObject jObj = jDoc.object();
if (jObj.isEmpty())
return {};
return PixelTextBoxData::fromJson(jObj);
}
//
// Container
//
QJsonObject PixelTextBoxData::pixelTextBoxContainerToJson(const QVector<PixelTextBoxData> &ptbdCont)
{
QJsonArray arr;
for (const PixelTextBoxData &item : qAsConst(ptbdCont))
arr.push_back(item.toJson());
QJsonObject jObj;
jObj[QStringLiteral("PixelTextBoxData")] = arr;
return jObj;
}
QVector<PixelTextBoxData> PixelTextBoxData::pixelTextBoxContainerFromJson(const QJsonValue &json)
{
QVector<PixelTextBoxData> ret;
if (!json.isObject())
return ret;
const QJsonObject jObj = json.toObject();
if (!jObj.contains(QStringLiteral("PixelTextBoxData")))
return ret;
const QJsonArray &array = jObj[QStringLiteral("PixelTextBoxData")].toArray();
ret.reserve(array.size());
for(int i = 0; i < array.size(); ++i)
ret.push_back(PixelTextBoxData::fromJson(array.at(i)));
return ret;
}
QString PixelTextBoxData::pixelTextBoxContainerToJsonStr(const QVector<PixelTextBoxData> &ptbdCont, QJsonDocument::JsonFormat format)
{
QJsonObject obj = pixelTextBoxContainerToJson(ptbdCont);
QJsonDocument jDoc(obj);
return QString::fromStdString(jDoc.toJson(format).toStdString());
}
QVector<PixelTextBoxData> PixelTextBoxData::pixelTextBoxContainerFromJsonStr(const QString &jsonStr)
{
if (jsonStr.isEmpty())
return {};
QJsonParseError parseError;
QJsonDocument jDoc(QJsonDocument::fromJson(jsonStr.toStdString().c_str(), &parseError));
if (parseError.error != QJsonParseError::NoError)
{
qCritical() << " QJsonParseError->errorString: '" << parseError.errorString() << "'"
<< ", offset: " << parseError.offset;
return {};
}
QJsonObject jObj = jDoc.object();
if (jObj.isEmpty())
return {};
return PixelTextBoxData::pixelTextBoxContainerFromJson(jObj);
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
GEOMETRYMODEL_EXPORT std::ostream &operator<<(std::ostream &stream, const PixelTextBoxData &value)
{
stream << value.toDebugString().toStdString().c_str();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* PixelTextBoxData as Addon to GeometryModel
* $Id$
* $HeadURL$
* $Author$
* Created by Norbert Thoden, SLSE, 2023-04-27
*/
#pragma once
#include <QString>
#include <QColor>
#include <QPoint>
#include <QSize>
#include <QJsonObject>
#include <QJsonDocument>
#include "geometry_model_export.h"
namespace SituationMap
{
class GEOMETRYMODEL_EXPORT PixelTextBoxData
{
public:
PixelTextBoxData() = default;
explicit PixelTextBoxData(const QString &htmlText, const QColor &backgroundColor);
void reset();
bool isEmpty() const;
QString toDebugString() const;
QString m_htmlText;
QColor m_backgroundColor;
QPoint m_position;
QSize m_size;
// operator
bool operator==(const PixelTextBoxData &o) const;
// Json:
QJsonObject toJson() const;
static PixelTextBoxData fromJson(const QJsonValue &json);
QString toJsonStr(QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented) const;
static PixelTextBoxData fromJsonStr(const QString &jsonStr);
static QJsonObject pixelTextBoxContainerToJson(const QVector<PixelTextBoxData> &ptbdCont);
static QVector<PixelTextBoxData> pixelTextBoxContainerFromJson(const QJsonValue &json);
static QString pixelTextBoxContainerToJsonStr(const QVector<PixelTextBoxData> &ptbdCont, QJsonDocument::JsonFormat format = QJsonDocument::JsonFormat::Indented);
static QVector<PixelTextBoxData> pixelTextBoxContainerFromJsonStr(const QString &jsonStr);
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
GEOMETRYMODEL_EXPORT std::ostream &operator<< (std::ostream &stream,
const PixelTextBoxData &value);
} // namespace SituationMap
@@ -0,0 +1,556 @@
/**
* @RDE_HEADER@
* AbstractGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "abstract_graphical_object.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GeoView.h>
#include <arcgis/Graphic.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Multipart.h>
// Qt
#include <QDebug>
#include <QSharedPointer>
#include <QApplication>
#include <QThread>
#include <QObject>
namespace SituationMap
{
AbstractGraphicalObject::AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: m_geoView(geoView)
, m_graphicsLayer(graphicsLayer)
, m_uniqueMapId(uniqueMapId)
, m_debugInfo(debugInfo)
, m_logger(getLogger("AbstractGraphicalObject"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_pickable = (m_uniqueMapId != INVALID_MAP_ID);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
AbstractGraphicalObject::~AbstractGraphicalObject()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", m_graphicsLayer-overlayId=" << (m_graphicsLayer ? m_graphicsLayer->overlayId() : QString(QStringLiteral("unknown")));
#if ACRGIS_VERSION >= 0x640F00 // 100.15.0
// removeOne ist helpful to let ModuleTest succeed: 'situationMapModuleTest checkOverlayHandler3 none'
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
m_graphicsLayer->graphics()->removeOne(m_graphic);
delete m_graphic;
m_graphic = nullptr;
#else
/*
// cleanup
// to make sure, the the graphic is removed from internal container BEFORE
// graphic is deleted, follow the documentation: use Layer::removeGraphic
// (instead 'delete m_graphic')
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
m_graphicsLayer->graphics()->removeOne(m_graphic);
if (m_graphic)
{
delete m_graphic;
m_graphic = nullptr;
}
*/
// Race condition between removeOne() and delete m_graphic :
// BUG-000116867 is for the deadlock
// BUG-000117464 is for the crash
// Use following workaround until ESRI will release the fix (with 100.5!?)
if (m_graphic && m_graphicsLayer && m_graphicsLayer->graphics())
{
const int idx = m_graphicsLayer->graphics()->indexOf(m_graphic);
if (idx < 0)
ERROR_LOG(m_logger) << LOGFUNCTION << " indexOf failed, debugInfo='" << m_debugInfo << "'";
auto graphicCopy = m_graphic;
QObject::connect (m_graphicsLayer->graphics(), &Esri::ArcGISRuntime::GraphicListModel::graphicRemoved,
m_graphic, [idx, graphicCopy](int index)
{
if (idx == index)
delete graphicCopy;
});
m_graphicsLayer->graphics()->removeOne(m_graphic);
}
// : Race condition between removeOne() and delete m_graphic
// m_esriSimpleLineSymbol AND m_esriSimpleFillSymbol will be destroyed m_graphic
#endif // ACRGIS_VERSION >= 0x640F00
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
Esri::ArcGISRuntime::Graphic *AbstractGraphicalObject::graphic()
{
return m_graphic;
}
Esri::ArcGISRuntime::GraphicsOverlay *AbstractGraphicalObject::graphicsLayer()
{
return m_graphicsLayer;
}
Esri::ArcGISRuntime::Geometry AbstractGraphicalObject::getGraphicGeometry() const
{
if (! m_graphic)
return Esri::ArcGISRuntime::Geometry();
return m_graphic->geometry();
}
bool AbstractGraphicalObject::updateView()
{
if (!changed())
return true;
bool success = updateViewImpl();
resetChanged();
return success;
}
QString AbstractGraphicalObject::debugInfo() const
{
return m_debugInfo;
}
void AbstractGraphicalObject::setDebugInfo(const QString &debugInfo)
{
m_debugInfo = debugInfo;
}
int AbstractGraphicalObject::drawOrder() const
{
return m_drawOrder.value();
}
void AbstractGraphicalObject::setDrawOrder(int drawOrder)
{
m_drawOrder = drawOrder;
// if (m_graphic)
// m_graphic->setDrawOrder(m_drawOrder.value());
}
QString AbstractGraphicalObject::toString() const
{
QString str;
if (!m_debugInfo.isEmpty())
str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo));
if (m_graphicsLayer)
str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName()));
str.append(QStringLiteral(" m_drawOrder='%1'").arg(m_drawOrder.value()));
if (m_graphic)
str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex()));
str.append(QStringLiteral(" visibility='%1'").arg(m_visibility.value()));
str.append(QStringLiteral(" selected='%1'").arg(m_selected.value()));
str.append(QStringLiteral(" scale='%1'").arg(m_scale.value()));
str.append(QStringLiteral(" positions.count='%1'").arg(m_positions.value().count()));
return str;
}
UniqueMapId AbstractGraphicalObject::uniqueMapId() const
{
return m_uniqueMapId;
}
void AbstractGraphicalObject::setUniqueMapId(UniqueMapId uniqueId)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << uniqueId;
m_uniqueMapId = uniqueId;
// use attribute to store application obj id (SimWorld) at graphic
if (m_graphic)
{
// use attributeModel to store application obj id (SimWorld) at graphic
Esri::ArcGISRuntime::AttributeListModel *attListModel = m_graphic->attributes();
if (attListModel)
attListModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId);
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << uniqueId;
}
Point3D AbstractGraphicalObject::origin() const
{
return m_positions.value().value(0); // value => if index is out of range -> default object
}
void AbstractGraphicalObject::setOrigin(const Point3D &origin)
{
// TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
// << " origin=" << origin;
if (m_positions.value().isEmpty())
m_positions.refValue().append(origin);
else
{
// https://stackoverflow.com/a/1573715/2352507
// Any comparison(except with "!=") with NaN returns false.
// NaN != NaN -> true!!!
if (m_positions.value().value(0) != origin) // NaN != NaN -> true!!!
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " value(0) = " << m_positions.value().value(0) << " origin = " << origin;
m_positions.refValue().clear();
m_positions.refValue().append(origin);
}
}
// TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " origin=" << origin
// << " changed=" << m_positions.changed()
// << " SIZE=" << m_positions.value().size();
}
void AbstractGraphicalObject::setOrigin(const QPointF &origin)
{
setOrigin(Point3D(origin.x(), origin.y(), .0));
}
PositionCont AbstractGraphicalObject::positions() const
{
return m_positions.value();
}
void AbstractGraphicalObject::setPositions(const PositionCont &posCont)
{
m_positions = posCont;
// TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
// << " changed=" << m_positions.changed();
}
bool AbstractGraphicalObject::isSinglePoint() const
{
return m_positions.value().count() == 1;
}
bool AbstractGraphicalObject::isMultiPoint() const
{
return m_positions.value().count() > 1;
}
bool AbstractGraphicalObject::isArea() const
{
return m_positions.value().count() > 2;
}
LineStyle AbstractGraphicalObject::lineStyle() const
{
return m_lineStyle.value();
}
LineStyle &AbstractGraphicalObject::rLineStyle()
{
return m_lineStyle.refValue();
}
void AbstractGraphicalObject::setLineStyle(const LineStyle &lineStyle)
{
m_lineStyle = lineStyle;
}
FillStyle AbstractGraphicalObject::fillStyle() const
{
return m_fillStyle.value();
}
FillStyle &AbstractGraphicalObject::rFillStyle()
{
return m_fillStyle.refValue();
}
void AbstractGraphicalObject::setFillStyle(const FillStyle &fillStyle)
{
m_fillStyle = fillStyle;
}
bool AbstractGraphicalObject::visibility() const
{
return m_visibility.value();
}
void AbstractGraphicalObject::setVisibility(bool visibility)
{
m_visibility = visibility;
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility;
}
bool AbstractGraphicalObject::selected() const
{
return m_selected.value();
}
void AbstractGraphicalObject::setSelected(bool selected)
{
m_selected = selected;
}
bool AbstractGraphicalObject::pickable() const
{
return m_pickable.value();
}
void AbstractGraphicalObject::setPickable(bool pickable)
{
// allow pickable only if m_uniqueMapId is valid:
m_pickable = pickable && (m_uniqueMapId != INVALID_MAP_ID);
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " pickable=" << m_pickable;
}
float AbstractGraphicalObject::scale() const
{
return m_scale.value();
}
void AbstractGraphicalObject::setScale(float scale)
{
m_scale = scale;
}
bool AbstractGraphicalObject::addUpdateGraphic()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " updateGraphic";
bool successGraphic = true;
if (! m_graphic)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no m_graphic -> return true";
return true;
}
// append just once :-)
if (! m_graphicsLayer->graphics()->contains(m_graphic))
{
m_graphicsLayer->graphics()->append(m_graphic);
successGraphic = m_graphicsLayer->graphics()->contains(m_graphic);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " successGraphic=" << successGraphic;
return successGraphic;
}
bool AbstractGraphicalObject::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId =" << m_uniqueMapId
<< " origin =" << this->origin()
<< " visibility=" << m_visibility;
bool success = true;
// some checks:
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no graphicsLayer";
return false;
}
if (!m_graphic)
{
m_graphic = new Esri::ArcGISRuntime::Graphic();
m_graphic->setObjectName(m_debugInfo);
m_graphic->setZIndex(m_drawOrder.value());
// use attributeModel to store application obj id (SimWorld) at graphic
Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes();
if (attLisModel)
attLisModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success;
return success;
}
bool AbstractGraphicalObject::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId
<< " origin=" << this->origin()
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " drawOrder=" << m_drawOrder
<< " pickable=" << m_pickable
<< " pickableCHG=" << m_pickable.changed();
if (QApplication::instance()->thread()->currentThreadId() != QThread::currentThreadId())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " wrong Thread, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
abort();
}
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
// Picking:
if (m_pickable.changed())
{
// use attributeModel to store m_pickable at graphic
Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes();
if (attLisModel)
attLisModel->insertAttribute(QLatin1String(SituationMap::IsPickableAttribute), m_pickable.value());
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId=" << m_uniqueMapId;
}
}
m_pickable.reset();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " uniqueMapId =" << m_uniqueMapId;
return true;
}
bool AbstractGraphicalObject::assignLineStyleToGraphic()
{
if (!m_graphic)
return false;
const LineStyle &ls = m_lineStyle.value();
if (!m_esriSimpleLineSymbol)
{
m_esriSimpleLineSymbol =
new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic);
} else {
m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern()));
m_esriSimpleLineSymbol->setColor(ls.lineColor());
m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value());
}
m_graphic->setSymbol(m_esriSimpleLineSymbol.data());
return true;
}
bool AbstractGraphicalObject::assignFillStyleToGraphic()
{
if (!m_graphic)
return false;
const LineStyle &ls = m_lineStyle.value();
const FillStyle &fs = m_fillStyle.value();
// 1. the Line
if (!m_esriSimpleLineSymbol)
{
m_esriSimpleLineSymbol =
new Esri::ArcGISRuntime::SimpleLineSymbol(convert(ls.linePattern()), ls.lineColor(), ls.lineWidth() * m_scale.value(), m_graphic);
} else {
m_esriSimpleLineSymbol->setStyle(convert(ls.linePattern()));
m_esriSimpleLineSymbol->setColor(ls.lineColor());
m_esriSimpleLineSymbol->setWidth(ls.lineWidth() * m_scale.value());
}
// 1. the Fill
if (!m_esriSimpleFillSymbol)
{
m_esriSimpleFillSymbol =
new Esri::ArcGISRuntime::SimpleFillSymbol(convert(fs.fillPattern()), fs.fillColor(), m_esriSimpleLineSymbol, m_graphic);
} else {
m_esriSimpleFillSymbol->setStyle(convert(fs.fillPattern()));
m_esriSimpleFillSymbol->setColor(fs.fillColor());
m_esriSimpleFillSymbol->setOutline(m_esriSimpleLineSymbol);
}
m_graphic->setSymbol(m_esriSimpleFillSymbol.data());
return true;
}
Esri::ArcGISRuntime::SimpleLineSymbolStyle AbstractGraphicalObject::convert(const LinePattern linePattern)
{
switch (linePattern)
{
case LinePattern::Dash: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dash;
case LinePattern::DashDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDot;
case LinePattern::DashDotDot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::DashDotDot;
case LinePattern::Dot: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Dot;
case LinePattern::Null: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Null;
case LinePattern::Solid: return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid;
} // switch
return Esri::ArcGISRuntime::SimpleLineSymbolStyle::Solid;
}
Esri::ArcGISRuntime::SimpleFillSymbolStyle AbstractGraphicalObject::convert(const FillPattern fillPattern)
{
switch (fillPattern)
{
case FillPattern::BackwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal;
case FillPattern::Cross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Cross;
case FillPattern::DiagonalCross: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::DiagonalCross;
case FillPattern::ForwardDiagonal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::ForwardDiagonal;
case FillPattern::Horizontal: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Horizontal;
case FillPattern::Null: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Null;
case FillPattern::Solid: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Solid;
case FillPattern::Vertical: return Esri::ArcGISRuntime::SimpleFillSymbolStyle::Vertical;
} // switch
return Esri::ArcGISRuntime::SimpleFillSymbolStyle::BackwardDiagonal;
}
int AbstractGraphicalObject::countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon)
{
if (polylinePolygon.isEmpty() || polylinePolygon.parts().isEmpty())
return -1;
return polylinePolygon.parts().part(0).pointCount();
}
const Esri::ArcGISRuntime::SpatialReference AbstractGraphicalObject::spatialReference() const
{
return m_geoView->spatialReference();
}
} // namespace SituationMap
@@ -0,0 +1,213 @@
/**
* @RDE_HEADER@
* AbstractGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <Map/situation_map_types.h>
#include <Map/line_style.h>
#include <Map/fill_style.h>
#include <Common/simple_value.h>
#include <Common/situation_map_logging.h>
// ESRI
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
// Qt
#include <QPointer>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class GeoView;
class Graphic;
class GraphicsOverlay;
class Geometry;
class Multipart;
class SpatialReference;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
//forward
class GraphicalObjectText;
class SITUATIONMAP_EXPORT AbstractGraphicalObject
{
public:
AbstractGraphicalObject(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
virtual ~AbstractGraphicalObject();
AbstractGraphicalObject(const AbstractGraphicalObject &other) = delete;
AbstractGraphicalObject &operator=(AbstractGraphicalObject &other) = delete;
virtual Esri::ArcGISRuntime::Graphic *graphic();
virtual Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer();
virtual QString toString() const;
virtual UniqueMapId uniqueMapId() const;
virtual void setUniqueMapId(UniqueMapId uniqueId);
virtual QString debugInfo() const;
virtual void setDebugInfo(const QString &debugInfo);
virtual int drawOrder() const;
virtual void setDrawOrder(int drawOrder);
// Data access:
// for SinglePoint:
virtual Point3D origin() const;
virtual void setOrigin(const Point3D &origin);
virtual void setOrigin(const QPointF &origin); // ToDO accept this??
// for MultiPoint:
virtual PositionCont positions() const;
virtual void setPositions(const PositionCont &posCont);
virtual bool isSinglePoint() const;
virtual bool isMultiPoint() const;
virtual bool isArea() const;
virtual LineStyle lineStyle() const;
virtual LineStyle &rLineStyle();
/**
* @brief setLineStyle (Font-)size is limited to 500.
* (High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty')
* @param lineStyle to set.
*/
virtual void setLineStyle(const LineStyle &lineStyle);
virtual FillStyle fillStyle() const;
virtual FillStyle &rFillStyle();
virtual void setFillStyle(const FillStyle &fillStyle);
// Scale :
virtual float scale() const;
virtual void setScale(float scale);
// : Scale
// Visibility :
virtual bool visibility() const;
virtual void setVisibility(bool visibility);
// : Visibility
// Selection, Picking :
bool selected() const;
void setSelected(bool selected);
bool pickable() const;
/**
* @brief setPickable The method allows pickable=true only if uniqueMapIdis valid.
* @param pickable new value.
*/
void setPickable(bool pickable);
// : Selection, Picking
virtual Esri::ArcGISRuntime::Geometry getGraphicGeometry() const;
bool updateView();
bool addUpdateGraphic();
static Esri::ArcGISRuntime::SimpleLineSymbolStyle convert(const LinePattern linePattern);
static Esri::ArcGISRuntime::SimpleFillSymbolStyle convert(const FillPattern fillPattern);
bool assignLineStyleToGraphic();
bool assignFillStyleToGraphic();
static int countOfPolygonFirstPart(const Esri::ArcGISRuntime::Multipart &polylinePolygon);
protected:
// Internal/Admin
const Esri::ArcGISRuntime::SpatialReference spatialReference() const;
virtual bool changed()
{
return m_drawOrder.changed()
|| m_positions.changed()
|| m_visibility.changed()
|| m_selected.changed()
|| m_pickable.changed()
|| m_scale.changed()
|| m_lineStyle.changed()
|| m_fillStyle.changed();
}
virtual bool updateViewImpl() = 0;
virtual void resetChanged()
{
m_drawOrder.reset();
m_positions.reset();
m_visibility.reset();
m_selected.reset();
m_pickable.reset();
m_scale.reset();
m_lineStyle.reset();
m_fillStyle.reset();
}
virtual bool create() = 0;
const Esri::ArcGISRuntime::GeoView *m_geoView = nullptr;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicsLayer = nullptr;
Esri::ArcGISRuntime::Graphic *m_graphic = nullptr;
SimpleValue<int> m_drawOrder = 0;
// Data
UniqueMapId m_uniqueMapId;
QString m_debugInfo; ///< for debugging
SimpleValue<PositionCont> m_positions;
SimpleValue<bool> m_visibility = true;
SimpleValue<bool> m_selected = false;
SimpleValue<bool> m_pickable = true;
SimpleValue<float> m_scale = 1.0;
SimpleValue<LineStyle> m_lineStyle;
SimpleValue<FillStyle> m_fillStyle;
QPointer<Esri::ArcGISRuntime::SimpleLineSymbol> m_esriSimpleLineSymbol = nullptr;
QPointer<Esri::ArcGISRuntime::SimpleFillSymbol> m_esriSimpleFillSymbol = nullptr;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,96 @@
/**
* @RDE_HEADER@
* EllipseParameters
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "ellipse_parameters.h"
// ESRI
// Qt
#include <QString>
namespace SituationMap
{
float EllipseParameters::lengthX() const
{
return m_lengthX;
}
void EllipseParameters::setLengthX(float lengthX)
{
m_lengthX = lengthX;
}
float EllipseParameters::lengthY() const
{
return m_lengthY;
}
void EllipseParameters::setLengthY(float lengthY)
{
m_lengthY = lengthY;
}
float EllipseParameters::directionDegree() const
{
return m_directionDegree;
}
void EllipseParameters::setDirectionDegree(float directionDegree)
{
m_directionDegree = directionDegree;
}
int EllipseParameters::maxSegmentLength() const
{
return m_maxSegmentLength;
}
void EllipseParameters::setMaxSegmentLength(int value)
{
m_maxSegmentLength = value;
}
int EllipseParameters::maxPointCount() const
{
return m_maxPointCount;
}
void EllipseParameters::setMaxPointCount(int maxPointCount)
{
m_maxPointCount = maxPointCount;
}
bool EllipseParameters::isValid() const
{
return ((m_lengthX > 0) && (m_lengthY > 0)) ;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const EllipseParameters &value)
{
QString str;
str.append(QStringLiteral(" lengthX=%1").arg(value.lengthX()));
str.append(QStringLiteral(" lengthY=%1").arg(value.lengthY()));
str.append(QStringLiteral(" directionDegree=%1").arg(value.directionDegree()));
str.append(QStringLiteral(" maxSegmentLength=%1").arg(value.maxSegmentLength()));
stream << str.toStdString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,95 @@
/**
* @RDE_HEADER@
* EllipseParameters
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include <situation_map_export.h>
#include <ostream>
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
class SITUATIONMAP_EXPORT EllipseParameters
{
public:
static constexpr unsigned int MAX_POINT_COUNT_DEFAULT = 30;
EllipseParameters() = default;
float lengthX() const;
void setLengthX(float lengthX);
float lengthY() const;
void setLengthY(float lengthY);
float directionDegree() const;
void setDirectionDegree(float directionDegree);
int maxSegmentLength() const;
void setMaxSegmentLength(int value);
int maxPointCount() const;
void setMaxPointCount(int maxPointCount);
bool isValid() const;
/**
* @brief operator== Compare two objects
* @param other to be compared with
* @return true if identical, otherwise false.
*/
bool operator==(const EllipseParameters &other) const
{
return ((this->m_lengthX == other.m_lengthX) &&
(this->m_lengthY == other.m_lengthY) &&
(this->m_directionDegree == other.m_directionDegree) &&
(this->m_maxSegmentLength == other.m_maxSegmentLength) &&
(this->m_maxPointCount == other.m_maxPointCount)
);
}
/**
* @brief operator!= Compare two objects
* @param other to be compared with
* @return true if NOT identical, otherwise false.
*/
bool operator!=(const EllipseParameters &other) const
{
return !(*this == other);
}
private:
float m_lengthX = 0;
float m_lengthY = 0;
float m_directionDegree = 0;
int m_maxSegmentLength = 10; // meter
int m_maxPointCount = MAX_POINT_COUNT_DEFAULT;
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const EllipseParameters &value);
} // namespace SituationMap
@@ -0,0 +1,190 @@
/**
* @RDE_HEADER@
* GraphicalObjectAttributes
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_attributes.h"
#include <MapImpl/map_impl.h>
#include <Map/situation_map_utils.h> // operator<<
#include <Common/simple_value.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
// Qt
namespace SituationMap
{
class GraphicalObjectAttributes::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
MapImpl *m_mapImpl = nullptr;
QString m_logicalLayerName;
// OWN data:
SimpleValue<QVariantMap> m_attributes;
}; // class GraphicalObjectAttributes::Private
//-----------------------------------------------------------------------------
GraphicalObjectAttributes::GraphicalObjectAttributes(MapImpl *mapImpl,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const QString &debugInfo,
const UniqueMapId &uniqueMapId)
: BaseClass(mapImpl->getEsriGeoView(), graphicsLayer, debugInfo, uniqueMapId),
m_logger(getLogger("GraphicalObjectAttributes")),
d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectAttributes::~GraphicalObjectAttributes()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectAttributes::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_visibility = " << m_visibility;
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
bool successGraphic = addUpdateGraphic(); // CHECK
if (!successGraphic)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId;
return false;
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
QVariantMap GraphicalObjectAttributes::attributes() const
{
return d->m_attributes.value();
}
void GraphicalObjectAttributes::setAttributes(const QVariantMap &attributes)
{
d->m_attributes = attributes;
}
bool GraphicalObjectAttributes::changed()
{
return BaseClass::changed()
|| d->m_attributes.changed();
}
void GraphicalObjectAttributes::resetChanged()
{
BaseClass::resetChanged();
d->m_attributes.reset();
}
Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const
{
if (m_graphic) return m_graphic->geometry();
ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined";
return Esri::ArcGISRuntime::Geometry();
} // Esri::ArcGISRuntime::Geometry GraphicalObjectAttributes::getGraphicGeometry() const
bool GraphicalObjectAttributes::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " origin=" << this->origin()
<< " scale=" << m_scale.value();
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
if (m_positions.changed())
{
const PositionCont &posCont = m_positions.value();
if (posCont.size() == 1)
{
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
}
}
if (d->m_attributes.changed())
{
QVariantMap rawAttributes = d->m_attributes.value();
// add basics:
rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId;
rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value();
m_graphic->attributes()->setAttributesMap(rawAttributes);
if (m_logger.isDebugEnabled())
qCritical() << " XXXXX set rawAttributes " << rawAttributes;
}
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
}
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " visibility=" << m_visibility;
return success;
}
} // namespace SituationMap
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* GraphicalObjectAttributes
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
// forward:
class MapImpl;
/**
* @brief The GraphicalObjectAttributes class
*/
class SITUATIONMAP_EXPORT GraphicalObjectAttributes : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
/**
* @brief GraphicalObjectAttributes
* @param mapImpl map to be used.
* @param logicalLayerName logical layer name to be used.
* @param debugInfo debugInfo to be used
* @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements)
*/
GraphicalObjectAttributes(MapImpl *mapImpl,
Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer,
const QString &debugInfo,
const UniqueMapId &uniqueMapId = INVALID_MAP_ID);
GraphicalObjectAttributes(const GraphicalObjectAttributes &other) = delete;
GraphicalObjectAttributes &operator=(GraphicalObjectAttributes &other) = delete;
virtual ~GraphicalObjectAttributes();
QVariantMap attributes() const;
void setAttributes(const QVariantMap &attributes);
Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override;
protected:
// Internal/Admin
virtual bool create() override;
bool changed() override;
void resetChanged() override;
bool updateViewImpl() override;
// logical
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
@@ -0,0 +1,162 @@
/**
* @RDE_HEADER@
* GraphicalObjectCube
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_cube.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
// Qt
namespace SituationMap
{
GraphicalObjectCube::GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectCube"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectCube::~GraphicalObjectCube()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectCube::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " drawOrder = " << m_graphic->drawOrder()
<< " success = " << success;
return success;
}
bool GraphicalObjectCube::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// some checks:
if (!this->isArea())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
// Attribute 'height':
QVariantMap attMap = m_graphic->attributes()->attributesMap();
attMap[QStringLiteral("height")] = QVariant(m_height.value());
//
// Attribute 'PartyName' is replaced by Attribute 'Extrusion_Color' to be more flexible
//
// Attribute 'Extrusion_Color':
if (m_extrusionColorName.value().isEmpty())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " m_extrusionColorName is empty";
// -> time for the defaultSymbol of the Renderer
attMap[QStringLiteral("Extrusion_Color")] = QStringLiteral("DOES_NOT_EXIST");
}
else
attMap[QStringLiteral("Extrusion_Color")] = QVariant(m_extrusionColorName.value());
m_graphic->attributes()->setAttributesMap(attMap);
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[height] ='" << attMap[QStringLiteral("height")].toDouble() << "'";
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " attributesMap[Extrusion_Color] ='" << attMap[QStringLiteral("Extrusion_Color")].toString() << "'";
// geometry:
Esri::ArcGISRuntime::PolygonBuilder builder(spatialReference());
const PositionCont &posCont = m_positions.value();
for (const Point3D &pos : posCont)
{
builder.addPoint(pos.x(), pos.y());
}
if (! builder.isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
m_graphic->setGeometry(builder.toGeometry());
// add
bool successGraphic = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex();
return successGraphic;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectCube &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,100 @@
/**
* @RDE_HEADER@
* GraphicalObjectCube
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The GraphicalObjectCube class for 3D
*
*/
class SITUATIONMAP_EXPORT GraphicalObjectCube : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
explicit GraphicalObjectCube(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectCube();
double height() const
{
return m_height.value();
}
void setHeight(double height)
{
m_height = height;
}
const QString &extrusionColorName() const
{
return m_extrusionColorName.value();
}
void setExtrusionColorName(const QString &extrusionColorName)
{
m_extrusionColorName = extrusionColorName;
}
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_height.changed()
|| m_extrusionColorName.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_height.reset();
m_extrusionColorName.reset();
}
bool updateViewImpl() override;
// Data
SimpleValue<double> m_height;
SimpleValue<QString> m_extrusionColorName;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectCube &value);
} // namespace SituationMap
@@ -0,0 +1,344 @@
/**
* @RDE_HEADER@
* GraphicalObjectEllipse
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_ellipse.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/Polygon.h>
#include <arcgis/GeometryEngine.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
// Qt
#include <QElapsedTimer>
namespace SituationMap
{
GraphicalObjectEllipse::GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectEllipse"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectEllipse::~GraphicalObjectEllipse()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_internalParameters;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectEllipse::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
// real work:
// empty
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " drawOrder = " << m_graphic->drawOrder()
<< " success = " << success;
return success;
}
bool GraphicalObjectEllipse::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// some checks:
if (!this->isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (!BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
if (!m_ellipseParameters.value().isValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " ellipseParameters not valid";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
bool successEllipse = true;
if (m_ellipseParameters.changed() || m_positions.changed())
{
// apply attributes:
parameterToInternal();
Esri::ArcGISRuntime::Polygon ellipse;
successEllipse = createEllipse(ellipse);
if (successEllipse)
m_graphic->setGeometry(ellipse);
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " createEllipse failed";
return false;
}
}
assignFillStyleToGraphic();
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
bool successGraphic = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex()
<< " successEllipse = " << successEllipse
<< " successGraphic = " << successGraphic;
return successEllipse && successGraphic;
}
void GraphicalObjectEllipse::parameterToInternal()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (!m_internalParameters)
{
m_internalParameters = new Esri::ArcGISRuntime::GeodesicEllipseParameters();
m_internalParameters->setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon);
}
// TODO !!??: set xy correct, calc maxSegLength...
const EllipseParameters &ellipseParameterRef = m_ellipseParameters.value();
// 1a, 1b
m_internalParameters->setMaxSegmentLength(ellipseParameterRef.maxSegmentLength());
m_internalParameters->setMaxPointCount(ellipseParameterRef.maxPointCount());
// 2
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point mapPoint(origin.x(), origin.y(), origin.z(), spatialReference()); // center of ellipse
Esri::ArcGISRuntime::Point normalizedPt =
Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint);
m_internalParameters->setCenter(normalizedPt);
// m_internalParameters->setCenter(mapPoint);
// 3, 4
m_internalParameters->setSemiAxis1Length(ellipseParameterRef.lengthX());
m_internalParameters->setSemiAxis2Length(ellipseParameterRef.lengthY());
// 5
m_internalParameters->setAxisDirection(ellipseParameterRef.directionDegree());
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
bool GraphicalObjectEllipse::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = createEllipsePolygon(*m_internalParameters, ellipse);
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
// << " pointCount = " << ellipse.pointCount()
<< " dimension = " << ellipse.dimension();
return success;
}
bool GraphicalObjectEllipse::createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters,
Esri::ArcGISRuntime::Polygon &polygon)
{
LOGGER_T m_logger = getLogger("GraphicalObjectEllipse");
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
QElapsedTimer timer;
if (m_logger.isInfoEnabled())
timer.start();
bool success = false;
Esri::ArcGISRuntime::Geometry ellipseGeometry =
Esri::ArcGISRuntime::GeometryEngine::ellipseGeodesic(ellipseParameters);
if (ellipseGeometry.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " There was an error computing the geodesic ellipse. Try a different segment length.";
}
else
{
polygon = Esri::ArcGISRuntime::Polygon(ellipseGeometry);
success = true;
}
int count = AbstractGraphicalObject::countOfPolygonFirstPart(polygon);
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " count=" << count
<< " (semiAxis1Length=" << ellipseParameters.semiAxis1Length()
<< " , semiAxis2Length=" << ellipseParameters.semiAxis2Length()
<< " , maxPointCount=" << ellipseParameters.maxPointCount()
<< " , maxSegmentLength=" << ellipseParameters.maxSegmentLength()
<< " , dimension = " << polygon.dimension()
<< ")";
if (m_logger.isDebugEnabled())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " took " << timer.elapsed() << "ms"
<< " (semiAxis1Length=" << ellipseParameters.semiAxis1Length()
<< " , semiAxis2Length=" << ellipseParameters.semiAxis2Length()
<< " , maxPointCount=" << ellipseParameters.maxPointCount()
<< " , maxSegmentLength=" << ellipseParameters.maxSegmentLength()
<< " , dimension = " << polygon.dimension()
<< ")";
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " success = " << success;
return success;
}
bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
Esri::ArcGISRuntime::Polygon &polygon)
{
LOGGER_T m_logger = getLogger("GraphicalObjectEllipse");
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " origin=" << originMap.x() << " ; " << originMap.y() << " ; " << originMap.z()
<< " radius=" << radius;
Esri::ArcGISRuntime::GeodesicEllipseParameters ePara;
ePara.setGeometryType(Esri::ArcGISRuntime::GeometryType::Polygon);
// 1
ePara.setMaxSegmentLength(10);
if (maxPointCount == 0)
ePara.setMaxPointCount(EllipseParameters::MAX_POINT_COUNT_DEFAULT);
else
ePara.setMaxPointCount(maxPointCount);
// 2
const Esri::ArcGISRuntime::Point &mapPoint = originMap; // center of ellipse
Esri::ArcGISRuntime::Point normalizedPt =
Esri::ArcGISRuntime::GeometryEngine::normalizeCentralMeridian(mapPoint);
ePara.setCenter(normalizedPt);
// 3, 4
ePara.setSemiAxis1Length(radius);
ePara.setSemiAxis2Length(radius);
// 5
ePara.setAxisDirection(0);
return createEllipsePolygon(ePara, polygon);
}
bool GraphicalObjectEllipse::createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
PositionCont &posCont)
{
Esri::ArcGISRuntime::Polygon circle;
bool success = GraphicalObjectEllipse::createCirclePolygon(originMap,
radius,
maxPointCount,
circle);
if (success && !circle.isEmpty())
{
posCont.clear();
const Esri::ArcGISRuntime::ImmutablePart &immutablePart = circle.parts().part(0);
const int size = immutablePart.pointCount();
for (int i = 0; i < size; ++i)
{
const Esri::ArcGISRuntime::Point &p = immutablePart.point(i);
posCont.append(Point3D(p.x(), p.y(), p.z()));
}
}
return success;
}
EllipseParameters GraphicalObjectEllipse::ellipseParameters() const
{
return m_ellipseParameters.value();
}
EllipseParameters &GraphicalObjectEllipse::rEllipseParameters()
{
return m_ellipseParameters.refValue();
}
void GraphicalObjectEllipse::setEllipseParameters(const EllipseParameters &ellipseParameters)
{
m_ellipseParameters = ellipseParameters;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectEllipse &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,121 @@
/**
* @RDE_HEADER@
* GraphicalObjectEllipse
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include "ellipse_parameters.h"
// Esri
// Qt
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Polygon;
class Point;
class GeodesicEllipseParameters;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectEllipse : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectEllipse(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectEllipse();
EllipseParameters ellipseParameters() const;
EllipseParameters &rEllipseParameters();
void setEllipseParameters(const EllipseParameters &ellipseParameters);
static bool createEllipsePolygon(const Esri::ArcGISRuntime::GeodesicEllipseParameters &ellipseParameters,
Esri::ArcGISRuntime::Polygon &polygon);
static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
Esri::ArcGISRuntime::Polygon &polygon);
static bool createCirclePolygon(const Esri::ArcGISRuntime::Point &originMap,
float radius,
unsigned int maxPointCount,
PositionCont &posCont);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_ellipseParameters.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_ellipseParameters.reset();
}
bool updateViewImpl() override;
// Data
Esri::ArcGISRuntime::GeodesicEllipseParameters *m_internalParameters = nullptr;
SimpleValue<EllipseParameters> m_ellipseParameters;
void parameterToInternal();
virtual bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse);
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
virtual PositionCont positions() const override
{
return PositionCont();
}
virtual void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
}
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectEllipse &value);
} // namespace SituationMap
@@ -0,0 +1,170 @@
/**
* @RDE_HEADER@
* GraphicalObjectModelSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_model_scene_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/ModelSceneSymbol.h>
// Qt
#include <QPainter>
#include <QFile>
namespace SituationMap
{
GraphicalObjectModelSceneMarker::GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectModelSceneMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectModelSceneMarker::~GraphicalObjectModelSceneMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_modelSceneSymbol;
m_modelSceneSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectModelSceneMarker::setUrl(const QUrl &url)
{
m_url = url;
}
QUrl GraphicalObjectModelSceneMarker::url() const
{
return m_url.value();
}
void GraphicalObjectModelSceneMarker::setHeading(float heading)
{
m_heading = heading;
}
float GraphicalObjectModelSceneMarker::heading() const
{
return m_heading.value();
}
bool GraphicalObjectModelSceneMarker::create()
{
return BaseClass::create();
}
bool GraphicalObjectModelSceneMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_url = " << m_url.value().toString()
<< " m_drawOrder = " << m_drawOrder
<< " m_scale = " << m_scale.value()
<< " m_heading = " << m_heading.value();
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
QFile fi(m_url.value().path());
if (!fi.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " m_url does not exists: '" << fi.fileName() << "'";
m_url.reset();
}
if (m_url.value().isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " no url provided (or does not exist)";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
// url changed?
if ( (m_modelSceneSymbol && (m_modelSceneSymbol->url() != m_url.value())) ||
m_scale.changed())
{
// force re-create ModelSceneSymbol:
delete m_modelSceneSymbol;
m_modelSceneSymbol = nullptr;
}
if (!m_modelSceneSymbol)
{
m_modelSceneSymbol = new Esri::ArcGISRuntime::ModelSceneSymbol(m_url.value(), m_scale.value()); // url, scale
}
m_modelSceneSymbol->setHeading(m_heading.value()); // degree, clockwise, 0 is north/12
// m_modelSceneSymbol->setAnchorPosition(Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom);
m_graphic->setSymbol(m_modelSceneSymbol);
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,111 @@
/**
* @RDE_HEADER@
* GraphicalObjectModelSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
#include <QUrl>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class ModelSceneSymbol;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectModelSceneMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectModelSceneMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectModelSceneMarker();
// Data access:
// to be cont.
void setUrl(const QUrl &url);
QUrl url() const;
// degree ; 0 is north/12
void setHeading(float heading);
float heading() const;
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_url.changed()
|| m_heading.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_url.reset();
m_heading.reset();
}
bool updateViewImpl() override;
// used ModelSceneSymbol:
Esri::ArcGISRuntime::ModelSceneSymbol *m_modelSceneSymbol{nullptr};
SimpleValue<QUrl> m_url; // e.g. /shd/CTC/TOOLS/ArcGIS/Runtime/Data/3D/Bristol/Collada/Bristol.dae
SimpleValue<float> m_heading{0.0}; // degree, clockwise, 0 is north/12
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,323 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_multi_path.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoView.h>
#include <arcgis/GeometryEngine.h>
// Qt
namespace SituationMap
{
GraphicalObjectMultiPath::GraphicalObjectMultiPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, UniqueMapId uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_pathType(type)
, m_logger(getLogger("GraphicalObjectMultiPath"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
GraphicalObjectMultiPath::~GraphicalObjectMultiPath()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
PositionCont GraphicalObjectMultiPath::path(int pathIndex) const
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex;
if (hasPosCont(pathIndex))
return getPosCont(pathIndex).value();
return {};
}
void GraphicalObjectMultiPath::setPath(int pathIndex, const PositionCont &positions)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex
<< " positions=" << positions;
m_positionsCont[pathIndex] = positions;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::clearPath(int pathIndex)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " pathIndex=" << pathIndex;
if (pathIndex == -1)
{
m_positions.refValue().clear();
m_positionsCont.clear();
}
else
{
if (hasPosCont(pathIndex))
{
// Do not remove the whole entry to let changed() work
m_positionsCont[pathIndex].refValue().clear();
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::clearAllPaths()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_positions.refValue().clear();
m_positionsCont.clear();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectMultiPath::reduceTo(const QList<int> &remainingPathIndexes)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " remainingPathIndexes=" << remainingPathIndexes;
QList<int> keys = m_positionsCont.keys();
for (int key : qAsConst(keys))
{
if (!remainingPathIndexes.contains(key))
{
// Do not remove the whole entry to let chanded() work
m_positionsCont[key].refValue().clear();
}
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QList<int> GraphicalObjectMultiPath::pathIndexInUse()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
QList<int> ret;
///// ret << 0; // pathIndex=0 -> m_positions
QMapIterator<int, SimpleValue<PositionCont> > mapIt(m_positionsCont);
while (mapIt.hasNext())
{
mapIt.next();
const SimpleValue<PositionCont> &posData = mapIt.value();
const PositionCont &posCont = posData.value();
if (! posCont.isEmpty())
ret += mapIt.key();
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return ret;
}
bool GraphicalObjectMultiPath::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
bool GraphicalObjectMultiPath::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = updatePositions();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectMultiPath::updatePositions()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = true;
int pathIdxToInsert = 0;
const int minPoints = this->pathType() == GraphicalObjectPathType::Polyline ? 2 : 3;
QSharedPointer<Esri::ArcGISRuntime::MultipartBuilder> builder;
// some preparations: ( do no check isMultiPoint / isArea: m_positions should typically be empty!!!
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
builder = QSharedPointer<Esri::ArcGISRuntime::PolylineBuilder>(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference()));
break;
case GraphicalObjectPathType::Polygon:
builder = QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder>(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
break;
} // switch
QSharedPointer<Esri::ArcGISRuntime::PartCollection> partCollection =
QSharedPointer<Esri::ArcGISRuntime::PartCollection>(new Esri::ArcGISRuntime::PartCollection(builder->spatialReference()));
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", m_positionsCont.size() /#rings=" << m_positionsCont.size();
QMutableMapIterator<int, SimpleValue<PositionCont> > mapIt(m_positionsCont);
//
// LOOP PER SINGLE POLY
//
while (mapIt.hasNext())
{
mapIt.next();
const int key = mapIt.key();
const SimpleValue<PositionCont> &posData = mapIt.value();
const PositionCont &posCont = posData.value();
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", key / #ring=" << key
<< ", rings.size()=" << posCont.size();
// maybe some pathIdx are cleared by clearPath() or similar:
if (posCont.isEmpty())
{
DEBUG_LOG(m_logger) << LOGFUNCTION
<< " posCont empty, cleared? -> remove/cleanup"
<< " pathIdxToInsert = " << pathIdxToInsert
<< " mapIt.key() = " << key;
mapIt.remove();
continue;
}
if (posCont.count() < minPoints)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " incorrect number of positions"
<< " (" << posCont.count() << ")"
<< ", minPoints=" << minPoints
<< ", pathIdxToInsert = " << pathIdxToInsert
<< ", mapIt.key() = " << key
<< ", m_debugInfo = " << m_debugInfo;
success = false;
}
else
{
Esri::ArcGISRuntime::Part part(builder->spatialReference());
//
// LOOP PER POINT
//
for (auto pos : qAsConst(posCont))
{
part.addPoint(pos.x(), pos.y(), pos.z());
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", key / #ring=" << key
<< ", addPoint=" << pos;
}
partCollection->addPart(&part);
} // if (posCont.count() < minPoints)
} // while
builder->setParts(partCollection.get());
//
// builder to geometry;
//
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
{
const Esri::ArcGISRuntime::Polyline &multipartPolyline =
static_cast<Esri::ArcGISRuntime::PolylineBuilder *>(builder.data())->toPolyline();
m_graphic->setGeometry(multipartPolyline);
assignLineStyleToGraphic();
}
break;
case GraphicalObjectPathType::Polygon:
{
const Esri::ArcGISRuntime::Polygon &multipartPolygon =
static_cast<Esri::ArcGISRuntime::PolygonBuilder *>(builder.data())->toPolygon();
m_graphic->setGeometry(multipartPolygon);
//assignLineStyleToGraphic();
assignFillStyleToGraphic();
}
break;
} // switch
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
success = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectMultiPath::hasPosCont(int idx) const
{
return m_positionsCont.contains(idx);
}
SimpleValue<PositionCont> &GraphicalObjectMultiPath::getPosCont(int idx)
{
return m_positionsCont[idx];
}
const SimpleValue<PositionCont> GraphicalObjectMultiPath::getPosCont(int idx) const
{
return m_positionsCont[idx];
}
} // namespace SituationMap
@@ -0,0 +1,105 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "graphical_object_path.h"
// Esri
// Qt
#include <QMap>
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectMultiPath : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectMultiPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, UniqueMapId uniqueMapId);
~GraphicalObjectMultiPath();
GraphicalObjectPathType pathType() const
{
return m_pathType;
}
virtual PositionCont path(int pathIndex) const;
virtual void setPath(int pathIndex, const PositionCont &positions);
/*! \name Subsidiary polyline/polygons */
//@{
virtual void clearPath(int pathIndex);
virtual void clearAllPaths();
virtual QList<int> pathIndexInUse();
virtual void reduceTo(const QList<int> &remainingPathIndexes);
//@}
protected:
// Internal/Admin
bool updatePositions();
bool changed() override
{
bool baseClassChanged = BaseClass::changed();
if (baseClassChanged)
return true;
for (const SimpleValue<PositionCont> &posData : qAsConst(m_positionsCont))
{
if (posData.changed())
return true;
}
return false;
}
void resetChanged() override
{
BaseClass::resetChanged();
for (SimpleValue<PositionCont> &posData : m_positionsCont)
posData.reset();
}
bool create() override;
bool updateViewImpl() override;
// logical
const GraphicalObjectPathType m_pathType;
//bool updatePositions();
private:
// Attention: will contain 'positions' of idx == 1 and higher :
bool hasPosCont(int idx) const;
SimpleValue<PositionCont> &getPosCont(int idx);
const SimpleValue<PositionCont> getPosCont(int idx) const;
QMap<int, SimpleValue<PositionCont> > m_positionsCont;
// : Attention: will contain 'positions' of idx == 1 and higher
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,260 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_path.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/SimpleLineSymbol.h>
#include <arcgis/SimpleFillSymbol.h>
#include <arcgis/GeoView.h>
#include <arcgis/GeometryEngine.h>
// Qt
namespace SituationMap
{
QLatin1String toString(const GraphicalObjectPathType subType)
{
switch (subType)
{
case GraphicalObjectPathType::Polyline:
return QLatin1String("Polyline");
break;
case GraphicalObjectPathType::Polygon:
return QLatin1String("Polygon");
break;
} // switch
return QLatin1String("GraphicalObjectPathTypeERROR");
}
GraphicalObjectPath::GraphicalObjectPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_pathType(type)
, m_logger(getLogger("GraphicalObjectPath"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
GraphicalObjectPath::~GraphicalObjectPath()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " m_pathType=" << m_pathType;
}
bool GraphicalObjectPath::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
bool success = BaseClass::create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
// << " drawOrder = " << m_graphic->drawOrder()
;
return success;
}
bool GraphicalObjectPath::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_positions= " << m_positions.value()
<< ", m_maxSegmentLength=" << m_maxSegmentLength;
QSharedPointer<Esri::ArcGISRuntime::MultipartBuilder> builder;
// some checks:
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
if (!this->isMultiPoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
builder = QSharedPointer<Esri::ArcGISRuntime::PolylineBuilder>(new Esri::ArcGISRuntime::PolylineBuilder(spatialReference()));
break;
case GraphicalObjectPathType::Polygon:
if (!this->isArea())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
builder = QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder>(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
break;
} // switch
if (! builder)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no builder";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
const PositionCont &posCont = m_positions.value();
for (const Point3D &pos : posCont)
builder->addPoint(pos.x(), pos.y(), pos.z());
if (! builder->isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
//
// apply attributes:
//
// N. Thoden, 13.02.2018
// Case #02062713 [BOS] BasicSceneView_lineNotComplete
// If a line has a line of ~10km (Borderline)
// the line may 'move' on the ground (3D, RenderMode::Dynamic, SurfarcePlacement::Draped)
// To avoid these kind of effect, the advice is to densify the geometry
// [similar: Case #02054233 [BOS] LineArtefacts in 3D ->
// BUG-000111565 : Lines created on the GraphicsOverlay with "Dynamic" Rendering mode on 3D doesn't render completely when zooming in]
//
// N. Thoden, 29.03.2018
// Consulting with Luke Smallwood / using the V100.2.1. debug libs:
// densify leads to an assert, so avoid densify
// (until now densify does not lead to an improvement..)
//
// N. Thoden, 19.04.2021
// [GUEZE-495]: 3D Lagedarstellung Trefferlinien
// Linie fehlt teilweise
// jetzt (typeweise) per m_maxSegmentLength aktivierbar
switch (m_pathType)
{
case GraphicalObjectPathType::Polyline:
{
const Esri::ArcGISRuntime::Polyline &polyline =
static_cast<Esri::ArcGISRuntime::PolylineBuilder *>(builder.data())->toPolyline();
if (m_maxSegmentLength > 0)
{
m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polyline,
m_maxSegmentLength,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic));
} else {
m_graphic->setGeometry(polyline);
} // if (densify)
assignLineStyleToGraphic();
}
break;
case GraphicalObjectPathType::Polygon:
{
const Esri::ArcGISRuntime::Polygon &polygon =
static_cast<Esri::ArcGISRuntime::PolygonBuilder *>(builder.data())->toPolygon();
if (m_maxSegmentLength > 0)
{
m_graphic->setGeometry(Esri::ArcGISRuntime::GeometryEngine::densifyGeodetic(polygon,
m_maxSegmentLength,
Esri::ArcGISRuntime::LinearUnit::meters(),
Esri::ArcGISRuntime::GeodeticCurveType::Geodesic));
} else {
m_graphic->setGeometry(polygon);
}
assignFillStyleToGraphic();
}
break;
} // switch
const int count = AbstractGraphicalObject::countOfPolygonFirstPart(m_graphic->geometry());
if (count > 1000)
WARNING_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< ", huge amount of vertices, countOfPolygonFirstPart = " << count;
else
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< ", amount of vertices, countOfPolygonFirstPart = " << count;
success = addUpdateGraphic();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
double GraphicalObjectPath::maxSegmentLength() const
{
return m_maxSegmentLength;
}
void GraphicalObjectPath::setMaxSegmentLength(double maxSegmentLength)
{
m_maxSegmentLength = maxSegmentLength;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectPathType &value)
{
stream << toString(value).latin1();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,78 @@
/**
* @RDE_HEADER@
* GraphicalObjectPath
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
enum class GraphicalObjectPathType {Polyline, Polygon};
QLatin1String toString(const GraphicalObjectPathType subType);
class SITUATIONMAP_EXPORT GraphicalObjectPath : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
explicit GraphicalObjectPath(const GraphicalObjectPathType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectPath();
GraphicalObjectPathType pathType() const
{
return m_pathType;
}
double maxSegmentLength() const;
void setMaxSegmentLength(double maxSegmentLength);
protected:
// Internal/Admin
bool create() override;
bool updateViewImpl() override;
// logical
const GraphicalObjectPathType m_pathType;
double m_maxSegmentLength{-1};
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectPathType &value);
} // namespace SituationMap
@@ -0,0 +1,573 @@
/**
* @RDE_HEADER@
* GraphicalObjectPictureMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_picture_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <SymbologyImpl/symbol_extension_renderer.h>
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PictureMarkerSymbol.h>
#include <arcgis/SimpleMarkerSymbol.h>
#include <arcgis/MultipointBuilder.h>
// Qt
#include <QPainter>
#include <QTimer>
namespace SituationMap
{
/**
* @brief getSimpleMarkerSymbolStyle
* @param markerSymbolType type to get
* @param filePath filePath of requested marker; if empty -> use return value, if !empty -> use filePath and get file out of qrc
* @param hotSpotPercent -0.5 to 0.5; e.g. x=center, y=bottom -> -0.5
* @return corrosponding Esri::ArcGISRuntime::SimpleMarkerSymbolStyle, see filePath
*/
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle getSimpleMarkerSymbolStyle(MarkerSymbolType markerSymbolType,
QString &filePath,
QPointF *hotSpotPercent = nullptr)
{
if (hotSpotPercent)
*hotSpotPercent = QPointF();
filePath.clear();
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS =
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
switch (markerSymbolType)
{
case SituationMap::MarkerSymbolType::Undefined:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
break;
case SituationMap::MarkerSymbolType::Circle:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
break;
case SituationMap::MarkerSymbolType::Cross:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Cross;
break;
case SituationMap::MarkerSymbolType::Diamond:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Diamond;
break;
case SituationMap::MarkerSymbolType::Square:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Square;
break;
case SituationMap::MarkerSymbolType::X:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::X;
break;
case SituationMap::MarkerSymbolType::Triangle:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Triangle;
break;
case SituationMap::MarkerSymbolType::PushPin2D:
esriSMSS = Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Circle;
filePath = QLatin1String(":/situationMap/pushPinRedClipArt.png");
if (hotSpotPercent)
*hotSpotPercent = QPointF(0, -0.5);
break;
} //switch
return esriSMSS;
}
GraphicalObjectPictureMarker::GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectPictureMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectPictureMarker::~GraphicalObjectPictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
resetPictureMarkerSymbol();
resetImage();
resetSimpleMarkerSymbol();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::setImage(const QImage &image, const QSize &defaultSize)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " image.size()=" << image.size()
<< " defaultSize=" << defaultSize
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageChanged=" << m_imageChanged
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed();
resetSimpleMarkerSymbol();
if (m_image != image)
{
m_image = image;
m_imageChanged = true;
}
// store defautlSize (member shouldn´t be modified)
m_imageDefaultSize = defaultSize;
if (m_imageDefaultSize.changed())
m_imageChanged = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " image.size()=" << image.size()
<< " defaultSize=" << defaultSize
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageChanged=" << m_imageChanged
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed();
}
void GraphicalObjectPictureMarker::setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize)
{
resetSimpleMarkerSymbol();
resetImage();
if (m_isOwnPms)
{
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = nullptr;
}
if (m_pictureMarkerSymbol == pms &&
m_imageDefaultSize.value() == defaultSize)
return;
m_isOwnPms = false;
m_pictureMarkerSymbol = pms;
m_imageDefaultSize = defaultSize;
m_imageChanged = true;
}
void GraphicalObjectPictureMarker::resetImage()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
m_image = QImage();
m_imageChanged = true;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::resetPictureMarkerSymbol()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (m_isOwnPms)
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = nullptr;
if (m_workaorundTimer)
m_workaorundTimer->stop();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectPictureMarker::resetSimpleMarkerSymbol()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_simpleMarkerSymbol;
m_simpleMarkerSymbol = nullptr;
m_markerSymbolType = MarkerSymbolType::Undefined;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
MarkerSymbolType GraphicalObjectPictureMarker::markerSymbolType()
{
return m_markerSymbolType.value();
}
int GraphicalObjectPictureMarker::markerSymbolSize()
{
return m_markerSize.value();
}
void GraphicalObjectPictureMarker::setMarkerSymbol(MarkerSymbolType markerSymbolType,
int size, const QColor &color)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " color=" << color;
resetImage();
m_markerSymbolType = markerSymbolType;
m_markerSize = size;
m_markerColor = color;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QSize GraphicalObjectPictureMarker::currentSize() const
{
return m_currentSize;
}
bool GraphicalObjectPictureMarker::create()
{
return BaseClass::create();
}
int GraphicalObjectPictureMarker::xOffset() const
{
return m_externalOffset.value().x();
}
void GraphicalObjectPictureMarker::setXOffset(int xOffset)
{
m_externalOffset.refValue().rx() = xOffset;
}
int GraphicalObjectPictureMarker::yOffset() const
{
return m_externalOffset.value().y();
}
void GraphicalObjectPictureMarker::setYOffset(int yOffset)
{
m_externalOffset.refValue().ry() = yOffset;
}
float GraphicalObjectPictureMarker::rotationAngle()
{
return m_angle.value();
}
void GraphicalObjectPictureMarker::setRotationAngle(float angle)
{
m_angle = angle;
}
bool GraphicalObjectPictureMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_angle = " << m_angle.value()
<< " m_drawOrder = " << m_drawOrder
<< " m_image.size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_imageDefaultSize=" << m_imageDefaultSize
<< " m_imageDefaultSize.changed()=" << m_imageDefaultSize.changed()
<< " m_scale = " << m_scale.value();
// some checks:
if (m_positions.value().size() < 1)
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
//
// Image or Marker ???
//
QString filePath;
QPointF hotSpotPercent;
Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS =
getSimpleMarkerSymbolStyle(m_markerSymbolType.value(),
filePath,
&hotSpotPercent);
m_markerHotSpotPercent = hotSpotPercent;
if (m_markerSymbolType.value() != MarkerSymbolType::Undefined)
{
if (m_markerSize.changed() ||
m_markerSymbolType.changed() ||
m_markerColor.changed() ||
m_markerSymbolType.changed() ||
m_angle.changed() ||
m_markerHotSpotPercent.changed())
{
if (!reCreateSymbol(filePath, esriSMSS))
return false;
}
}
else
{
if (m_image.isNull() && (!m_isOwnPms && m_pictureMarkerSymbol == nullptr))
{
// in context of class 'TacticalAppearance' the PictureMarker exist always...
DEBUG_LOG(m_logger) << LOGFUNCTION << " no image and m_markerSymbolType==Undefined -> nothing to do";
// -> nothing to do:
return true; // -> nothing to do:
}
if (m_scale.changed() ||
m_imageChanged ||
m_markerSymbolType.changed() ||
m_markerSize.changed() ||
m_angle.changed() ||
!m_pictureMarkerSymbol)
{
if (!reCreateSymbol(filePath, esriSMSS))
return false;
}
}
const PositionCont &posCont = m_positions.value();
if (posCont.size() == 1)
{
const Point3D &origin = this->origin();
Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
}
else
{
// Eception: Layer with name "AES2" should NOT have no renderer 8 to enable multipoint graphics
// [GUEZE-496] RPL Nr 770: 3D Lagedarstellung Einfrieren der SW Appl.
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " posCont.size() = " << posCont.size();
Esri::ArcGISRuntime::MultipointBuilder builder(spatialReference());
for (const Point3D &pos : posCont)
builder.points()->addPoint(pos.x(), pos.y(), pos.z());
if (! builder.isSketchValid())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " builder -> sketch invalid";
return false;
}
m_graphic->setGeometry(builder.toGeometry());
}
bool success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
QSize imgSize = m_image.size();
QSizeF pmSize;
if (m_pictureMarkerSymbol)
pmSize = QSizeF(m_pictureMarkerSymbol->width(), m_pictureMarkerSymbol->height());
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex()
<< " m_image->size() = " << imgSize
<< " pictureMarkerSymbol->size() = " << pmSize;
return success;
}
bool GraphicalObjectPictureMarker::reCreateSymbol(const QString &filePath,
const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS)
{
const bool imageRequired = !m_image.isNull() || !filePath.isEmpty();
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " filePath = '" << filePath << "'"
<< " m_image->size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_angle = " << m_angle.value()
<< " m_currentSize = " << m_currentSize
<< " m_imageDefaultSize = " << m_imageDefaultSize.value()
<< " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed();
//
// Now we know what to use: Image or Marker
//
if (!create())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
// what is used currently?
// image or m_simpleMarkerSymbol(esri)?
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " imageRequired=" << imageRequired
<< " m_pictureMarkerSymbol=" << m_pictureMarkerSymbol
<< " m_simpleMarkerSymbol=" << m_simpleMarkerSymbol;
if (!filePath.isEmpty())
{
// e.g. case PushPin2D
QImage img;
if (img.load(filePath))
{
int iSize = qRound(m_markerSize.value() * m_scale.value());
m_currentSize = QSize(iSize, iSize);
img = img.scaledToHeight(iSize, Qt::SmoothTransformation);
QColor localColor = m_markerColor.value();
if (m_markerSymbolType.value() == MarkerSymbolType::PushPin2D && localColor.isValid())
{
QColor colorToBeReplaced(255, 0, 0);
AdvancedSymbology::ExtensionRenderer::replaceColor(img, colorToBeReplaced.rgba(), localColor.rgba());
}
setImage(img);
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " loading image failed, debugInfo='" << m_debugInfo << "'"
<< " path='" << filePath << "'";
return false;
}
}
//
// Transfer data to esriSymbols:
//
// Image:
if (!m_image.isNull() || (!m_isOwnPms && m_pictureMarkerSymbol))
{
resetSimpleMarkerSymbol();
QSize realImageSize;
if (!m_isOwnPms && m_pictureMarkerSymbol)
{
if (m_imageDefaultSize.value().isEmpty())
m_currentSize = m_image.size() * m_scale.value();
else
m_currentSize = m_imageDefaultSize.value() * m_scale.value();
realImageSize = m_currentSize;
}
else
{
// scale localImage by m_scale
QImage localImage;
if (!filePath.isEmpty())
{
// case PushPin2D -> already scaled by m_scale. So don´t scale again:
localImage = m_image;
m_currentSize = localImage.size();
}
else
{
if (m_imageDefaultSize.value().isEmpty())
m_currentSize = m_image.size() * m_scale.value();
else
m_currentSize = m_imageDefaultSize.value() * m_scale.value();
// scaled required?
if (m_image.size() != m_currentSize)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " SCALE FROM " << m_image.size() << " TO " << m_currentSize;
localImage = m_image.scaled(m_currentSize, Qt::KeepAspectRatio, Qt::SmoothTransformation);
++m_scaleCounter4Moduletest;
} else {
localImage = m_image;
m_currentSize = localImage.size();
}
}
DEBUG_LOG(m_logger) << LOGFUNCTION << " m_scaleCounter4Moduletest = " << m_scaleCounter4Moduletest
<< ", localImage.size() = " << localImage.size();
delete m_pictureMarkerSymbol;
m_pictureMarkerSymbol = new Esri::ArcGISRuntime::PictureMarkerSymbol(localImage);
realImageSize = localImage.size();
// Workaround :
// Norbert Thoden, 11.06.2020:
// do some calls again to fix ESRI-Bug:
// Bug: new Image('localImage#) / pictureMarkerSymbol, but sometimes it will not be shown :-(
// Using try and error, we recognize that an additional call seems to fix that issue:
if (!m_workaorundTimer)
{
m_workaorundTimer = new QTimer(m_graphic);
m_workaorundTimer->setInterval(150);
m_workaorundTimer->setSingleShot(true);
QObject::connect(m_workaorundTimer, &QTimer::timeout, m_graphic, [this]() {
if (!m_graphic)
return;
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
});
}
m_workaorundTimer->start();
// : Workaround
}
m_imageChanged = false;
// set image to pms AND ALSO the Heigth and Width!!!
// // !!! Important !!!
// // Case #01713501 - PictureMarker-offset
// // Esri::PictureMarkerSymbol:: setHeight/setWidth -> typographic points.
// // 36pt -> 48pixel -> factor 0.75
// double pixelToPointFactor = 0.71;
// m_pictureMarkerSymbol->setHeight(m_image->size().height() * pixelToPointFactor);
// m_pictureMarkerSymbol->setWidth (m_image->size().width() * pixelToPointFactor);
// // !!! Important !!!
QPointF markerHotSpot = QPointF(realImageSize.width() / 2.0 * m_markerHotSpotPercent.value().x(),
realImageSize.height() / 2.0 * m_markerHotSpotPercent.value().y());
QPointF finalOffset = (markerHotSpot.toPoint() * -1) * 1.33 // 1.33; // try and error (48pixel / 36pt)
+
(m_externalOffset.value() * 0.75); // 0.75; // try and error (36pt / 48pixel)
m_pictureMarkerSymbol->setOffsetX(finalOffset.x());
m_pictureMarkerSymbol->setOffsetY(finalOffset.y());
m_pictureMarkerSymbol->setAngle(m_angle.value());
m_graphic->setSymbol(m_pictureMarkerSymbol);
}
else
{
resetPictureMarkerSymbol();
resetImage();
if (!m_simpleMarkerSymbol)
m_simpleMarkerSymbol = new Esri::ArcGISRuntime::SimpleMarkerSymbol();
m_simpleMarkerSymbol->setSize(m_markerSize.value() * m_scale.value());
m_simpleMarkerSymbol->setStyle(esriSMSS);
m_simpleMarkerSymbol->setAngle(m_angle.value());
m_simpleMarkerSymbol->setOffsetX(m_externalOffset.value().x());
m_simpleMarkerSymbol->setOffsetY(m_externalOffset.value().y());
m_simpleMarkerSymbol->setColor(m_markerColor.value());
m_graphic->setSymbol(m_simpleMarkerSymbol);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " filePath = '" << filePath << "'"
<< " m_image->size() = " << m_image.size()
<< " m_imageChanged = " << m_imageChanged
<< " m_currentSize = " << m_currentSize
<< " m_imageDefaultSize = " << m_imageDefaultSize.value()
<< " m_imageDefaultSize.changed() = " << m_imageDefaultSize.changed();
return true;
}
} // namespace SituationMap
@@ -0,0 +1,153 @@
/**
* @RDE_HEADER@
* GraphicalObjectPictureMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
#include <arcgis/DistanceCompositeSceneSymbol.h>
// Qt
#include <QImage>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class PictureMarkerSymbol;
class SimpleMarkerSymbol;
}
}
class QTimer;
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectPictureMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectPictureMarker(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectPictureMarker();
// Data access:
const QImage &image() const { return m_image; }
QSize imageDefaultSize() const { return m_imageDefaultSize.value(); }
void setImage(const QImage &image, const QSize &defaultSize = QSize());
void setPictureMarkerSymbol(Esri::ArcGISRuntime::PictureMarkerSymbol *pms, QSize defaultSize);
virtual MarkerSymbolType markerSymbolType();
virtual int markerSymbolSize();
virtual void setMarkerSymbol(MarkerSymbolType markerSymbolType,
int size,
const QColor &color = QColor());
virtual QSize currentSize() const;
// Offset (x: to right ; y: to north)
virtual int xOffset() const;
virtual void setXOffset(int xOffset);
virtual int yOffset() const;
virtual void setYOffset(int yOffset);
// rotation / angle
virtual float rotationAngle();
virtual void setRotationAngle(float angle);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_markerSymbolType.changed()
|| m_markerSize.changed()
|| m_markerColor.changed()
|| m_angle.changed()
|| m_markerHotSpotPercent.changed()
|| m_externalOffset.changed()
|| m_imageChanged
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_markerSymbolType.reset();
m_markerSize.reset();
m_markerColor.reset();
m_angle.reset();
m_markerHotSpotPercent.reset();
m_externalOffset.reset();
m_imageChanged = false;
m_imageDefaultSize.reset();
}
bool updateViewImpl() override;
void resetImage();
void resetPictureMarkerSymbol();
void resetSimpleMarkerSymbol();
bool reCreateSymbol(const QString &filePath,
const Esri::ArcGISRuntime::SimpleMarkerSymbolStyle esriSMSS);
// used for image:
QImage m_image;
SimpleValue<QSize> m_imageDefaultSize;
Esri::ArcGISRuntime::PictureMarkerSymbol *m_pictureMarkerSymbol = nullptr;
bool m_imageChanged = true;
bool m_isOwnPms = true;
// used marker:
Esri::ArcGISRuntime::SimpleMarkerSymbol *m_simpleMarkerSymbol = nullptr;
QTimer *m_workaorundTimer = nullptr; // for re-new of Esri::ArcGISRuntime::PictureMarkerSymbol
QSize m_currentSize;
int m_scaleCounter4Moduletest = 0;
SimpleValue<MarkerSymbolType> m_markerSymbolType = MarkerSymbolType::Undefined;
SimpleValue<int> m_markerSize = 10;
SimpleValue<QColor> m_markerColor = QColor(Qt::red);
SimpleValue<float> m_angle = 0; ///
SimpleValue<QPointF> m_markerHotSpotPercent; ///< Store marker-specific offset.
SimpleValue<QPoint> m_externalOffset; ///< Stores external offset.
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
} // namespace SituationMap
@@ -0,0 +1,163 @@
/**
* @RDE_HEADER@
* GraphicalObjectRange
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_range.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/MultipartBuilder.h>
#include <arcgis/ImmutablePartCollection.h>
// Qt
namespace SituationMap
{
GraphicalObjectRange::GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectRange"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectRange::~GraphicalObjectRange()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
int GraphicalObjectRange::range() const
{
return m_range.value();
}
float GraphicalObjectRange::angle() const
{
return m_angleDegree.value();
}
void GraphicalObjectRange::setRange(int range, float angleDegree)
{
m_angleDegree = angleDegree;
m_range = range;
}
bool GraphicalObjectRange::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
EllipseParameters ellipsePara;
ellipsePara.setLengthX(m_range.value());
ellipsePara.setLengthY(m_range.value());
ellipsePara.setDirectionDegree(m_angleDegree.value()) ;
ellipsePara.setDirectionDegree((m_angleDegree.value() * -1) + 90);
ellipsePara.setMaxSegmentLength(10);
ellipsePara.setMaxPointCount(30); // limit to 30 to reduce the number of vertices. If to high, keep performance impact to 3D in mind!!!
this->setEllipseParameters(ellipsePara);
bool success = BaseClass::updateViewImpl();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " drawOrder = " << m_graphic->zIndex()
<< " success = " << success;
return success;
}
bool GraphicalObjectRange::createEllipse(Esri::ArcGISRuntime::Polygon &ellipse)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " range=" << m_range
<< " angle=" << m_angleDegree;
bool success = BaseClass::createEllipse(ellipse);
if (success &&
!ellipse.isEmpty() &&
!ellipse.parts().isEmpty() &&
!ellipse.parts().part(0).isEmpty())
{
const Point3D &center = this->origin();
// 'modify' ellipe (by copying, adding and re-assigning):
// part 0 - ellipse
// we have to copy the point since the ellipse does not contain the z value anymore :-(
Esri::ArcGISRuntime::Part partCircle(ellipse.spatialReference());
const Esri::ArcGISRuntime::ImmutablePart &im = ellipse.parts().part(0);
for (int i = 0; i < im.pointCount(); ++i)
{
const Esri::ArcGISRuntime::Point &p = im.point(i);
partCircle.addPoint(Esri::ArcGISRuntime::Point(p.x(),
p.y(),
center.z() // p.z()
));
}
// part 1 - line to center
Esri::ArcGISRuntime::Part partLine(ellipse.spatialReference());
partLine.addPoint(im.startPoint().x(),
im.startPoint().y(),
center.z());
partLine.addPoint(center.x(),
center.y(),
center.z());
// a polyGON requiree to have at least 3 points:
partLine.addPoint(center.x(),
center.y(),
center.z()+0.01);
// combine them to a PartCollection:
Esri::ArcGISRuntime::PartCollection pc(ellipse.spatialReference());
pc.addPart(&partCircle);
pc.addPart(&partLine);
Esri::ArcGISRuntime::PolygonBuilder builder(ellipse.spatialReference());
builder.setParts(&pc);
ellipse = builder.toPolygon();
success = true;
}
else
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'";
return success;
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<<(std::ostream &stream,
const GraphicalObjectRange &value)
{
stream << value.toString();
return stream;
}
} // namespace SituationMap
@@ -0,0 +1,87 @@
/**
* @RDE_HEADER@
* GraphicalObjectRange
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "graphical_object_ellipse.h"
// Esri
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectRange : public GraphicalObjectEllipse
{
using BaseClass = GraphicalObjectEllipse;
public:
GraphicalObjectRange(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectRange();
int range() const;
float angle() const;
void setRange(int range, float angleDegree);
protected:
// Internal/Admin
// bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_angleDegree.changed()
|| m_range.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_angleDegree.reset();
m_range.reset();
}
bool updateViewImpl() override;
bool createEllipse(Esri::ArcGISRuntime::Polygon &ellipse) override;
private:
SimpleValue<float> m_angleDegree = 0;
SimpleValue<int> m_range = 0;
LOGGER_T m_logger; ///< Interface of the Logging-Facility
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const GraphicalObjectRange &value);
} // namespace SituationMap
@@ -0,0 +1,136 @@
/**
* @RDE_HEADER@
* GraphicalObjectRendererAttributes
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_renderer_attributes.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/ModelSceneSymbol.h>
// Qt
#include <QPainter>
#include <QFile>
namespace SituationMap
{
GraphicalObjectRendererAttributes::GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_logger(getLogger("GraphicalObjectRendererAttributes"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectRendererAttributes::~GraphicalObjectRendererAttributes()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectRendererAttributes::setAttributeMap(const QVariantMap &attribMap)
{
m_attributeMap = attribMap;
}
QVariantMap GraphicalObjectRendererAttributes::attributeMap() const
{
return m_attributeMap.value();
}
bool GraphicalObjectRendererAttributes::create()
{
return BaseClass::create();
}
bool GraphicalObjectRendererAttributes::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_attributeMap = " << m_attributeMap.value().size()
<< " m_drawOrder = " << m_drawOrder;
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const Point3D &origin = this->origin();
m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(),
origin.y(),
origin.z(),
spatialReference()));
if (m_attributeMap.changed())
{
QVariantMap cur = m_graphic->attributes()->attributesMap();
if (m_logger.isDebugEnabled())
qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap before unite " << cur;
cur.unite(m_attributeMap.value());
if (m_logger.isDebugEnabled())
qCritical() << " debugInfo='" << m_debugInfo << "'" << ", QVariantMap after unite " << cur;
m_graphic->attributes()->setAttributesMap(cur);
}
// keep anchor like Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Bottom in mind.
// assigned during setup the Renderer (LayerFactory, AdapterDds...)
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,102 @@
/**
* @RDE_HEADER@
* GraphicalObjectRendererAttributes
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
#include <QVariantMap>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class ModelSceneSymbol;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectRendererAttributes : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectRendererAttributes(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectRendererAttributes();
// Data access:
// to be cont.
void setAttributeMap(const QVariantMap &attribMap);
QVariantMap attributeMap() const;
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_attributeMap.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_attributeMap.reset();
}
bool updateViewImpl() override;
// no ModelSceneSymbol since Renderer-based
SimpleValue<QVariantMap> m_attributeMap;
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,205 @@
/**
* @RDE_HEADER@
* GraphicalObjectSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_scene_marker.h"
#include <Map/situation_map_utils.h> // operator<<
#include <MathUtils/simple_helper.h>
#include <LtsCoordTrans/lts_coord_trans.h>
// ESRI
#include <arcgis/arcgisversion.h> // This file is created by RME, not ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/SimpleMarkerSceneSymbol.h>
// Qt
#include <QPainter>
#include <QTimer>
namespace SituationMap
{
GraphicalObjectSceneMarker::GraphicalObjectSceneMarker(SceneMarkerType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_type(type)
, m_logger(getLogger("GraphicalObjectSceneMarker"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectSceneMarker::~GraphicalObjectSceneMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete m_simpleMarkerSceneSymbol;
m_simpleMarkerSceneSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void GraphicalObjectSceneMarker::setDiameter(double diameter)
{
m_diameter = diameter;
}
double GraphicalObjectSceneMarker::diameter() const
{
return m_diameter.value();
}
bool GraphicalObjectSceneMarker::create()
{
return BaseClass::create();
}
bool GraphicalObjectSceneMarker::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " pickable=" << m_pickable
<< " pickable.changed=" << m_pickable.changed()
<< " selected=" << m_selected
<< " m_diameter = " << m_diameter.value()
<< " m_heading = " << m_heading.value()
<< " m_pitch = " << m_pitch.value()
<< " m_roll = " << m_roll.value()
<< " m_drawOrder = " << m_drawOrder
<< " m_lineStyle = " << m_lineStyle.value()
<< " m_fillStyle = " << m_fillStyle.value()
<< " fillColor = " << m_fillStyle.value().fillColor()
<< " m_scale = " << m_scale.value();
if (m_graphicsLayer->renderingMode() != Esri::ArcGISRuntime::GraphicsRenderingMode::Dynamic)
for (int i=0; i<10; ++i)
ERROR_LOG(m_logger) << " RenderingMode::Dynamic required!!! , debugInfo='" << m_debugInfo << "'";
// some checks:
if (!isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION
<< " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
const double radiusHeight = m_diameter.value()/2.0;
Point3D origin = this->origin();
// Try to set AnchorPosition::Top, to let the cone rotate around top/tip.
// Using a cone, the anchor point does not work as expected, we have to move
if (m_type == SceneMarkerType::CONE)
{
//
// It´s a SCENE Marker. So we are per definition in 3D and origin is in degree.
//
// heading is in degree:m_heading.value()
// offset as vector:
QPointF offsetMeter(0, radiusHeight/2.0);
const int rotAngleDegree = (static_cast<int>(m_heading.value())-90)%360;
DEBUG_LOG(m_logger) << " offsetMeter = " << offsetMeter << ", m_heading = " << m_heading.value() << ", rotAngleDegree = " << rotAngleDegree;
MathUtils::rotatePoint(offsetMeter, QPointF(), rotAngleDegree);
// new temporary origin...
// -> rad
origin *= LtsCoordTrans::deg2rad();
LtsCoordTrans::UtmCoordinate newOrigin = LtsCoordTrans::UtmCoordinate::fromGeographic(origin.x(), origin.y());
DEBUG_LOG(m_logger) << " newOrigin = " << newOrigin.mgrs();
// add rotated offset to temporary origin:
const QPointF origin2D = newOrigin.geographic(offsetMeter) * LtsCoordTrans::rad2deg();
// -> deg
// write back; z-value stays untouched:
origin.rx() = origin2D.x();
origin.ry() = origin2D.y();
}
const Esri::ArcGISRuntime::Point originGeometry(origin.x(),
origin.y(),
origin.z(),
spatialReference());
m_graphic->setGeometry(originGeometry);
if (!m_simpleMarkerSceneSymbol)
{
switch (m_type)
{
case SceneMarkerType::CONE: // Kegel
m_simpleMarkerSceneSymbol =
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::cone(
m_fillStyle.value().fillColor(),
radiusHeight, radiusHeight, // V100.8: cone: 'diameter' is interpreted as radius
Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center); // V100.8: cone: Top does not work as aspected - grrr
break;
case SceneMarkerType::SPHERE: // Kugel
m_simpleMarkerSceneSymbol =
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol::sphere(
m_fillStyle.value().fillColor(),
radiusHeight*2.0,
Esri::ArcGISRuntime::SceneSymbolAnchorPosition::Center);
break;
} // switch
}
m_simpleMarkerSceneSymbol->setColor(m_fillStyle.value().fillColor());
if (m_type == SceneMarkerType::CONE)
{
m_simpleMarkerSceneSymbol->setWidth(radiusHeight);
m_simpleMarkerSceneSymbol->setHeight(radiusHeight);
m_simpleMarkerSceneSymbol->setDepth(radiusHeight);
} else {
m_simpleMarkerSceneSymbol->setWidth(m_diameter.value());
m_simpleMarkerSceneSymbol->setHeight(m_diameter.value());
m_simpleMarkerSceneSymbol->setDepth(m_diameter.value());
}
m_simpleMarkerSceneSymbol->setHeading(m_heading.value());
m_simpleMarkerSceneSymbol->setPitch(m_pitch.value());
m_simpleMarkerSceneSymbol->setRoll(m_roll.value());
m_graphic->setSymbol(m_simpleMarkerSceneSymbol);
success = BaseClass::updateViewImpl();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
success = addUpdateGraphic();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateView failed";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success=" << success
<< " m_drawOrder = " << m_drawOrder
<< " m_graphic->drawOrder = " << m_graphic->zIndex();
return success;
}
} // namespace SituationMap
@@ -0,0 +1,137 @@
/**
* @RDE_HEADER@
* GraphicalObjectSceneMarker
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Common/situation_map_logging.h>
// Esri
#include <arcgis/SymbolTypes.h>
// Qt
#include <QImage>
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class SimpleMarkerSceneSymbol;
}
}
class QTimer;
//----------------------------------------------------------------------------
namespace SituationMap
{
//
//
//
// !!! Layer with RenderingMode::Dynamic required !!!
// !!! Layer with RenderingMode::Dynamic required !!!
// !!! Layer with RenderingMode::Dynamic required !!!
//
//
//
enum class SceneMarkerType {CONE, SPHERE};
class SITUATIONMAP_EXPORT GraphicalObjectSceneMarker : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectSceneMarker(SceneMarkerType type
, const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId);
~GraphicalObjectSceneMarker();
// Data access:
// to be cont. (type is sphere, to support cube, cylinder etc)
void setDiameter(double diameter);
double diameter() const;
double heading() const { return m_heading.value(); }
void setHeading(double headingDegree) { m_heading = headingDegree; }
double pitch() const { return m_pitch.value(); }
void setPitch(double pitchDegree) { m_pitch = pitchDegree; }
double roll() const { return m_roll.value(); }
void setRoll(double rollDegree) { m_roll = rollDegree; }
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_diameter.changed()
|| m_heading.changed()
|| m_pitch.changed()
|| m_roll.changed()
;
}
void resetChanged() override
{
BaseClass::resetChanged();
m_diameter.reset();
m_heading.reset();
m_pitch.reset();
m_roll.reset();
}
bool updateViewImpl() override;
// used marker:
Esri::ArcGISRuntime::SimpleMarkerSceneSymbol *m_simpleMarkerSceneSymbol{nullptr};
SceneMarkerType m_type{SceneMarkerType::SPHERE};
SimpleValue<double> m_diameter{25};
SimpleValue<double> m_heading{0}; // degree
SimpleValue<double> m_pitch{0}; // degree
SimpleValue<double> m_roll{0}; // degree
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
PositionCont positions() const override
{
return PositionCont();
}
void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
Q_ASSERT(false);
}
};
} // namespace SituationMap
@@ -0,0 +1,340 @@
/**
* @RDE_HEADER@
* GraphicalObjectText
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "graphical_object_text.h"
#include <Map/situation_map_utils.h> // operator<<
// ESRI
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/TextSymbol.h>
#include <arcgis/PictureMarkerSymbol.h>
// Qt
#include <QString>
#include <iostream>
#include <algorithm>
namespace SituationMap
{
GraphicalObjectText::GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId)
: BaseClass(geoView, graphicsLayer, debugInfo, uniqueMapId)
, m_textSymbol(nullptr)
, m_logger(getLogger("GraphicalObjectText"))
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
GraphicalObjectText::~GraphicalObjectText()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'";
delete m_textSymbol;
m_textSymbol = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'";
}
QString GraphicalObjectText::toString() const
{
QString str;
// str.append( BaseClass::toString() );
if (!m_debugInfo.isEmpty())
str.append(QStringLiteral("debugInfo=%1").arg(m_debugInfo));
if (m_graphicsLayer)
str.append(QStringLiteral(" layer='%1'").arg(m_graphicsLayer->objectName()));
if (m_graphic)
str.append(QStringLiteral(" zIndex='%1'").arg(m_graphic->zIndex()));
std::ostringstream stream;
stream << this->origin();
str.append(QLatin1String(stream.str().c_str()));
if (!m_text.value().isEmpty())
str.append(QStringLiteral("text=%1").arg(m_text.value()));
return str;
}
QString GraphicalObjectText::text() const
{
return m_text.value();
}
void GraphicalObjectText::setText(const QString &text)
{
m_text = text;
}
TextStyle GraphicalObjectText::textStyle() const
{
return m_textStyle.value();
}
TextStyle &GraphicalObjectText::rTextStyle()
{
return m_textStyle.refValue();
}
void GraphicalObjectText::setTextStyle(const TextStyle &textStyle)
{
m_textStyle = textStyle;
}
float GraphicalObjectText::xOffset() const
{
return m_offset.value().x();
}
void GraphicalObjectText::setXOffset(float xOffset)
{
QPointF temp = m_offset.value();
temp.rx() = xOffset;
m_offset = temp;
}
float GraphicalObjectText::yOffset() const
{
return m_offset.value().y();
}
void GraphicalObjectText::setYOffset(float yOffset)
{
QPointF temp = m_offset.value();
temp.ry() = yOffset;
m_offset = temp;
}
bool GraphicalObjectText::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility;
bool success = true;
// some checks:
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no graphicsLayer";
return false;
}
BaseClass::create();
if (!m_textSymbol)
m_textSymbol = new Esri::ArcGISRuntime::TextSymbol();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " success=" << success
<< " drawOrder = " << m_graphic->zIndex();
return success;
}
bool GraphicalObjectText::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " origin =" << this->origin() // .x() << " , " << this->origin().y()
<< " visibility=" << m_visibility
<< " m_textStyle=" << m_textStyle
<< " offset=" << m_offset;
// some checks:
if (!this->isSinglePoint())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " incorrect number of positions"
<< " (" << this->positions().count() << ")";
return false;
}
bool success = true;
if (m_visibility.value())
{
success = create(); // create only if visible
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (! BaseClass::updateViewImpl())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " updateViewImpl failed";
}
// FATAL_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
// << " text='" << m_text << "'"
// << " changed1=" << m_text.changed()
// << " changed2="<< m_textStyle.changed();
bool assignTextSymbol = false;
// || m_visibility.changed()) -> szenario: create invisible object and make it visible
if (m_text.changed() || m_visibility.changed())
{
m_textSymbol->setText(m_text.value());
assignTextSymbol = true;
}
// TextStyle to TextSymbol@ESRI :
if (m_textStyle.changed()
|| m_scale.changed()
|| m_visibility.changed() // -> szenario: create invisible object and make it visible
)
{
const TextStyle &textStyle = m_textStyle.value();
m_textSymbol->setColor(textStyle.textColor());
// High values can lead to coredump with 'SymbolDrawHelper::create_line_image FAILED, SkBitmap is NULL or empty' -> limit
m_textSymbol->setSize(std::min(500.0f, textStyle.size() * m_scale.value()));
m_textSymbol->setAngle(textStyle.angle());
m_textSymbol->setFontFamily(textStyle.fontFamily());
m_textSymbol->setFontWeight(textStyle.bold() ? Esri::ArcGISRuntime::FontWeight::Bold : Esri::ArcGISRuntime::FontWeight::Normal);
m_textSymbol->setFontStyle(textStyle.italic() ? Esri::ArcGISRuntime::FontStyle::Italic : Esri::ArcGISRuntime::FontStyle::Normal);
Esri::ArcGISRuntime::FontDecoration fd = Esri::ArcGISRuntime::FontDecoration::None;
if (textStyle.underline())
fd = Esri::ArcGISRuntime::FontDecoration::Underline;
if (textStyle.strikeThrough())
fd = Esri::ArcGISRuntime::FontDecoration::LineThrough;
m_textSymbol->setFontDecoration(fd);
float outlineWidth = 0;
if (textStyle.textOutlineWidth() > 0)
outlineWidth = textStyle.textOutlineWidth() * m_scale.value();
if (m_textSymbol->size() <= 15) // small size -> outline reduces readability :-(
outlineWidth = 0.0;
m_textSymbol->setHaloWidth(outlineWidth);
if (textStyle.textOutlineColor().isValid())
m_textSymbol->setHaloColor(textStyle.textOutlineColor());
// qWarning() << " haloWidth = " << m_textSymbol->haloWidth();
// m_textSymbol->setHaloColor(Qt::white);
// m_textSymbol->setHaloWidth(outlineWidth);
// qWarning() << " fontSize " << textStyle.size() * m_scale.value()
// <<" (textStyle.size() " << textStyle.size()
// << " m_scale.value() " << m_scale.value() << ")";
// qWarning() << " outlineWidth " << outlineWidth
// <<" (textStyle.textOutlineWidth() " << textStyle.textOutlineWidth()
// << " m_scale.value() " << m_scale.value() << ")";
// if (textStyle.backgroundColor().isValid())
m_textSymbol->setBackgroundColor(textStyle.backgroundColor());
// m_textSymbol->setAntiAliased(textStyle.antiAliased());
m_textSymbol->setHorizontalAlignment(convert(textStyle.horizontalAlignment()));
m_textSymbol->setVerticalAlignment(convert(textStyle.verticalAlignment()));
assignTextSymbol = true;
// : TextStyle to TextSymbol@ESRI
}
// || m_visibility.changed()) -> szenario: create invisible object and make it visible
if (m_offset.changed() || m_visibility.changed())
{
m_textSymbol->setOffsetX(m_offset.value().x());
m_textSymbol->setOffsetY(m_offset.value().y());
assignTextSymbol = true;
}
if (assignTextSymbol)
m_graphic->setSymbol(m_textSymbol);
// check ChangedFlags:
// Object is created and text is invisble due to scale (meterPerPixel)
// map is zoomed in -> text should arise, but on m_visibility is changed:
if (m_visibility.changed() || m_positions.changed())
{
const Point3D &origin = this->origin();
m_graphic->setGeometry(Esri::ArcGISRuntime::Point(origin.x(),
origin.y(),
origin.z(),
spatialReference()));
success = addUpdateGraphic();
} // m_visibility.changed() || m_positions.changed()
}
else // if visible
{
BaseClass::updateViewImpl();
success = addUpdateGraphic();
}
if (m_logger.isTraceEnabled())
{
int drawOrder = -1;
if (m_graphic)
drawOrder = m_graphic->zIndex();
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " text='" << m_text << "'"
<< " success=" << success
<< " drawOrder = " << drawOrder;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
// << " fontFamily='" << m_textSymbol->fontFamily() << "'"
;
return success;
}
Esri::ArcGISRuntime::HorizontalAlignment GraphicalObjectText::convert(const HorizontalAlignment horizontalAlignment)
{
Esri::ArcGISRuntime::HorizontalAlignment esriAlignmenmt =
Esri::ArcGISRuntime::HorizontalAlignment::Left;
switch (horizontalAlignment)
{
case HorizontalAlignment::Left: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Left; break;
case HorizontalAlignment::Center: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Center; break;
case HorizontalAlignment::Right: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Right; break;
case HorizontalAlignment::Justify: esriAlignmenmt = Esri::ArcGISRuntime::HorizontalAlignment::Justify; break;
} // switch
return esriAlignmenmt;
}
Esri::ArcGISRuntime::VerticalAlignment GraphicalObjectText::convert(const VerticalAlignment verticalAlignment)
{
Esri::ArcGISRuntime::VerticalAlignment esriAlignmenmt =
Esri::ArcGISRuntime::VerticalAlignment::Bottom;
switch (verticalAlignment)
{
case VerticalAlignment::Bottom: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Bottom; break;
case VerticalAlignment::Middle: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Middle; break;
case VerticalAlignment::Top: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Top; break;
case VerticalAlignment::BaseLine: esriAlignmenmt = Esri::ArcGISRuntime::VerticalAlignment::Baseline; break;
} // switch
return esriAlignmenmt;
}
} // namespace SituationMap
@@ -0,0 +1,125 @@
/**
* @RDE_HEADER@
* GraphicalObjectText
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/text_style.h>
#include <Map/situation_map_utils.h>
#include <Common/simple_value.h>
#include <Common/situation_map_logging.h>
// Esri
// Qt
// forward:
namespace Esri
{
namespace ArcGISRuntime
{
class Graphic;
class GraphicsOverlay;
class PictureMarkerSymbol;
class TextSymbol;
enum class HorizontalAlignment;
enum class VerticalAlignment;
}
}
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT GraphicalObjectText : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
GraphicalObjectText(const Esri::ArcGISRuntime::GeoView *geoView
, Esri::ArcGISRuntime::GraphicsOverlay *graphicsLayer
, const QString &debugInfo
, const UniqueMapId &uniqueMapId
);
~GraphicalObjectText();
QString toString() const override; // specialized
// Data access:
QString text() const;
void setText(const QString &text);
TextStyle textStyle() const;
TextStyle &rTextStyle();
void setTextStyle(const TextStyle &textStyle);
// Offset
virtual float xOffset() const;
virtual void setXOffset(float xOffset);
virtual float yOffset() const;
virtual void setYOffset(float yOffset);
static Esri::ArcGISRuntime::HorizontalAlignment convert(const HorizontalAlignment horizontalAlignment);
static Esri::ArcGISRuntime::VerticalAlignment convert(const VerticalAlignment verticalAlignment);
protected:
// Internal/Admin
bool create() override;
bool changed() override
{
return BaseClass::changed()
|| m_text.changed()
|| m_textStyle.changed()
|| m_offset.changed();
}
void resetChanged() override
{
BaseClass::resetChanged();
m_text.reset();
m_textStyle.reset();
m_offset.reset();
}
bool updateViewImpl() override;
Esri::ArcGISRuntime::TextSymbol *m_textSymbol = nullptr;
SimpleValue<QString> m_text; ///< to be displayed on map
SimpleValue<TextStyle> m_textStyle;
SimpleValue<QPointF> m_offset; ///< Offset (pixel).
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
// Forbid some overloaded methods without effects of this object;
virtual PositionCont positions() const override
{
return PositionCont();
}
virtual void setPositions(const PositionCont &posCont) override
{
Q_UNUSED(posCont);
}
};
} // namespace SituationMap
@@ -0,0 +1,69 @@
/**
* @RDE_HEADER@
* StubGraphicalObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
#include <Map/situation_map_types.h>
// Esri
namespace SituationMap
{
/**
@class StubGraphicalObject stub_graphical_object.h
The class \c StubGraphicalObject is used as substitution for a graphical object. It does
nothing and the purpose is only to satisfy the interface of AbstractGraphicalObject.
@see AbstractGraphicalObject
*/
class StubGraphicalObject : public AbstractGraphicalObject
{
public:
explicit StubGraphicalObject()
: AbstractGraphicalObject(nullptr, nullptr, QString(), UniqueMapId())
{
}
~StubGraphicalObject() = default;
StubGraphicalObject(const StubGraphicalObject &other) = delete;
StubGraphicalObject &operator=(StubGraphicalObject &other) = delete;
protected:
bool changed() override
{
return false;
}
bool updateViewImpl() override
{
return true;
}
bool create() override
{
return true;
}
};
} // namespace SituationMap
@@ -0,0 +1,622 @@
/**
* @RDE_HEADER@
* TacticalAppearance
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "tactical_appearance.h"
#include <MapImpl/map_impl.h>
#include <Symbology/symbol_dictionary.h>
#include <Symbology/sidc_builder_base.h>
#include <Symbology/sidc_codec.h>
#include <GraphicalObjects/graphical_object_picture_marker.h>
#include <Map/situation_map_utils.h> // operator<<
#include <Common/simple_value.h>
// ESRI
#include <arcgis/Map.h>
#include <arcgis/GraphicsOverlay.h>
#include <arcgis/PolygonBuilder.h>
#include <arcgis/PolylineBuilder.h>
#include <arcgis/MultipointBuilder.h>
// Qt
#include <QElapsedTimer>
namespace SituationMap
{
class TacticalAppearance::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
MapImpl *m_mapImpl = nullptr;
QString m_logicalLayerName;
Esri::ArcGISRuntime::GraphicsOverlay *m_graphicOverlayTactical = nullptr;
AdvancedSymbology::SymbolDictionary *m_dictionary = nullptr;
AdvancedSymbology::SymbolResultWatcher m_symbolResultWatcher;
// OWN data:
SimpleValue<QString> m_uniqueDesignation;
SimpleValue<SymbolData> m_symbol;
AdvancedSymbology::SymbolDataToRender m_symDataToRender;
GraphicalObjectPictureMarker *m_pictureMarker = nullptr;
}; // class TacticalAppearance::Private
//-----------------------------------------------------------------------------
TacticalAppearance::TacticalAppearance(MapImpl *mapImpl,
const QString &logicalLayerName,
const QString &debugInfo,
const UniqueMapId &uniqueMapId)
: BaseClass(mapImpl->getEsriGeoView(), nullptr, debugInfo, uniqueMapId),
m_logger(getLogger("TacticalAppearance")),
d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", logicalLayerName='" << logicalLayerName << "'";
d->m_symDataToRender.sizeL = 100;
d->m_mapImpl = mapImpl;
d->m_logicalLayerName = logicalLayerName;
if (!m_graphicsLayer && d->m_mapImpl)
m_graphicsLayer = d->m_mapImpl->getGraphicsLayer(d->m_logicalLayerName);
if (!m_graphicsLayer)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " getGraphicsLayer failed";
}
if (d->m_mapImpl)
d->m_dictionary = &(d->m_mapImpl->symbolDictionary());
// lambda:
// m_symbolResultWatcher is canceled on destruction.
// So it`s safe that no signal will be fired afterwards.
// To avoid CLang warnings use m_symbolResultWatcher as context (3rd para), although it´s not intended.
// Better idea?
QObject::connect(&d->m_symbolResultWatcher, &AdvancedSymbology::SymbolResultWatcher::resultReady, &d->m_symbolResultWatcher, [this]()
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " resultReady...";
QSharedPointer<AdvancedSymbology::SymbolResult> symResult = d->m_symbolResultWatcher.symbolResult();
if (! symResult)
return; // should NEVER happen! (Which object should have triggered the signal???)
if (d->m_pictureMarker)
{
d->m_pictureMarker->setImage(symResult->symbolImage().image());
d->m_pictureMarker->updateView();
}
});
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
TacticalAppearance::~TacticalAppearance()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
// abort pending call to SymbolDictionary::findSymbol:
d->m_symbolResultWatcher.cancel();
delete d->m_pictureMarker;
d->m_pictureMarker = nullptr;
removeGraphicFromGraphicOverlayTactical();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void TacticalAppearance::removeGraphicFromGraphicOverlayTactical()
{
if (m_logger.isTraceEnabled())
{
if (m_graphic)
{
Esri::ArcGISRuntime::GraphicsOverlay *gOverlay = m_graphic->graphicsOverlay();
if (gOverlay)
TRACE_LOG(m_logger) << LOGFUNCTION << " START debugInfo='" << m_debugInfo << "'" << " name = '" << gOverlay->overlayId() << "'";
}
}
if (m_graphic && d->m_graphicOverlayTactical && d->m_graphicOverlayTactical->graphics())
d->m_graphicOverlayTactical->graphics()->removeOne(m_graphic);
delete m_graphic;
m_graphic = nullptr;
}
bool TacticalAppearance::useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const
{
bool usePictureMarker = isSinglePoint() && (AdvancedSymbology::GeometryType::Point == geomType || AdvancedSymbology::GeometryType::Undefined == geomType);
return !usePictureMarker || symbol().useAlwaysDictionaryRenderer;
} // bool TacticalAppearance::useDictionaryRenderMode() const
bool TacticalAppearance::create()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " m_visibility = " << m_visibility;
bool success = true;
//
// Prepare / create :
//
// a) singlePoint / exactly one coordinate -> use pictureMarker
// or
// b) multiPoint / more than one coordinate -> m_graphic / DictionaryRenderer
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* This assumption doesn't work any more because some polyline and polygon symbols will have only one
* anchor point and the real geometry is defined by additional attributes like radius, with, length ...
*
* JT 2016/12/20
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (m_graphicsLayer && m_visibility.value())
{
const SymbolData &symbolData_ref = d->m_symbol.value();
const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc);
const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode);
AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType();
if (useDictionaryRenderMode(geomType))
{
if (!d->m_graphicOverlayTactical)
d->m_graphicOverlayTactical = d->m_mapImpl->getGraphicsLayerDict(d->m_logicalLayerName);
if (!d->m_graphicOverlayTactical)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " getGraphicsLayerDict failed";
return false;
}
if (!m_graphic)
{
m_graphic = new Esri::ArcGISRuntime::Graphic();
m_graphic->setObjectName(m_debugInfo);
m_graphic->setZIndex(m_drawOrder.value());
// use attributeModel to store application obj id (SimWorld) at graphic
Esri::ArcGISRuntime::AttributeListModel *attLisModel = m_graphic->attributes();
if (attLisModel)
attLisModel->insertAttribute(QLatin1String(SituationMap::UniqueMapIdAttribute), m_uniqueMapId);
else
ERROR_LOG(m_logger) << LOGFUNCTION << " no AttributeListModel, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId;
// Adding an empty graphic leads to Segmentation fault.
// (in Esri_runtimecore::Map_renderer::Dictionary_renderer::derive_geometry)
bool successGraphic = addUpdateGraphic(); // CHECK
if (!successGraphic)
ERROR_LOG(m_logger) << LOGFUNCTION << " addUpdateGraphic failed, debugInfo='" << m_debugInfo << "'" << " uniqueMapId=" << m_uniqueMapId;
} // if (!m_graphic)
}
else
{
// m_graphic is needless now:
removeGraphicFromGraphicOverlayTactical();
}
}
// : Prepare / create
bool positionsEmpty = m_positions.value().isEmpty();
if (positionsEmpty)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " positions empty";
success = success && !positionsEmpty;
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " create failed";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
return success;
}
void TacticalAppearance::createPictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
if (!d->m_pictureMarker && d->m_mapImpl)
{
d->m_pictureMarker = new GraphicalObjectPictureMarker(d->m_mapImpl->getEsriGeoView()
, m_graphicsLayer
, m_debugInfo + QLatin1String("#PicMarkTacticalAppearance")
, m_uniqueMapId);
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
void TacticalAppearance::removePictureMarker()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'";
delete d->m_pictureMarker;
d->m_pictureMarker = nullptr;
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'";
}
QString TacticalAppearance::uniqueDesignation() const
{
return d->m_uniqueDesignation.value();
}
void TacticalAppearance::setUniqueDesignation(const QString &uniqueDesignation)
{
d->m_uniqueDesignation = uniqueDesignation;
}
SymbolImage TacticalAppearance::symbolImage() const
{
SymbolImage symImage;
if (d->m_pictureMarker)
{
symImage.setImage(d->m_pictureMarker->image());
symImage.setValid(!symImage.image().isNull());
}
return symImage;
}
SymbolData TacticalAppearance::symbol() const
{
return d->m_symbol.value();
}
void TacticalAppearance::setSymbol(const SymbolData &symbol)
{
d->m_symbol = symbol;
}
bool TacticalAppearance::changed()
{
return BaseClass::changed()
|| d->m_uniqueDesignation.changed()
|| d->m_symbol.changed();
}
void TacticalAppearance::resetChanged()
{
BaseClass::resetChanged();
d->m_uniqueDesignation.reset();
d->m_symbol.reset();
}
Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const
{
if (d->m_pictureMarker) return d->m_pictureMarker->getGraphicGeometry();
if (m_graphic) return m_graphic->geometry();
ERROR_LOG(m_logger) << LOGFUNCTION << "Geometry is undefined";
return Esri::ArcGISRuntime::Geometry();
} // Esri::ArcGISRuntime::Geometry TacticalAppearance::getGraphicGeometry() const
bool TacticalAppearance::updateViewImpl()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< " symbol='" << d->m_symbol.value().sidc << "'"
<< " visibility=" << m_visibility
<< " selected=" << m_selected
<< " origin=" << this->origin()
<< " scale=" << m_scale.value();
const SymbolData &symbolData_ref = d->m_symbol.value();
const QString &baseSymbolCode = AdvancedSymbology::SidcBuilderBase::concreteSidcToBaseSymbolCode(symbolData_ref.sidc);
const AdvancedSymbology::SymbolProperties &symbolProperties = d->m_dictionary->symbolProperties(baseSymbolCode);
AdvancedSymbology::GeometryType geomType = symbolProperties.geometryType();
if (geomType == AdvancedSymbology::GeometryType::Undefined)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " GeometryType::Undefined, symbolProperties failed!?!";
return false;
}
TRACE_LOG(m_logger) << LOGFUNCTION << " START, debugInfo='" << m_debugInfo << "'"
<< ", geomType=" << geomType
<< ", useDictionaryRenderMode(geomType)=" << useDictionaryRenderMode(geomType);
bool success = create();
if (!success)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " create failed";
return false;
}
if (useDictionaryRenderMode(geomType))
{
// real work:
if (m_positions.changed() || d->m_symbol.changed() || m_visibility.changed())
{
auto buildPointClusterGeometry = [this]() -> Esri::ArcGISRuntime::Geometry
{
QSharedPointer<Esri::ArcGISRuntime::MultipointBuilder> builder(new Esri::ArcGISRuntime::MultipointBuilder(spatialReference()));
Esri::ArcGISRuntime::PointCollection points(spatialReference());
const auto posCont = positions();
for (const auto &pos : posCont)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " addPoint:" << pos.x() << " - " << pos.y();
points.addPoint(pos.x(), pos.y());
}
builder->setPoints(&points);
return builder->toGeometry();
}; // buildPointClusterGeometry
Esri::ArcGISRuntime::Geometry geom;
switch (geomType)
{
case AdvancedSymbology::GeometryType::Undefined : break;
case AdvancedSymbology::GeometryType::Point :
case AdvancedSymbology::GeometryType::Polyline :
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count();
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* If a PolylineBuilder is used here to create symbol geometry the construction appropriate symbol standard (MIL2525C / MIL2525D / App6)
* will not be done and the symbol is always a simple polyline. Using MultipointBuilder to build symbol geometry the result is based on
* underlying symbol standard.
* 2016/12/9 JT
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
geom = buildPointClusterGeometry();
} break;
case AdvancedSymbology::GeometryType::Polygon :
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " m_positions.count()=" << m_positions.value().count();
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* Some symbols have polygon geometry and could have only one or two positions. In case of less than 3 positions do the same as for
* polylines and use MultipointBuilder to create the symbol geometry. In other case use PolygonBuilder.
* 2016/12/13 JT
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
if (positions().size() > 2)
{
QSharedPointer<Esri::ArcGISRuntime::PolygonBuilder> builder(new Esri::ArcGISRuntime::PolygonBuilder(spatialReference()));
const auto posCont = positions();
for (const auto &pos : posCont)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " addPoint:" << pos.x() << " - " << pos.y();
builder->addPoint(pos.x(), pos.y());
}
geom = builder->toGeometry();
}
else
geom = buildPointClusterGeometry();
} break;
} // switch
if (geom.isEmpty())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " Geometry NOT OK -> fallback symbol" << " (geometryType was " << geomType << ")";
createPictureMarker();
d->m_pictureMarker->setDebugInfo(m_debugInfo + QLatin1String("#fallback"));
int size = symbolData_ref.size;
if (size < 10) size = 10;
if (size > 100) size = 100;
d->m_pictureMarker->setMarkerSymbol(MarkerSymbolType::Diamond, size, symbolData_ref.color);
d->m_pictureMarker->setOrigin(this->origin());
d->m_pictureMarker->setDrawOrder(m_drawOrder.value());
d->m_pictureMarker->updateView();
success = false;
}
else // geom is not empty
{
removePictureMarker();
if (m_graphic)
{
success = true;
// move to graphic:
m_graphic->setGeometry(geom);
// prepare rawAttributes for DictionaryRenderer:
QVariantMap rawAttributes = d->m_symbol.value().attributeMap;
// build from scratch to honor removed attributes:
// (Otherwise the will remain wrongly)
rawAttributes[QLatin1String(SituationMap::UniqueMapIdAttribute)] = m_uniqueMapId;
rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21
rawAttributes[QLatin1String("_type")] = QLatin1String("position_report");
rawAttributes[QLatin1String("_action")] = QLatin1String("update");
rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525
rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6
Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes();
if (attributeModel)
attributeModel->setAttributesMap(rawAttributes);
else
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'" << " no AttributeListModel";
// // prepare rawAttributes for DictionaryRenderer:
// QVariantMap rawAttributes = d->m_symbol.value().attributeMap;
// rawAttributes[QLatin1String(SituationMap::IsPickableAttribute)] = m_pickable.value(); // set pickable attribute JT 2016/12/21
// rawAttributes[QLatin1String("_type")] = QLatin1String("position_report");
// rawAttributes[QLatin1String("_action")] = QLatin1String("update");
// rawAttributes[QLatin1String("sidc")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of MIL2525
// rawAttributes[QLatin1String("sic")] = AdvancedSymbology::SidcCodec::decodeRawSidc(d->m_symbol.value().sidc); // in case of APP6
// // move to graphic:
// m_graphic->setGeometry(geom);
// /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// * Replace:
// * m_graphic->attributes()->setAttributesMap(rawAttributes);
// *
// * because it doesn't work anymore in ArcGISRuntime V100.2
// * JT, 2018/01/24
// * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
// Esri::ArcGISRuntime::AttributeListModel * attributeModel = m_graphic->attributes();
// if (attributeModel)
// {
// if (!attributeModel->isEmpty())
// {
// for (auto it = rawAttributes.cbegin(); it != rawAttributes.cend(); ++it)
// {
// const QString &attrKey = it.key();
// if (attributeModel->containsAttribute(attrKey))
// {
// attributeModel->replaceAttribute(attrKey, rawAttributes[attrKey]);
// continue;
// }
// attributeModel->insertAttribute(attrKey, rawAttributes[attrKey]);
// }
// }
// else attributeModel->setAttributesMap(rawAttributes);
// }
// 2021-08, N. Thoden
// While switching back to the behaviour that one logical layer has
// 1st getGraphicsLayer and 2nd getGraphicsLayerDict [GUEZE-496],
// the tactical appereance of DangerArea and similar disappears.
// Reason: m_graphicsLayer and m_graphicOverlayTactical are different now!
// It seems like that in former versions (< V100.8) the graphic could
// be added to more than one Esri layer.
// Now (V100.8) adding to a second layer fails.
// But we can remove the m_graphic from the default m_graphicsLayer
// before adding the the desired one: m_graphicOverlayTactical
if (m_graphicsLayer->graphics()->contains(m_graphic))
m_graphicsLayer->graphics()->removeOne(m_graphic);
// add to graphicOverlayTactical:
if (! d->m_graphicOverlayTactical->graphics()->contains(m_graphic))
d->m_graphicOverlayTactical->graphics()->append(m_graphic);
bool successGraphic = d->m_graphicOverlayTactical->graphics()->contains(m_graphic);
if (!successGraphic)
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " m_graphic not added to m_graphicOverlayTactical";
}
else
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " no m_graphic";
// success = false;
success = true; // if the DictionaryRenderer is used, m_graphic is null and that is ok.
}
} // if (geom.isEmpty())
}
}
else // !useDictionaryRenderMode(geomType)
{
createPictureMarker();
d->m_pictureMarker->setOrigin(this->origin());
// real work:
DEBUG_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " update geometry";
if (d->m_symbol.changed() || m_scale.changed())
{
if (d->m_dictionary)
{
int widthPixel = SituationMap::SymbolSizeLdefault;
widthPixel *= symbolData_ref.size * m_scale.value();
d->m_symDataToRender.sidc = symbolData_ref.sidc;
d->m_symDataToRender.fillColor = symbolData_ref.color;
d->m_symDataToRender.fillColorReplace = true;
d->m_symDataToRender.sizeL = widthPixel;
if (d->m_pictureMarker->image().isNull())
d->m_pictureMarker->setImage(d->m_dictionary->temporaryImage(d->m_symDataToRender.sizeL));
d->m_symbolResultWatcher.setSymbolResult(d->m_dictionary->fetchSymbol(d->m_symDataToRender));
success = true;
}
else
{
ERROR_LOG(m_logger) << LOGFUNCTION << " debugInfo='" << m_debugInfo << "'"
<< " visibility=" << m_visibility
<< " no dictionary";
}
}
if (d->m_pictureMarker)
{
d->m_pictureMarker->setDrawOrder(m_drawOrder.value());
d->m_pictureMarker->setVisibility(m_visibility.value());
d->m_pictureMarker->setSelected(m_selected.value());
d->m_pictureMarker->setPickable(m_pickable.value());
success = success && d->m_pictureMarker->updateView();
}
} // else !useDictionaryRenderMode(geomType)
if (d->m_pictureMarker)
{
d->m_pictureMarker->setOrigin(this->origin()); // to avoid incorrect number of positions..
d->m_pictureMarker->setVisibility(m_visibility.value());
d->m_pictureMarker->setSelected(m_selected.value());
success = d->m_pictureMarker->updateView() && success;
}
// in case of no pictureMarker:
if (m_graphic)
{
m_graphic->setZIndex(m_drawOrder.value());
m_graphic->setVisible(m_visibility.value());
m_graphic->setSelected(m_selected.value());
}
// reset flags
d->m_symbol.reset();
if (!success)
ERROR_LOG(m_logger) << LOGFUNCTION << " failure, debugInfo='" << m_debugInfo << "'";
TRACE_LOG(m_logger) << LOGFUNCTION << " END, debugInfo='" << m_debugInfo << "'"
<< " success = " << success
<< " visibility=" << m_visibility;
return success;
}
} // namespace SituationMap
@@ -0,0 +1,117 @@
/**
* @RDE_HEADER@
* TacticalAppearance
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "abstract_graphical_object.h"
// Esri
// Qt
// forward:
//----------------------------------------------------------------------------
namespace SituationMap
{
// forward:
class MapImpl;
class GraphicalObjectPictureMarker;
/**
* @brief The TacticalAppearance class displays sidcs.
* To do so, the number of coordinate lead to use
* - the GraphicalObjectPictureMarker (exactly one coordinate)
* or
* - the Message (more than one coordinate)
*
* Use cases:
* - Participant: sidc with #coords == 1, TacticalAppearance should get exactly one coordinate.
* (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used)
* - Tactical display of a Building: sidc #coords >= 2, TacticalAppearance should get list of positions.
* (Message on ESRI::MessageGroupLayer is used)
* - (Tactical) PreviewIcon of a Building: sidc #coords == 1, TacticalAppearance should get exactly one coordinate.
* (GraphicalObjectPictureMarker on ESRI::GraphicsLayer is used)
* - Tactical symbols of plan editor
*
* In case of GraphicalObjectPictureMarker the image creation is done by AdvancedSymbology::SymbolDictionary::symbolImageBySidcExtended().
*/
class SITUATIONMAP_EXPORT TacticalAppearance : public AbstractGraphicalObject
{
using BaseClass = AbstractGraphicalObject;
public:
/**
* @brief TacticalAppearance
* @param mapImpl map to be used.
* @param logicalLayerName logical layer name to be used.
* @param debugInfo debugInfo to be used
* @param uniqueMapId uniqueMapId to be used. (Optional to fulfill rubberband requirements)
*/
TacticalAppearance(MapImpl *mapImpl,
const QString &logicalLayerName,
const QString &debugInfo,
const UniqueMapId &uniqueMapId = INVALID_MAP_ID);
TacticalAppearance(const TacticalAppearance &other) = delete;
TacticalAppearance &operator=(TacticalAppearance &other) = delete;
virtual ~TacticalAppearance();
SymbolData symbol() const;
void setSymbol(const SymbolData &symbol);
QString uniqueDesignation() const;
void setUniqueDesignation(const QString &uniqueDesignation);
SymbolImage symbolImage() const;
Esri::ArcGISRuntime::Geometry getGraphicGeometry() const override;
protected:
// Internal/Admin
virtual bool create() override;
virtual void createPictureMarker();
virtual void removePictureMarker();
void removeGraphicFromGraphicOverlayTactical();
bool changed() override;
void resetChanged() override;
bool updateViewImpl() override;
bool useDictionaryRenderMode(const AdvancedSymbology::GeometryType &geomType) const;
// logical
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
class Private;
/**
* The private d-pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
@@ -0,0 +1,603 @@
/**
**
** @RDE_HEADER@
** Handles aggregation of participant layer
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 16.08.2017, JT : first edition
**
** -----------------------------------------------------------------------------------------------
*/
#include "layer_aggregation_widget.h"
#include <Map/map.h>
#include <Map/layer_parameter.h>
#include <LtsWidgets/lts_message_dialog.h>
#include <Common/situation_map_logging.h>
#include <QtCore/QMap>
#include <QtCore/QRegularExpression>
#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtWidgets/QCheckBox>
#include <QtWidgets/QLabel>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QGridLayout>
#include <cmath>
using namespace SituationMap;
struct AggregationData
{
QString layerName;
QString displayName;
double minScale = LayerParameter::UNDEFINED_SCALE; // 0
double maxScale = LayerParameter::UNDEFINED_SCALE; // 0
bool minScaleValid = false;
bool maxScaleValid = false;
}; // AggregationData
struct AggregationWidgets
{
QCheckBox *cbMinScaleValid = nullptr;
QPushButton *pbMinScale = nullptr;
QPushButton *pbMinScaleTo = nullptr;
QCheckBox *cbMaxScaleValid = nullptr;
QPushButton *pbMaxScale = nullptr;
QPushButton *pbMaxScaleTo = nullptr;
QPushButton *pbReset = nullptr;
QPushButton *pbApply = nullptr;
};
class LayerAggregationWidget::InternalData
{
public:
Map *m_map = nullptr;
bool m_valid = false;
LayerType m_layerType;
QVector<AggregationData> m_aggregationList;
QMap<QString, AggregationData> m_aggregationReset;
QMap<QString, AggregationWidgets> m_aggregationWidgets;
LOGGER_T m_logger;
explicit InternalData(Map *map,LayerType layerType)
: m_map(map)
, m_layerType(layerType)
, m_logger(getLogger("LayerAggregation"))
{}
}; // LayerAggregationWidget::InternalData
//------------------------------------------------------------------------------
QString LayerAggregationWidget::scaleText(double scale)
{
return (scale >= 1) ? QStringLiteral("1:%1").arg(int(scale)) : tr("undefined");
} // QString LayerAggregationWidget::scaleText(double scale)
void LayerAggregationWidget::setMinScaleLabel(QPushButton *btn, double scale)
{
btn->setText(tr("Min. Scale from Map\n%1").arg(scaleText(scale)));
} // void LayerAggregationWidget::setMinScaleLabel(QPushButton * btn, double scale)
void LayerAggregationWidget::setMaxScaleLabel(QPushButton *btn, double scale)
{
btn->setText(tr("Max. Scale from Map\n%1").arg(scaleText(scale)));
} // void LayerAggregationWidget::setMaxScaleLabel(QPushButton * btn, double scale)
LayerAggregationWidget::LayerAggregationWidget(Map *map, LayerType layerType, const QRegularExpression &nameCondition, QWidget *parent)
: QWidget(parent), m_internal(new InternalData(map,layerType))
{
if (!nameCondition.isValid())
{
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Illegal regular expression in nameCondition.";
return;
}
if (map == nullptr)
{
ERROR_LOG(m_internal->m_logger) << LOGFUNCTION << " Invalid situation map, cannot initialize dialog";
return;
}
QGridLayout *layout = new QGridLayout(this);
layout->setSpacing(10);
const LayerFactory &layerFactory = map->layerFactory();
QIcon scaleToIcon(QStringLiteral("icon:search.svg"));
const auto ln = layerFactory.layernames(layerType);
for (const QString &layerName : ln)
{
if (nameCondition.match(layerName).hasMatch())
{
// get layer
AggregationData data;
AggregationWidgets &widgets = m_internal->m_aggregationWidgets[layerName];
const LayerParameter &layerPara = layerFactory.layerParameter(layerName);
data.layerName = layerName;
data.minScale = layerPara.minScale;
data.maxScale = layerPara.maxScale;
data.minScaleValid = layerPara.minScaleValid();
data.maxScaleValid = layerPara.maxScaleValid();
data.displayName = layerPara.displayName;
int row = m_internal->m_aggregationList.size();
QLabel *laName = new QLabel(layerPara.displayName.isEmpty() ? layerName : layerPara.displayName, this);
widgets.pbReset = new QPushButton(this);
widgets.pbApply = new QPushButton(this);
widgets.pbReset->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
widgets.pbApply->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
QHBoxLayout *minLayout = new QHBoxLayout();
minLayout->addSpacing(20);
minLayout->addWidget(widgets.cbMinScaleValid = new QCheckBox(QString(), this));
minLayout->addWidget(widgets.pbMinScale = new QPushButton(QString(), this));
minLayout->addWidget(widgets.pbMinScaleTo = new QPushButton(scaleToIcon, QString(), this));
minLayout->addSpacing(20);
setMinScaleLabel(widgets.pbMinScale, layerPara.minScale);
QHBoxLayout *maxLayout = new QHBoxLayout();
maxLayout->addWidget(widgets.cbMaxScaleValid = new QCheckBox(QString(), this));
maxLayout->addWidget(widgets.pbMaxScale = new QPushButton(QString(), this));
maxLayout->addWidget(widgets.pbMaxScaleTo = new QPushButton(scaleToIcon, QString(), this));
maxLayout->addSpacing(20);
setMaxScaleLabel(widgets.pbMaxScale, layerPara.maxScale);
widgets.pbMinScale->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
widgets.pbMaxScale->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Expanding));
layout->addWidget(laName, row, 0);
layout->addLayout(minLayout, row, 1);
layout->addLayout(maxLayout, row, 2);
layout->addWidget(widgets.pbReset, row, 3);
layout->addWidget(widgets.pbApply, row, 4);
m_internal->m_aggregationList << data;
m_internal->m_aggregationReset[layerName] = data;
widgets.pbMinScale->setProperty("row", row);
widgets.pbMaxScale->setProperty("row", row);
widgets.cbMinScaleValid->setProperty("row", row);
widgets.cbMaxScaleValid->setProperty("row", row);
widgets.pbMinScaleTo->setProperty("row", row);
widgets.pbMaxScaleTo->setProperty("row", row);
widgets.pbReset->setProperty("row", row);
widgets.pbApply->setProperty("row", row);
connect(widgets.cbMinScaleValid, &QCheckBox::clicked, this, &LayerAggregationWidget::onSetValid);
connect(widgets.cbMaxScaleValid, &QCheckBox::clicked, this, &LayerAggregationWidget::onSetValid);
connect(widgets.pbMinScale, &QPushButton::clicked, this, &LayerAggregationWidget::onGetMinScale);
connect(widgets.pbMaxScale, &QPushButton::clicked, this, &LayerAggregationWidget::onGetMaxScale);
connect(widgets.pbMinScaleTo, &QPushButton::clicked, this, &LayerAggregationWidget::onZoomTo);
connect(widgets.pbMaxScaleTo, &QPushButton::clicked, this, &LayerAggregationWidget::onZoomTo);
connect(widgets.pbReset, &QPushButton::clicked, this, &LayerAggregationWidget::onReset);
connect(widgets.pbApply, &QPushButton::clicked, this, &LayerAggregationWidget::onApply);
widgets.cbMinScaleValid->setChecked(data.minScaleValid);
widgets.cbMinScaleValid->setEnabled(data.minScaleValid);
widgets.cbMaxScaleValid->setChecked(data.maxScaleValid);
widgets.cbMaxScaleValid->setEnabled(data.maxScaleValid);
widgets.pbMinScaleTo->setEnabled(data.minScaleValid);
widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid);
widgets.pbReset->setEnabled(false);
widgets.pbApply->setEnabled(false);
}
}
retranslate();
if (m_internal->m_aggregationList.isEmpty())
{
QLabel *lbEmpty = new QLabel(tr("Can't find any layer"), this);
QPixmap icon(QStringLiteral("icon:overlays.svg"));
lbEmpty->setWindowIcon(icon);
layout->addWidget(lbEmpty, 0, 0, Qt::AlignCenter);
return;
}
} // LayerAggregation::LayerAggregation( Map * map, QObject * parent )
void LayerAggregationWidget::retranslate()
{
if (m_internal->m_map == nullptr)
return;
const LayerFactory &layerFactory = m_internal->m_map->layerFactory();
const auto ln = layerFactory.layernames(m_internal->m_layerType);
for (const QString &layerName : ln)
{
if (!m_internal->m_aggregationWidgets.contains(layerName)) continue;
AggregationWidgets &widgets = m_internal->m_aggregationWidgets[layerName];
widgets.pbReset->setText(tr("Reset"));
widgets.pbApply->setText(tr("Apply to Map"));
widgets.pbReset->setToolTip(tr("Restore initial scales of this level"));
widgets.pbApply->setToolTip(tr("Set scales to corresponding map layer temporarily"));
widgets.cbMinScaleValid->setToolTip(tr("Activate or deactivate this scale value"));
widgets.pbMinScale ->setToolTip(tr("Get current map scale of map view"));
widgets.pbMinScaleTo ->setToolTip(tr("Set this scale value to map view as current scale"));
widgets.cbMaxScaleValid->setToolTip(tr("Activate or deactivate this scale value"));
widgets.pbMaxScale ->setToolTip(tr("Get current map scale of map view"));
widgets.pbMaxScaleTo ->setToolTip(tr("Set this scale value to map view as current scale"));
}
} // void LayerAggregationWidget::retranslate()
LayerAggregationWidget::~LayerAggregationWidget()
{
delete m_internal; m_internal = nullptr;
} // LayerAggregationWidget::~LayerAggregationWidget()
void LayerAggregationWidget::onZoomTo()
{
QPushButton *senderButton = qobject_cast<QPushButton *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
const AggregationData &data = m_internal->m_aggregationList[row];
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
double scale = 0.0;
if ((widgets.pbMinScaleTo == senderButton) && (data.minScaleValid))
scale = data.minScale;
if ((widgets.pbMaxScaleTo == senderButton) && (data.maxScaleValid))
scale = data.maxScale;
if (scale < 1)
return;
m_internal->m_map->setScale(scale);
} // void LayerAggregationWidget::onZoomTo()
void LayerAggregationWidget::onGetMinScale()
{
QPushButton *senderButton = qobject_cast<QPushButton *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
double scale = m_internal->m_map->scale();
AggregationData &data = m_internal->m_aggregationList[row];
if (data.maxScaleValid && (data.maxScale >= scale))
{
const QString &msg = tr("Chosen scale is too small");
LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation"));
return;
}
data.minScale = scale;
data.minScaleValid = true;
setMinScaleLabel(senderButton, data.minScale);
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
widgets.cbMinScaleValid->setChecked(true);
widgets.cbMinScaleValid->setEnabled(true);
widgets.pbMinScaleTo->setEnabled(true);
widgets.pbReset->setEnabled(true);
widgets.pbApply->setEnabled(true);
emit changesMade();
} // void LayerAggregationWidget::onGetMinScale()
void LayerAggregationWidget::onGetMaxScale()
{
QPushButton *senderButton = qobject_cast<QPushButton *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
double scale = m_internal->m_map->scale();
AggregationData &data = m_internal->m_aggregationList[row];
if (data.minScaleValid && (data.minScale <= scale))
{
const QString &msg = tr("Chosen scale is too large");
LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation"));
return;
}
data.maxScale = scale;
data.maxScaleValid = true;
setMaxScaleLabel(senderButton, data.maxScale);
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
widgets.cbMaxScaleValid->setChecked(true);
widgets.cbMaxScaleValid->setEnabled(true);
widgets.pbMaxScaleTo->setEnabled(true);
widgets.pbReset->setEnabled(true);
widgets.pbApply->setEnabled(true);
emit changesMade();
} // void LayerAggregationWidget::onGetMaxScale()
void LayerAggregationWidget::onSetValid(bool value)
{
QCheckBox *senderButton = qobject_cast<QCheckBox *>(sender());
if (nullptr == senderButton) return;
int row = senderButton->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
AggregationData &data = m_internal->m_aggregationList[row];
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
if (senderButton == widgets.cbMinScaleValid)
{
if (data.maxScaleValid && value && (data.maxScale >= data.minScale))
{
senderButton->setChecked(false);
const QString &msg = tr("Minimum scale is too small");
LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation"));
return;
}
data.minScaleValid = value;
}
if (senderButton == widgets.cbMaxScaleValid)
{
if (data.minScaleValid && value && (data.minScale <= data.maxScale))
{
senderButton->setChecked(false);
const QString &msg = tr("Maximum scale is too large");
LtsWidgets::LtsMessageDialog::showMessage(this, msg, tr("Layer Aggregation"));
return;
}
data.maxScaleValid = value;
}
widgets.pbMinScaleTo->setEnabled(data.minScaleValid);
widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid);
widgets.pbReset->setEnabled(true);
widgets.pbApply->setEnabled(true);
emit changesMade();
} // void LayerAggregationWidget::onSetValid(bool value)
void LayerAggregationWidget::doReset(int row)
{
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
AggregationData &data = m_internal->m_aggregationList[row];
data = m_internal->m_aggregationReset[data.layerName];
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
setMinScaleLabel(widgets.pbMinScale, data.minScale);
setMaxScaleLabel(widgets.pbMaxScale, data.maxScale);
widgets.cbMinScaleValid->setChecked(data.minScaleValid);
widgets.cbMinScaleValid->setEnabled(data.minScaleValid);
widgets.cbMaxScaleValid->setChecked(data.maxScaleValid);
widgets.cbMaxScaleValid->setEnabled(data.maxScaleValid);
widgets.pbMinScaleTo->setEnabled(data.minScaleValid);
widgets.pbMaxScaleTo->setEnabled(data.maxScaleValid);
widgets.pbReset->setEnabled(false);
widgets.pbApply->setEnabled(!widgets.pbApply->isEnabled());
} // void doReset(SituationMap::Map * map, const AggregationData & data)
void LayerAggregationWidget::onReset()
{
QObject *senderObj = sender();
if (nullptr == senderObj) return;
int row = senderObj->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
doReset(row);
} // void LayerAggregationWidget::onReset()
static void doApply(SituationMap::Map *map, const AggregationData &data)
{
LayerFactory &layerFactory = map->layerFactory();
LayerParameter layerParamter = layerFactory.layerParameter(data.layerName);
layerParamter.minScale = data.minScaleValid ? data.minScale : LayerParameter::UNDEFINED_SCALE; // 0;
layerParamter.maxScale = data.maxScaleValid ? data.maxScale : LayerParameter::UNDEFINED_SCALE; // 0;
layerFactory.setLayerParameter(data.layerName, layerParamter);
} // void doApply(SituationMap::Map * map, const AggregationData & data)
void LayerAggregationWidget::onApply()
{
QObject *senderObj = sender();
if (!senderObj)
return;
int row = senderObj->property("row").toInt();
if ((row < 0) || (row >= m_internal->m_aggregationList.size()))
return;
const AggregationData &data = m_internal->m_aggregationList[row];
doApply(m_internal->m_map, data);
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
widgets.pbApply->setEnabled(false);
} // void LayerAggregationWidget::onApply()
void LayerAggregationWidget::onApplyAll()
{
for (const AggregationData &data : qAsConst(m_internal->m_aggregationList))
{
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[data.layerName];
if (widgets.pbApply->isEnabled())
{
doApply(m_internal->m_map, data);
widgets.pbApply->setEnabled(false);
}
}
} // void LayerAggregationWidget::onApplyAll()
void LayerAggregationWidget::onResetAll()
{
for (const auto &aggData : qAsConst(m_internal->m_aggregationList))
{
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[aggData.layerName];
doReset(widgets.pbApply->property("row").toInt());
doApply(m_internal->m_map, aggData);
widgets.pbApply->setEnabled(false);
}
} // void LayerAggregationWidget::onResetAll()
void LayerAggregationWidget::onSetDefaultScales()
{
if (m_internal->m_aggregationList.isEmpty())
return;
// Calculate scale defaults here ...
double mapMinScaleSQRT = sqrt(m_internal->m_map->minScale());
double mapMaxScaleSQRT = sqrt(m_internal->m_map->maxScale());
const int aggregationListCount = m_internal->m_aggregationList.size();
double stepScaleSQRT = (mapMinScaleSQRT - mapMaxScaleSQRT) / static_cast<double>(aggregationListCount);
unsigned int step = 0;
for (auto &aggData : m_internal->m_aggregationList)
{
double dScaleSQRT = mapMaxScaleSQRT + (step * stepScaleSQRT);
aggData.maxScaleValid = true;
aggData.maxScale = dScaleSQRT * dScaleSQRT;
dScaleSQRT = mapMaxScaleSQRT + ((step + 1) * stepScaleSQRT);
aggData.minScaleValid = true;
aggData.minScale = dScaleSQRT * dScaleSQRT;
} // for
// disable start and end
if (aggregationListCount > 0)
{
// front -> max
m_internal->m_aggregationList.front().maxScale = 0;
m_internal->m_aggregationList.front().maxScaleValid = false;
// back -> min
m_internal->m_aggregationList.back().minScale = 0;
m_internal->m_aggregationList.back().minScaleValid = false;
} // if
// update ui to m_aggregationList:
for (const auto &aggData : qAsConst(m_internal->m_aggregationList))
{
const AggregationWidgets &widgets = m_internal->m_aggregationWidgets[aggData.layerName];
widgets.cbMinScaleValid->setEnabled(aggData.minScaleValid);
widgets.cbMinScaleValid->setChecked(aggData.minScaleValid);
widgets.pbMinScaleTo->setEnabled(aggData.minScaleValid);
widgets.cbMaxScaleValid->setEnabled(aggData.maxScaleValid);
widgets.cbMaxScaleValid->setChecked(aggData.maxScaleValid);
widgets.pbMaxScaleTo->setEnabled(aggData.maxScaleValid);
widgets.pbApply->setEnabled(true);
widgets.pbReset->setEnabled(true);
setMinScaleLabel(widgets.pbMinScale, aggData.minScale);
setMaxScaleLabel(widgets.pbMaxScale, aggData.maxScale);
} // for
emit changesMade();
} // void LayerAggregationWidget::onSetDefaultScales()
@@ -0,0 +1,110 @@
/**
**
** @RDE_HEADER@
** Handles aggregation of participant layer
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 16.08.2017, JT : first edition
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <Map/layer_factory.h>
#include <situation_map_export.h>
#include <QtWidgets/QWidget>
class QPushButton;
class QRegularExpression;
namespace SituationMap
{
class Map;
/**
@class LayerAggregationWidget layer_aggregation_widget.h
@brief Class to handle aggregation of participant layer
*/
class SITUATIONMAP_EXPORT LayerAggregationWidget : public QWidget
{
Q_OBJECT
public:
/**
Constructor to build gui widget for handling aggregation layers.
@param map Map the layer are part of
@param layerType Type of layer in general this will be LayerType::Operational
@param nameCondition If the nameCondition doesn't match with the layername the layer will be skipped and is not handled.
Example of an expression "^Participant.+".
@param parent Parent for Qt object hierarchy
*/
explicit LayerAggregationWidget(Map *map, LayerType layerType, const QRegularExpression &nameCondition, QWidget *parent);
/**
Destructor to clean up memory.
*/
~LayerAggregationWidget();
void retranslate();
public slots:
void onApplyAll();
void onResetAll();
void onSetDefaultScales();
signals:
void changesMade();
protected slots:
void onSetValid(bool value);
void onGetMinScale();
void onGetMaxScale();
void onZoomTo();
void onReset();
void onApply();
private:
void doReset(int row);
static QString scaleText(double scale);
static void setMinScaleLabel(QPushButton *btn, double scale);
static void setMaxScaleLabel(QPushButton *btn, double scale);
class InternalData;
InternalData *m_internal;
}; // LayerAggregationWidget
} /* namespace SituationMap */
@@ -0,0 +1,105 @@
/**
**
** @RDE_HEADER@
** Dialog of aggregation of participant layer
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 18.08.2017, JT : first edition
**
** -----------------------------------------------------------------------------------------------
*/
#include "participant_aggregation_dialog.h"
#include "layer_aggregation_widget.h"
#include <Map/map.h>
#include <QtWidgets/QVBoxLayout>
using namespace SituationMap;
ParticipantAggregationDialog::ParticipantAggregationDialog(Map *map, QWidget *parent)
: LtsWidgets::LtsDialog(parent, LtsWidgets::ButtonCombination::OkApplyCancel)
{
m_law = new LayerAggregationWidget(map, LayerType::Operational, QRegularExpression(QStringLiteral("^Participant.+"), QRegularExpression::CaseInsensitiveOption), this);
if (map)
connect(map, &Map::beginDestroy, this, &ParticipantAggregationDialog::onMapDestroyed, Qt::UniqueConnection);
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(m_law);
layout->addStretch(2);
LtsWidgets::DialogButton btnSetDefault = LtsWidgets::DialogButton::Print;
m_buttonBox->connectButton(btnSetDefault, m_law, &LayerAggregationWidget::onSetDefaultScales);
m_buttonBox->setButtonEnabled(btnSetDefault, true);
m_buttonBox->setButtonVisible(btnSetDefault, true);
connect(this, &ParticipantAggregationDialog::canceled, m_law, &LayerAggregationWidget::onResetAll);
connect(this, &ParticipantAggregationDialog::applied, m_law, &LayerAggregationWidget::onApplyAll);
connect(m_law, &LayerAggregationWidget::changesMade, this, &ParticipantAggregationDialog::slotChangesMade);
retranslate();
setOnlineHelpFilename(QStringLiteral("Situation-Participant_Aggregation"));
} // ParticipantAggregationDialog::ParticipantAggregationDialog( Map * map, QWidget * parent )
void ParticipantAggregationDialog::retranslate()
{
// lupdate doesn't like translate(tr("foo")) ...
const QString txt(tr("Participant Aggregation"));
translate(txt);
LtsWidgets::DialogButton btnSetDefault = LtsWidgets::DialogButton::Print;
m_buttonBox->setText( btnSetDefault, tr("Set Scale Defaults"));
m_buttonBox->setToolTip( btnSetDefault, tr("Calculate scale values for each aggregation from map scale range"));
m_law->retranslate();
} // void ParticipantAggregationDialog::retranslate()
void ParticipantAggregationDialog::dialogCanceled()
{
emit canceled();
} // void ParticipantAggregationDialog::dialogCanceled()
bool ParticipantAggregationDialog::apply()
{
emit applied();
return true;
}
void ParticipantAggregationDialog::onMapDestroyed()
{
// commit suicide
delete this;
} // bool ParticipantAggregationDialog::apply()
@@ -0,0 +1,79 @@
/**
**
** @RDE_HEADER@
** Dialog of aggregation of participant layer
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 18.08.2017, JT : first edition
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <LtsWidgets/lts_dialog.h>
#include <situation_map_export.h>
#include <QtWidgets/QWidget>
namespace SituationMap
{
class Map;
class LayerAggregationWidget;
/**
@class ParticpantAggregation particpant_aggregation.h
@brief Class to handle aggregation of participant layer dialog
*/
class SITUATIONMAP_EXPORT ParticipantAggregationDialog : public LtsWidgets::LtsDialog
{
Q_OBJECT
public:
explicit ParticipantAggregationDialog(SituationMap::Map *map, QWidget *parent);
~ParticipantAggregationDialog() = default;
void retranslate() override;
void dialogCanceled() override;
protected:
bool apply() override;
Q_SIGNALS:
void canceled();
void applied();
protected Q_SLOTS:
void onMapDestroyed();
private:
LayerAggregationWidget *m_law;
}; // ParticipantAggregationDialog
} /* namespace SituationMap */
+37
View File
@@ -0,0 +1,37 @@
#
# $Id$
# $HeadURL$
# $Author$
#
# LIBRARY
SET(LocalServer_SRCS
local_server_export
local_server.cpp
local_server.h
)
SOURCE_GROUP(LocalServer FILES ${LocalServer_SRCS})
NGL_ADD_LIBRARY(WS situationMapLocalServer SHARED ${LocalServer_SRCS})
NGL_TARGET_LINK_LIBRARIES(situationMapLocalServer
PRIVATE
ArcGIS::ArcGIS
NGLLogger
Qt5::Gui # can be removed once we get an official ESRI ArcGIS Qt version again
PUBLIC
Qt5::Core
situationMapCommon
)
# ESRI: /usr/include/arcgis/LocalServer/LocalServer.h: '#include "Object.h"'
# so add required include path here as PRIVATE:
NGL_TARGET_INCLUDE_DIRECTORIES(situationMapLocalServer
PRIVATE
${ARCGIS_INCLUDE_DIR}/arcgis
INTERFACE
${CMAKE_CURRENT_SOURCE_DIR}/..
)
NGL_INSTALL_TARGET(situationMapLocalServer)
+519
View File
@@ -0,0 +1,519 @@
/**
* @RDE_HEADER@
* LocalServer
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "local_server.h"
#include <Common/resource_path.h>
// ESRI
#include <arcgis/Error.h>
#include <arcgis/LocalServer/LocalServer.h>
#include <arcgis/LocalServer/LocalGeoprocessingService.h>
//Qt
#include <QMap>
namespace Esri
{
namespace ArcGISRuntime
{
QString toString(const Esri::ArcGISRuntime::Error &value)
{
std::ostringstream stream;
stream << "Esri::ArcGISRuntime::Error: "
<< " code=" << value.code()
<< ", domain=" << (int)value.domain()
<< ", extendedErrorType=" << (int)value.extendedErrorType()
<< ", message='" << value.message() << "'"
<< ", additionalMessage='" << value.additionalMessage() << "'";
return QString::fromStdString(stream.str());
}
std::ostream &operator<< (std::ostream &stream,
const Esri::ArcGISRuntime::Error &value)
{
stream << toString(value);
return stream;
}
} // namespace ArcGISRuntime
} // namespace Esri
//----------------------------------------------------------------------------
namespace SituationMap
{
bool LocalServiceData::isEmpty() const
{
return m_svcUrl.isEmpty();
}
void LocalServiceData::setSvcUrl(const QUrl &svcUrl)
{
m_svcUrl = svcUrl;
}
const QUrl &LocalServiceData::svcUrl() const
{
return m_svcUrl;
}
void LocalServiceData::setSync(bool sync)
{
m_sync = sync;
}
bool LocalServiceData::isSync() const
{
return m_sync;
}
//----------------------------------------------------------------------------
static QString toString(const Esri::ArcGISRuntime::LocalServerStatus &value)
{
switch (value)
{
case Esri::ArcGISRuntime::LocalServerStatus::Stopped:
return QLatin1String("Stopped");
case Esri::ArcGISRuntime::LocalServerStatus::Starting:
return QLatin1String("Starting");
case Esri::ArcGISRuntime::LocalServerStatus::Started:
return QLatin1String("Started");
case Esri::ArcGISRuntime::LocalServerStatus::Stopping:
return QLatin1String("Stopping");
case Esri::ArcGISRuntime::LocalServerStatus::Failed:
return QLatin1String("Failed");
} // switch
return QLatin1String("Esri-LocalServerStatusERROR");
}
//-----------------------------------------------------------------------------
class LocalServer::Private
{
public:
Private() = default;
~Private() = default;
// ESRI data:
Esri::ArcGISRuntime::LocalServer *m_localServer = nullptr;
// Own data:
QMap<QString, Esri::ArcGISRuntime::LocalGeoprocessingService *> m_gpServiceMap;
QMap<QString, QString> m_serviceSubLocationMap;
QMap<QString, LocalServiceData> m_svcData;
}; // class LocalServer::Private
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
LocalServer *LocalServer::instance()
{
static LocalServer *_instance = nullptr;
if (_instance == nullptr)
_instance = new LocalServer;
return _instance;
}
//-----------------------------------------------------------------------------
LocalServer::LocalServer()
: BaseClass()
, m_logger(getLogger("LocalServer"))
, d(new Private)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
DEBUG_LOG(m_logger) << LOGFUNCTION << " installPath = "
<< " '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'";
QString newInstallPath;
QString user = QString::fromLocal8Bit( qgetenv("USER") );
if (user.compare(QLatin1Literal("ctc")) == 0)
newInstallPath = SituationMap::getResourcePath() + QLatin1String("/localserver100.2_ctc/"); // USER ctc
else
newInstallPath = SituationMap::getResourcePath() + QLatin1String("/localserver100.2/"); // USER ctc_ws
Esri::ArcGISRuntime::LocalServer::setInstallPath(newInstallPath);
if (Esri::ArcGISRuntime::LocalServer::isInstallValid())
NOTICE_LOG(m_logger) << LOGFUNCTION << " installPath ok = "
<< " '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'";
else
FATAL_LOG(m_logger) << LOGFUNCTION << " installPath failed = "
<< " InstallPath = '" << Esri::ArcGISRuntime::LocalServer::installPath() << "'";
d->m_localServer = Esri::ArcGISRuntime::LocalServer::instance();
connect(d->m_localServer, &Esri::ArcGISRuntime::LocalServer::errorOccurred,
this, &LocalServer::onLocalServerError);
connect(d->m_localServer, &Esri::ArcGISRuntime::LocalServer::statusChanged,
this, &LocalServer::onLocalServerStatusChanged);
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
LocalServer::~LocalServer()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
this->stop();
delete d;
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LocalServer::start()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
d->m_localServer->start();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LocalServer::stop()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
stopAllLocalGeoprocessingServices();
d->m_localServer->stop();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
bool LocalServer::isLocalServerStarted() const
{
return d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Started;
}
bool LocalServer::isLocalServerStopped() const
{
return d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Stopped;
}
bool LocalServer::setLocalGeoprocessingService(const QString &serviceName,
const QString &packagePath,
const QString &serviceSubLocation,
bool sync)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START"
<< " serviceName: '" << serviceName << "'"
<< " packagePath: '" << packagePath << "'"
<< " serviceSubLocation: '" << serviceSubLocation << "'";
if (d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'" << " already provided";
return false;
}
QString localPackagePath = packagePath;
{
QFileInfo fileinfo(packagePath);
if (!fileinfo.isAbsolute())
localPackagePath = SituationMap::getResourcePath() + QLatin1String("/gpk/") + packagePath;
}
QFileInfo fileinfo(localPackagePath);
if (!fileinfo.exists())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'"
<< " packagePath does not exist: "
<< " localPackagePath: '" << localPackagePath << "'";
return false;
}
if (!fileinfo.isReadable())
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName: '" << serviceName << "'"
<< " packagePath is not readable: "
<< " localPackagePath: '" << localPackagePath << "'";
return false;
}
NOTICE_LOG(m_logger) << LOGFUNCTION << " create LocalGeoprocessingService: "
<< " localPackagePath: '" << localPackagePath << "'";
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps =
new Esri::ArcGISRuntime::LocalGeoprocessingService(localPackagePath, this);
lgps->setObjectName(QString(QLatin1Literal("LocalGeoprocessingService-%1")).arg(serviceName));
if (sync)
lgps->setServiceType(Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute); // SynchronousExecute | AsynchronousSubmit | ...
else
lgps->setServiceType(Esri::ArcGISRuntime::GeoprocessingServiceType::AsynchronousSubmit); // SynchronousExecute | AsynchronousSubmit | ...
connect(lgps, &Esri::ArcGISRuntime::LocalGeoprocessingService::errorOccurred,
this, &LocalServer::onLGPSError);
connect(lgps, &Esri::ArcGISRuntime::LocalGeoprocessingService::statusChanged,
this, &LocalServer::onLGPSStatusChanged);
// add to own management:
d->m_gpServiceMap[serviceName] = lgps;
d->m_serviceSubLocationMap[serviceName] = serviceSubLocation;
TRACE_LOG(m_logger) << LOGFUNCTION << " END"
<< " serviceName: '" << serviceName << "'"
<< " localPackagePath: '" << localPackagePath << "'"
<< " serviceSubLocation: '" << serviceSubLocation << "'";
return true;
}
bool LocalServer::startLocalGeoprocessingService(const QString &serviceName)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
if (isLocalServerStarted())
lgps->start();
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
return true;
}
bool LocalServer::stopLocalGeoprocessingService(const QString &serviceName)
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
lgps->stop();
return true;
}
bool LocalServer::isLocalGeoprocessingServiceStarted(const QString &serviceName) const
{
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
return lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Started;
}
bool LocalServer::isLocalGeoprocessingServiceStopped(const QString &serviceName) const
{
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
return lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Stopped;
}
bool LocalServer::isLocalGeoprocessingServiceSynchronousExecute(const QString &serviceName) const
{
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
return lgps->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute;
}
bool LocalServer::isLocalGeoprocessingServiceAsynchronousSubmit(const QString &serviceName) const
{
if (!d->m_gpServiceMap.contains(serviceName))
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = d->m_gpServiceMap[serviceName];
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
return false;
}
return lgps->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::AsynchronousSubmit;
}
bool LocalServer::localGeoprocessingServiceData(const QString &serviceName, LocalServiceData &localServiceDataOut) const
{
if (!d->m_svcData.contains(serviceName))
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << " not found";
return false;
}
localServiceDataOut = d->m_svcData[serviceName];
return true;
}
void LocalServer::startAllPendingLocalGeoprocessingServices()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
QMapIterator<QString, Esri::ArcGISRuntime::LocalGeoprocessingService *> mapIt(d->m_gpServiceMap);
while (mapIt.hasNext())
{
mapIt.next();
const QString &serviceName = mapIt.key();
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = mapIt.value();
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
continue;
}
if (lgps->status() == Esri::ArcGISRuntime::LocalServerStatus::Started)
{
DEBUG_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", already started, continue";
continue;
}
else
lgps->start();
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LocalServer::stopAllLocalGeoprocessingServices()
{
QMapIterator<QString, Esri::ArcGISRuntime::LocalGeoprocessingService *> mapIt(d->m_gpServiceMap);
while (mapIt.hasNext())
{
mapIt.next();
const QString &serviceName = mapIt.key();
Esri::ArcGISRuntime::LocalGeoprocessingService *lgps = mapIt.value();
if (!lgps)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " serviceName '" << serviceName << "'" << ", no service";
continue;
}
lgps->stop();
} // while
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LocalServer::onLocalServerError(const Esri::ArcGISRuntime::Error &error)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " EsriError occured: " << error;
}
void LocalServer::onLocalServerStatusChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalServer: status changed to: " << toString(d->m_localServer->status());
emit localServerStatusChanged();
if (d->m_localServer->status() == Esri::ArcGISRuntime::LocalServerStatus::Started)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalServer: STARTED"
<< " (url= '" << d->m_localServer->url().toString() << "')"
<< " (Esri::ArcGISRuntime::LocalServer->tempDataPath= '" << d->m_localServer->tempDataPath() << "')";
startAllPendingLocalGeoprocessingServices();
}
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
void LocalServer::onLGPSError(const Esri::ArcGISRuntime::Error &error)
{
QString senderName;
Esri::ArcGISRuntime::LocalGeoprocessingService *lgs = qobject_cast<Esri::ArcGISRuntime::LocalGeoprocessingService *>(sender());
if (lgs)
senderName = lgs->objectName();
ERROR_LOG(m_logger) << LOGFUNCTION << " LocalGeoprocessingService -> EsriError occured: " << error;
}
void LocalServer::onLGPSStatusChanged()
{
TRACE_LOG(m_logger) << LOGFUNCTION << " START";
Esri::ArcGISRuntime::LocalGeoprocessingService *lgsp = qobject_cast<Esri::ArcGISRuntime::LocalGeoprocessingService *>(sender());
if (!lgsp)
{
ERROR_LOG(m_logger) << LOGFUNCTION << " qobject_cast failed to get sender!?";
return;
}
QString senderName = lgsp->objectName();
const QString &serviceName = d->m_gpServiceMap.key(lgsp);
if (lgsp->status() == Esri::ArcGISRuntime::LocalServerStatus::Started)
{
NOTICE_LOG(m_logger) << LOGFUNCTION << " ***** LocalGeoprocessingService:"
<< " sender= '" << senderName << "'"
<< " status changed to: " << toString(lgsp->status())
<< " url= '" << lgsp->url().toString() << "'";
LocalServiceData svcData;
const QString &serviceSubLocation = d->m_serviceSubLocationMap[serviceName];
const QString &url = lgsp->url().toString() + QLatin1String("/") + serviceSubLocation;
svcData.setSvcUrl(QUrl(url));
svcData.setSync(lgsp->serviceType() == Esri::ArcGISRuntime::GeoprocessingServiceType::SynchronousExecute);
d->m_svcData[serviceName] = svcData; // store in own management
emit localGeoprocessingServiceStatusChanged(serviceName);
emit localGeoprocessingServiceStarted(serviceName, svcData);
}
NOTICE_LOG(m_logger) << LOGFUNCTION << " LocalGeoprocessingService:"
<< " sender= '" << senderName << "'"
<< " status changed to: " << toString(lgsp->status());
TRACE_LOG(m_logger) << LOGFUNCTION << " END";
}
} // namespace SituationMap
+128
View File
@@ -0,0 +1,128 @@
/**
* @RDE_HEADER@
* LocalServer
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "local_server_export.h"
#include <Common/situation_map_logging.h>
namespace Esri {
namespace ArcGISRuntime {
class Error;
}
}
// Qt
#include <QObject>
#include <QUrl>
#include <QString>
//----------------------------------------------------------------------------
namespace SituationMap
{
class LOCALSERVER_EXPORT LocalServiceData
{
private:
QUrl m_svcUrl;
bool m_sync = false;
public:
bool isEmpty() const;
void setSvcUrl(const QUrl &svcUrl);
const QUrl &svcUrl() const;
void setSync(bool sync);
bool isSync() const;
};
/**
* @brief The LocalServer class wrap ESRI`s LocalServer.
* This is a container for geoprocessing services
* The LocalServer has to be started as well as the seperate instances of LocalGeoprocessingService.
* To ease the usage you should set the required number of LocalGeoprocessingService (with different serviceNames).
* Then simply start the LocalServer (and if that is up all pending will be started.)
*/
class LOCALSERVER_EXPORT LocalServer : public QObject
{
Q_OBJECT
using BaseClass = QObject;
private:
explicit LocalServer();
~LocalServer();
public:
/*! \name Check Installation ( no guarantee )*/
static bool isInstallValid();
/*! \name Singleton */
static LocalServer *instance();
/*! \name LocalServer */
//@{
void start();
void stop();
bool isLocalServerStarted() const;
bool isLocalServerStopped() const;
//@}
/*! \name LocalGeoprocessingService */
//@{
bool setLocalGeoprocessingService(const QString &serviceName, const QString &packagePath, const QString &serviceSubLocation, bool sync);
bool startLocalGeoprocessingService(const QString &serviceName);
bool stopLocalGeoprocessingService(const QString &serviceName);
bool isLocalGeoprocessingServiceStarted(const QString &serviceName) const;
bool isLocalGeoprocessingServiceStopped(const QString &serviceName) const;
bool isLocalGeoprocessingServiceSynchronousExecute(const QString &serviceName) const;
bool isLocalGeoprocessingServiceAsynchronousSubmit(const QString &serviceName) const;
bool localGeoprocessingServiceData(const QString &serviceName, LocalServiceData &localServiceDataOut) const;
//@}
protected:
void startAllPendingLocalGeoprocessingServices();
void stopAllLocalGeoprocessingServices();
protected Q_SLOTS:
void onLocalServerError(const Esri::ArcGISRuntime::Error &error);
void onLocalServerStatusChanged();
void onLGPSError(const Esri::ArcGISRuntime::Error &error);
void onLGPSStatusChanged();
Q_SIGNALS:
void localServerStatusChanged();
void localGeoprocessingServiceStatusChanged(const QString &serviceName);
void localGeoprocessingServiceStarted(const QString &serviceName, const LocalServiceData&localSvcData);
private:
LOGGER_T m_logger; ///< Interface of the Logging-Facility
class Private;
/**
* The private d->pointer to not break BC when changing internals of this class
*/
Private *d = nullptr;
};
} // namespace SituationMap
+21
View File
@@ -0,0 +1,21 @@
/**
* Export definitions
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include <qglobal.h>
#ifndef LOCALSERVER_EXPORT
# ifdef BUILD_STATIC_LIBRARIES
# define LOCALSERVER_EXPORT
# else
# if defined(MAKE_SITUATIONMAPLOCALSERVER_LIB)
# define LOCALSERVER_EXPORT Q_DECL_EXPORT
# else
# define LOCALSERVER_EXPORT Q_DECL_IMPORT
# endif
# endif
#endif
Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

+52
View File
@@ -0,0 +1,52 @@
/**
* @RDE_HEADER@
* Interface of AbstractEffectSimulation
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
#include "abstract_map_object.h"
#include "line_style.h"
#include "fill_style.h"
//----------------------------------------------------------------------------
namespace SituationMap
{
/**
* @brief The AesEffectState enum define the different states a EffectSimulation can have.
*/
enum class AesEffectState
{
Unknown = 0, ///< EffectSimulation is unknown.
Error = -1, ///< EffectSimulation has an error.
Expired = 1, ///< EffectSimulation has finished.
Removed = 2, ///< EffectSimulation is deleted.
Created = 4, ///< EffectSimulation is created.
Prepared = 5, ///< EffectSimulation is created, but activation time is in future.
Activated = 6, ///< EffectSimulation is created, and activation time is aimed.
Paused = 7 ///< A triggered EffectSimulation is paused.
};
/**
* @brief The AbstractEffectSimulation class contains some specific parameters.
* Inherenting of this class ease the adding of this functionality.
*/
class SITUATIONMAP_EXPORT AbstractEffectSimulation
{
public:
virtual ~AbstractEffectSimulation() = default;
virtual AesEffectState aesState() const = 0;
virtual void setAesState(AesEffectState aesState) = 0;
};
} // namespace SituationMap
+424
View File
@@ -0,0 +1,424 @@
/**
* @RDE_HEADER@
* Interface of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_types.h"
#include "situation_map_utils.h"
#include "text_style.h"
#include <Common/referenced.h>
#include <situation_map_export.h>
#include <MathUtils/geometry_engine.h>
// Qt
#include <QString>
#include <QColor>
#include <QList>
//----------------------------------------------------------------------------
namespace SituationMap
{
// forward:
class BoundingBox;
/**
* @brief The AbstractMapObject class is the baseclass of all MapObjects.
* (The class 'Referenced' is for the RefPointer only)
*
* This class supports main parameters like uniqueMapId, debugInfo and identifier
* to differentiate the objects.
*
* Second, the SymbolData can be set to use the TacticalAppearance of objects.
*
* Last but not least, visibility and drawOrder can be modified.
*
* An object has typically two Appearances:
* The Technical and the Tactical Appearance.
* Both can be switched on of off separatly,
* see setVisibility(ObjectAppearance appearance, bool visibility).
* @internal
* The AbstractMapObjectImpl has to be initialized (set the Map, Layer and so on).
* This call is done by the ObjectFactory.
*/
class SITUATIONMAP_EXPORT AbstractMapObject : virtual public Referenced
{
public:
/*! \name Construction, Destruction */
//@{
/// Constructor
AbstractMapObject() = default;
/// Destructor
virtual ~AbstractMapObject() = default;
virtual void removeMySelf() = 0;
//@}
/*! \name Emulate QObject::property */
//@{
/**
* @brief Set named property to value.
* @param name name.
* @param value value.
* @return True on success, otherwise false.
*/
virtual bool setProperty(const char *name, const QVariant &value) = 0;
/**
* @brief Get named property to value.
* @param name to retrieve
* @return Requested value.
*/
virtual QVariant property(const char *name) const = 0;
//@}
/*! \name Identification */
//@{
/**
* @brief Get uniqueMapId.
* @return uniqueMapId.
*/
virtual UniqueMapId uniqueMapId() const = 0;
/**
* @brief Get debugInfo.
* @return debugInfo.
*/
virtual QString debugInfo() const = 0;
/**
* @brief Set DebugInfo.
* @param debugInfo new value.
*/
virtual void setDebugInfo(const QString &debugInfo) = 0;
/**
* @brief toString delivers a string with some parameters.
* @return String with some parameters.
*/
virtual QString toString() const = 0;
/**
* @brief Get identifier.
* @return identifier.
*/
virtual QString identifier() const = 0;
/**
* @brief Set identifier of object to be displayed on the Map.
* @param identifier new value.
*/
virtual void setIdentifier(const QString &identifier) = 0;
virtual bool identifierVisible() const = 0;
virtual void setIdentifierVisible(bool identifierVisible) = 0;
//@}
/*! \name Direct and indirect assignment
* These values (partyId) has a high priority. Therefore it will overwrite
* some other values.
* \n An example is the line and fill color of an AreaFire.
* \n You can specify AreaFire::rLineStyle.setLineColor(Qt::blue) (== direct).
* \n But if a valid partyId is provided, this will overwrite the line and fill
* \n Exeption:
* \n If forcePartyColor is used, this has more priority than the partyId.
* parameter, color (== indirect).
*/
//@{
/**
* @brief Get partyId.
* @return partyId.
*/
virtual int partyId() const = 0;
/**
* @brief Check partyId
* @return True if partyId is valid, otherwise false.
*/
virtual bool isPartyIdValid() const = 0;
/**
* @brief Set PartyId
* @param partyId new value.
*/
virtual void setPartyId(int partyId) = 0;
/**
* @brief If the color of a party changes the partyId has to re-evaluated to the new color.
* @param partyColorToForce the color to be forced.
* This method set the internal flag, that the next updateView will take the new color.
*/
/**
* @brief If the color of a party changes the partyId has to re-evaluated to the new color.
* If relevant this method will set the internal flag, that the next updateView will take the new color.
* @param partyId causing the change, If less then 0, setting the internal flag is forced.
* @return true if relevant for this object, otherwise false
*/
virtual bool setPartyIdChanged(int partyId = -1) = 0;
/**
* @brief forcePartyColor to fulfill the flashing (e.g. of a participant)
* This forcedColor has more priority that than the partyId.
* @param partyColorToForce the color to be forced.
*/
virtual void forcePartyColor(const QColor &partyColorToBeForced) = 0;
//@}
/**
* @brief useLightColor Use the light party color instead the normal one
*/
virtual bool useLightColor() const = 0; ///< Only for Participants
virtual void setUseLightColor(bool useLightColor) = 0; ///< Only for Participants
//@}
//@{
/**
* @brief Get copy of textStyle.
* @return textStyle.
*/
virtual TextStyle textStyle() const = 0;
/**
* @brief Get reference to TextStyle (to modify easily).
* @return Reference to TextStyle.
* @note: Use this with caution. This will set the internal changed flag.
* Maybe you should prefer setTextStyle.
*/
virtual TextStyle &rTextStyle() = 0;
/**
* @brief Set TextStyle to be used for identifier.
* @param textStyle new value.
*/
virtual void setTextStyle(const TextStyle &textStyle) = 0;
//@}
/*! \name User scale */
//@{
/**
* @brief Get scale.
* @return scale
*/
virtual float scale() const = 0;
/**
* @brief Set scale of object.
* @param newScale new value.
*/
virtual void setScale(float newScale) = 0;
virtual bool isScaleEnabled() const = 0;
virtual void setScaleEnable(bool enabled) = 0;
//@}
/*! \name Layer and draworder */
//@{
/**
* @brief Get logical layername.
* @return logical layername.
*/
virtual QString layer() const = 0;
/**
* @brief Get drawOrder.
* @return drawOrder.
*/
virtual int drawOrder() const = 0;
/**
* @brief Set drawOrder.
* Caused by internal dependencies, the value MUST be within -3000...+3000. (INT_MAX=+32767 / 10 = 3276 -> ~3000).
* \n Otherwise the internal graphics will be disordered.
* \internal Background: Due to missing relative drawOrder an absolute drawOrder has to be used.\n
* To create an absolute drawOrder the specified drawOrder is multiplied with 10.
* The components (usually GraphicalObjects) receive that multiplied valued added by s small offset.
* drawOrder() is not influenced by this and will return the value you set before.
* @param drawOrder new value.
*/
virtual void setDrawOrder(int drawOrder) = 0;
//@}
/** \name Visibility
* If the object has to be invisible setVisibility() should be used.
*
* If only a single part has to be modified, remember the Technical and the Tactical ObjectAppearance.
* Both can be switch on or off separately.
*
* Attention: If the two ObjectAppearances are invisible setVisibility(true) will make them <B>NOT</B> visibile.
**/
//@{
// Visibility :
// Prio 1:
/**
* @brief Get visibility of the whole object (Prio 1).
* @return visibility of the whole object.
*/
virtual bool visibility() const = 0;
/**
* @brief Set visibility of the whole object (Prio 1).
* @param visibility new value.
*/
virtual void setVisibility(bool visibility) = 0;
// Prio 2
/**
* @brief Get visibility of a specific ObjectAppearance (Prio 2).
* @param appearance to be retrieved.
* @return Current value.
*/
virtual bool visibility(ObjectAppearance appearance) const = 0;
/**
* @brief Set Visibility of a specific ObjectAppearance (Prio 2).
* @param appearance to be modified.
* @param visibility new value.
*/
virtual void setVisibility(ObjectAppearance appearance, bool visibility) = 0;
/**
Calculates the intersection of map object another geometry
@param intersector_polyline Geometry to intersect with
@note: The geometry must be closed like a polyline. (not really sure)
*/
virtual bool geometryIntersect(const MathUtils::GeometryEngine::Geometry &intersector_polyline) const = 0;
/**
Calculates if the map object is inside another geometry.
@param hull_polygon Geometry to check against.
@note: The geometry must be closed like a polygon. (not really sure)
*/
virtual bool geometryIsInHull(const MathUtils::GeometryEngine::Geometry &hull_polygon) const = 0;
protected:
static int s_scaleHasVisibilityValue;
public:
static void setScaleHasVisibility(int scaleHasVisibility)
{
s_scaleHasVisibilityValue = scaleHasVisibility;
}
// : Visibility
//@}
/*! \name Selection, Picking */
//@{
/**
* @brief Get selected state.
* @return true if selected, otherwise false.
*/
virtual bool selected() const = 0;
/**
* @brief Set selected state.
* @param selected new state.
*/
virtual void setSelected(bool selected) = 0;
/**
* @brief Get pickable state.
* @return true if pickable, otherwise false.
*/
virtual bool pickable() const = 0;
/**
* @brief Set pickable state.
* @param pickable new state.
*/
virtual void setPickable(bool pickable) = 0;
//@}
/*! \name Symbol */
//@{
/**
* @brief Get current symbol.
* @return Current symbol.
*/
virtual SymbolData symbol() const = 0;
/**
* @brief Set new Symbol
* @param symbol new value.
*/
virtual void setSymbol(const SymbolData &symbol) = 0;
/**
* @brief reRenderSymbol
* Will set the changed flag to true, following updateView is required
*/
virtual void reRenderSymbol() = 0;
virtual SymbolDataToRender symbolDataToRender() const = 0;
/**
* @brief symbolImage returns an image, maybe to be used as preview icon.
* @return image of this MapObject.
*/
virtual SymbolImage symbolImage() const = 0;
virtual QImage previewImage() const = 0;
virtual QImage previewImageTypical() const = 0;
virtual QImage previewImageSpecific() const = 0;
//@}
/*! \name Common convenience methode, e.g. for MapObjectIndicator */
//@{
virtual void boundingBox(BoundingBox &bbox) const = 0;
virtual void setForceMajorColor(const QColor &majorColor) = 0;
virtual QColor majorColor() const = 0;
//@}
/*! \name Some methods regarding extrusion (renderer approach) */
//@{
virtual double extrusionHeight() const = 0;
virtual void setExtrusionHeight(double height = std::nan("")) = 0;
virtual const QString extrusionColorName() const = 0;
virtual void setExtrusionColorName(const QString &extrusionColorName) = 0;
//@}
/*! \name updateView */
//@{
/**
* @brief Transfer all values to view
* @return true on success
*/
virtual bool updateView() = 0;
//@}
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
/**
* @brief Output operator used fo all deriving classes.
* @param stream used to stream into.
* @param value to be streamed.
* @return appended stream.
*/
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const AbstractMapObject &value);
} // namespace SituationMap
+170
View File
@@ -0,0 +1,170 @@
/**
* @RDE_HEADER@
* Interface of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "bounding_box.h"
#include "situation_map_utils.h"
#include <situation_map_export.h>
//----------------------------------------------------------------------------
namespace SituationMap
{
BoundingBox::BoundingBox(ExtentType extentType)
{
reset();
m_extentType = extentType;
}
BoundingBox::BoundingBox(ExtentType extentType, const Point3D &center, double width, double height)
: m_extentType(extentType),
m_center(center),
m_width(width),
m_height(height)
{
}
void BoundingBox::reset()
{
m_extentType = ExtentType::Undefined;
m_center = Point3D();
m_width = m_height = std::numeric_limits<double>::lowest();
}
BoundingBox::ExtentType BoundingBox::extentType() const
{
return m_extentType;
}
void BoundingBox::setExtentType(BoundingBox::ExtentType extentType)
{
m_extentType = extentType;
}
bool BoundingBox::isValid() const
{
return
m_extentType != ExtentType::Undefined &&
!m_center.isNull() &&
m_width != std::numeric_limits<double>::lowest() &&
m_height != std::numeric_limits<double>::lowest();
}
Point3D BoundingBox::center() const
{
return m_center;
}
double BoundingBox::width() const
{
return m_width;
}
double BoundingBox::height() const
{
return m_height;
}
Point3D BoundingBox::topLeft() const
{
return Point3D(m_center.x() - width() / 2.0,
m_center.y() + height() / 2.0,
m_center.z());
}
Point3D BoundingBox::topRight() const
{
return Point3D(m_center.x() + width() / 2.0,
m_center.y() + height() / 2.0,
m_center.z());
}
Point3D BoundingBox::bottomLeft() const
{
return Point3D(m_center.x() - width() / 2.0,
m_center.y() - height() / 2.0,
m_center.z());
}
Point3D BoundingBox::bottomRight() const
{
return Point3D(m_center.x() + width() / 2.0,
m_center.y() - height() / 2.0,
m_center.z());
}
Point3D BoundingBox::centerRight() const
{
return Point3D(m_center.x() + width() / 2.0,
m_center.y(),
m_center.z());
}
PositionCont BoundingBox::positions() const
{
PositionCont posCont;
posCont.reserve(4);
posCont.append(topLeft());
posCont.append(topRight());
posCont.append(bottomRight());
posCont.append(bottomLeft());
return posCont;
}
QString BoundingBox::toString() const
{
QString asString = toString(m_extentType);
asString += QStringLiteral(" center=(%1;%2;%3), width=%4, height=%5")
.arg(m_center.x()).arg(m_center.y()).arg(m_center.z()).arg(m_width).arg(m_height);
return asString;
}
QString BoundingBox::toString(BoundingBox::ExtentType value)
{
switch (value)
{
case BoundingBox::ExtentType::Undefined:
return QLatin1String("Undefined");
case BoundingBox::ExtentType::Sim:
return QLatin1String("Sim");
case BoundingBox::ExtentType::Pixel:
return QLatin1String("Pixel");
} // switch
return QLatin1String("BoundingBox::ExtentTypeERROR");
}
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
std::ostream &operator<< (std::ostream &stream,
const BoundingBox &value)
{
stream << value.toString();
return stream;
}
std::ostream &operator<< (std::ostream &stream,
const BoundingBox::ExtentType &value)
{
stream << BoundingBox::toString(value);
return stream;
}
} // namespace SituationMap
+85
View File
@@ -0,0 +1,85 @@
/**
* @RDE_HEADER@
* Interface of AbstractMapObject
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_types.h"
#include "situation_map_utils.h"
#include <situation_map_export.h>
// Qt
//----------------------------------------------------------------------------
namespace SituationMap
{
class SITUATIONMAP_EXPORT BoundingBox
{
public:
enum class ExtentType {Undefined, Sim, Pixel};
static QString toString(BoundingBox::ExtentType value);
public:
explicit BoundingBox(ExtentType extentType = ExtentType::Undefined);
explicit BoundingBox(ExtentType extentType, const Point3D &center, double width = 0, double height = 0); ///< relative to BasePoint
void reset();
ExtentType extentType() const;
void setExtentType(ExtentType extentType);
bool isValid() const;
Point3D center() const;
double width() const; ///< interpret values depending on type
double height() const; ///< interpret values depending on type
Point3D topLeft() const; ///< use quadrant 1
Point3D topRight() const; ///< use quadrant 1
Point3D bottomLeft() const; ///< use quadrant 1
Point3D bottomRight() const; ///< use quadrant 1
Point3D centerRight() const; ///< use quadrant 1
PositionCont positions() const;
QString toString() const;
protected:
ExtentType m_extentType = ExtentType::Undefined; ///< relevant for width and height
Point3D m_center; ///< simCoordinates
double m_width; ///< simCoordinates or pixel
double m_height; ///< simCoordinates or pixel
};
//-----------------------------------------------------------------------------
// output operator:
//-----------------------------------------------------------------------------
/**
* @brief Output operator
* @param stream used to stream into.
* @param value to be streamed.
* @return appended stream.
*/
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const BoundingBox &value);
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const BoundingBox::ExtentType &value);
} // namespace SituationMap
+99
View File
@@ -0,0 +1,99 @@
/**
* @RDE_HEADER@
* Class Camera
* $Id$
* $HeadURL$
* $Author$
*/
// Own
#include "camera.h"
// Qt
#include <QJsonObject>
//----------------------------------------------------------------------------
namespace SituationMap
{
Camera::Camera()
{
resetCamera();
}
void Camera::resetCamera()
{
loc_x_lon = 0;
loc_y_lat = 0;
loc_z = 0;
heading = 0;
pitch = 0;
roll = 0;
}
bool Camera::isNull() const
{
return qFuzzyIsNull(loc_x_lon) && qFuzzyIsNull(loc_y_lat) && qFuzzyIsNull(loc_z) &&
qFuzzyIsNull(heading) && qFuzzyIsNull(pitch) && qFuzzyIsNull(roll);
}
Camera Camera::read(const QJsonObject &json)
{
Camera cam;
if (json.contains(QLatin1String("loc_x_lon")))
cam.loc_x_lon = json[QLatin1String("loc_x_lon")].toDouble();
if (json.contains(QLatin1String("loc_y_lat")))
cam.loc_y_lat = json[QLatin1String("loc_y_lat")].toDouble();
if (json.contains(QLatin1String("loc_z")))
cam.loc_z = json[QLatin1String("loc_z")].toDouble();
if (json.contains(QLatin1String("heading")))
cam.heading = json[QLatin1String("heading")].toDouble();
if (json.contains(QLatin1String("loc_x_lon")))
cam.pitch = json[QLatin1String("pitch")].toDouble();
if (json.contains(QLatin1String("roll")))
cam.roll = json[QLatin1String("roll")].toDouble();
return cam;
}
QJsonObject Camera::toJsonObject() const
{
QJsonObject json;
json[QLatin1String("loc_x_lon")] = loc_x_lon;
json[QLatin1String("loc_y_lat")] = loc_y_lat;
json[QLatin1String("loc_z")] = loc_z;
json[QLatin1String("heading")] = heading;
json[QLatin1String("pitch")] = pitch;
json[QLatin1String("roll")] = roll;
return json;
}
QString Camera::toString() const
{
QString str;
str.append(QStringLiteral("xLon;yLat=%1;%2[rad").arg(loc_x_lon).arg(loc_y_lat) );
str.append(QStringLiteral("xLon;yLat=%1;%2[deg").arg(loc_x_lon*180.0/M_PI).arg(loc_y_lat*180.0/M_PI) );
str.append(QStringLiteral(" z=%1").arg(loc_z));
str.append(QStringLiteral(" heading=%1").arg(heading));
str.append(QStringLiteral(" pitch=%1").arg(pitch));
str.append(QStringLiteral(" roll=%1").arg(roll));
return str;
}
std::ostream &operator<< (std::ostream &stream,
const Camera &value)
{
stream << "Camera(" << value.toString() << "): ";
return stream;
}
} // namespace SituationMap
+63
View File
@@ -0,0 +1,63 @@
/**
* @RDE_HEADER@
* Class Camera
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_types.h"
#include <Common/situation_map_logging.h>
#include <situation_map_export.h>
// Qt
#include <QString>
class QJsonObject;
//----------------------------------------------------------------------------
namespace SituationMap
{
//----------------------------------------------------------------------------
// for 3D:
/**
* @brief A Camera contains the spatial parameters to set a 3D view on a Scene (3D):
* A virtual camera's location, orientation and field of view.
*
*/
class SITUATIONMAP_EXPORT Camera
{
public:
Camera();
~Camera() = default;
void resetCamera();
bool isNull() const;
static Camera read(const QJsonObject &json);
QJsonObject toJsonObject() const;
QString toString() const;
double loc_x_lon; ///< location x (longitude)
double loc_y_lat; ///< location y (latitude)
double loc_z; ///< location z
double heading;
double pitch;
double roll;
};
SITUATIONMAP_EXPORT std::ostream &operator<< (std::ostream &stream,
const SituationMap::Camera &value);
} // namespace SituationMap
+88
View File
@@ -0,0 +1,88 @@
/**
* @RDE_HEADER@
* Interface-Implementation of Map
* $Id$
* $HeadURL$
* $Author$
*/
#pragma once
// Own
#include "situation_map_types.h"
// ESRI
// Qt
#include <QString>
namespace Esri
{
namespace ArcGISRuntime
{
class Point;
}
}
namespace SituationMap
{
/**
* @brief The CoordTranslationFunctor is a base class translating between
* the GeographicCoordinats (WGS84 - EPSG:4326) and the
* EsriMap working on map coordinates.
*
* Forward (geographicToMap):
* Geo (lon, lat) to map.
*
* Backward (mapToGeographic):
* Map to Geo (lon, lat).
*/
class CoordTranslationFunctor
{
public:
/*! \name Construction, Destruction */
//@{
explicit CoordTranslationFunctor() = default; ///< Contructor
virtual ~CoordTranslationFunctor() = default; ///< Destructor
//@}
/*! \name Complete translation */
//@{
Q_REQUIRED_RESULT virtual QPointF geographicToMap(const double lon, const double lat) const = 0; ///< Forward: geographic coord to mapCoord, in radian
Q_REQUIRED_RESULT virtual QPointF mapToGeographic(const double x, const double y) const = 0; ///< Backward: mapCoord to geographic Coord
Q_REQUIRED_RESULT virtual QPointF geographicToMap(const QPointF &coord) const = 0; ///< Forward: geographic coord to mapCoord
Q_REQUIRED_RESULT virtual QPointF mapToGeographic(const QPointF &coord) const = 0; ///< Backward: mapCoord to geographic Coord
Q_REQUIRED_RESULT virtual QPointF geographicToMap(const Esri::ArcGISRuntime::Point &coord) const = 0; ///< Forward: geographic coord to mapCoord
Q_REQUIRED_RESULT virtual QPointF mapToGeographic(const Esri::ArcGISRuntime::Point &coord) const = 0; ///< Backward: mapCoord to geographic Coord
Q_REQUIRED_RESULT virtual Point3D geographicToMap(const Point3D &point3D) const = 0; ///< Forward: geographic coord to mapCoord
Q_REQUIRED_RESULT virtual Point3D mapToGeographic(const Point3D &point3D) const = 0; ///< Backward: mapCoord to geographic Coord
Q_REQUIRED_RESULT virtual bool mapIsGeographic() const = 0;
virtual void geographicToUtm(const double referenceLon, QList<QPointF> &points) const = 0; ///< tanslate to proper utm coordinate system
virtual void utmToGeographic(const double referenceLon, QList<QPointF> &points) const = 0; ///< translate back to goegraphic coordiante system
//@}
/*! \name For statistic or tests */
//@{
virtual int projectionCounter() const = 0;
virtual void resetProjectionCounter() = 0;
//@}
};
//------------------------------------------------------------------------------
} // namespace SituationMap
+146
View File
@@ -0,0 +1,146 @@
/**
**
** @RDE_HEADER@
** Simple DropEventManager to manage drop events in SituationMap Map Widget
** $Id$
** $HeadURL$
** $Author$
**
** -----------------------------------------------------------------------------------------------
**
** Hardware-Environment: Intel-PC
** Software-Environment: Windows 7, Linux, Qt 5.4. ArcGIS 10.2.5
**
** Language ........: C++ 11
**
** -----------------------------------------------------------------------------------------------
**
** Author ...........: Jörg Turla, SPD G, Tel. 3132
**
** -----------------------------------------------------------------------------------------------
**
** Notes .........:
**
** 18.12.2015, JT : First edition for use with Qt5
**
** -----------------------------------------------------------------------------------------------
*/
#pragma once
#include <Common/referenced.h>
#include <QtCore/QPoint>
class QMimeData;
namespace SituationMap
{
/**
@class DropEventManager drop_event_manager.h
@brief Drop event manager interface class.
@see SituationMap::Map, PBase::PDropHandler
To process drop events the map window has to decide accepting a drop when it gets a drag enter event or not, depending on the type of mime data. Later
when receiving a drop event the dropped data has to be processed. The DropEventManager class provides information of mime data type to decide handling
the type of drop event or not. The function mimeFormatString() should return a uniquely defined string. To handle a special drop event the map will call
the manageDrop function. Both functions have to be implemented in the derived class.
Following example shows how to implement a drop event mamager.
@code
#include <Map/drop_event_manager.h>
class DropEventManager : public SituationMap::DropEventManager
{
public:
DropEventManager() = default;
virtual ~DropEventManager()
{
// delete all dropHandler
for (auto key : m_dropHandlerMap.keys())
{
delete m_dropHandlerMap[key];
m_dropHandlerMap[key] = nullptr;
}
} // ~DropEventManager()
QString mimeFormatString() const { return QLatin1String("DropHandlerPtr"); }
void manageDrop( QPoint windowPos, const QMimeData * mimeData ) override
{
bool ok = false;
qulonglong dropHandlerKey = mimeData->data(mimeFormatString()).toULongLong(&ok);
if (ok)
{
QMap<qulonglong,PBase::PDropHandler*>::iterator dropHandler;
dropHandler = m_dropHandlerMap.find( dropHandlerKey );
if (dropHandler != m_dropHandlerMap.end())
{
(*dropHandler)->doDrop( windowPos.x(), windowPos.y(), mimeData );
}
}
} // manageDrop( const QMimeData * mimeData )
qulonglong add( PBase::PDropHandler * dropHandler )
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// DropEvewntManager get ownership of dropHandler and delete them in dtor.
// See dtor above.
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
qulonglong key = qulonglong(dropHandler);
if (dropHandler)
{
m_dropHandlerMap[key] = dropHandler;
}
return key;
} // add(PBase::PDropHandler * dropHandler)
protected:
QMap<qulonglong,PBase::PDropHandler*> m_dropHandlerMap;
}; // DropEventManager
void installDropEventManager()
{
m_dropEventManager = new DropEventManager();
m_map -> installDropEventManager( m_dropEventManager );
}
@endcode
*/
class DropEventManager : public Referenced
{
public:
DropEventManager() = default;
~DropEventManager() = default;
DropEventManager(const DropEventManager *) = delete;
/*
The function \c mimeFormatString() returns a uniquely defined string to identify type of mime data. The string will be used to call \c QMimeData::hasFormat()
@see QMimeData
*/
virtual QString mimeFormatString() const = 0;
/*
The function \c manageDrop() will be called to process drop events
@param windowPos Mouse cursor from drop event
@param mimeData Data of drop event to process.
*/
virtual void manageDrop(QPoint windowPos, const QMimeData *mimeData) = 0;
}; // DropEventManager
} // namespace SituationMap
Loaded 100 of 16245 files, more files were not shown because too many files have changed in this diff. Show more