From 50422a116516cfb7e05f2e60062eb9d7555592ae Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 6 Apr 2018 16:11:50 +0200 Subject: [PATCH 1/6] [FEATURE] QgsMeshLayer part 1: Reading raw mesh Introducting MDAL, QgsMeshLayer, mesh data providers (mesh_memory, mdal) to read and visualize raw meshes: vertices and faces. Support dragging 2dm files from browser on canvas to visualize 2dm meshes. Support for QgsMeshLayer in Python API. --- CMakeLists.txt | 10 + cmake/FindMDAL.cmake | 32 +++ external/mdal/api/mdal.h | 91 +++++++ external/mdal/frmts/mdal_2dm.cpp | 204 ++++++++++++++++ external/mdal/frmts/mdal_2dm.hpp | 28 +++ external/mdal/mdal.cpp | 86 +++++++ external/mdal/mdal_defines.hpp | 31 +++ external/mdal/mdal_loader.cpp | 13 + external/mdal/mdal_loader.hpp | 24 ++ external/mdal/mdal_utils.cpp | 56 +++++ external/mdal/mdal_utils.hpp | 34 +++ python/CMakeLists.txt | 1 + python/core/core_auto.sip | 3 + python/core/mesh/qgsmeshdataprovider.sip.in | 98 ++++++++ python/core/mesh/qgsmeshlayer.sip.in | 174 ++++++++++++++ python/core/mesh/qgsnativemesh.sip.in | 28 +++ python/core/qgsdataitem.sip.in | 4 +- python/core/qgsdataprovider.sip.in | 4 + python/core/qgsmaplayer.sip.in | 6 +- python/core/qgsmimedatautils.sip.in | 8 + python/core/qgsproviderregistry.sip.in | 1 + .../processing/qgsalgorithmpackage.cpp | 6 + src/app/CMakeLists.txt | 1 + src/app/qgisapp.cpp | 6 + src/core/CMakeLists.txt | 14 ++ src/core/expression/qgsexpressionfunction.cpp | 2 + src/core/mesh/qgsmeshdataprovider.cpp | 40 ++++ src/core/mesh/qgsmeshdataprovider.h | 104 ++++++++ src/core/mesh/qgsmeshlayer.cpp | 217 +++++++++++++++++ src/core/mesh/qgsmeshlayer.h | 224 ++++++++++++++++++ src/core/mesh/qgsmeshlayerrenderer.cpp | 109 +++++++++ src/core/mesh/qgsmeshlayerrenderer.h | 79 ++++++ src/core/mesh/qgsmeshmemorydataprovider.cpp | 166 +++++++++++++ src/core/mesh/qgsmeshmemorydataprovider.h | 100 ++++++++ src/core/mesh/qgstriangularmesh.cpp | 110 +++++++++ src/core/mesh/qgstriangularmesh.h | 83 +++++++ src/core/processing/qgsprocessingutils.cpp | 2 + src/core/qgsdataitem.cpp | 13 + src/core/qgsdataitem.h | 4 +- src/core/qgsdataprovider.h | 4 + src/core/qgsmaplayer.h | 6 +- src/core/qgsmaplayermodel.cpp | 5 + src/core/qgsmimedatautils.cpp | 13 + src/core/qgsmimedatautils.h | 10 +- src/core/qgsproviderregistry.cpp | 3 +- src/core/qgsproviderregistry.h | 1 + src/gui/CMakeLists.txt | 1 + src/gui/qgsbrowserdockwidget_p.cpp | 16 +- src/providers/CMakeLists.txt | 1 + src/providers/mdal/CMakeLists.txt | 80 +++++++ src/providers/mdal/qgsmdaldataitems.cpp | 76 ++++++ src/providers/mdal/qgsmdaldataitems.h | 29 +++ src/providers/mdal/qgsmdalprovider.cpp | 137 +++++++++++ src/providers/mdal/qgsmdalprovider.h | 71 ++++++ src/providers/ogr/qgsgeopackagedataitems.cpp | 6 + tests/src/core/CMakeLists.txt | 5 +- tests/src/core/testqgsmeshlayer.cpp | 132 +++++++++++ tests/src/core/testqgsmeshlayerrenderer.cpp | 123 ++++++++++ ...expected_quad_and_triangle_native_mesh.png | Bin 0 -> 80307 bytes ...cted_quad_and_triangle_triangular_mesh.png | Bin 0 -> 80307 bytes tests/testdata/mesh/quad_and_triangle.2dm | 8 + tests/testdata/mesh/quad_and_triangle.txt | 8 + 62 files changed, 2943 insertions(+), 8 deletions(-) create mode 100644 cmake/FindMDAL.cmake create mode 100644 external/mdal/api/mdal.h create mode 100644 external/mdal/frmts/mdal_2dm.cpp create mode 100644 external/mdal/frmts/mdal_2dm.hpp create mode 100644 external/mdal/mdal.cpp create mode 100644 external/mdal/mdal_defines.hpp create mode 100644 external/mdal/mdal_loader.cpp create mode 100644 external/mdal/mdal_loader.hpp create mode 100644 external/mdal/mdal_utils.cpp create mode 100644 external/mdal/mdal_utils.hpp create mode 100644 python/core/mesh/qgsmeshdataprovider.sip.in create mode 100644 python/core/mesh/qgsmeshlayer.sip.in create mode 100644 python/core/mesh/qgsnativemesh.sip.in create mode 100644 src/core/mesh/qgsmeshdataprovider.cpp create mode 100644 src/core/mesh/qgsmeshdataprovider.h create mode 100644 src/core/mesh/qgsmeshlayer.cpp create mode 100644 src/core/mesh/qgsmeshlayer.h create mode 100644 src/core/mesh/qgsmeshlayerrenderer.cpp create mode 100644 src/core/mesh/qgsmeshlayerrenderer.h create mode 100644 src/core/mesh/qgsmeshmemorydataprovider.cpp create mode 100644 src/core/mesh/qgsmeshmemorydataprovider.h create mode 100644 src/core/mesh/qgstriangularmesh.cpp create mode 100644 src/core/mesh/qgstriangularmesh.h create mode 100644 src/providers/mdal/CMakeLists.txt create mode 100644 src/providers/mdal/qgsmdaldataitems.cpp create mode 100644 src/providers/mdal/qgsmdaldataitems.h create mode 100644 src/providers/mdal/qgsmdalprovider.cpp create mode 100644 src/providers/mdal/qgsmdalprovider.h create mode 100644 tests/src/core/testqgsmeshlayer.cpp create mode 100644 tests/src/core/testqgsmeshlayerrenderer.cpp create mode 100644 tests/testdata/control_images/mesh/expected_quad_and_triangle_native_mesh/expected_quad_and_triangle_native_mesh.png create mode 100644 tests/testdata/control_images/mesh/expected_quad_and_triangle_triangular_mesh/expected_quad_and_triangle_triangular_mesh.png create mode 100644 tests/testdata/mesh/quad_and_triangle.2dm create mode 100644 tests/testdata/mesh/quad_and_triangle.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 357224ad21d..1288712817a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,6 +95,12 @@ IF(WITH_CORE) SET (HAVE_GUI TRUE) # used in qgsconfig.h ENDIF() + # try to configure and build MDAL support + SET (WITH_INTERNAL_MDAL TRUE CACHE BOOL "Determines whether MDAL support should be built") + IF (NOT WITH_INTERNAL_MDAL) + SET (MDAL_PREFIX "" CACHE PATH "Path to MDAL base directory") + ENDIF (NOT WITH_INTERNAL_MDAL) + # try to configure and build POSTGRESQL support SET (WITH_POSTGRESQL TRUE CACHE BOOL "Determines whether POSTGRESQL support should be built") IF (WITH_POSTGRESQL) @@ -258,6 +264,10 @@ IF(WITH_CORE) FIND_PACKAGE(Postgres) # PostgreSQL provider ENDIF (WITH_POSTGRESQL) + IF (NOT WITH_INTERNAL_MDAL) + FIND_PACKAGE(MDAL REQUIRED) # MDAL provider + ENDIF (NOT WITH_INTERNAL_MDAL) + FIND_PACKAGE(SpatiaLite REQUIRED) IF (NOT PROJ_FOUND OR NOT GEOS_FOUND OR NOT GDAL_FOUND) diff --git a/cmake/FindMDAL.cmake b/cmake/FindMDAL.cmake new file mode 100644 index 00000000000..72d8ecbfbe0 --- /dev/null +++ b/cmake/FindMDAL.cmake @@ -0,0 +1,32 @@ +# Find MDAL +# ~~~~~~~~~ +# Copyright (c) 2018, Peter Petrik +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# +# +# Once run this will define: +# MDAL_FOUND - System has MDAL +# MDAL_INCLUDE_DIRS - The MDAL include directories +# MDAL_LIBRARIES - The libraries needed to use MDAL +# MDAL_DEFINITIONS - Compiler switches required for using MDAL + +FIND_PACKAGE(PkgConfig) +PKG_CHECK_MODULES(PC_MDAL QUIET libmdal) +SET(MDAL_DEFINITIONS ${PC_MDAL_CFLAGS_OTHER}) + +FIND_PATH(MDAL_INCLUDE_DIR mdal.h + HINTS ${PC_MDAL_INCLUDEDIR} ${PC_MDAL_INCLUDE_DIRS} ${MDAL_PREFIX}/include + PATH_SUFFIXES libmdal ) + +FIND_LIBRARY(MDAL_LIBRARY NAMES mdal libmdal + HINTS ${PC_MDAL_LIBDIR} ${PC_MDAL_LIBRARY_DIRS} ${MDAL_PREFIX}/lib) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(MDAL DEFAULT_MSG + MDAL_LIBRARY MDAL_INCLUDE_DIR) + +MARK_AS_ADVANCED(MDAL_INCLUDE_DIR MDAL_LIBRARY ) + +SET(MDAL_LIBRARIES ${MDAL_LIBRARY} ) +SET(MDAL_INCLUDE_DIRS ${MDAL_INCLUDE_DIR} ) diff --git a/external/mdal/api/mdal.h b/external/mdal/api/mdal.h new file mode 100644 index 00000000000..0569ac4d1e4 --- /dev/null +++ b/external/mdal/api/mdal.h @@ -0,0 +1,91 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#ifndef MDAL_H +#define MDAL_H + +#ifdef MDAL_STATIC +# define MDAL_EXPORT +#else +# if defined _WIN32 || defined __CYGWIN__ +# ifdef mdal_EXPORTS +# ifdef __GNUC__ +# define MDAL_EXPORT __attribute__ ((dllexport)) +# else +# define MDAL_EXPORT __declspec(dllexport) // Note: actually gcc seems to also supports this syntax. +# endif +# else +# ifdef __GNUC__ +# define MDAL_EXPORT __attribute__ ((dllimport)) +# else +# define MDAL_EXPORT __declspec(dllimport) // Note: actually gcc seems to also supports this syntax. +# endif +# endif +# else +# if __GNUC__ >= 4 +# define MDAL_EXPORT __attribute__ ((visibility ("default"))) +# else +# define MDAL_EXPORT +# endif +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Statuses */ +enum Status +{ + None, + // Errors + Err_NotEnoughMemory, + Err_FileNotFound, + Err_UnknownFormat, + Err_IncompatibleMesh, + Err_InvalidData, + Err_MissingDriver, + // Warnings + Warn_UnsupportedElement, + Warn_InvalidElements, + Warn_ElementWithInvalidNode, + Warn_ElementNotUnique, + Warn_NodeNotUnique +}; + +/* Mesh */ +typedef void *MeshH; + +//! Return MDAL version +MDAL_EXPORT const char *MDAL_Version(); + +//! Return last status message +MDAL_EXPORT Status MDAL_LastStatus(); + +//! Load mesh file. On error see MDAL_LastStatus for error type This effectively loads whole mesh in-memory +MDAL_EXPORT MeshH MDAL_LoadMesh( const char *meshFile ); +//! Close mesh, free the memory +MDAL_EXPORT void MDAL_CloseMesh( MeshH mesh ); + +//! Return vertex count for the mesh +MDAL_EXPORT size_t MDAL_M_vertexCount( MeshH mesh ); +//! Return vertex X coord for the mesh +MDAL_EXPORT double MDAL_M_vertexXCoordinatesAt( MeshH mesh, size_t index ); +//! Return vertex Y coord for the mesh +MDAL_EXPORT double MDAL_M_vertexYCoordinatesAt( MeshH mesh, size_t index ); +//! Return face count for the mesh +MDAL_EXPORT size_t MDAL_M_faceCount( MeshH mesh ); +//! Return number of vertices face consist of, e.g. 3 for triangle +MDAL_EXPORT size_t MDAL_M_faceVerticesCountAt( MeshH mesh, size_t index ); +//! Return vertex index for face +MDAL_EXPORT size_t MDAL_M_faceVerticesIndexAt( MeshH mesh, size_t face_index, size_t vertex_index ); + +#ifdef __cplusplus +} +#endif + +#endif //MDAL_H diff --git a/external/mdal/frmts/mdal_2dm.cpp b/external/mdal/frmts/mdal_2dm.cpp new file mode 100644 index 00000000000..2428ba87804 --- /dev/null +++ b/external/mdal/frmts/mdal_2dm.cpp @@ -0,0 +1,204 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdal_2dm.hpp" +#include "mdal.h" +#include "mdal_utils.hpp" + +MDAL::Loader2dm::Loader2dm( const std::string &meshFile ): + mMeshFile( meshFile ) +{ +} + +MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) +{ + if ( status ) *status = Status::None; + + if ( !MDAL::fileExists( mMeshFile ) ) + { + if ( status ) *status = Status::Err_FileNotFound; + return 0; + } + + std::ifstream in( mMeshFile, std::ifstream::in ); + std::string line; + if ( !std::getline( in, line ) || !startsWith( line, "MESH2D" ) ) + { + if ( status ) *status = Status::Err_UnknownFormat; + return 0; + } + + size_t elemCount = 0; + size_t nodeCount = 0; + + // Find out how many nodes and elements are contained in the .2dm mesh file + while ( std::getline( in, line ) ) + { + if ( startsWith( line, "E4Q" ) || + startsWith( line, "E3T" ) ) + { + elemCount++; + } + else if ( startsWith( line, "ND" ) ) + { + nodeCount++; + } + else if ( startsWith( line, "E2L" ) || + startsWith( line, "E3L" ) || + startsWith( line, "E6T" ) || + startsWith( line, "E8Q" ) || + startsWith( line, "E9Q" ) ) + { + if ( status ) *status = Status::Warn_UnsupportedElement; + elemCount += 1; // We still count them as elements + } + } + + // Allocate memory + std::vector vertices( nodeCount ); + std::vector faces( elemCount ); + + in.clear(); + in.seekg( 0, std::ios::beg ); + + std::vector chunks; + + size_t elemIndex = 0; + size_t nodeIndex = 0; + std::map elemIDtoIndex; + std::map nodeIDtoIndex; + + while ( std::getline( in, line ) ) + { + if ( startsWith( line, "E4Q" ) ) + { + chunks = split( line, " ", SplitBehaviour::SkipEmptyParts ); + assert( elemIndex < elemCount ); + + size_t elemID = toSizeT( chunks[1] ); + + std::map::iterator search = elemIDtoIndex.find( elemID ); + if ( search != elemIDtoIndex.end() ) + { + if ( status ) *status = Status::Warn_ElementNotUnique; + continue; + } + elemIDtoIndex[elemID] = elemIndex; + Face &face = faces[elemIndex]; + face.resize( 4 ); + // Right now we just store node IDs here - we will convert them to node indices afterwards + for ( size_t i = 0; i < 4; ++i ) + face[i] = toSizeT( chunks[i + 2] ); + + elemIndex++; + } + else if ( startsWith( line, "E3T" ) ) + { + chunks = split( line, " ", SplitBehaviour::SkipEmptyParts ); + assert( elemIndex < elemCount ); + + size_t elemID = toSizeT( chunks[1] ); + + std::map::iterator search = elemIDtoIndex.find( elemID ); + if ( search != elemIDtoIndex.end() ) + { + if ( status ) *status = Status::Warn_ElementNotUnique; + continue; + } + elemIDtoIndex[elemID] = elemIndex; + Face &face = faces[elemIndex]; + face.resize( 3 ); + // Right now we just store node IDs here - we will convert them to node indices afterwards + for ( size_t i = 0; i < 3; ++i ) + { + face[i] = toSizeT( chunks[i + 2] ); + } + + elemIndex++; + } + else if ( startsWith( line, "E2L" ) || + startsWith( line, "E3L" ) || + startsWith( line, "E6T" ) || + startsWith( line, "E8Q" ) || + startsWith( line, "E9Q" ) ) + { + // We do not yet support these elements + chunks = split( line, " ", SplitBehaviour::SkipEmptyParts ); + assert( elemIndex < elemCount ); + + size_t elemID = toSizeT( chunks[1] ); + + std::map::iterator search = elemIDtoIndex.find( elemID ); + if ( search != elemIDtoIndex.end() ) + { + if ( status ) *status = Status::Warn_ElementNotUnique; + continue; + } + elemIDtoIndex[elemID] = elemIndex; + assert( false ); //TODO mark element as unusable + + elemIndex++; + } + else if ( startsWith( line, "ND" ) ) + { + chunks = split( line, " ", SplitBehaviour::SkipEmptyParts ); + size_t nodeID = toSizeT( chunks[1] ); + + std::map::iterator search = nodeIDtoIndex.find( nodeID ); + if ( search != nodeIDtoIndex.end() ) + { + if ( status ) *status = Status::Warn_NodeNotUnique; + continue; + } + nodeIDtoIndex[nodeID] = nodeIndex; + assert( nodeIndex < nodeCount ); + Vertex &vertex = vertices[nodeIndex]; + vertex.x = toDouble( chunks[2] ); + vertex.y = toDouble( chunks[3] ); + + nodeIndex++; + } + } + + for ( std::vector::iterator it = faces.begin(); it != faces.end(); ++it ) + { + Face &face = *it; + for ( Face::size_type nd = 0; nd < face.size(); ++nd ) + { + size_t nodeID = face[nd]; + + std::map::iterator ni2i = nodeIDtoIndex.find( nodeID ); + if ( ni2i != nodeIDtoIndex.end() ) + { + face[nd] = ni2i->second; // convert from ID to index + } + else + { + assert( false ); //TODO mark element as unusable + + if ( status ) *status = Status::Warn_ElementWithInvalidNode; + } + } + + //TODO check validity of the face + //check that we have distinct nodes + } + + Mesh *mesh = new Mesh; + mesh->faces = faces; + mesh->vertices = vertices; + + return mesh; +} diff --git a/external/mdal/frmts/mdal_2dm.hpp b/external/mdal/frmts/mdal_2dm.hpp new file mode 100644 index 00000000000..28cce0ff028 --- /dev/null +++ b/external/mdal/frmts/mdal_2dm.hpp @@ -0,0 +1,28 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#ifndef MDAL_2DM_HPP +#define MDAL_2DM_HPP + +#include + +#include "mdal_defines.hpp" +#include "mdal.h" + +namespace MDAL +{ + + class Loader2dm + { + public: + Loader2dm( const std::string &meshFile ); + Mesh *load( Status *status ); + + private: + std::string mMeshFile; + }; + +} // namespace MDAL +#endif //MDAL_2DM_HPP diff --git a/external/mdal/mdal.cpp b/external/mdal/mdal.cpp new file mode 100644 index 00000000000..a6d18fe2e7d --- /dev/null +++ b/external/mdal/mdal.cpp @@ -0,0 +1,86 @@ +#include +#include +#include + +#include "mdal.h" +#include "mdal_loader.hpp" +#include "mdal_defines.hpp" + +static Status sLastStatus; + +const char *MDAL_Version() +{ + return "0.0.1"; +} + +Status MDAL_LastStatus() +{ + return sLastStatus; +} + +MeshH MDAL_LoadMesh( const char *meshFile ) +{ + if ( !meshFile ) + return nullptr; + + std::string filename( meshFile ); + return ( MeshH ) MDAL::Loader::load( filename, &sLastStatus ); +} + + +void MDAL_CloseMesh( MeshH mesh ) +{ + if ( mesh ) + { + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + delete m; + } +} + + +size_t MDAL_M_vertexCount( MeshH mesh ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + return m->vertices.size(); +} + +double MDAL_M_vertexXCoordinatesAt( MeshH mesh, size_t index ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + assert( m->vertices.size() > index ); + return m->vertices[index].x; +} + +double MDAL_M_vertexYCoordinatesAt( MeshH mesh, size_t index ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + assert( m->vertices.size() > index ); + return m->vertices[index].y; +} + +size_t MDAL_M_faceCount( MeshH mesh ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + return m->faces.size(); +} + +size_t MDAL_M_faceVerticesCountAt( MeshH mesh, size_t index ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + assert( m->faces.size() > index ); + return m->faces[index].size(); +} + +size_t MDAL_M_faceVerticesIndexAt( MeshH mesh, size_t face_index, size_t vertex_index ) +{ + assert( mesh ); + MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; + assert( m->faces.size() > face_index ); + assert( m->faces[face_index].size() > vertex_index ); + return m->faces[face_index][vertex_index]; +} diff --git a/external/mdal/mdal_defines.hpp b/external/mdal/mdal_defines.hpp new file mode 100644 index 00000000000..f1259b6c653 --- /dev/null +++ b/external/mdal/mdal_defines.hpp @@ -0,0 +1,31 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#ifndef MDAL_DEFINES_HPP +#define MDAL_DEFINES_HPP + +#include +#include + +namespace MDAL +{ + + typedef struct + { + double x; + double y; + } Vertex; + + typedef std::vector Face; + + struct Mesh + { + std::vector vertices; + std::vector faces; + }; + +} // namespace MDAL +#endif //MDAL_DEFINES_HPP + diff --git a/external/mdal/mdal_loader.cpp b/external/mdal/mdal_loader.cpp new file mode 100644 index 00000000000..7671d40e67e --- /dev/null +++ b/external/mdal/mdal_loader.cpp @@ -0,0 +1,13 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#include "mdal_loader.hpp" +#include "frmts/mdal_2dm.hpp" + +MDAL::Mesh *MDAL::Loader::load( const std::string &meshFile, Status *status ) +{ + MDAL::Loader2dm loader( meshFile ); + return loader.load( status ); +} diff --git a/external/mdal/mdal_loader.hpp b/external/mdal/mdal_loader.hpp new file mode 100644 index 00000000000..edd917b11b1 --- /dev/null +++ b/external/mdal/mdal_loader.hpp @@ -0,0 +1,24 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#ifndef MDAL_LOADER_HPP +#define MDAL_LOADER_HPP + +#include + +#include "mdal.h" +#include "mdal_defines.hpp" + +namespace MDAL +{ + + class Loader + { + public: + static Mesh *load( const std::string &meshFile, Status *status ); + }; + +} // namespace MDAL +#endif //MDAL_LOADER_HPP diff --git a/external/mdal/mdal_utils.cpp b/external/mdal/mdal_utils.cpp new file mode 100644 index 00000000000..57a94e1ec5a --- /dev/null +++ b/external/mdal/mdal_utils.cpp @@ -0,0 +1,56 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#include "mdal_utils.hpp" +#include + +bool MDAL::fileExists( const std::string &filename ) +{ + std::ifstream in( filename ); + return in.good(); +} + + +bool MDAL::startsWith( const std::string &str, const std::string &substr ) +{ + return str.rfind( substr, 0 ) == 0; +} + +std::vector MDAL::split( const std::string &str, const std::string &delimiter, SplitBehaviour behaviour ) +{ + std::string remaining( str ); + std::vector list; + size_t pos = 0; + std::string token; + while ( ( pos = remaining.find( delimiter ) ) != std::string::npos ) + { + token = remaining.substr( 0, pos ); + + if ( behaviour == SplitBehaviour::SkipEmptyParts ) + { + if ( !token.empty() ) + list.push_back( token ); + } + else + list.push_back( token ); + + remaining.erase( 0, pos + delimiter.length() ); + } + list.push_back( remaining ); + return list; +} + +size_t MDAL::toSizeT( const std::string &str ) +{ + int i = atoi( str.c_str() ); + if ( i < 0 ) // consistent with atoi return + i = 0; + return i; +} + +double MDAL::toDouble( const std::string &str ) +{ + return atof( str.c_str() ); +} diff --git a/external/mdal/mdal_utils.hpp b/external/mdal/mdal_utils.hpp new file mode 100644 index 00000000000..b50d6caaff6 --- /dev/null +++ b/external/mdal/mdal_utils.hpp @@ -0,0 +1,34 @@ +/* + MDAL - Mesh Data Abstraction Library (MIT License) + Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com) +*/ + +#ifndef MDAL_UTILS_HPP +#define MDAL_UTILS_HPP + +#include +#include +#include + +namespace MDAL +{ + + /** Return whether file exists */ + bool fileExists( const std::string &filename ); + + // strings + bool startsWith( const std::string &str, const std::string &substr ); + + /** Return 0 if not possible to convert */ + size_t toSizeT( const std::string &str ); + double toDouble( const std::string &str ); + + enum SplitBehaviour + { + SkipEmptyParts, + KeepEmptyParts + }; + std::vector split( const std::string &str, const std::string &delimiter, SplitBehaviour behaviour ); + +} // namespace MDAL +#endif //MDAL_UTILS_HPP diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index e8ffb9f293e..24a1fe6ab08 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -119,6 +119,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/layout ${CMAKE_SOURCE_DIR}/src/core/locator ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/processing ${CMAKE_SOURCE_DIR}/src/core/processing/models ${CMAKE_SOURCE_DIR}/src/core/providers diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index 9732f9a36f4..fa21ee1df5c 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -433,3 +433,6 @@ %Include qgsuserprofilemanager.sip %Include symbology/qgsarrowsymbollayer.sip %Include qgsuserprofile.sip +%Include mesh/qgsmeshdataprovider.sip +%Include mesh/qgsmeshlayer.sip + diff --git a/python/core/mesh/qgsmeshdataprovider.sip.in b/python/core/mesh/qgsmeshdataprovider.sip.in new file mode 100644 index 00000000000..3bb56cb64c0 --- /dev/null +++ b/python/core/mesh/qgsmeshdataprovider.sip.in @@ -0,0 +1,98 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsmeshdataprovider.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + +typedef QgsPoint QgsMeshVertex; //xyz coords of vertex +typedef QVector QgsMeshFace; //list of vertex indexes + +class QgsMeshSource /Abstract/ +{ +%Docstring +Mesh is a collection of vertices and faces in 2D or 3D space +- vertex - XY(Z) point (in the mesh's coordinate reference system) +- faces - sets of vertices forming a closed shape - typically triangles or quadrilaterals + +Base on the underlying data provider/format, whole mesh is either stored in memory or +read on demand + +.. versionadded:: 3.2 +%End + +%TypeHeaderCode +#include "qgsmeshdataprovider.h" +%End + public: + virtual ~QgsMeshSource(); + + virtual size_t vertexCount() const = 0; +%Docstring + Return number of vertexes in the native mesh + +:return: Number of vertexes in the mesh +%End + + virtual size_t faceCount() const = 0; +%Docstring + Return number of faces in the native mesh + +:return: Number of faces in the mesh +%End + + virtual QgsMeshVertex vertex( size_t index ) const = 0; +%Docstring + Factory for mesh vertex with index + +:return: new mesh vertex on index +%End + + virtual QgsMeshFace face( size_t index ) const = 0; +%Docstring + Factory for mesh face with index + +:return: new mesh face on index +%End +}; + +class QgsMeshDataProvider: QgsDataProvider, QgsMeshSource +{ +%Docstring +Base class for providing data for :py:class:`QgsMeshLayer` + +Responsible for reading native mesh data + +.. seealso:: :py:class:`QgsMeshSource` +%End + +%TypeHeaderCode +#include "qgsmeshdataprovider.h" +%End + public: + QgsMeshDataProvider( const QString &uri = QString() ); +%Docstring +Ctor +%End + + virtual QgsRectangle extent() const; +%Docstring +Returns the extent of the layer + +:return: QgsRectangle containing the extent of the layer +%End +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsmeshdataprovider.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/mesh/qgsmeshlayer.sip.in b/python/core/mesh/qgsmeshlayer.sip.in new file mode 100644 index 00000000000..7bee996bcf6 --- /dev/null +++ b/python/core/mesh/qgsmeshlayer.sip.in @@ -0,0 +1,174 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsmeshlayer.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + + + +class QgsMeshLayer : QgsMapLayer +{ +%Docstring + +Represents a mesh layer supporting display of data on structured or unstructured meshes + +The QgsMeshLayer is instantiated by specifying the name of a data provider, +such as mdal, and url defining the specific data set to connect to. +The vector layer constructor in turn instantiates a QgsMeshDataProvider subclass +corresponding to the provider type, and passes it the url. The data provider +connects to the data source. + +The QgsMeshLayer provides a common interface to the different data types. It does not +yet support editing transactions. + +The main data providers supported by QGIS are listed below. + +\section providers Mesh data providers + +\subsection memory Memory data providerType (mesh_memory) + +The memory data provider is used to construct in memory data, for example scratch +data. There is no inherent persistent storage of the data. The data source uri is constructed. +Data can be populated by setMesh(const QString &vertices, const QString &faces), where +vertices and faces is comma separated coordinates and connections for mesh. +E.g. to create mesh with one quad and one triangle + +.. code-block:: + + QString uri( + "1.0, 2.0 \n" \ + "2.0, 2.0 \n" \ + "3.0, 2.0 \n" \ + "2.0, 3.0 \n" \ + "1.0, 3.0 \n" \ + "---" + "0, 1, 3, 4 \n" \ + "1, 2, 3 \n" + ); + QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch layer", "memory_mesh"); + +\subsection mdal MDAL data provider (mdal) + +Accesses data using the MDAL drivers (https://github.com/lutraconsulting/MDAL). The url +is the MDAL connection string. QGIS must be built with MDAL support to allow this provider. + +.. code-block:: + + QString uri = "test/land.2dm"; + QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch Layer", "mdal"); + +.. versionadded:: 3.2 +%End + +%TypeHeaderCode +#include "qgsmeshlayer.h" +%End + public: + + explicit QgsMeshLayer( const QString &path = QString(), const QString &baseName = QString(), const QString &providerLib = "mesh_memory" ); +%Docstring +Constructor - creates a mesh layer + +The QgsMeshLayer is constructed by instantiating a data provider. The provider +interprets the supplied path (url) of the data source to connect to and access the +data. + +:param path: The path or url of the parameter. Typically this encodes +parameters used by the data provider as url query items. +:param baseName: The name used to represent the layer in the legend +:param providerLib: The name of the data provider, e.g., "mesh_memory", "mdal" +%End + ~QgsMeshLayer(); + + + virtual QgsMeshDataProvider *dataProvider(); + +%Docstring +Return data provider +%End + + + virtual QgsMeshLayer *clone() const /Factory/; + +%Docstring +Returns a new instance equivalent to this one. A new provider is +created for the same data source and renderers are cloned too. + +:return: a new layer instance +%End + + virtual QgsRectangle extent() const; + +%Docstring +Returns the extent of the layer. +%End + + virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) /Factory/; +%Docstring +Return new instance of QgsMapLayerRenderer that will be used for rendering of given context +%End + + QString providerType() const; +%Docstring +Return the provider type for this layer +%End + + + + QgsSymbol *nativeMeshSymbol(); +%Docstring +Returns a line symbol used for rendering native mesh. +%End + + QgsSymbol *triangularMeshSymbol(); +%Docstring +Returns a line symbol used for rendering of triangular (derived) mesh. + +.. seealso:: :py:func:`toggleTriangularMeshRendering` +%End + + void toggleTriangularMeshRendering( bool toggle ); +%Docstring +Toggle rendering of triangular (derived) mesh. Off by default +%End + + bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ); +%Docstring +Read the symbology for the current layer from the Dom node supplied. + +:param node: node that will contain the symbology definition for this layer. +:param errorMessage: reference to string that will be updated with any error messages +:param context: reading context (used for transform from relative to absolute paths) + +:return: true in case of success. +%End + + bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const; +%Docstring +Write the symbology for the layer into the docment provided. + +:param node: the node that will have the style element added to it. +:param doc: the document that will have the QDomNode added. +:param errorMessage: reference to string that will be updated with any error messages +:param context: writing context (used for transform from absolute to relative paths) + +:return: true in case of success. +%End + + private: // Private methods + QgsMeshLayer( const QgsMeshLayer &rhs ); +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsmeshlayer.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/mesh/qgsnativemesh.sip.in b/python/core/mesh/qgsnativemesh.sip.in new file mode 100644 index 00000000000..5be24a6f0ce --- /dev/null +++ b/python/core/mesh/qgsnativemesh.sip.in @@ -0,0 +1,28 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsnativemesh.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ + + + + + +typedef QgsPoint QgsMeshVertex; //xyz coords of vertex +typedef std::vector QgsMeshFace; //list of vertex indexes + +struct QgsNativeMesh +{ + std::vector vertices; + std::vector faces; +}; + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/mesh/qgsnativemesh.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.pl again * + ************************************************************************/ diff --git a/python/core/qgsdataitem.sip.in b/python/core/qgsdataitem.sip.in index 0bcc087614a..9edd3c04712 100644 --- a/python/core/qgsdataitem.sip.in +++ b/python/core/qgsdataitem.sip.in @@ -394,7 +394,8 @@ Item that represents a layer that can be opened with one of the providers TableLayer, Database, Table, - Plugin + Plugin, + Mesh }; @@ -470,6 +471,7 @@ Returns the icon name of the given ``layerType`` static QIcon iconTable(); static QIcon iconRaster(); static QIcon iconDefault(); + static QIcon iconMesh(); virtual QString layerName() const; %Docstring diff --git a/python/core/qgsdataprovider.sip.in b/python/core/qgsdataprovider.sip.in index 2c6f7317470..71b3c84b508 100644 --- a/python/core/qgsdataprovider.sip.in +++ b/python/core/qgsdataprovider.sip.in @@ -38,6 +38,10 @@ to generic QgsVectorDataProvider's) depends on it. { sipType = sipType_QgsRasterDataProvider; } + else if ( qobject_cast( sipCpp ) ) + { + sipType = sipType_QgsMeshDataProvider; + } else { sipType = 0; diff --git a/python/core/qgsmaplayer.sip.in b/python/core/qgsmaplayer.sip.in index b87409f1eee..ecd1fdde649 100644 --- a/python/core/qgsmaplayer.sip.in +++ b/python/core/qgsmaplayer.sip.in @@ -41,6 +41,9 @@ This is the base class for all map layer types (vector, raster). case QgsMapLayer::PluginLayer: sipType = sipType_QgsPluginLayer; break; + case QgsMapLayer::MeshLayer: + sipType = sipType_QgsMeshLayer; + break; default: sipType = nullptr; break; @@ -53,7 +56,8 @@ This is the base class for all map layer types (vector, raster). { VectorLayer, RasterLayer, - PluginLayer + PluginLayer, + MeshLayer }; enum PropertyType diff --git a/python/core/qgsmimedatautils.sip.in b/python/core/qgsmimedatautils.sip.in index a8130dbac14..d0b028ac96b 100644 --- a/python/core/qgsmimedatautils.sip.in +++ b/python/core/qgsmimedatautils.sip.in @@ -52,6 +52,14 @@ Get vector layer from uri if possible, otherwise returns 0 and error is set %Docstring Get raster layer from uri if possible, otherwise returns 0 and error is set +:param owner: set to true if caller becomes owner +:param error: set to error message if cannot get raster +%End + + QgsMeshLayer *meshLayer( bool &owner, QString &error ) const; +%Docstring +Get mesh layer from uri if possible, otherwise returns 0 and error is set + :param owner: set to true if caller becomes owner :param error: set to error message if cannot get raster %End diff --git a/python/core/qgsproviderregistry.sip.in b/python/core/qgsproviderregistry.sip.in index 1bc45faacec..e739ee915c9 100644 --- a/python/core/qgsproviderregistry.sip.in +++ b/python/core/qgsproviderregistry.sip.in @@ -162,6 +162,7 @@ buildSupportedRasterFileFilter to a string, which is then returned. This replaces :py:func:`QgsRasterLayer.buildSupportedRasterFileFilter()` %End + virtual QString databaseDrivers() const; %Docstring Return a string containing the available database drivers diff --git a/src/analysis/processing/qgsalgorithmpackage.cpp b/src/analysis/processing/qgsalgorithmpackage.cpp index 60e795585f0..a6771a0ca60 100644 --- a/src/analysis/processing/qgsalgorithmpackage.cpp +++ b/src/analysis/processing/qgsalgorithmpackage.cpp @@ -142,6 +142,12 @@ QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap ¶meters feedback->pushDebugInfo( QObject::tr( "Packaging plugin layers is not supported." ) ); errored = true; break; + + case QgsMapLayer::MeshLayer: + //not supported + feedback->pushDebugInfo( QObject::tr( "Packaging mesh layers is not supported." ) ); + errored = true; + break; } } diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 346f51deec4..a9a151c58ec 100755 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -597,6 +597,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/geometry ${CMAKE_SOURCE_DIR}/src/core/geocms/geonode ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/layertree ${CMAKE_SOURCE_DIR}/src/core/locator ${CMAKE_SOURCE_DIR}/src/core/providers/memory diff --git a/src/app/qgisapp.cpp b/src/app/qgisapp.cpp index 1143d8b1151..96ea3bce3d9 100644 --- a/src/app/qgisapp.cpp +++ b/src/app/qgisapp.cpp @@ -228,6 +228,7 @@ Q_GUI_EXPORT extern int qt_defaultDpiX(); #include "qgsmessageviewer.h" #include "qgsmessagebar.h" #include "qgsmessagebaritem.h" +#include "qgsmeshlayer.h" #include "qgsmemoryproviderutils.h" #include "qgsmimedatautils.h" #include "qgsmessagelog.h" @@ -1667,6 +1668,11 @@ void QgisApp::handleDropUriList( const QgsMimeDataUtils::UriList &lst ) { addRasterLayer( uri, u.name, u.providerKey ); } + else if ( u.layerType == QLatin1String( "mesh" ) ) + { + QgsMeshLayer *layer = new QgsMeshLayer( uri, u.name, u.providerKey ); + addMapLayer( layer ); + } else if ( u.layerType == QLatin1String( "plugin" ) ) { addPluginLayer( uri, u.name, u.providerKey ); diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 4fb219682cf..fc32ca63329 100755 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -446,6 +446,12 @@ SET(QGIS_CORE_SRCS raster/qgssinglebandpseudocolorrenderer.cpp raster/qgshillshaderenderer.cpp + mesh/qgsmeshdataprovider.cpp + mesh/qgsmeshlayer.cpp + mesh/qgsmeshlayerrenderer.cpp + mesh/qgsmeshmemorydataprovider.cpp + mesh/qgstriangularmesh.cpp + geometry/qgsabstractgeometry.cpp geometry/qgsbox3d.cpp geometry/qgscircle.cpp @@ -682,6 +688,10 @@ SET(QGIS_CORE_MOC_HDRS raster/qgsrasterlayerrenderer.h raster/qgsrasterprojector.h + mesh/qgsmeshdataprovider.h + mesh/qgsmeshlayer.h + mesh/qgsmeshmemorydataprovider.h + geometry/qgsabstractgeometry.h geometry/qgsgeometry.h geometry/qgspoint.h @@ -1058,6 +1068,9 @@ SET(QGIS_CORE_HDRS raster/qgssinglebandpseudocolorrenderer.h raster/qgshillshaderenderer.h + mesh/qgstriangularmesh.h + mesh/qgsmeshlayerrenderer.h + scalebar/qgsdoubleboxscalebarrenderer.h scalebar/qgsnumericscalebarrenderer.h scalebar/qgsscalebarsettings.h @@ -1177,6 +1190,7 @@ INCLUDE_DIRECTORIES( scalebar symbology metadata + mesh ${CMAKE_SOURCE_DIR}/external/nmea ) IF (WITH_INTERNAL_QEXTSERIALPORT) diff --git a/src/core/expression/qgsexpressionfunction.cpp b/src/core/expression/qgsexpressionfunction.cpp index 0fdecb3e243..abb530ccfe7 100644 --- a/src/core/expression/qgsexpressionfunction.cpp +++ b/src/core/expression/qgsexpressionfunction.cpp @@ -3622,6 +3622,8 @@ static QVariant fcnGetLayerProperty( const QVariantList &values, const QgsExpres return QCoreApplication::translate( "expressions", "Vector" ); case QgsMapLayer::RasterLayer: return QCoreApplication::translate( "expressions", "Raster" ); + case QgsMapLayer::MeshLayer: + return QCoreApplication::translate( "expressions", "Mesh" ); case QgsMapLayer::PluginLayer: return QCoreApplication::translate( "expressions", "Plugin" ); } diff --git a/src/core/mesh/qgsmeshdataprovider.cpp b/src/core/mesh/qgsmeshdataprovider.cpp new file mode 100644 index 00000000000..208a6d6c234 --- /dev/null +++ b/src/core/mesh/qgsmeshdataprovider.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + qgsmeshdataprovider.cpp + ----------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmeshdataprovider.h" + +QgsMeshDataProvider::QgsMeshDataProvider( const QString &uri ) + : QgsDataProvider( uri ) +{ +} + + +QgsRectangle QgsMeshDataProvider::extent() const +{ + QgsRectangle rec; + rec.setMinimal(); + for ( size_t i = 0; i < vertexCount(); ++i ) + { + QgsMeshVertex v = vertex( i ); + rec.setXMinimum( std::min( rec.xMinimum(), v.x() ) ); + rec.setYMinimum( std::min( rec.yMinimum(), v.y() ) ); + rec.setXMaximum( std::max( rec.xMaximum(), v.x() ) ); + rec.setYMaximum( std::max( rec.yMaximum(), v.y() ) ); + } + return rec; + +} diff --git a/src/core/mesh/qgsmeshdataprovider.h b/src/core/mesh/qgsmeshdataprovider.h new file mode 100644 index 00000000000..05f6c18308b --- /dev/null +++ b/src/core/mesh/qgsmeshdataprovider.h @@ -0,0 +1,104 @@ +/*************************************************************************** + qgsmeshdataprovider.h + --------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMESHDATAPROVIDER_H +#define QGSMESHDATAPROVIDER_H + +#include + +#include "qgis_core.h" +#include "qgis.h" +#include "qgspoint.h" +#include "qgsrectangle.h" +#include "qgsdataprovider.h" +#include "qgscoordinatereferencesystem.h" + +#include +#include +#include +#include + +typedef QgsPoint QgsMeshVertex; //xyz coords of vertex +typedef QVector QgsMeshFace; //list of vertex indexes + +/** + * \ingroup core + * Mesh is a collection of vertices and faces in 2D or 3D space + * - vertex - XY(Z) point (in the mesh's coordinate reference system) + * - faces - sets of vertices forming a closed shape - typically triangles or quadrilaterals + * + * Base on the underlying data provider/format, whole mesh is either stored in memory or + * read on demand + * + * \since QGIS 3.2 + */ +class CORE_EXPORT QgsMeshSource SIP_ABSTRACT +{ + public: + //! Dtor + virtual ~QgsMeshSource() = default; + + /** + * \brief Return number of vertexes in the native mesh + * \returns Number of vertexes in the mesh + */ + virtual size_t vertexCount() const = 0; + + /** + * \brief Return number of faces in the native mesh + * \returns Number of faces in the mesh + */ + virtual size_t faceCount() const = 0; + + /** + * \brief Factory for mesh vertex with index + * \returns new mesh vertex on index + */ + virtual QgsMeshVertex vertex( size_t index ) const = 0; + + /** + * \brief Factory for mesh face with index + * \returns new mesh face on index + */ + virtual QgsMeshFace face( size_t index ) const = 0; +}; + +/** + * \ingroup core + * Base class for providing data for QgsMeshLayer + * + * Responsible for reading native mesh data + * + * \see QgsMeshSource + * + */ +class CORE_EXPORT QgsMeshDataProvider: public QgsDataProvider, public QgsMeshSource +{ + Q_OBJECT + + public: + //! Ctor + QgsMeshDataProvider( const QString &uri = QString() ); + + /** + * Returns the extent of the layer + * \returns QgsRectangle containing the extent of the layer + */ + virtual QgsRectangle extent() const; +}; + +#endif // QGSMESHDATAPROVIDER_H diff --git a/src/core/mesh/qgsmeshlayer.cpp b/src/core/mesh/qgsmeshlayer.cpp new file mode 100644 index 00000000000..0b50b737d65 --- /dev/null +++ b/src/core/mesh/qgsmeshlayer.cpp @@ -0,0 +1,217 @@ +/*************************************************************************** + qgsmeshlayer.cpp + ---------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include + +#include "qgsmeshlayer.h" +#include "qgis.h" +#include "qgsmaplayerrenderer.h" +#include "qgsmeshdataprovider.h" +#include "qgsmeshlayerrenderer.h" +#include "qgstriangularmesh.h" +#include "qgssinglesymbolrenderer.h" +#include "qgsmeshmemorydataprovider.h" +#include "qgsfillsymbollayer.h" +#include "qgsproviderregistry.h" +#include "qgslogger.h" + +QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath, + const QString &baseName, + const QString &providerKey ) + : QgsMapLayer( MeshLayer, baseName, meshLayerPath ) + , mProviderKey( providerKey ) +{ + // load data + setDataProvider( providerKey ); + + QgsSymbolLayerList l1; + l1 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::black, Qt::SolidLine, 1.0 ); + mNativeMeshSymbol = new QgsFillSymbol( l1 ); + + + toggleTriangularMeshRendering( false ); + +} // QgsMeshLayer ctor + + + +QgsMeshLayer::~QgsMeshLayer() +{ + clearMeshes(); + + if ( mDataProvider ) + delete mDataProvider; + + if ( mNativeMeshSymbol ) + delete mNativeMeshSymbol; + + if ( mTriangularMeshSymbol ) + delete mTriangularMeshSymbol; +} + +QgsMeshDataProvider *QgsMeshLayer::dataProvider() +{ + return mDataProvider; +} + +const QgsMeshDataProvider *QgsMeshLayer::dataProvider() const +{ + return mDataProvider; +} + +QgsMeshLayer *QgsMeshLayer::clone() const +{ + QgsMeshLayer *layer = new QgsMeshLayer( source(), name(), mProviderKey ); + QgsMapLayer::clone( layer ); + return layer; +} + +QgsRectangle QgsMeshLayer::extent() const +{ + if ( mDataProvider ) + return mDataProvider->extent(); + else + { + QgsRectangle rec; + rec.setMinimal(); + return rec; + } +} + +QString QgsMeshLayer::providerType() const +{ + return mProviderKey; +} + +void QgsMeshLayer::toggleTriangularMeshRendering( bool toggle ) +{ + if ( toggle && mTriangularMeshSymbol ) + return; + + if ( mTriangularMeshSymbol ) + delete mTriangularMeshSymbol; + + if ( toggle ) + { + QgsSymbolLayerList l2; + l2 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::red, Qt::SolidLine, 0.26 ); + mTriangularMeshSymbol = new QgsFillSymbol( l2 ); + } + else + { + mTriangularMeshSymbol = nullptr; + } + triggerRepaint(); +} + +void QgsMeshLayer::fillNativeMesh() +{ + Q_ASSERT( !mNativeMesh ); + + mNativeMesh = new QgsMesh(); + + if ( !( dataProvider() && dataProvider()->isValid() ) ) + return; + + mNativeMesh->vertices.resize( dataProvider()->vertexCount() ); + for ( size_t i = 0; i < dataProvider()->vertexCount(); ++i ) + { + mNativeMesh->vertices[i] = dataProvider()->vertex( i ); + } + + mNativeMesh->faces.resize( dataProvider()->faceCount() ); + for ( size_t i = 0; i < dataProvider()->faceCount(); ++i ) + { + mNativeMesh->faces[i] = dataProvider()->face( i ); + } +} + +QgsMapLayerRenderer *QgsMeshLayer::createMapRenderer( QgsRenderContext &rendererContext ) +{ + if ( !mNativeMesh ) + { + fillNativeMesh(); + } + + if ( !mTriangularMesh ) + mTriangularMesh = new QgsTriangularMesh(); + + triangularMesh()->update( mNativeMesh, &rendererContext ); + return new QgsMeshLayerRenderer( this, rendererContext ); +} + +bool QgsMeshLayer::readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ) +{ + Q_UNUSED( node ); + Q_UNUSED( errorMessage ); + Q_UNUSED( context ); + return true; +} + +bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const +{ + Q_UNUSED( node ); + Q_UNUSED( doc ); + Q_UNUSED( errorMessage ); + Q_UNUSED( context ); + return true; +} + +void QgsMeshLayer::clearMeshes() +{ + if ( mTriangularMesh ) + delete mTriangularMesh; + + if ( mNativeMesh ) + delete mNativeMesh; + +} + +bool QgsMeshLayer::setDataProvider( QString const &provider ) +{ + clearMeshes(); + + mProviderKey = provider; + QString dataSource = mDataSource; + + if ( mDataProvider ) + delete mDataProvider; + + mDataProvider = qobject_cast( QgsProviderRegistry::instance()->createProvider( provider, dataSource ) ); + if ( !mDataProvider ) + { + QgsDebugMsgLevel( QStringLiteral( "Unable to get mesh data provider" ), 2 ); + return false; + } + + mDataProvider->setParent( this ); + QgsDebugMsgLevel( QStringLiteral( "Instantiated the mesh data provider plugin" ), 2 ); + + mValid = mDataProvider->isValid(); + if ( !mValid ) + { + QgsDebugMsgLevel( QStringLiteral( "Invalid mesh provider plugin %1" ).arg( QString( mDataSource.toUtf8() ) ), 2 ); + return false; + } + + if ( provider == QStringLiteral( "mesh_memory" ) ) + { + // required so that source differs between memory layers + mDataSource = mDataSource + QStringLiteral( "&uid=%1" ).arg( QUuid::createUuid().toString() ); + } + + return true; +} // QgsMeshLayer:: setDataProvider diff --git a/src/core/mesh/qgsmeshlayer.h b/src/core/mesh/qgsmeshlayer.h new file mode 100644 index 00000000000..fe8df374e62 --- /dev/null +++ b/src/core/mesh/qgsmeshlayer.h @@ -0,0 +1,224 @@ +/*************************************************************************** + qgsmeshlayer.h + -------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMESHLAYER_H +#define QGSMESHLAYER_H + +#include "qgis_core.h" +#include +#include +#include +#include +#include +#include +#include + +#include "qgis.h" +#include "qgsmaplayer.h" +#include "qgsrendercontext.h" +#include "qgsmeshdataprovider.h" + +class QgsMapLayerRenderer; +class QgsSymbol; + +class QgsMeshDataProvider; +class QgsNativeMesh; +class QgsTriangularMesh; +struct QgsMesh; + +/** + * \ingroup core + * + * Represents a mesh layer supporting display of data on structured or unstructured meshes + * + * The QgsMeshLayer is instantiated by specifying the name of a data provider, + * such as mdal, and url defining the specific data set to connect to. + * The vector layer constructor in turn instantiates a QgsMeshDataProvider subclass + * corresponding to the provider type, and passes it the url. The data provider + * connects to the data source. + * + * The QgsMeshLayer provides a common interface to the different data types. It does not + * yet support editing transactions. + * + * The main data providers supported by QGIS are listed below. + * + * \section providers Mesh data providers + * + * \subsection memory Memory data providerType (mesh_memory) + * + * The memory data provider is used to construct in memory data, for example scratch + * data. There is no inherent persistent storage of the data. The data source uri is constructed. + * Data can be populated by setMesh(const QString &vertices, const QString &faces), where + * vertices and faces is comma separated coordinates and connections for mesh. + * E.g. to create mesh with one quad and one triangle + * + * \code + * QString uri( + * "1.0, 2.0 \n" \ + * "2.0, 2.0 \n" \ + * "3.0, 2.0 \n" \ + * "2.0, 3.0 \n" \ + * "1.0, 3.0 \n" \ + * "---" + * "0, 1, 3, 4 \n" \ + * "1, 2, 3 \n" + * ); + * QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch layer", "memory_mesh"); + * \endcode + * + * \subsection mdal MDAL data provider (mdal) + * + * Accesses data using the MDAL drivers (https://github.com/lutraconsulting/MDAL). The url + * is the MDAL connection string. QGIS must be built with MDAL support to allow this provider. + + * \code + * QString uri = "test/land.2dm"; + * QgsMeshLayer *scratchLayer = new QgsMeshLayer(uri, "My Scratch Layer", "mdal"); + * \endcode + * + * \since QGIS 3.2 + */ +class CORE_EXPORT QgsMeshLayer : public QgsMapLayer +{ + Q_OBJECT + public: + + /** + * Constructor - creates a mesh layer + * + * The QgsMeshLayer is constructed by instantiating a data provider. The provider + * interprets the supplied path (url) of the data source to connect to and access the + * data. + * + * \param path The path or url of the parameter. Typically this encodes + * parameters used by the data provider as url query items. + * \param baseName The name used to represent the layer in the legend + * \param providerLib The name of the data provider, e.g., "mesh_memory", "mdal" + */ + explicit QgsMeshLayer( const QString &path = QString(), const QString &baseName = QString(), const QString &providerLib = "mesh_memory" ); + //! Dtor + ~QgsMeshLayer() override; + + //! QgsMeshLayer cannot be copied. + QgsMeshLayer( const QgsMeshLayer &rhs ) = delete; + //! QgsMeshLayer cannot be copied. + QgsMeshLayer &operator=( QgsMeshLayer const &rhs ) = delete; + + //! Return data provider + QgsMeshDataProvider *dataProvider() override; + + //! Return const data provider + const QgsMeshDataProvider *dataProvider() const override SIP_SKIP; + + /** + * Returns a new instance equivalent to this one. A new provider is + * created for the same data source and renderers are cloned too. + * \returns a new layer instance + */ + QgsMeshLayer *clone() const override SIP_FACTORY; + + //! Returns the extent of the layer. + QgsRectangle extent() const override; + + /** + * Return new instance of QgsMapLayerRenderer that will be used for rendering of given context + */ + virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) SIP_FACTORY; + + //! Return the provider type for this layer + QString providerType() const; + + //! return native mesh (nullprt before rendering) + QgsMesh *nativeMesh() SIP_SKIP {return mNativeMesh;} + + //! return triangular mesh (nullprt before rendering) + QgsTriangularMesh *triangularMesh() SIP_SKIP {return mTriangularMesh;} + + //! Returns a line symbol used for rendering native mesh. + QgsSymbol *nativeMeshSymbol() {return mNativeMeshSymbol;} + + /** + * Returns a line symbol used for rendering of triangular (derived) mesh. + * \see toggleTriangularMeshRendering + */ + QgsSymbol *triangularMeshSymbol() {return mTriangularMeshSymbol;} + + //! Toggle rendering of triangular (derived) mesh. Off by default + void toggleTriangularMeshRendering( bool toggle ); + + /** + * Read the symbology for the current layer from the Dom node supplied. + * \param node node that will contain the symbology definition for this layer. + * \param errorMessage reference to string that will be updated with any error messages + * \param context reading context (used for transform from relative to absolute paths) + * \returns true in case of success. + */ + bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ); + + /** + * Write the symbology for the layer into the docment provided. + * \param node the node that will have the style element added to it. + * \param doc the document that will have the QDomNode added. + * \param errorMessage reference to string that will be updated with any error messages + * \param context writing context (used for transform from absolute to relative paths) + * \returns true in case of success. + */ + bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const; + + private: // Private methods + + /** + * Returns true if the provider is in read-only mode + */ + bool isReadOnly() const override {return true;} + + /** + * Bind layer to a specific data provider + * \param provider provider key string, must match a valid QgsMeshDataProvider key. E.g. "mesh_memory", etc. + */ + bool setDataProvider( QString const &provider ); + +#ifdef SIP_RUN + QgsMeshLayer( const QgsMeshLayer &rhs ); +#endif + + private: + //! Clear native and triangular mesh + void clearMeshes(); + void fillNativeMesh(); + + private: + //! Pointer to native mesh structure, used as cache for rendering + QgsMesh *mNativeMesh = nullptr; + + //! Pointer to derived mesh structure + QgsTriangularMesh *mTriangularMesh = nullptr; + + //! Pointer to data provider derived from the abastract base class QgsMeshDataProvider + QgsMeshDataProvider *mDataProvider = nullptr; + + //! Data provider key + QString mProviderKey; + + //! rendering native mesh + QgsSymbol *mNativeMeshSymbol = nullptr; + + //! rendering triangular mesh + QgsSymbol *mTriangularMeshSymbol = nullptr; +}; + +#endif //QGSMESHLAYER_H diff --git a/src/core/mesh/qgsmeshlayerrenderer.cpp b/src/core/mesh/qgsmeshlayerrenderer.cpp new file mode 100644 index 00000000000..45071ad3c6d --- /dev/null +++ b/src/core/mesh/qgsmeshlayerrenderer.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + qgsmeshlayerrenderer.cpp + ------------------------ + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmeshlayerrenderer.h" + +#include "qgsrenderer.h" +#include "qgsrendercontext.h" +#include "qgsmeshlayer.h" +#include "qgsexception.h" +#include "qgslogger.h" +#include "qgssettings.h" +#include "qgssinglesymbolrenderer.h" +#include "qgsfield.h" +#include "qgstriangularmesh.h" +#include "qgspointxy.h" + +#include + + +QgsMeshLayerRenderer::QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context ) + : QgsMapLayerRenderer( layer->id() ) + , mContext( context ) +{ + // make copies for mesh data + Q_ASSERT( layer->nativeMesh() ); + Q_ASSERT( layer->triangularMesh() ); + mNativeMesh = *( layer->nativeMesh() ); + mTriangularMesh = *( layer->triangularMesh() ); + + // make copies for symbols + if ( layer->nativeMeshSymbol() ) + { + mNativeMeshSymbol = layer->nativeMeshSymbol()->clone(); + } + + if ( layer->triangularMeshSymbol() ) + { + mTriangularMeshSymbol = layer->triangularMeshSymbol()->clone(); + } +} + + +QgsMeshLayerRenderer::~QgsMeshLayerRenderer() +{ + if ( mNativeMeshSymbol ) + delete mNativeMeshSymbol; + + if ( mTriangularMeshSymbol ) + delete mTriangularMeshSymbol; +} + + + +bool QgsMeshLayerRenderer::render() +{ + renderMesh( mNativeMeshSymbol, mNativeMesh.faces ); // native mesh + renderMesh( mTriangularMeshSymbol, mTriangularMesh.triangles() ); // triangular mesh + + return true; +} + +void QgsMeshLayerRenderer::renderMesh( QgsSymbol *symbol, const QVector &faces ) +{ + if ( !symbol ) + return; + + QgsFields fields; + QgsSingleSymbolRenderer renderer( symbol->clone() ); + renderer.startRender( mContext, fields ); + + for ( int i = 0; i < faces.size(); ++i ) + { + if ( mContext.renderingStopped() ) + break; + + const QgsMeshFace &face = faces[i]; + QgsFeature feat; + feat.setFields( fields ); + QVector ring; + for ( int j = 0; j < face.size(); ++j ) + { + int vertex_id = face[j]; + Q_ASSERT( vertex_id < mTriangularMesh.vertices().size() ); //Triangular mesh vertices contains also native mesh vertices + const QgsPoint &vertex = mTriangularMesh.vertices()[vertex_id]; + ring.append( vertex ); + } + QgsPolygonXY polygon; + polygon.append( ring ); + QgsGeometry geom = QgsGeometry::fromPolygonXY( polygon ); + feat.setGeometry( geom ); + renderer.renderFeature( feat, mContext ); + } + + renderer.stopRender( mContext ); +} diff --git a/src/core/mesh/qgsmeshlayerrenderer.h b/src/core/mesh/qgsmeshlayerrenderer.h new file mode 100644 index 00000000000..a6d7841d5fc --- /dev/null +++ b/src/core/mesh/qgsmeshlayerrenderer.h @@ -0,0 +1,79 @@ +/*************************************************************************** + qgsmeshlayerrenderer.h + ---------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMESHLAYERRENDERER_H +#define QGSMESHLAYERRENDERER_H + +class QgsRenderContext; +class QgsMeshLayer; +class QgsMeshVectorFieldRenderer; +class QgsSingleSymbolRenderer; +class QgsTriangularMesh; +class QgsSymbol; + +#define SIP_NO_FILE + +#include +#include + +#include "qgis.h" +#include "qgsfeedback.h" + +#include "qgsmaplayerrenderer.h" +#include "qgsmeshdataprovider.h" +#include "qgstriangularmesh.h" + +/** + * \ingroup core + * Implementation of threaded rendering for mesh layers. + * + * \since QGIS 3.2 + * \note not available in Python bindings + */ +class QgsMeshLayerRenderer : public QgsMapLayerRenderer +{ + public: + QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context ); + ~QgsMeshLayerRenderer() override; + + bool render() override; + + private: + void renderMesh( QgsSymbol *symbol, const QVector &faces ); + + + protected: + // copy from mesh layer + QgsMesh mNativeMesh; + + // copy from mesh layer + QgsTriangularMesh mTriangularMesh; + + // copy from mesh layer + QgsSymbol *mNativeMeshSymbol = nullptr; + + // copy from mesh layer + QgsSymbol *mTriangularMeshSymbol = nullptr; + + // rendering context + QgsRenderContext &mContext; + + +}; + + +#endif // QGSMESHLAYERRENDERER_H diff --git a/src/core/mesh/qgsmeshmemorydataprovider.cpp b/src/core/mesh/qgsmeshmemorydataprovider.cpp new file mode 100644 index 00000000000..814165a0b0d --- /dev/null +++ b/src/core/mesh/qgsmeshmemorydataprovider.cpp @@ -0,0 +1,166 @@ +/*************************************************************************** + qgsmeshmemorydataprovider.cpp + ----------------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmeshmemorydataprovider.h" +#include +#include +#include + +static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mesh_memory" ); +static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "Mesh memory provider" ); + +bool QgsMeshMemoryDataProvider::isValid() const +{ + return true; +} + +QString QgsMeshMemoryDataProvider::name() const +{ + return "mesh_memory"; +} + +QString QgsMeshMemoryDataProvider::description() const +{ + return "memory data provider for mesh layer"; +} + +QgsCoordinateReferenceSystem QgsMeshMemoryDataProvider::crs() const +{ + return QgsCoordinateReferenceSystem(); +} + +QgsMeshMemoryDataProvider::QgsMeshMemoryDataProvider( const QString &uri ) + : QgsMeshDataProvider( uri ) + , mIsValid( false ) +{ + mIsValid = splitSections( uri ); +} + +QgsMeshMemoryDataProvider::~QgsMeshMemoryDataProvider() +{ +} + +QString QgsMeshMemoryDataProvider::providerKey() +{ + return TEXT_PROVIDER_KEY; +} + +QString QgsMeshMemoryDataProvider::providerDescription() +{ + return TEXT_PROVIDER_DESCRIPTION; +} + +QgsMeshMemoryDataProvider *QgsMeshMemoryDataProvider::createProvider( const QString &uri ) +{ + return new QgsMeshMemoryDataProvider( uri ); +} + +bool QgsMeshMemoryDataProvider::splitSections( const QString &uri ) +{ + QStringList sections = uri.split( "---", QString::SkipEmptyParts ); + if ( sections.size() != 2 ) + { + setError( QgsError( QStringLiteral( "Invalid mesh definition, does not contain 2 sections" ), + QStringLiteral( "Mesh Memory Provider" ) ) ); + return false; + } + + if ( addVertices( sections[0] ) ) + return addFaces( sections[1] ); + else + return false; +} + +bool QgsMeshMemoryDataProvider::addVertices( const QString &def ) +{ + QVector vertices; + + QStringList verticesCoords = def.split( "\n", QString::SkipEmptyParts ); + for ( int i = 0; i < verticesCoords.size(); ++i ) + { + QStringList coords = verticesCoords[i].split( ",", QString::SkipEmptyParts ); + if ( coords.size() != 2 ) + { + setError( QgsError( QStringLiteral( "Invalid mesh definition, vertex definition does not contain x, y" ), + QStringLiteral( "Mesh Memory Provider" ) ) ); + return false; + } + double x = coords.at( 0 ).toDouble(); + double y = coords.at( 1 ).toDouble(); + QgsMeshVertex vertex( x, y ); + vertices.push_back( vertex ); + } + + mVertices = vertices; + return true; +} + +bool QgsMeshMemoryDataProvider::addFaces( const QString &def ) +{ + QVector faces; + + QStringList facesVertices = def.split( "\n", QString::SkipEmptyParts ); + for ( int i = 0; i < facesVertices.size(); ++i ) + { + QStringList vertices = facesVertices[i].split( ",", QString::SkipEmptyParts ); + if ( vertices.size() < 3 ) + { + setError( QgsError( QStringLiteral( "Invalid mesh definition, face must contain at least 3 vertices" ), + QStringLiteral( "Mesh Memory Provider" ) ) ); + return false; + } + QgsMeshFace face; + for ( int j = 0; j < vertices.size(); ++j ) + { + int vertex_id = vertices[j].toInt(); + face.push_back( vertex_id ); + if ( face[j] >= mVertices.size() ) + { + setError( QgsError( QStringLiteral( "Invalid mesh definition, missing vertex id defined in face" ), QStringLiteral( "Mesh Memory Provider" ) ) ); + return false; + } + } + faces.push_back( face ); + } + + mFaces = faces; + return true; +} + +size_t QgsMeshMemoryDataProvider::vertexCount() const +{ + return mVertices.size(); +} + +size_t QgsMeshMemoryDataProvider::faceCount() const +{ + return mFaces.size(); +} + +QgsMeshVertex QgsMeshMemoryDataProvider::vertex( size_t index ) const +{ + Q_ASSERT( vertexCount() > index ); + return mVertices[index]; +} + +QgsMeshFace QgsMeshMemoryDataProvider::face( size_t index ) const +{ + Q_ASSERT( faceCount() > index ); + return mFaces[index]; +} + + diff --git a/src/core/mesh/qgsmeshmemorydataprovider.h b/src/core/mesh/qgsmeshmemorydataprovider.h new file mode 100644 index 00000000000..dbbe72c439b --- /dev/null +++ b/src/core/mesh/qgsmeshmemorydataprovider.h @@ -0,0 +1,100 @@ +/*************************************************************************** + qgsmeshmemorydataprovider.h + --------------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMESHMEMORYDATAPROVIDER_H +#define QGSMESHMEMORYDATAPROVIDER_H + +#define SIP_NO_FILE + +///@cond PRIVATE + +#include + +#include + +#include "qgis_core.h" +#include "qgis.h" +#include "qgsmeshdataprovider.h" +#include "qgsrectangle.h" + +/** + * \ingroup core + * Provides data stored in-memory for QgsMeshLayer. Useful for plugins or tests. + * \since QGIS 3.2 + */ +class QgsMeshMemoryDataProvider: public QgsMeshDataProvider +{ + Q_OBJECT + + public: + + /** + * Construct a mesh in-memory data provider from data string + * + * Data string constains simple definition of vertices and faces + * Each entry is separated by "\n" sign and section deliminer "---" + * vertex is x and y coordinate separated by comma + * face is list of vertex indexes, numbered from 0 + * For example: + * + * \code + * QString uri( + * "1.0, 2.0 \n" \ + * "2.0, 2.0 \n" \ + * "3.0, 2.0 \n" \ + * "2.0, 3.0 \n" \ + * "1.0, 3.0 \n" \ + * "---" + * "0, 1, 3, 4 \n" \ + * "1, 2, 3 \n" + * ); + * \endcode + */ + QgsMeshMemoryDataProvider( const QString &uri = QString() ); + ~QgsMeshMemoryDataProvider(); + + bool isValid() const override; + QString name() const override; + QString description() const override; + QgsCoordinateReferenceSystem crs() const override; + + size_t vertexCount() const override; + size_t faceCount() const override; + QgsMeshVertex vertex( size_t index ) const override; + QgsMeshFace face( size_t index ) const override; + + //! Returns the memory provider key + static QString providerKey(); + //! Returns the memory provider description + static QString providerDescription(); + //! Provider factory + static QgsMeshMemoryDataProvider *createProvider( const QString &uri ); + private: + bool splitSections( const QString &uri ); + + bool addVertices( const QString &def ); + bool addFaces( const QString &def ); + + QVector mVertices; + QVector mFaces; + + bool mIsValid; +}; + +///@endcond + +#endif // QGSMESHMEMORYDATAPROVIDER_H diff --git a/src/core/mesh/qgstriangularmesh.cpp b/src/core/mesh/qgstriangularmesh.cpp new file mode 100644 index 00000000000..c61b8779fca --- /dev/null +++ b/src/core/mesh/qgstriangularmesh.cpp @@ -0,0 +1,110 @@ +/*************************************************************************** + qgstriangularmesh.cpp + --------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstriangularmesh.h" +#include "qgsmeshdataprovider.h" +#include "qgsrendercontext.h" +#include "qgscoordinatereferencesystem.h" +#include "qgscoordinatetransform.h" +#include "qgsmeshlayer.h" + + +QgsTriangularMesh::QgsTriangularMesh( ) +{ +} + +QgsTriangularMesh::~QgsTriangularMesh() +{ +} + +void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context ) +{ + Q_ASSERT( nativeMesh ); + Q_ASSERT( context ); + + mTriangularMesh.vertices.clear(); + mTriangularMesh.faces.clear(); + mTrianglesToNativeFaces.clear(); + + // TRANSFORM VERTICES + QgsCoordinateTransform transform = context->coordinateTransform(); + mTriangularMesh.vertices.resize( nativeMesh->vertices.size() ); + for ( int i = 0; i < nativeMesh->vertices.size(); ++i ) + { + QgsMeshVertex vertex = nativeMesh->vertices[i]; + if ( transform.isValid() ) + { + QgsPointXY mapPoint = transform.transform( QgsPointXY( vertex.x(), vertex.y() ) ); + QgsMeshVertex mapVertex( mapPoint ); + mapVertex.setZ( vertex.z() ); + mapVertex.setM( vertex.m() ); + mTriangularMesh.vertices[i] = mapVertex; + } + else + { + mTriangularMesh.vertices[i] = vertex; + } + } + + // CREATE TRIANGULAR MESH + for ( int i = 0; i < nativeMesh->faces.size(); ++i ) + { + QgsMeshFace face = nativeMesh->faces[i] ; + if ( face.size() == 3 ) + { + // triangle + mTriangularMesh.faces.push_back( face ); + mTrianglesToNativeFaces.push_back( i ); + } + else if ( face.size() == 4 ) + { + // quad + QgsMeshFace face1; + face1.push_back( face[0] ); + face1.push_back( face[1] ); + face1.push_back( face[2] ); + + mTriangularMesh.faces.push_back( face1 ); + mTrianglesToNativeFaces.push_back( i ); + + QgsMeshFace face2; + face2.push_back( face[0] ); + face2.push_back( face[2] ); + face2.push_back( face[3] ); + + mTriangularMesh.faces.push_back( face2 ); + mTrianglesToNativeFaces.push_back( i ); + } + } + +} + +const QVector &QgsTriangularMesh::vertices() const +{ + return mTriangularMesh.vertices; +} + +const QVector &QgsTriangularMesh::triangles() const +{ + return mTriangularMesh.faces; +} + +const QVector &QgsTriangularMesh::trianglesToNativeFaces() const +{ + return mTrianglesToNativeFaces; +} + diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h new file mode 100644 index 00000000000..d97732a3215 --- /dev/null +++ b/src/core/mesh/qgstriangularmesh.h @@ -0,0 +1,83 @@ +/*************************************************************************** + qgstriangularmesh.h + ------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSTRIANGULARMESH_H +#define QGSTRIANGULARMESH_H + + +#define SIP_NO_FILE + +#include +#include +#include + +#include "qgis.h" +#include "qgis_core.h" +#include "qgsmeshdataprovider.h" +#include "qgscoordinatereferencesystem.h" + +class QgsRenderContext; + +//! Mesh - vertices and faces +struct CORE_EXPORT QgsMesh +{ + //! vertices + QVector vertices; + //! faces + QVector faces; +}; + +/** + * Triangular/Derived Mesh + * \since QGIS 3.2 + */ +class CORE_EXPORT QgsTriangularMesh +{ + public: + //! Ctor + QgsTriangularMesh(); + //! Dtor + ~QgsTriangularMesh(); + + /** + * Construct triangular mesh from layer's native mesh and context + * \param layer QgsMeshLayer to get native mesh data + * \param context Rendering context to estimate number of triagles to create for an face + */ + void update( QgsMesh *nativeMesh, QgsRenderContext *context ); + + /** + * Return vertices in map CRS + * + * The list of consist of vertices from native mesh (0-N) and + * extra vertices needed to create triangles (N+1 - len) + */ + const QVector &vertices() const ; + //! Return triangles + const QVector &triangles() const ; + //! Return mapping between triangles and original faces + const QVector &trianglesToNativeFaces() const ; + + private: + // vertices: map CRS; 0-N ... native vertices, N+1 - len ... extra vertices + // faces are derived triangles + QgsMesh mTriangularMesh; + QVector mTrianglesToNativeFaces; //len(mTrianglesToNativeFaces) == len(mTriangles). Mapping derived -> native +}; + + +#endif // QGSTRIANGULARMESH_H diff --git a/src/core/processing/qgsprocessingutils.cpp b/src/core/processing/qgsprocessingutils.cpp index cfb0632b5ff..cc358f889a5 100644 --- a/src/core/processing/qgsprocessingutils.cpp +++ b/src/core/processing/qgsprocessingutils.cpp @@ -111,6 +111,8 @@ QgsMapLayer *QgsProcessingUtils::mapLayerFromStore( const QString &string, QgsMa return !canUseLayer( qobject_cast< QgsRasterLayer * >( layer ) ); case QgsMapLayer::PluginLayer: return true; + case QgsMapLayer::MeshLayer: + return false; } return true; } ), layers.end() ); diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 28b07943296..191d61bd5f1 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -74,6 +74,12 @@ QIcon QgsLayerItem::iconRaster() return QgsApplication::getThemeIcon( QStringLiteral( "/mIconRaster.svg" ) ); } +QIcon QgsLayerItem::iconMesh() +{ + // TODO new icon! + return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) ); +} + QIcon QgsLayerItem::iconDefault() { return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLayer.png" ) ); @@ -602,6 +608,8 @@ QgsMapLayer::LayerType QgsLayerItem::mapLayerType() const { if ( mLayerType == QgsLayerItem::Raster ) return QgsMapLayer::RasterLayer; + if ( mLayerType == QgsLayerItem::Mesh ) + return QgsMapLayer::MeshLayer; if ( mLayerType == QgsLayerItem::Plugin ) return QgsMapLayer::PluginLayer; return QgsMapLayer::VectorLayer; @@ -637,6 +645,8 @@ QString QgsLayerItem::iconName( QgsLayerItem::LayerType layerType ) case Raster: return QStringLiteral( "/mIconRaster.svg" ); break; + case Mesh: + //TODO add icon! default: return QStringLiteral( "/mIconLayer.png" ); break; @@ -670,6 +680,9 @@ QgsMimeDataUtils::Uri QgsLayerItem::mimeUri() const case QgsMapLayer::RasterLayer: u.layerType = QStringLiteral( "raster" ); break; + case QgsMapLayer::MeshLayer: + u.layerType = QStringLiteral( "mesh" ); + break; case QgsMapLayer::PluginLayer: u.layerType = QStringLiteral( "plugin" ); break; diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index 962cb97fe03..2b69f8622c9 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -422,7 +422,8 @@ class CORE_EXPORT QgsLayerItem : public QgsDataItem TableLayer, Database, Table, - Plugin //!< Added in 2.10 + Plugin, //!< Added in 2.10 + Mesh //!< Added in 3.2 }; Q_ENUM( LayerType ); @@ -498,6 +499,7 @@ class CORE_EXPORT QgsLayerItem : public QgsDataItem static QIcon iconTable(); static QIcon iconRaster(); static QIcon iconDefault(); + static QIcon iconMesh(); //! \returns the layer name virtual QString layerName() const { return name(); } diff --git a/src/core/qgsdataprovider.h b/src/core/qgsdataprovider.h index 6047cedd2a6..a86974a5dd5 100644 --- a/src/core/qgsdataprovider.h +++ b/src/core/qgsdataprovider.h @@ -59,6 +59,10 @@ class CORE_EXPORT QgsDataProvider : public QObject { sipType = sipType_QgsRasterDataProvider; } + else if ( qobject_cast( sipCpp ) ) + { + sipType = sipType_QgsMeshDataProvider; + } else { sipType = 0; diff --git a/src/core/qgsmaplayer.h b/src/core/qgsmaplayer.h index 3563b92a01a..3b381f30e64 100644 --- a/src/core/qgsmaplayer.h +++ b/src/core/qgsmaplayer.h @@ -85,6 +85,9 @@ class CORE_EXPORT QgsMapLayer : public QObject case QgsMapLayer::PluginLayer: sipType = sipType_QgsPluginLayer; break; + case QgsMapLayer::MeshLayer: + sipType = sipType_QgsMeshLayer; + break; default: sipType = nullptr; break; @@ -100,7 +103,8 @@ class CORE_EXPORT QgsMapLayer : public QObject { VectorLayer, RasterLayer, - PluginLayer + PluginLayer, + MeshLayer //!< Added in 3.2 }; /** diff --git a/src/core/qgsmaplayermodel.cpp b/src/core/qgsmaplayermodel.cpp index 5be821b7998..f76e93284ae 100644 --- a/src/core/qgsmaplayermodel.cpp +++ b/src/core/qgsmaplayermodel.cpp @@ -359,6 +359,11 @@ QIcon QgsMapLayerModel::iconForLayer( QgsMapLayer *layer ) return QgsLayerItem::iconRaster(); } + case QgsMapLayer::MeshLayer: + { + return QgsLayerItem::iconMesh(); + } + case QgsMapLayer::VectorLayer: { QgsVectorLayer *vl = dynamic_cast( layer ); diff --git a/src/core/qgsmimedatautils.cpp b/src/core/qgsmimedatautils.cpp index 765cd7fa382..1b8cc84ba75 100644 --- a/src/core/qgsmimedatautils.cpp +++ b/src/core/qgsmimedatautils.cpp @@ -24,6 +24,7 @@ #include "qgsrasterlayer.h" #include "qgsvectordataprovider.h" #include "qgsvectorlayer.h" +#include "qgsmeshlayer.h" static const char *QGIS_URILIST_MIMETYPE = "application/x-vnd.qgis.qgis.uri"; @@ -108,6 +109,18 @@ QgsRasterLayer *QgsMimeDataUtils::Uri::rasterLayer( bool &owner, QString &error return new QgsRasterLayer( uri, name, providerKey ); } +QgsMeshLayer *QgsMimeDataUtils::Uri::meshLayer( bool &owner, QString &error ) const +{ + owner = false; + if ( layerType != QLatin1String( "mesh" ) ) + { + error = QObject::tr( "%1: Not a mesh layer." ).arg( name ); + return nullptr; + } + owner = true; + return new QgsMeshLayer( uri, name, providerKey ); +} + // ----- bool QgsMimeDataUtils::isUriList( const QMimeData *data ) diff --git a/src/core/qgsmimedatautils.h b/src/core/qgsmimedatautils.h index 879bd132a17..c121e6345da 100644 --- a/src/core/qgsmimedatautils.h +++ b/src/core/qgsmimedatautils.h @@ -24,6 +24,7 @@ class QgsLayerItem; class QgsLayerTreeNode; class QgsVectorLayer; class QgsRasterLayer; +class QgsMeshLayer; /** * \ingroup core @@ -63,7 +64,14 @@ class CORE_EXPORT QgsMimeDataUtils */ QgsRasterLayer *rasterLayer( bool &owner, QString &error ) const; - //! Type of URI. Recognized types: "vector" / "raster" / "plugin" / "custom" / "project" + /** + * Get mesh layer from uri if possible, otherwise returns 0 and error is set + * \param owner set to true if caller becomes owner + * \param error set to error message if cannot get raster + */ + QgsMeshLayer *meshLayer( bool &owner, QString &error ) const; + + //! Type of URI. Recognized types: "vector" / "raster" / "mesh" / "plugin" / "custom" / "project" QString layerType; /** diff --git a/src/core/qgsproviderregistry.cpp b/src/core/qgsproviderregistry.cpp index fe68c604363..ef74da62042 100644 --- a/src/core/qgsproviderregistry.cpp +++ b/src/core/qgsproviderregistry.cpp @@ -31,7 +31,7 @@ #include "qgsvectorlayer.h" #include "qgsproject.h" #include "providers/memory/qgsmemoryprovider.h" - +#include "mesh/qgsmeshmemorydataprovider.h" // typedefs for provider plugin functions of interest typedef QString providerkey_t(); @@ -87,6 +87,7 @@ void QgsProviderRegistry::init() { // add standard providers mProviders[ QgsMemoryProvider::providerKey() ] = new QgsProviderMetadata( QgsMemoryProvider::providerKey(), QgsMemoryProvider::providerDescription(), &QgsMemoryProvider::createProvider ); + mProviders[ QgsMeshMemoryDataProvider::providerKey() ] = new QgsProviderMetadata( QgsMeshMemoryDataProvider::providerKey(), QgsMeshMemoryDataProvider::providerDescription(), &QgsMeshMemoryDataProvider::createProvider ); mLibraryDirectory.setSorting( QDir::Name | QDir::IgnoreCase ); mLibraryDirectory.setFilter( QDir::Files | QDir::NoSymLinks ); diff --git a/src/core/qgsproviderregistry.h b/src/core/qgsproviderregistry.h index b38c41ca12f..fb8aa1694d3 100644 --- a/src/core/qgsproviderregistry.h +++ b/src/core/qgsproviderregistry.h @@ -167,6 +167,7 @@ class CORE_EXPORT QgsProviderRegistry \note This replaces QgsRasterLayer::buildSupportedRasterFileFilter() */ virtual QString fileRasterFilters() const; + //! Return a string containing the available database drivers virtual QString databaseDrivers() const; //! Return a string containing the available directory drivers diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e96f8c82b82..acffbfc33a1 100755 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -890,6 +890,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/src/core/locator ${CMAKE_SOURCE_DIR}/src/core/metadata ${CMAKE_SOURCE_DIR}/src/core/processing + ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/providers/memory ${CMAKE_SOURCE_DIR}/src/core/raster ${CMAKE_SOURCE_DIR}/src/core/scalebar diff --git a/src/gui/qgsbrowserdockwidget_p.cpp b/src/gui/qgsbrowserdockwidget_p.cpp index 625e590e60a..6db14611582 100644 --- a/src/gui/qgsbrowserdockwidget_p.cpp +++ b/src/gui/qgsbrowserdockwidget_p.cpp @@ -35,7 +35,7 @@ #include "qgsvectorlayer.h" #include "qgsproject.h" #include "qgssettings.h" - +#include "qgsmeshlayer.h" #include @@ -154,6 +154,20 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) delete layer; } } + else if ( type == QgsMapLayer::MeshLayer ) + { + QgsDebugMsg( "creating mesh layer" ); + QgsMeshLayer *layer = new QgsMeshLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ); + if ( layer ) + { + if ( layer->isValid() ) + { + layerCrs = layer->crs(); + layerMetadata = layer->htmlMetadata(); + } + delete layer; + } + } else if ( type == QgsMapLayer::VectorLayer ) { QgsDebugMsg( "creating vector layer" ); diff --git a/src/providers/CMakeLists.txt b/src/providers/CMakeLists.txt index 258069639c8..81d04df2a69 100644 --- a/src/providers/CMakeLists.txt +++ b/src/providers/CMakeLists.txt @@ -16,6 +16,7 @@ ADD_SUBDIRECTORY(wfs) ADD_SUBDIRECTORY(spatialite) ADD_SUBDIRECTORY(virtual) ADD_SUBDIRECTORY(db2) +ADD_SUBDIRECTORY(mdal) IF (WITH_ORACLE) ADD_SUBDIRECTORY(oracle) diff --git a/src/providers/mdal/CMakeLists.txt b/src/providers/mdal/CMakeLists.txt new file mode 100644 index 00000000000..20bbb1b0199 --- /dev/null +++ b/src/providers/mdal/CMakeLists.txt @@ -0,0 +1,80 @@ +SET(MDAL_SRCS + qgsmdalprovider.cpp + qgsmdaldataitems.cpp +) +SET(MDAL_MOC_HDRS + qgsmdalprovider.h + qgsmdaldataitems.h +) +SET(MDAL_HDRS +) + + +######################################################## +# Compile internal MDAL +IF (WITH_INTERNAL_MDAL) + ADD_DEFINITIONS(-DMDAL_STATIC) + + INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/external/mdal + ${CMAKE_SOURCE_DIR}/external/mdal/api + ) + + SET(MDAL_LIB_SRCS + ${CMAKE_SOURCE_DIR}/external/mdal/mdal.cpp + ${CMAKE_SOURCE_DIR}/external/mdal/mdal_utils.cpp + ${CMAKE_SOURCE_DIR}/external/mdal/mdal_loader.cpp + ${CMAKE_SOURCE_DIR}/external/mdal/frmts/mdal_2dm.cpp + ) + + SET(MDAL_LIB_HDRS + ${CMAKE_SOURCE_DIR}/external/mdal/api/mdal.h + ${CMAKE_SOURCE_DIR}/external/mdal/mdal_utils.hpp + ${CMAKE_SOURCE_DIR}/external/mdal/mdal_loader.hpp + ${CMAKE_SOURCE_DIR}/external/mdal/mdal_defines.hpp + ${CMAKE_SOURCE_DIR}/external/mdal/frmts/mdal_2dm.hpp + ) + + UNSET(MDAL_LIBRARY) + UNSET(MDAL_INCLUDE_DIR) + +ELSE (WITH_INTERNAL_MDAL) + + INCLUDE_DIRECTORIES (SYSTEM + ${MDAL_INCLUDE_DIR} + ) + +ENDIF (WITH_INTERNAL_MDAL) + +######################################################## + +INCLUDE_DIRECTORIES ( + ${CMAKE_SOURCE_DIR}/src/core + ${CMAKE_SOURCE_DIR}/src/core/mesh + ${CMAKE_SOURCE_DIR}/src/core/expression + ${CMAKE_SOURCE_DIR}/src/core/geometry + ${CMAKE_SOURCE_DIR}/src/core/metadata + + ${CMAKE_BINARY_DIR}/src/core +) + +QT5_WRAP_CPP(MDAL_MOC_SRCS ${MDAL_MOC_HDRS}) +ADD_LIBRARY (mdalprovider MODULE ${MDAL_SRCS} ${MDAL_MOC_HDRS} ${MDAL_MOC_SRCS} ${MDAL_LIB_SRCS} ${MDAL_LIB_HDRS}) + +TARGET_LINK_LIBRARIES (mdalprovider + qgis_core + ${MDAL_LIBRARY} +) + +# clang-tidy +IF(CLANG_TIDY_EXE) + SET_TARGET_PROPERTIES( + mdalprovider PROPERTIES + CXX_CLANG_TIDY "${DO_CLANG_TIDY}" + ) +ENDIF(CLANG_TIDY_EXE) + +INSTALL(TARGETS mdalprovider + RUNTIME DESTINATION ${QGIS_PLUGIN_DIR} + LIBRARY DESTINATION ${QGIS_PLUGIN_DIR}) + diff --git a/src/providers/mdal/qgsmdaldataitems.cpp b/src/providers/mdal/qgsmdaldataitems.cpp new file mode 100644 index 00000000000..9adf1158f28 --- /dev/null +++ b/src/providers/mdal/qgsmdaldataitems.cpp @@ -0,0 +1,76 @@ +/*************************************************************************** + qgsmdaldataitems.cpp + --------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#include "qgsmdaldataitems.h" +#include "qgsmdalprovider.h" +#include "qgslogger.h" +#include "qgssettings.h" + +#include + + +QgsMdalLayerItem::QgsMdalLayerItem( QgsDataItem *parent, + const QString &name, const QString &path, const QString &uri ) + : QgsLayerItem( parent, name, path, uri, QgsLayerItem::Mesh, QStringLiteral( "mdal" ) ) +{ + mToolTip = uri; + setState( Populated ); +} + +QString QgsMdalLayerItem::layerName() const +{ + QFileInfo info( name() ); + return info.completeBaseName(); +} + +// --------------------------------------------------------------------------- +static QStringList sExtensions = QStringList(); + +QGISEXTERN int dataCapabilities() +{ + return QgsDataProvider::File; +} + +QGISEXTERN QgsDataItem *dataItem( QString path, QgsDataItem *parentItem ) +{ + if ( path.isEmpty() ) + return nullptr; + + QgsDebugMsgLevel( "thePath = " + path, 2 ); + + // get suffix, removing .gz if present + QFileInfo info( path ); + QString suffix = info.suffix().toLower(); + // extract basename with extension + info.setFile( path ); + QString name = info.fileName(); + + // allow only normal files + if ( !info.isFile() ) + return nullptr; + + // get supported extensions + if ( sExtensions.isEmpty() ) + { + // TODO ask MDAL for extensions ! + sExtensions << QStringLiteral( "2dm" ); + } + + // Filter files by extension + if ( !sExtensions.contains( suffix ) ) + return nullptr; + + return new QgsMdalLayerItem( parentItem, name, path, path ); +} + diff --git a/src/providers/mdal/qgsmdaldataitems.h b/src/providers/mdal/qgsmdaldataitems.h new file mode 100644 index 00000000000..d7d034ffeb4 --- /dev/null +++ b/src/providers/mdal/qgsmdaldataitems.h @@ -0,0 +1,29 @@ +/*************************************************************************** + qgsmdaldataitems.h + ------------------ + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSMDALDATAITEMS_H +#define QGSMDALDATAITEMS_H + +#include "qgsdataitem.h" + +class QgsMdalLayerItem : public QgsLayerItem +{ + Q_OBJECT + public: + QgsMdalLayerItem( QgsDataItem *parent, const QString &name, const QString &path, const QString &uri ); + + QString layerName() const override; +}; + +#endif // QGSMDALDATAITEMS_H diff --git a/src/providers/mdal/qgsmdalprovider.cpp b/src/providers/mdal/qgsmdalprovider.cpp new file mode 100644 index 00000000000..0481962e89d --- /dev/null +++ b/src/providers/mdal/qgsmdalprovider.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + qgsmeshmemorydataprovider.cpp + ----------------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsmdalprovider.h" +#include +#include +#include +#include "mdal.h" + +static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mdal" ); +static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "MDAL provider" ); + +bool QgsMdalProvider::isValid() const +{ + return mMeshH != nullptr; +} + +QString QgsMdalProvider::name() const +{ + return TEXT_PROVIDER_KEY; +} + +QString QgsMdalProvider::description() const +{ + return TEXT_PROVIDER_DESCRIPTION; +} + +QgsCoordinateReferenceSystem QgsMdalProvider::crs() const +{ + return QgsCoordinateReferenceSystem(); +} + +QgsMdalProvider::QgsMdalProvider( const QString &uri ) + : QgsMeshDataProvider( uri ) +{ + QByteArray curi = uri.toAscii(); + mMeshH = MDAL_LoadMesh( curi.constData() ); +} + +QgsMdalProvider::~QgsMdalProvider() +{ + if ( mMeshH ) + MDAL_CloseMesh( mMeshH ); +} + +size_t QgsMdalProvider::vertexCount() const +{ + if ( mMeshH ) + return MDAL_M_vertexCount( mMeshH ); + else + return ( size_t ) 0; +} + +size_t QgsMdalProvider::faceCount() const +{ + if ( mMeshH ) + return MDAL_M_faceCount( mMeshH ); + else + return ( size_t ) 0; +} + +QgsMeshVertex QgsMdalProvider::vertex( size_t index ) const +{ + Q_ASSERT( index < vertexCount() ); + double x = MDAL_M_vertexXCoordinatesAt( mMeshH, index ); + double y = MDAL_M_vertexYCoordinatesAt( mMeshH, index ); + QgsMeshVertex vertex( x, y ); + return vertex; +} + +QgsMeshFace QgsMdalProvider::face( size_t index ) const +{ + Q_ASSERT( index < faceCount() ); + QgsMeshFace face; + int n_face_vertices = MDAL_M_faceVerticesCountAt( mMeshH, index ); + for ( size_t j = 0; j < n_face_vertices; ++j ) + { + int vertex_index = MDAL_M_faceVerticesIndexAt( mMeshH, index, j ); + face.push_back( vertex_index ); + } + return face; +} + +/*----------------------------------------------------------------------------------------------*/ + +/** + * Class factory to return a pointer to a newly created + * QgsGdalProvider object + */ +QGISEXTERN QgsMdalProvider *classFactory( const QString *uri ) +{ + return new QgsMdalProvider( *uri ); +} + +/** + * Required key function (used to map the plugin to a data store type) +*/ +QGISEXTERN QString providerKey() +{ + return TEXT_PROVIDER_KEY; +} + +/** + * Required description function + */ +QGISEXTERN QString description() +{ + return TEXT_PROVIDER_DESCRIPTION; +} + +/** + * Required isProvider function. Used to determine if this shared library + * is a data provider plugin + */ +QGISEXTERN bool isProvider() +{ + return true; +} + +QGISEXTERN void cleanupProvider() +{ +} + diff --git a/src/providers/mdal/qgsmdalprovider.h b/src/providers/mdal/qgsmdalprovider.h new file mode 100644 index 00000000000..08c7af2f83f --- /dev/null +++ b/src/providers/mdal/qgsmdalprovider.h @@ -0,0 +1,71 @@ +/*************************************************************************** + qgsmdalprovider.h + ----------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSMDALPROVIDER_H +#define QGSGDALPROVIDER_H + +#include + +#include "qgscoordinatereferencesystem.h" +#include "qgsdataitem.h" +#include "qgsmeshdataprovider.h" +#include "qgsrectangle.h" + +#include +#include +#include +#include +#include + +#include + +class QMutex; +class QgsCoordinateTransform; + +/** + \brief Data provider for MDAL layers. +*/ +class QgsMdalProvider : public QgsMeshDataProvider +{ + Q_OBJECT + + public: + + /** + * Constructor for the provider. + * + * \param uri file name + * \param newDataset handle of newly created dataset. + * + */ + QgsMdalProvider( const QString &uri = QString() ); + ~QgsMdalProvider(); + + bool isValid() const override; + QString name() const override; + QString description() const override; + QgsCoordinateReferenceSystem crs() const override; + + size_t vertexCount() const override; + size_t faceCount() const override; + QgsMeshVertex vertex( size_t index ) const override; + QgsMeshFace face( size_t index ) const override; + + private: + MeshH mMeshH; +}; + +#endif + diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 22f1bf67207..74305afdc80 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -228,6 +228,12 @@ bool QgsGeoPackageCollectionItem::handleDrop( const QMimeData *data, Qt::DropAct srcLayer = dropUri.vectorLayer( owner, error ); isVector = true; } + else if ( dropUri.layerType == QStringLiteral( "mesh" ) ) + { + // unsuported + hasError = true; + continue; + } else { srcLayer = dropUri.rasterLayer( owner, error ); diff --git a/tests/src/core/CMakeLists.txt b/tests/src/core/CMakeLists.txt index 4769a6c9d72..837d56adf56 100755 --- a/tests/src/core/CMakeLists.txt +++ b/tests/src/core/CMakeLists.txt @@ -20,6 +20,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/src/core/raster ${CMAKE_SOURCE_DIR}/src/core/scalebar ${CMAKE_SOURCE_DIR}/src/core/symbology + ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/test ${CMAKE_BINARY_DIR}/src/core ) @@ -186,7 +187,9 @@ SET(TESTS testqgsvectorlayerjoinbuffer.cpp testqgsvectorlayer.cpp testziplayer.cpp - ) + testqgsmeshlayer.cpp + testqgsmeshlayerrenderer.cpp + ) IF(WITH_QTWEBKIT) SET(TESTS ${TESTS} diff --git a/tests/src/core/testqgsmeshlayer.cpp b/tests/src/core/testqgsmeshlayer.cpp new file mode 100644 index 00000000000..4dd3aab9df6 --- /dev/null +++ b/tests/src/core/testqgsmeshlayer.cpp @@ -0,0 +1,132 @@ +/*************************************************************************** + testqgsmeshlayer.cpp + -------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstest.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//qgis includes... +#include "qgsmaplayer.h" +#include "qgsmeshlayer.h" +#include "qgsapplication.h" +#include "qgsproviderregistry.h" +#include "qgsproject.h" +#include "qgsmaprenderersequentialjob.h" +#include "qgsmeshmemorydataprovider.h" + +/** + * \ingroup UnitTests + * This is a unit test for a mesh layer + */ +class TestQgsMeshLayer : public QObject +{ + Q_OBJECT + + public: + TestQgsMeshLayer() = default; + + private: + QgsMeshLayer *mMemoryLayer = nullptr; + QgsMeshLayer *mMdalLayer = nullptr; + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + + void test_data_provider(); + void test_extent(); +}; + +void TestQgsMeshLayer::initTestCase() +{ + // init QGIS's paths - true means that all path will be inited from prefix + QgsApplication::init(); + QgsApplication::initQgis(); + QgsApplication::showSettings(); + QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + myDataDir += "/mesh"; + // Memory layer + QFile f( myDataDir + "/quad_and_triangle.txt" ); + QVERIFY( f.open( QIODevice::ReadOnly | QIODevice::Text ) ); + QString uri( f.readAll() ); + QVERIFY( !uri.isEmpty() ); + mMemoryLayer = new QgsMeshLayer( uri, "Triangle and Quad Memory", "mesh_memory" ); + QVERIFY( mMemoryLayer->isValid() ); + QgsProject::instance()->addMapLayers( + QList() << mMemoryLayer ); + + // MDAL Layer + uri = myDataDir + "/quad_and_triangle.2dm"; + mMdalLayer = new QgsMeshLayer( uri, "Triangle and Quad MDAL", "mdal" ); + QVERIFY( mMdalLayer->isValid() ); + QgsProject::instance()->addMapLayers( + QList() << mMdalLayer ); +} + +void TestQgsMeshLayer::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +void TestQgsMeshLayer::test_data_provider() +{ + QList dataProviders; + dataProviders.append( mMemoryLayer->dataProvider() ); + dataProviders.append( mMdalLayer->dataProvider() ); + + QgsRectangle expectedExtent( 1000.0, 2000.0, 3000.0, 3000.0 ); + + foreach ( auto dp, dataProviders ) + { + QVERIFY( dp != nullptr ); + QVERIFY( dp->isValid() ); + QCOMPARE( expectedExtent, dp->extent() ); + + QCOMPARE( ( size_t ) 5, dp->vertexCount() ); + QCOMPARE( QgsMeshVertex( 1000.0, 2000.0 ), dp->vertex( 0 ) ); + QCOMPARE( QgsMeshVertex( 2000.0, 2000.0 ), dp->vertex( 1 ) ); + QCOMPARE( QgsMeshVertex( 3000.0, 2000.0 ), dp->vertex( 2 ) ); + QCOMPARE( QgsMeshVertex( 2000.0, 3000.0 ), dp->vertex( 3 ) ); + QCOMPARE( QgsMeshVertex( 1000.0, 3000.0 ), dp->vertex( 4 ) ); + + QCOMPARE( ( size_t ) 2, dp->faceCount() ); + QgsMeshFace f1; + f1 << 0 << 1 << 3 << 4; + QCOMPARE( f1, dp->face( 0 ) ); + + QgsMeshFace f2; + f2 << 1 << 2 << 3; + QCOMPARE( f2, dp->face( 1 ) ); + } +} + +void TestQgsMeshLayer::test_extent() +{ + QCOMPARE( mMemoryLayer->dataProvider()->extent(), mMemoryLayer->extent() ); + QCOMPARE( mMdalLayer->dataProvider()->extent(), mMdalLayer->extent() ); +} + +QGSTEST_MAIN( TestQgsMeshLayer ) +#include "testqgsmeshlayer.moc" diff --git a/tests/src/core/testqgsmeshlayerrenderer.cpp b/tests/src/core/testqgsmeshlayerrenderer.cpp new file mode 100644 index 00000000000..b2c82a87e51 --- /dev/null +++ b/tests/src/core/testqgsmeshlayerrenderer.cpp @@ -0,0 +1,123 @@ +/*************************************************************************** + testqgsmeshlayer.cpp + -------------------- + begin : April 2018 + copyright : (C) 2018 by Peter Petrik + email : zilolv at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgstest.h" +#include +#include +#include +#include +#include +#include +#include +#include + +//qgis includes... +#include "qgsmaplayer.h" +#include "qgsmeshlayer.h" +#include "qgsapplication.h" +#include "qgsproviderregistry.h" +#include "qgsproject.h" +#include "qgsmaprenderersequentialjob.h" +#include "qgsmeshmemorydataprovider.h" + +//qgis test includes +#include "qgsrenderchecker.h" + +/** + * \ingroup UnitTests + * This is a unit test for the different renderers for mesh layers. + */ +class TestQgsMeshRenderer : public QObject +{ + Q_OBJECT + + public: + TestQgsMeshRenderer() = default; + + private: + QgsMeshLayer *mMemoryLayer = nullptr; + QgsMapSettings *mMapSettings = nullptr; + + private slots: + void initTestCase();// will be called before the first testfunction is executed. + void cleanupTestCase();// will be called after the last testfunction was executed. + void init() {} // will be called before each testfunction is executed. + void cleanup() {} // will be called after every testfunction. + bool imageCheck( const QString &testType ); + + void test_native_mesh_rendering(); + void test_triangular_mesh_rendering(); +}; + +void TestQgsMeshRenderer::initTestCase() +{ + // init QGIS's paths - true means that all path will be inited from prefix + QgsApplication::init(); + QgsApplication::initQgis(); + QgsApplication::showSettings(); + QString myDataDir( TEST_DATA_DIR ); //defined in CmakeLists.txt + myDataDir += "/mesh"; + + mMapSettings = new QgsMapSettings(); + + // Memory layer + QFile f( myDataDir + "/quad_and_triangle.txt" ); + QVERIFY( f.open( QIODevice::ReadOnly | QIODevice::Text ) ); + QString uri( f.readAll() ); + QVERIFY( !uri.isEmpty() ); + mMemoryLayer = new QgsMeshLayer( uri, "Triangle and Quad Memory", "mesh_memory" ); + QVERIFY( mMemoryLayer->isValid() ); + QgsProject::instance()->addMapLayers( + QList() << mMemoryLayer ); + mMapSettings->setLayers( + QList() << mMemoryLayer ); +} +void TestQgsMeshRenderer::cleanupTestCase() +{ + QgsApplication::exitQgis(); +} + +bool TestQgsMeshRenderer::imageCheck( const QString &testType ) +{ + mMapSettings->setExtent( mMemoryLayer->extent() ); + mMapSettings->setDestinationCrs( mMemoryLayer->crs() ); + mMapSettings->setOutputDpi( 96 ); + QgsRenderChecker myChecker; + myChecker.setControlPathPrefix( QStringLiteral( "mesh" ) ); + myChecker.setControlName( "expected_" + testType ); + myChecker.setMapSettings( *mMapSettings ); + myChecker.setColorTolerance( 15 ); + bool myResultFlag = myChecker.runTest( testType, 0 ); + return myResultFlag; +} + +void TestQgsMeshRenderer::test_native_mesh_rendering() +{ + mMemoryLayer->toggleTriangularMeshRendering( false ); + QVERIFY( mMemoryLayer->triangularMeshSymbol() == nullptr ); + QVERIFY( imageCheck( "quad_and_triangle_native_mesh" ) ); +} + +void TestQgsMeshRenderer::test_triangular_mesh_rendering() +{ + mMemoryLayer->toggleTriangularMeshRendering( true ); + QVERIFY( mMemoryLayer->triangularMeshSymbol() != nullptr ); + QVERIFY( imageCheck( "quad_and_triangle_triangular_mesh" ) ); +} + +QGSTEST_MAIN( TestQgsMeshRenderer ) +#include "testqgsmeshlayerrenderer.moc" diff --git a/tests/testdata/control_images/mesh/expected_quad_and_triangle_native_mesh/expected_quad_and_triangle_native_mesh.png b/tests/testdata/control_images/mesh/expected_quad_and_triangle_native_mesh/expected_quad_and_triangle_native_mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f30a58c103b82f797a406f49e0bb38069e4f3f GIT binary patch literal 80307 zcmeI4J7^S96o&66_)1U$3Xv3IA=+99TG&M5W1Apip;jVRir}LWn}~&IVqf+C2Z zA{JJ53R+lNiC`mwosEix(H(V7c4s%cGjq?m=g$2C>+a6ZJ?GB%pO3{bdUgNo-qmAM zW6rtN&3!WmTFdWHQX%{1X#XHdn}q%|g6K0J*l}cJ^v_?=kz^n$@e>e49C5h@*>A71-(n$2IexxY zA&4)M0(Fo?0BXUK2vQK3_UTBXO}w)of^_mQ`3_0K#S)ny??xX#Z{G_L4qE8W2oolO z$Ycov$x2rw(N6+F^qE)ch$NNp6<&-Wg^`6{fFybuAc!7oubq&@E6%>f5Tq{}`yU{Q zd`1W&&sekrl8EAU2qMc@r8<(xVMY)+wyKqpLK#|l49{8NHJcuI2%cZj}bwJXH}~+ z2TOAH?!#*#?gg-m8CmRRBfhi2!iaCdd;O>d zf+W4;Mv&fIl01qe`PdO8&m`$1NYWWUf^_oWU5fycEQ|qyEU@6!A_7SA7Y2eX8VX)^ z5STMo?mA0?ARY%LmQpS2oo(rpf2Ae2qH2b6wL_KK@tREmPHA~E(wB&jt9jv0x?U1Ak44|fmkI$ z5S8(uNk$+>Nf3nDRV5IcBnZ-TJU*UU_JDfTsmLx$C;jk%Rj(`&#(^D=USW*VT+l|A zfRrf+BAXl46$wa4f*^_k(n_9yZ%GhDJ~KKf6Ywbsf+&YaI~@YPBta0Jyyy~ufDcI! zBtUSq)FV(;5(Lr9icWzDRFMQh0)<9fT>@n#K@i=X=oXMb8A*Bwa%|1~Ugy^TXwFO@ z40tuPhJY}EqQdl$MRbP+7=)j@!raObI>3=~~c0dyd#75xW W$qOrvOx(}5-P}Dp^LW?MOTPhli=JBm literal 0 HcmV?d00001 diff --git a/tests/testdata/control_images/mesh/expected_quad_and_triangle_triangular_mesh/expected_quad_and_triangle_triangular_mesh.png b/tests/testdata/control_images/mesh/expected_quad_and_triangle_triangular_mesh/expected_quad_and_triangle_triangular_mesh.png new file mode 100644 index 0000000000000000000000000000000000000000..01142263691f44e2dfedf1caaa5c02e01e3c3853 GIT binary patch literal 80307 zcmeI)O^8)x7zgn8&dB&NNJWIhU|^64ttDF6!ko~%8EkOV#y$|jMG#0Z8=J{cbnU8$ z+KC_*xNso|MFX{nR<$ZDT<^!Z=i_~z_v3m0181Il@44sw_@C#e zqg=gma{l<J6S!cPux^h@d+gjjZ9e?!mU;Q`k?;%4cmN+?d)XP~iCd_qCxc2+7(Ol0SZ48F~+1kWTk|hp8%QV#nF=6GxrV?&O9v84 zg1-U?L#82d(DH$VlHjcX;+SU)IA{w%LP_vd0CC8&E{<SvIGJgy#^?I1SFfuWCsK|%18{q4kVfV zhy@(O2&!I54aqxaUsx3TEI@bp_EI*#r9exSjq>xUsVUz{^D|ih0gmR4F?dC;NYn!X zj=@%M|2#(|s(}DUdkke)=7vNq5a1|larf7ALZT7~aJVC=8X*@X>VN=8RcrH|&jE=l zAi%-b{B=jtjzkR*;HcZu{O{9@LT-!$+ID&7lQJazKE?t#N%`tpQ0=K!C$% zd-KedI+A3700&RQh8QJfBuM}Pju36lJ7=m$q6Y#Typ0-iv=os<4FotswzJIKsUe9L zNLU>EpZVmlug?O=G-4{FrUWKREMcLf(q?I|GZiG!0RfJ*8^6iwQ$P|G5a4LiUOL_z zJCbOC0Edo-Z?lV-kwgImING$8u4`gN5*!F{=xTJA-Nc9_C=lRqX(yf6#)c#q5a7_+ z;2gV(2}uwjz`@Z*y2pqGiFY8tA=lWy&AfF^>t_LC0OJ)4pvXbdI&x?v-hcpy))toE zS4bpYfB=Vl6KS6z6cTYDz@fc${p~LV5>X();jejenaMX2F(AN^OUwG-r%xmzK!C%4 z)8sazFC^?hfFrk74Yyw(NLYaYN4RFmb!KTKY(Rh`*A{i%_puuXKIQu?fFzjC!fQ8w zWDx@`u3dxcT@#R!;tT{hTyHI|=Sn~Vi4zdu;JURKhbsYYB&~q}2iGmdINS+vB54H# zIJj>q#^X+a3rS-jz`=bhF`iTc97q}g0S>8KiE*V8a79uZ2yjTnd2qAP*ANQQs_hptAzZpss=gQO1#aFmCMGddHfLb446ICM4u zc2%9gI3zoY%UAE;GtcdtHRi}m)uZ7ZA_=UoKi(dJeg|Bet%?- zDf_&$Va&#PUn{^oV-t99>+IKK--VOD5?HnqT|E8Y>6I5(|1IY1zBxbl%^UAt_#f7o BFiije literal 0 HcmV?d00001 diff --git a/tests/testdata/mesh/quad_and_triangle.2dm b/tests/testdata/mesh/quad_and_triangle.2dm new file mode 100644 index 00000000000..01a1aeaa95b --- /dev/null +++ b/tests/testdata/mesh/quad_and_triangle.2dm @@ -0,0 +1,8 @@ +MESH2D 1000.000 2000.000 0.000000 200 300 1.000 1.000 +ND 1 1000.000 2000.000 20.000 +ND 2 2000.000 2000.000 30.000 +ND 3 3000.000 2000.000 40.000 +ND 4 2000.000 3000.000 50.000 +ND 5 1000.000 3000.000 10.000 +E4Q 1 1 2 4 5 1 +E3T 2 2 3 4 1 diff --git a/tests/testdata/mesh/quad_and_triangle.txt b/tests/testdata/mesh/quad_and_triangle.txt new file mode 100644 index 00000000000..c29cd14f60c --- /dev/null +++ b/tests/testdata/mesh/quad_and_triangle.txt @@ -0,0 +1,8 @@ +1000.0, 2000.0 +2000.0, 2000.0 +3000.0, 2000.0 +2000.0, 3000.0 +1000.0, 3000.0 +--- +0, 1, 3, 4 +1, 2, 3 From 1efdbc5c20834d36d006d04833bd554deea35a4e Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Thu, 19 Apr 2018 16:42:01 +0200 Subject: [PATCH 2/6] fix build errors, use MDAL 0.0.2 (int API) --- external/mdal/api/mdal.h | 20 +++-- external/mdal/frmts/mdal_2dm.cpp | 20 ++--- external/mdal/frmts/mdal_2dm.hpp | 2 +- external/mdal/mdal.cpp | 54 +++++++++----- external/mdal/mdal_loader.cpp | 2 +- external/mdal/mdal_loader.hpp | 2 +- images/themes/default/mIconMeshLayer.svg | 78 ++++++++++++++++++++ python/core/mesh/qgsmeshdataprovider.sip.in | 15 ++-- python/core/mesh/qgsmeshlayer.sip.in | 49 ++---------- python/core/qgsdataitem.sip.in | 3 + src/core/mesh/qgsmeshdataprovider.cpp | 2 +- src/core/mesh/qgsmeshdataprovider.h | 20 ++--- src/core/mesh/qgsmeshlayer.cpp | 29 +++++--- src/core/mesh/qgsmeshlayer.h | 61 ++------------- src/core/mesh/qgsmeshlayerrenderer.cpp | 14 ++-- src/core/mesh/qgsmeshlayerrenderer.h | 13 +--- src/core/mesh/qgsmeshmemorydataprovider.cpp | 22 +++--- src/core/mesh/qgsmeshmemorydataprovider.h | 10 +-- src/core/mesh/qgstriangularmesh.cpp | 4 - src/core/mesh/qgstriangularmesh.h | 4 - src/core/qgsdataitem.cpp | 1 - src/core/qgsdataitem.h | 1 + src/providers/mdal/qgsmdaldataitems.h | 2 + src/providers/mdal/qgsmdalprovider.cpp | 18 ++--- src/providers/mdal/qgsmdalprovider.h | 22 ++---- src/providers/ogr/qgsgeopackagedataitems.cpp | 2 +- tests/src/core/testqgsmeshlayer.cpp | 12 +-- 27 files changed, 228 insertions(+), 254 deletions(-) create mode 100644 images/themes/default/mIconMeshLayer.svg diff --git a/external/mdal/api/mdal.h b/external/mdal/api/mdal.h index 0569ac4d1e4..c049b41d76d 100644 --- a/external/mdal/api/mdal.h +++ b/external/mdal/api/mdal.h @@ -36,10 +36,8 @@ extern "C" { #endif -#include - /* Statuses */ -enum Status +enum MDAL_Status { None, // Errors @@ -57,14 +55,14 @@ enum Status Warn_NodeNotUnique }; -/* Mesh */ +//! Mesh typedef void *MeshH; //! Return MDAL version MDAL_EXPORT const char *MDAL_Version(); //! Return last status message -MDAL_EXPORT Status MDAL_LastStatus(); +MDAL_EXPORT MDAL_Status MDAL_LastStatus(); //! Load mesh file. On error see MDAL_LastStatus for error type This effectively loads whole mesh in-memory MDAL_EXPORT MeshH MDAL_LoadMesh( const char *meshFile ); @@ -72,17 +70,17 @@ MDAL_EXPORT MeshH MDAL_LoadMesh( const char *meshFile ); MDAL_EXPORT void MDAL_CloseMesh( MeshH mesh ); //! Return vertex count for the mesh -MDAL_EXPORT size_t MDAL_M_vertexCount( MeshH mesh ); +MDAL_EXPORT int MDAL_M_vertexCount( MeshH mesh ); //! Return vertex X coord for the mesh -MDAL_EXPORT double MDAL_M_vertexXCoordinatesAt( MeshH mesh, size_t index ); +MDAL_EXPORT double MDAL_M_vertexXCoordinatesAt( MeshH mesh, int index ); //! Return vertex Y coord for the mesh -MDAL_EXPORT double MDAL_M_vertexYCoordinatesAt( MeshH mesh, size_t index ); +MDAL_EXPORT double MDAL_M_vertexYCoordinatesAt( MeshH mesh, int index ); //! Return face count for the mesh -MDAL_EXPORT size_t MDAL_M_faceCount( MeshH mesh ); +MDAL_EXPORT int MDAL_M_faceCount( MeshH mesh ); //! Return number of vertices face consist of, e.g. 3 for triangle -MDAL_EXPORT size_t MDAL_M_faceVerticesCountAt( MeshH mesh, size_t index ); +MDAL_EXPORT int MDAL_M_faceVerticesCountAt( MeshH mesh, int index ); //! Return vertex index for face -MDAL_EXPORT size_t MDAL_M_faceVerticesIndexAt( MeshH mesh, size_t face_index, size_t vertex_index ); +MDAL_EXPORT int MDAL_M_faceVerticesIndexAt( MeshH mesh, int face_index, int vertex_index ); #ifdef __cplusplus } diff --git a/external/mdal/frmts/mdal_2dm.cpp b/external/mdal/frmts/mdal_2dm.cpp index 2428ba87804..5ee1d308a38 100644 --- a/external/mdal/frmts/mdal_2dm.cpp +++ b/external/mdal/frmts/mdal_2dm.cpp @@ -22,13 +22,13 @@ MDAL::Loader2dm::Loader2dm( const std::string &meshFile ): { } -MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) +MDAL::Mesh *MDAL::Loader2dm::load( MDAL_Status *status ) { - if ( status ) *status = Status::None; + if ( status ) *status = MDAL_Status::None; if ( !MDAL::fileExists( mMeshFile ) ) { - if ( status ) *status = Status::Err_FileNotFound; + if ( status ) *status = MDAL_Status::Err_FileNotFound; return 0; } @@ -36,7 +36,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) std::string line; if ( !std::getline( in, line ) || !startsWith( line, "MESH2D" ) ) { - if ( status ) *status = Status::Err_UnknownFormat; + if ( status ) *status = MDAL_Status::Err_UnknownFormat; return 0; } @@ -61,7 +61,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) startsWith( line, "E8Q" ) || startsWith( line, "E9Q" ) ) { - if ( status ) *status = Status::Warn_UnsupportedElement; + if ( status ) *status = MDAL_Status::Warn_UnsupportedElement; elemCount += 1; // We still count them as elements } } @@ -92,7 +92,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) std::map::iterator search = elemIDtoIndex.find( elemID ); if ( search != elemIDtoIndex.end() ) { - if ( status ) *status = Status::Warn_ElementNotUnique; + if ( status ) *status = MDAL_Status::Warn_ElementNotUnique; continue; } elemIDtoIndex[elemID] = elemIndex; @@ -114,7 +114,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) std::map::iterator search = elemIDtoIndex.find( elemID ); if ( search != elemIDtoIndex.end() ) { - if ( status ) *status = Status::Warn_ElementNotUnique; + if ( status ) *status = MDAL_Status::Warn_ElementNotUnique; continue; } elemIDtoIndex[elemID] = elemIndex; @@ -143,7 +143,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) std::map::iterator search = elemIDtoIndex.find( elemID ); if ( search != elemIDtoIndex.end() ) { - if ( status ) *status = Status::Warn_ElementNotUnique; + if ( status ) *status = MDAL_Status::Warn_ElementNotUnique; continue; } elemIDtoIndex[elemID] = elemIndex; @@ -159,7 +159,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) std::map::iterator search = nodeIDtoIndex.find( nodeID ); if ( search != nodeIDtoIndex.end() ) { - if ( status ) *status = Status::Warn_NodeNotUnique; + if ( status ) *status = MDAL_Status::Warn_NodeNotUnique; continue; } nodeIDtoIndex[nodeID] = nodeIndex; @@ -188,7 +188,7 @@ MDAL::Mesh *MDAL::Loader2dm::load( Status *status ) { assert( false ); //TODO mark element as unusable - if ( status ) *status = Status::Warn_ElementWithInvalidNode; + if ( status ) *status = MDAL_Status::Warn_ElementWithInvalidNode; } } diff --git a/external/mdal/frmts/mdal_2dm.hpp b/external/mdal/frmts/mdal_2dm.hpp index 28cce0ff028..e1bdf9936a3 100644 --- a/external/mdal/frmts/mdal_2dm.hpp +++ b/external/mdal/frmts/mdal_2dm.hpp @@ -18,7 +18,7 @@ namespace MDAL { public: Loader2dm( const std::string &meshFile ); - Mesh *load( Status *status ); + Mesh *load( MDAL_Status *status ); private: std::string mMeshFile; diff --git a/external/mdal/mdal.cpp b/external/mdal/mdal.cpp index a6d18fe2e7d..c5db6db755f 100644 --- a/external/mdal/mdal.cpp +++ b/external/mdal/mdal.cpp @@ -6,14 +6,14 @@ #include "mdal_loader.hpp" #include "mdal_defines.hpp" -static Status sLastStatus; +static MDAL_Status sLastStatus; const char *MDAL_Version() { - return "0.0.1"; + return "0.0.2"; } -Status MDAL_LastStatus() +MDAL_Status MDAL_LastStatus() { return sLastStatus; } @@ -38,49 +38,63 @@ void MDAL_CloseMesh( MeshH mesh ) } -size_t MDAL_M_vertexCount( MeshH mesh ) +int MDAL_M_vertexCount( MeshH mesh ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - return m->vertices.size(); + int len = static_cast( m->vertices.size() ); + return len; } -double MDAL_M_vertexXCoordinatesAt( MeshH mesh, size_t index ) +double MDAL_M_vertexXCoordinatesAt( MeshH mesh, int index ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - assert( m->vertices.size() > index ); - return m->vertices[index].x; + assert( index > -1 ); + size_t i = static_cast( index ); + assert( m->vertices.size() > i ); + return m->vertices[i].x; } -double MDAL_M_vertexYCoordinatesAt( MeshH mesh, size_t index ) +double MDAL_M_vertexYCoordinatesAt( MeshH mesh, int index ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - assert( m->vertices.size() > index ); - return m->vertices[index].y; + assert( index > -1 ); + size_t i = static_cast( index ); + assert( m->vertices.size() > i ); + return m->vertices[i].y; } -size_t MDAL_M_faceCount( MeshH mesh ) +int MDAL_M_faceCount( MeshH mesh ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - return m->faces.size(); + int len = static_cast( m->faces.size() ); + return len; } -size_t MDAL_M_faceVerticesCountAt( MeshH mesh, size_t index ) +int MDAL_M_faceVerticesCountAt( MeshH mesh, int index ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - assert( m->faces.size() > index ); - return m->faces[index].size(); + assert( index > -1 ); + size_t i = static_cast( index ); + assert( m->faces.size() > i ); + int len = static_cast( m->faces[i].size() ); + return len; } -size_t MDAL_M_faceVerticesIndexAt( MeshH mesh, size_t face_index, size_t vertex_index ) +int MDAL_M_faceVerticesIndexAt( MeshH mesh, int face_index, int vertex_index ) { assert( mesh ); MDAL::Mesh *m = ( MDAL::Mesh * ) mesh; - assert( m->faces.size() > face_index ); - assert( m->faces[face_index].size() > vertex_index ); - return m->faces[face_index][vertex_index]; + assert( face_index > -1 ); + size_t fi = static_cast( face_index ); + assert( m->faces.size() > fi ); + assert( vertex_index > -1 ); + size_t vi = static_cast( vertex_index ); + assert( m->faces[fi].size() > vi ); + int len = static_cast( m->faces[fi][vi] ); + return len; } diff --git a/external/mdal/mdal_loader.cpp b/external/mdal/mdal_loader.cpp index 7671d40e67e..cde668b04ce 100644 --- a/external/mdal/mdal_loader.cpp +++ b/external/mdal/mdal_loader.cpp @@ -6,7 +6,7 @@ #include "mdal_loader.hpp" #include "frmts/mdal_2dm.hpp" -MDAL::Mesh *MDAL::Loader::load( const std::string &meshFile, Status *status ) +MDAL::Mesh *MDAL::Loader::load( const std::string &meshFile, MDAL_Status *status ) { MDAL::Loader2dm loader( meshFile ); return loader.load( status ); diff --git a/external/mdal/mdal_loader.hpp b/external/mdal/mdal_loader.hpp index edd917b11b1..2e412c99d35 100644 --- a/external/mdal/mdal_loader.hpp +++ b/external/mdal/mdal_loader.hpp @@ -17,7 +17,7 @@ namespace MDAL class Loader { public: - static Mesh *load( const std::string &meshFile, Status *status ); + static Mesh *load( const std::string &meshFile, MDAL_Status *status ); }; } // namespace MDAL diff --git a/images/themes/default/mIconMeshLayer.svg b/images/themes/default/mIconMeshLayer.svg new file mode 100644 index 00000000000..f1e02cc61f6 --- /dev/null +++ b/images/themes/default/mIconMeshLayer.svg @@ -0,0 +1,78 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/python/core/mesh/qgsmeshdataprovider.sip.in b/python/core/mesh/qgsmeshdataprovider.sip.in index 3bb56cb64c0..7d7652d80d3 100644 --- a/python/core/mesh/qgsmeshdataprovider.sip.in +++ b/python/core/mesh/qgsmeshdataprovider.sip.in @@ -10,9 +10,8 @@ - typedef QgsPoint QgsMeshVertex; //xyz coords of vertex -typedef QVector QgsMeshFace; //list of vertex indexes +typedef QVector QgsMeshFace; //list of vertex indexes class QgsMeshSource /Abstract/ { @@ -33,28 +32,28 @@ read on demand public: virtual ~QgsMeshSource(); - virtual size_t vertexCount() const = 0; + virtual int vertexCount() const = 0; %Docstring - Return number of vertexes in the native mesh + Return number of vertices in the native mesh -:return: Number of vertexes in the mesh +:return: Number of vertices in the mesh %End - virtual size_t faceCount() const = 0; + virtual int faceCount() const = 0; %Docstring Return number of faces in the native mesh :return: Number of faces in the mesh %End - virtual QgsMeshVertex vertex( size_t index ) const = 0; + virtual QgsMeshVertex vertex( int index ) const = 0; %Docstring Factory for mesh vertex with index :return: new mesh vertex on index %End - virtual QgsMeshFace face( size_t index ) const = 0; + virtual QgsMeshFace face( int index ) const = 0; %Docstring Factory for mesh face with index diff --git a/python/core/mesh/qgsmeshlayer.sip.in b/python/core/mesh/qgsmeshlayer.sip.in index 7bee996bcf6..1a97dae00c4 100644 --- a/python/core/mesh/qgsmeshlayer.sip.in +++ b/python/core/mesh/qgsmeshlayer.sip.in @@ -10,8 +10,6 @@ - - class QgsMeshLayer : QgsMapLayer { %Docstring @@ -90,29 +88,17 @@ parameters used by the data provider as url query items. virtual QgsMeshDataProvider *dataProvider(); %Docstring -Return data provider +QgsMeshLayer cannot be copied. %End - - virtual QgsMeshLayer *clone() const /Factory/; -%Docstring -Returns a new instance equivalent to this one. A new provider is -created for the same data source and renderers are cloned too. - -:return: a new layer instance -%End - virtual QgsRectangle extent() const; -%Docstring -Returns the extent of the layer. -%End - virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) /Factory/; -%Docstring -Return new instance of QgsMapLayerRenderer that will be used for rendering of given context -%End + virtual bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ); + + virtual bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const; + QString providerType() const; %Docstring @@ -138,30 +124,7 @@ Returns a line symbol used for rendering of triangular (derived) mesh. Toggle rendering of triangular (derived) mesh. Off by default %End - bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ); -%Docstring -Read the symbology for the current layer from the Dom node supplied. - -:param node: node that will contain the symbology definition for this layer. -:param errorMessage: reference to string that will be updated with any error messages -:param context: reading context (used for transform from relative to absolute paths) - -:return: true in case of success. -%End - - bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const; -%Docstring -Write the symbology for the layer into the docment provided. - -:param node: the node that will have the style element added to it. -:param doc: the document that will have the QDomNode added. -:param errorMessage: reference to string that will be updated with any error messages -:param context: writing context (used for transform from absolute to relative paths) - -:return: true in case of success. -%End - - private: // Private methods + private: // Private methods QgsMeshLayer( const QgsMeshLayer &rhs ); }; diff --git a/python/core/qgsdataitem.sip.in b/python/core/qgsdataitem.sip.in index 9edd3c04712..ff9f8cbbd52 100644 --- a/python/core/qgsdataitem.sip.in +++ b/python/core/qgsdataitem.sip.in @@ -472,6 +472,9 @@ Returns the icon name of the given ``layerType`` static QIcon iconRaster(); static QIcon iconDefault(); static QIcon iconMesh(); +%Docstring +Return icon for mesh layer type +%End virtual QString layerName() const; %Docstring diff --git a/src/core/mesh/qgsmeshdataprovider.cpp b/src/core/mesh/qgsmeshdataprovider.cpp index 208a6d6c234..257cf9fda26 100644 --- a/src/core/mesh/qgsmeshdataprovider.cpp +++ b/src/core/mesh/qgsmeshdataprovider.cpp @@ -27,7 +27,7 @@ QgsRectangle QgsMeshDataProvider::extent() const { QgsRectangle rec; rec.setMinimal(); - for ( size_t i = 0; i < vertexCount(); ++i ) + for ( int i = 0; i < vertexCount(); ++i ) { QgsMeshVertex v = vertex( i ); rec.setXMinimum( std::min( rec.xMinimum(), v.x() ) ); diff --git a/src/core/mesh/qgsmeshdataprovider.h b/src/core/mesh/qgsmeshdataprovider.h index 05f6c18308b..89d96cc74cb 100644 --- a/src/core/mesh/qgsmeshdataprovider.h +++ b/src/core/mesh/qgsmeshdataprovider.h @@ -18,22 +18,16 @@ #ifndef QGSMESHDATAPROVIDER_H #define QGSMESHDATAPROVIDER_H -#include - #include "qgis_core.h" -#include "qgis.h" #include "qgspoint.h" #include "qgsrectangle.h" #include "qgsdataprovider.h" -#include "qgscoordinatereferencesystem.h" #include -#include #include -#include typedef QgsPoint QgsMeshVertex; //xyz coords of vertex -typedef QVector QgsMeshFace; //list of vertex indexes +typedef QVector QgsMeshFace; //list of vertex indexes /** * \ingroup core @@ -53,28 +47,28 @@ class CORE_EXPORT QgsMeshSource SIP_ABSTRACT virtual ~QgsMeshSource() = default; /** - * \brief Return number of vertexes in the native mesh - * \returns Number of vertexes in the mesh + * \brief Return number of vertices in the native mesh + * \returns Number of vertices in the mesh */ - virtual size_t vertexCount() const = 0; + virtual int vertexCount() const = 0; /** * \brief Return number of faces in the native mesh * \returns Number of faces in the mesh */ - virtual size_t faceCount() const = 0; + virtual int faceCount() const = 0; /** * \brief Factory for mesh vertex with index * \returns new mesh vertex on index */ - virtual QgsMeshVertex vertex( size_t index ) const = 0; + virtual QgsMeshVertex vertex( int index ) const = 0; /** * \brief Factory for mesh face with index * \returns new mesh face on index */ - virtual QgsMeshFace face( size_t index ) const = 0; + virtual QgsMeshFace face( int index ) const = 0; }; /** diff --git a/src/core/mesh/qgsmeshlayer.cpp b/src/core/mesh/qgsmeshlayer.cpp index 0b50b737d65..5267a61fe3c 100644 --- a/src/core/mesh/qgsmeshlayer.cpp +++ b/src/core/mesh/qgsmeshlayer.cpp @@ -14,19 +14,18 @@ * (at your option) any later version. * * * ***************************************************************************/ + +#include + #include -#include "qgsmeshlayer.h" -#include "qgis.h" -#include "qgsmaplayerrenderer.h" -#include "qgsmeshdataprovider.h" -#include "qgsmeshlayerrenderer.h" -#include "qgstriangularmesh.h" -#include "qgssinglesymbolrenderer.h" -#include "qgsmeshmemorydataprovider.h" #include "qgsfillsymbollayer.h" -#include "qgsproviderregistry.h" #include "qgslogger.h" +#include "qgsmeshdataprovider.h" +#include "qgsmeshlayer.h" +#include "qgsmeshlayerrenderer.h" +#include "qgsproviderregistry.h" +#include "qgstriangularmesh.h" QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath, const QString &baseName, @@ -96,6 +95,14 @@ QString QgsMeshLayer::providerType() const return mProviderKey; } +QgsMesh *QgsMeshLayer::nativeMesh() SIP_SKIP {return mNativeMesh;} + +QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP {return mTriangularMesh;} + +QgsSymbol *QgsMeshLayer::nativeMeshSymbol() {return mNativeMeshSymbol;} + +QgsSymbol *QgsMeshLayer::triangularMeshSymbol() {return mTriangularMeshSymbol;} + void QgsMeshLayer::toggleTriangularMeshRendering( bool toggle ) { if ( toggle && mTriangularMeshSymbol ) @@ -127,13 +134,13 @@ void QgsMeshLayer::fillNativeMesh() return; mNativeMesh->vertices.resize( dataProvider()->vertexCount() ); - for ( size_t i = 0; i < dataProvider()->vertexCount(); ++i ) + for ( int i = 0; i < dataProvider()->vertexCount(); ++i ) { mNativeMesh->vertices[i] = dataProvider()->vertex( i ); } mNativeMesh->faces.resize( dataProvider()->faceCount() ); - for ( size_t i = 0; i < dataProvider()->faceCount(); ++i ) + for ( int i = 0; i < dataProvider()->faceCount(); ++i ) { mNativeMesh->faces[i] = dataProvider()->face( i ); } diff --git a/src/core/mesh/qgsmeshlayer.h b/src/core/mesh/qgsmeshlayer.h index fe8df374e62..92b8f7ec171 100644 --- a/src/core/mesh/qgsmeshlayer.h +++ b/src/core/mesh/qgsmeshlayer.h @@ -19,24 +19,12 @@ #define QGSMESHLAYER_H #include "qgis_core.h" -#include -#include -#include -#include -#include -#include -#include - -#include "qgis.h" #include "qgsmaplayer.h" #include "qgsrendercontext.h" #include "qgsmeshdataprovider.h" class QgsMapLayerRenderer; class QgsSymbol; - -class QgsMeshDataProvider; -class QgsNativeMesh; class QgsTriangularMesh; struct QgsMesh; @@ -110,7 +98,6 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer * \param providerLib The name of the data provider, e.g., "mesh_memory", "mdal" */ explicit QgsMeshLayer( const QString &path = QString(), const QString &baseName = QString(), const QString &providerLib = "mesh_memory" ); - //! Dtor ~QgsMeshLayer() override; //! QgsMeshLayer cannot be copied. @@ -118,68 +105,36 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer //! QgsMeshLayer cannot be copied. QgsMeshLayer &operator=( QgsMeshLayer const &rhs ) = delete; - //! Return data provider QgsMeshDataProvider *dataProvider() override; - - //! Return const data provider const QgsMeshDataProvider *dataProvider() const override SIP_SKIP; - - /** - * Returns a new instance equivalent to this one. A new provider is - * created for the same data source and renderers are cloned too. - * \returns a new layer instance - */ QgsMeshLayer *clone() const override SIP_FACTORY; - - //! Returns the extent of the layer. QgsRectangle extent() const override; - - /** - * Return new instance of QgsMapLayerRenderer that will be used for rendering of given context - */ - virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) SIP_FACTORY; + virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) override SIP_FACTORY; + bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ) override; + bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const override; //! Return the provider type for this layer QString providerType() const; //! return native mesh (nullprt before rendering) - QgsMesh *nativeMesh() SIP_SKIP {return mNativeMesh;} + QgsMesh *nativeMesh() SIP_SKIP; //! return triangular mesh (nullprt before rendering) - QgsTriangularMesh *triangularMesh() SIP_SKIP {return mTriangularMesh;} + QgsTriangularMesh *triangularMesh() SIP_SKIP; //! Returns a line symbol used for rendering native mesh. - QgsSymbol *nativeMeshSymbol() {return mNativeMeshSymbol;} + QgsSymbol *nativeMeshSymbol(); /** * Returns a line symbol used for rendering of triangular (derived) mesh. * \see toggleTriangularMeshRendering */ - QgsSymbol *triangularMeshSymbol() {return mTriangularMeshSymbol;} + QgsSymbol *triangularMeshSymbol(); //! Toggle rendering of triangular (derived) mesh. Off by default void toggleTriangularMeshRendering( bool toggle ); - /** - * Read the symbology for the current layer from the Dom node supplied. - * \param node node that will contain the symbology definition for this layer. - * \param errorMessage reference to string that will be updated with any error messages - * \param context reading context (used for transform from relative to absolute paths) - * \returns true in case of success. - */ - bool readSymbology( const QDomNode &node, QString &errorMessage, QgsReadWriteContext &context ); - - /** - * Write the symbology for the layer into the docment provided. - * \param node the node that will have the style element added to it. - * \param doc the document that will have the QDomNode added. - * \param errorMessage reference to string that will be updated with any error messages - * \param context writing context (used for transform from absolute to relative paths) - * \returns true in case of success. - */ - bool writeSymbology( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QgsReadWriteContext &context ) const; - - private: // Private methods + private: // Private methods /** * Returns true if the provider is in read-only mode diff --git a/src/core/mesh/qgsmeshlayerrenderer.cpp b/src/core/mesh/qgsmeshlayerrenderer.cpp index 45071ad3c6d..f7ee892afac 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.cpp +++ b/src/core/mesh/qgsmeshlayerrenderer.cpp @@ -17,18 +17,14 @@ #include "qgsmeshlayerrenderer.h" -#include "qgsrenderer.h" -#include "qgsrendercontext.h" -#include "qgsmeshlayer.h" -#include "qgsexception.h" -#include "qgslogger.h" -#include "qgssettings.h" -#include "qgssinglesymbolrenderer.h" #include "qgsfield.h" -#include "qgstriangularmesh.h" +#include "qgslogger.h" +#include "qgsmeshlayer.h" #include "qgspointxy.h" +#include "qgsrenderer.h" +#include "qgssinglesymbolrenderer.h" +#include "qgssymbol.h" -#include QgsMeshLayerRenderer::QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context ) diff --git a/src/core/mesh/qgsmeshlayerrenderer.h b/src/core/mesh/qgsmeshlayerrenderer.h index a6d7841d5fc..eb10d7e6da1 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.h +++ b/src/core/mesh/qgsmeshlayerrenderer.h @@ -18,23 +18,15 @@ #ifndef QGSMESHLAYERRENDERER_H #define QGSMESHLAYERRENDERER_H -class QgsRenderContext; class QgsMeshLayer; -class QgsMeshVectorFieldRenderer; -class QgsSingleSymbolRenderer; -class QgsTriangularMesh; class QgsSymbol; #define SIP_NO_FILE -#include -#include - #include "qgis.h" -#include "qgsfeedback.h" #include "qgsmaplayerrenderer.h" -#include "qgsmeshdataprovider.h" +#include "qgsrendercontext.h" #include "qgstriangularmesh.h" /** @@ -47,9 +39,10 @@ class QgsSymbol; class QgsMeshLayerRenderer : public QgsMapLayerRenderer { public: + //! Ctor QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context ); - ~QgsMeshLayerRenderer() override; + ~QgsMeshLayerRenderer() override; bool render() override; private: diff --git a/src/core/mesh/qgsmeshmemorydataprovider.cpp b/src/core/mesh/qgsmeshmemorydataprovider.cpp index 814165a0b0d..6347a70fe9b 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.cpp +++ b/src/core/mesh/qgsmeshmemorydataprovider.cpp @@ -14,11 +14,7 @@ * (at your option) any later version. * * * ***************************************************************************/ - #include "qgsmeshmemorydataprovider.h" -#include -#include -#include static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mesh_memory" ); static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "Mesh memory provider" ); @@ -127,12 +123,18 @@ bool QgsMeshMemoryDataProvider::addFaces( const QString &def ) for ( int j = 0; j < vertices.size(); ++j ) { int vertex_id = vertices[j].toInt(); - face.push_back( vertex_id ); - if ( face[j] >= mVertices.size() ) + if ( vertex_id < 0 ) + { + setError( QgsError( QStringLiteral( "Invalid mesh definition, vertex index must be positive value" ), QStringLiteral( "Mesh Memory Provider" ) ) ); + return false; + } + if ( mVertices.size() < vertex_id ) { setError( QgsError( QStringLiteral( "Invalid mesh definition, missing vertex id defined in face" ), QStringLiteral( "Mesh Memory Provider" ) ) ); return false; } + + face.push_back( vertex_id ); } faces.push_back( face ); } @@ -141,23 +143,23 @@ bool QgsMeshMemoryDataProvider::addFaces( const QString &def ) return true; } -size_t QgsMeshMemoryDataProvider::vertexCount() const +int QgsMeshMemoryDataProvider::vertexCount() const { return mVertices.size(); } -size_t QgsMeshMemoryDataProvider::faceCount() const +int QgsMeshMemoryDataProvider::faceCount() const { return mFaces.size(); } -QgsMeshVertex QgsMeshMemoryDataProvider::vertex( size_t index ) const +QgsMeshVertex QgsMeshMemoryDataProvider::vertex( int index ) const { Q_ASSERT( vertexCount() > index ); return mVertices[index]; } -QgsMeshFace QgsMeshMemoryDataProvider::face( size_t index ) const +QgsMeshFace QgsMeshMemoryDataProvider::face( int index ) const { Q_ASSERT( faceCount() > index ); return mFaces[index]; diff --git a/src/core/mesh/qgsmeshmemorydataprovider.h b/src/core/mesh/qgsmeshmemorydataprovider.h index dbbe72c439b..15f46a86afc 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.h +++ b/src/core/mesh/qgsmeshmemorydataprovider.h @@ -22,8 +22,6 @@ ///@cond PRIVATE -#include - #include #include "qgis_core.h" @@ -72,10 +70,10 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider QString description() const override; QgsCoordinateReferenceSystem crs() const override; - size_t vertexCount() const override; - size_t faceCount() const override; - QgsMeshVertex vertex( size_t index ) const override; - QgsMeshFace face( size_t index ) const override; + int vertexCount() const override; + int faceCount() const override; + QgsMeshVertex vertex( int index ) const override; + QgsMeshFace face( int index ) const override; //! Returns the memory provider key static QString providerKey(); diff --git a/src/core/mesh/qgstriangularmesh.cpp b/src/core/mesh/qgstriangularmesh.cpp index c61b8779fca..ce6fc4dfa59 100644 --- a/src/core/mesh/qgstriangularmesh.cpp +++ b/src/core/mesh/qgstriangularmesh.cpp @@ -16,12 +16,8 @@ ***************************************************************************/ #include "qgstriangularmesh.h" -#include "qgsmeshdataprovider.h" #include "qgsrendercontext.h" -#include "qgscoordinatereferencesystem.h" #include "qgscoordinatetransform.h" -#include "qgsmeshlayer.h" - QgsTriangularMesh::QgsTriangularMesh( ) { diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h index d97732a3215..5c5b39ebd09 100644 --- a/src/core/mesh/qgstriangularmesh.h +++ b/src/core/mesh/qgstriangularmesh.h @@ -21,14 +21,10 @@ #define SIP_NO_FILE -#include -#include #include -#include "qgis.h" #include "qgis_core.h" #include "qgsmeshdataprovider.h" -#include "qgscoordinatereferencesystem.h" class QgsRenderContext; diff --git a/src/core/qgsdataitem.cpp b/src/core/qgsdataitem.cpp index 191d61bd5f1..4fb6333c1f4 100644 --- a/src/core/qgsdataitem.cpp +++ b/src/core/qgsdataitem.cpp @@ -76,7 +76,6 @@ QIcon QgsLayerItem::iconRaster() QIcon QgsLayerItem::iconMesh() { - // TODO new icon! return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) ); } diff --git a/src/core/qgsdataitem.h b/src/core/qgsdataitem.h index 2b69f8622c9..bf4a313bda3 100644 --- a/src/core/qgsdataitem.h +++ b/src/core/qgsdataitem.h @@ -499,6 +499,7 @@ class CORE_EXPORT QgsLayerItem : public QgsDataItem static QIcon iconTable(); static QIcon iconRaster(); static QIcon iconDefault(); + //! Return icon for mesh layer type static QIcon iconMesh(); //! \returns the layer name diff --git a/src/providers/mdal/qgsmdaldataitems.h b/src/providers/mdal/qgsmdaldataitems.h index d7d034ffeb4..8ec76c7626a 100644 --- a/src/providers/mdal/qgsmdaldataitems.h +++ b/src/providers/mdal/qgsmdaldataitems.h @@ -17,6 +17,8 @@ #include "qgsdataitem.h" +#include + class QgsMdalLayerItem : public QgsLayerItem { Q_OBJECT diff --git a/src/providers/mdal/qgsmdalprovider.cpp b/src/providers/mdal/qgsmdalprovider.cpp index 0481962e89d..10d0c69b78e 100644 --- a/src/providers/mdal/qgsmdalprovider.cpp +++ b/src/providers/mdal/qgsmdalprovider.cpp @@ -16,10 +16,6 @@ ***************************************************************************/ #include "qgsmdalprovider.h" -#include -#include -#include -#include "mdal.h" static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mdal" ); static const QString TEXT_PROVIDER_DESCRIPTION = QStringLiteral( "MDAL provider" ); @@ -57,23 +53,23 @@ QgsMdalProvider::~QgsMdalProvider() MDAL_CloseMesh( mMeshH ); } -size_t QgsMdalProvider::vertexCount() const +int QgsMdalProvider::vertexCount() const { if ( mMeshH ) return MDAL_M_vertexCount( mMeshH ); else - return ( size_t ) 0; + return 0; } -size_t QgsMdalProvider::faceCount() const +int QgsMdalProvider::faceCount() const { if ( mMeshH ) return MDAL_M_faceCount( mMeshH ); else - return ( size_t ) 0; + return 0; } -QgsMeshVertex QgsMdalProvider::vertex( size_t index ) const +QgsMeshVertex QgsMdalProvider::vertex( int index ) const { Q_ASSERT( index < vertexCount() ); double x = MDAL_M_vertexXCoordinatesAt( mMeshH, index ); @@ -82,12 +78,12 @@ QgsMeshVertex QgsMdalProvider::vertex( size_t index ) const return vertex; } -QgsMeshFace QgsMdalProvider::face( size_t index ) const +QgsMeshFace QgsMdalProvider::face( int index ) const { Q_ASSERT( index < faceCount() ); QgsMeshFace face; int n_face_vertices = MDAL_M_faceVerticesCountAt( mMeshH, index ); - for ( size_t j = 0; j < n_face_vertices; ++j ) + for ( int j = 0; j < n_face_vertices; ++j ) { int vertex_index = MDAL_M_faceVerticesIndexAt( mMeshH, index, j ); face.push_back( vertex_index ); diff --git a/src/providers/mdal/qgsmdalprovider.h b/src/providers/mdal/qgsmdalprovider.h index 08c7af2f83f..9fb40331d54 100644 --- a/src/providers/mdal/qgsmdalprovider.h +++ b/src/providers/mdal/qgsmdalprovider.h @@ -16,21 +16,13 @@ #ifndef QGSMDALPROVIDER_H #define QGSGDALPROVIDER_H -#include - -#include "qgscoordinatereferencesystem.h" -#include "qgsdataitem.h" -#include "qgsmeshdataprovider.h" -#include "qgsrectangle.h" - #include -#include -#include -#include -#include #include +#include "qgscoordinatereferencesystem.h" +#include "qgsmeshdataprovider.h" + class QMutex; class QgsCoordinateTransform; @@ -58,10 +50,10 @@ class QgsMdalProvider : public QgsMeshDataProvider QString description() const override; QgsCoordinateReferenceSystem crs() const override; - size_t vertexCount() const override; - size_t faceCount() const override; - QgsMeshVertex vertex( size_t index ) const override; - QgsMeshFace face( size_t index ) const override; + int vertexCount() const override; + int faceCount() const override; + QgsMeshVertex vertex( int index ) const override; + QgsMeshFace face( int index ) const override; private: MeshH mMeshH; diff --git a/src/providers/ogr/qgsgeopackagedataitems.cpp b/src/providers/ogr/qgsgeopackagedataitems.cpp index 74305afdc80..2f0e1eb9ece 100644 --- a/src/providers/ogr/qgsgeopackagedataitems.cpp +++ b/src/providers/ogr/qgsgeopackagedataitems.cpp @@ -230,7 +230,7 @@ bool QgsGeoPackageCollectionItem::handleDrop( const QMimeData *data, Qt::DropAct } else if ( dropUri.layerType == QStringLiteral( "mesh" ) ) { - // unsuported + // unsupported hasError = true; continue; } diff --git a/tests/src/core/testqgsmeshlayer.cpp b/tests/src/core/testqgsmeshlayer.cpp index 4dd3aab9df6..61c6b763967 100644 --- a/tests/src/core/testqgsmeshlayer.cpp +++ b/tests/src/core/testqgsmeshlayer.cpp @@ -18,12 +18,6 @@ #include "qgstest.h" #include #include -#include -#include -#include -#include -#include -#include //qgis includes... #include "qgsmaplayer.h" @@ -31,8 +25,6 @@ #include "qgsapplication.h" #include "qgsproviderregistry.h" #include "qgsproject.h" -#include "qgsmaprenderersequentialjob.h" -#include "qgsmeshmemorydataprovider.h" /** * \ingroup UnitTests @@ -104,14 +96,14 @@ void TestQgsMeshLayer::test_data_provider() QVERIFY( dp->isValid() ); QCOMPARE( expectedExtent, dp->extent() ); - QCOMPARE( ( size_t ) 5, dp->vertexCount() ); + QCOMPARE( 5, dp->vertexCount() ); QCOMPARE( QgsMeshVertex( 1000.0, 2000.0 ), dp->vertex( 0 ) ); QCOMPARE( QgsMeshVertex( 2000.0, 2000.0 ), dp->vertex( 1 ) ); QCOMPARE( QgsMeshVertex( 3000.0, 2000.0 ), dp->vertex( 2 ) ); QCOMPARE( QgsMeshVertex( 2000.0, 3000.0 ), dp->vertex( 3 ) ); QCOMPARE( QgsMeshVertex( 1000.0, 3000.0 ), dp->vertex( 4 ) ); - QCOMPARE( ( size_t ) 2, dp->faceCount() ); + QCOMPARE( 2, dp->faceCount() ); QgsMeshFace f1; f1 << 0 << 1 << 3 << 4; QCOMPARE( f1, dp->face( 0 ) ); From c42af60dfef74b9acc28a1152094ab1cbb69b87c Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 09:19:11 +0200 Subject: [PATCH 3/6] use smart pointer internally and remove unnecessary code --- doc/CMakeLists.txt | 1 + python/core/mesh/qgsmeshlayer.sip.in | 1 + src/core/mesh/qgsmeshlayer.cpp | 60 +++++++++++--------------- src/core/mesh/qgsmeshlayer.h | 20 ++++----- src/core/mesh/qgsmeshlayerrenderer.cpp | 18 ++------ src/core/mesh/qgsmeshlayerrenderer.h | 12 +++--- src/core/mesh/qgstriangularmesh.cpp | 12 +----- src/core/mesh/qgstriangularmesh.h | 4 +- 8 files changed, 50 insertions(+), 78 deletions(-) diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index d85ab01316d..631f8ed7c6a 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -70,6 +70,7 @@ IF(WITH_APIDOC) ${CMAKE_SOURCE_DIR}/src/core/layout ${CMAKE_SOURCE_DIR}/src/core/locator ${CMAKE_SOURCE_DIR}/src/core/metadata + ${CMAKE_SOURCE_DIR}/src/core/mesh ${CMAKE_SOURCE_DIR}/src/core/pal ${CMAKE_SOURCE_DIR}/src/core/processing ${CMAKE_SOURCE_DIR}/src/core/providers diff --git a/python/core/mesh/qgsmeshlayer.sip.in b/python/core/mesh/qgsmeshlayer.sip.in index 1a97dae00c4..b7ace1c386a 100644 --- a/python/core/mesh/qgsmeshlayer.sip.in +++ b/python/core/mesh/qgsmeshlayer.sip.in @@ -10,6 +10,7 @@ + class QgsMeshLayer : QgsMapLayer { %Docstring diff --git a/src/core/mesh/qgsmeshlayer.cpp b/src/core/mesh/qgsmeshlayer.cpp index 5267a61fe3c..bbd382ac08d 100644 --- a/src/core/mesh/qgsmeshlayer.cpp +++ b/src/core/mesh/qgsmeshlayer.cpp @@ -38,7 +38,7 @@ QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath, QgsSymbolLayerList l1; l1 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::black, Qt::SolidLine, 1.0 ); - mNativeMeshSymbol = new QgsFillSymbol( l1 ); + mNativeMeshSymbol.reset( new QgsFillSymbol( l1 ) ); toggleTriangularMeshRendering( false ); @@ -49,16 +49,8 @@ QgsMeshLayer::QgsMeshLayer( const QString &meshLayerPath, QgsMeshLayer::~QgsMeshLayer() { - clearMeshes(); - if ( mDataProvider ) delete mDataProvider; - - if ( mNativeMeshSymbol ) - delete mNativeMeshSymbol; - - if ( mTriangularMeshSymbol ) - delete mTriangularMeshSymbol; } QgsMeshDataProvider *QgsMeshLayer::dataProvider() @@ -95,31 +87,41 @@ QString QgsMeshLayer::providerType() const return mProviderKey; } -QgsMesh *QgsMeshLayer::nativeMesh() SIP_SKIP {return mNativeMesh;} +QgsMesh *QgsMeshLayer::nativeMesh() SIP_SKIP +{ + return mNativeMesh.get(); +} -QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP {return mTriangularMesh;} -QgsSymbol *QgsMeshLayer::nativeMeshSymbol() {return mNativeMeshSymbol;} +QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP +{ + return mTriangularMesh.get(); +} -QgsSymbol *QgsMeshLayer::triangularMeshSymbol() {return mTriangularMeshSymbol;} +QgsSymbol *QgsMeshLayer::nativeMeshSymbol() +{ + return mNativeMeshSymbol.get(); +} + +QgsSymbol *QgsMeshLayer::triangularMeshSymbol() +{ + return mTriangularMeshSymbol.get(); +} void QgsMeshLayer::toggleTriangularMeshRendering( bool toggle ) { if ( toggle && mTriangularMeshSymbol ) return; - if ( mTriangularMeshSymbol ) - delete mTriangularMeshSymbol; - if ( toggle ) { QgsSymbolLayerList l2; l2 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::red, Qt::SolidLine, 0.26 ); - mTriangularMeshSymbol = new QgsFillSymbol( l2 ); + mTriangularMeshSymbol.reset( new QgsFillSymbol( l2 ) ); } else { - mTriangularMeshSymbol = nullptr; + mTriangularMeshSymbol.reset(); } triggerRepaint(); } @@ -128,7 +130,7 @@ void QgsMeshLayer::fillNativeMesh() { Q_ASSERT( !mNativeMesh ); - mNativeMesh = new QgsMesh(); + mNativeMesh.reset( new QgsMesh() ); if ( !( dataProvider() && dataProvider()->isValid() ) ) return; @@ -150,13 +152,14 @@ QgsMapLayerRenderer *QgsMeshLayer::createMapRenderer( QgsRenderContext &renderer { if ( !mNativeMesh ) { + // lazy loading of mesh data fillNativeMesh(); } if ( !mTriangularMesh ) - mTriangularMesh = new QgsTriangularMesh(); + mTriangularMesh.reset( new QgsTriangularMesh() ); - triangularMesh()->update( mNativeMesh, &rendererContext ); + mTriangularMesh->update( mNativeMesh.get(), &rendererContext ); return new QgsMeshLayerRenderer( this, rendererContext ); } @@ -177,26 +180,13 @@ bool QgsMeshLayer::writeSymbology( QDomNode &node, QDomDocument &doc, QString &e return true; } -void QgsMeshLayer::clearMeshes() -{ - if ( mTriangularMesh ) - delete mTriangularMesh; - - if ( mNativeMesh ) - delete mNativeMesh; - -} - bool QgsMeshLayer::setDataProvider( QString const &provider ) { - clearMeshes(); + Q_ASSERT( !mDataProvider ); //called from ctor mProviderKey = provider; QString dataSource = mDataSource; - if ( mDataProvider ) - delete mDataProvider; - mDataProvider = qobject_cast( QgsProviderRegistry::instance()->createProvider( provider, dataSource ) ); if ( !mDataProvider ) { diff --git a/src/core/mesh/qgsmeshlayer.h b/src/core/mesh/qgsmeshlayer.h index 92b8f7ec171..b185010e864 100644 --- a/src/core/mesh/qgsmeshlayer.h +++ b/src/core/mesh/qgsmeshlayer.h @@ -18,6 +18,8 @@ #ifndef QGSMESHLAYER_H #define QGSMESHLAYER_H +#include + #include "qgis_core.h" #include "qgsmaplayer.h" #include "qgsrendercontext.h" @@ -152,28 +154,26 @@ class CORE_EXPORT QgsMeshLayer : public QgsMapLayer #endif private: - //! Clear native and triangular mesh - void clearMeshes(); void fillNativeMesh(); private: - //! Pointer to native mesh structure, used as cache for rendering - QgsMesh *mNativeMesh = nullptr; - - //! Pointer to derived mesh structure - QgsTriangularMesh *mTriangularMesh = nullptr; - //! Pointer to data provider derived from the abastract base class QgsMeshDataProvider QgsMeshDataProvider *mDataProvider = nullptr; //! Data provider key QString mProviderKey; + //! Pointer to native mesh structure, used as cache for rendering + std::unique_ptr mNativeMesh; + + //! Pointer to derived mesh structure + std::unique_ptr mTriangularMesh; + //! rendering native mesh - QgsSymbol *mNativeMeshSymbol = nullptr; + std::unique_ptr mNativeMeshSymbol; //! rendering triangular mesh - QgsSymbol *mTriangularMeshSymbol = nullptr; + std::unique_ptr mTriangularMeshSymbol; }; #endif //QGSMESHLAYER_H diff --git a/src/core/mesh/qgsmeshlayerrenderer.cpp b/src/core/mesh/qgsmeshlayerrenderer.cpp index f7ee892afac..eec161a7fb5 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.cpp +++ b/src/core/mesh/qgsmeshlayerrenderer.cpp @@ -40,27 +40,15 @@ QgsMeshLayerRenderer::QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContex // make copies for symbols if ( layer->nativeMeshSymbol() ) { - mNativeMeshSymbol = layer->nativeMeshSymbol()->clone(); + mNativeMeshSymbol.reset( layer->nativeMeshSymbol()->clone() ); } if ( layer->triangularMeshSymbol() ) { - mTriangularMeshSymbol = layer->triangularMeshSymbol()->clone(); + mTriangularMeshSymbol.reset( layer->triangularMeshSymbol()->clone() ); } } - -QgsMeshLayerRenderer::~QgsMeshLayerRenderer() -{ - if ( mNativeMeshSymbol ) - delete mNativeMeshSymbol; - - if ( mTriangularMeshSymbol ) - delete mTriangularMeshSymbol; -} - - - bool QgsMeshLayerRenderer::render() { renderMesh( mNativeMeshSymbol, mNativeMesh.faces ); // native mesh @@ -69,7 +57,7 @@ bool QgsMeshLayerRenderer::render() return true; } -void QgsMeshLayerRenderer::renderMesh( QgsSymbol *symbol, const QVector &faces ) +void QgsMeshLayerRenderer::renderMesh( const std::unique_ptr &symbol, const QVector &faces ) { if ( !symbol ) return; diff --git a/src/core/mesh/qgsmeshlayerrenderer.h b/src/core/mesh/qgsmeshlayerrenderer.h index eb10d7e6da1..7771759eb89 100644 --- a/src/core/mesh/qgsmeshlayerrenderer.h +++ b/src/core/mesh/qgsmeshlayerrenderer.h @@ -23,6 +23,8 @@ class QgsSymbol; #define SIP_NO_FILE +#include + #include "qgis.h" #include "qgsmaplayerrenderer.h" @@ -42,11 +44,11 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer //! Ctor QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context ); - ~QgsMeshLayerRenderer() override; + ~QgsMeshLayerRenderer() override = default; bool render() override; private: - void renderMesh( QgsSymbol *symbol, const QVector &faces ); + void renderMesh( const std::unique_ptr &symbol, const QVector &faces ); protected: @@ -57,15 +59,13 @@ class QgsMeshLayerRenderer : public QgsMapLayerRenderer QgsTriangularMesh mTriangularMesh; // copy from mesh layer - QgsSymbol *mNativeMeshSymbol = nullptr; + std::unique_ptr mNativeMeshSymbol = nullptr; // copy from mesh layer - QgsSymbol *mTriangularMeshSymbol = nullptr; + std::unique_ptr mTriangularMeshSymbol = nullptr; // rendering context QgsRenderContext &mContext; - - }; diff --git a/src/core/mesh/qgstriangularmesh.cpp b/src/core/mesh/qgstriangularmesh.cpp index ce6fc4dfa59..d490156e646 100644 --- a/src/core/mesh/qgstriangularmesh.cpp +++ b/src/core/mesh/qgstriangularmesh.cpp @@ -19,14 +19,6 @@ #include "qgsrendercontext.h" #include "qgscoordinatetransform.h" -QgsTriangularMesh::QgsTriangularMesh( ) -{ -} - -QgsTriangularMesh::~QgsTriangularMesh() -{ -} - void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context ) { Q_ASSERT( nativeMesh ); @@ -41,7 +33,7 @@ void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context ) mTriangularMesh.vertices.resize( nativeMesh->vertices.size() ); for ( int i = 0; i < nativeMesh->vertices.size(); ++i ) { - QgsMeshVertex vertex = nativeMesh->vertices[i]; + const QgsMeshVertex &vertex = nativeMesh->vertices.at( i ); if ( transform.isValid() ) { QgsPointXY mapPoint = transform.transform( QgsPointXY( vertex.x(), vertex.y() ) ); @@ -59,7 +51,7 @@ void QgsTriangularMesh::update( QgsMesh *nativeMesh, QgsRenderContext *context ) // CREATE TRIANGULAR MESH for ( int i = 0; i < nativeMesh->faces.size(); ++i ) { - QgsMeshFace face = nativeMesh->faces[i] ; + const QgsMeshFace &face = nativeMesh->faces.at( i ) ; if ( face.size() == 3 ) { // triangle diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h index 5c5b39ebd09..26ee2f27218 100644 --- a/src/core/mesh/qgstriangularmesh.h +++ b/src/core/mesh/qgstriangularmesh.h @@ -45,9 +45,9 @@ class CORE_EXPORT QgsTriangularMesh { public: //! Ctor - QgsTriangularMesh(); + QgsTriangularMesh() = default; //! Dtor - ~QgsTriangularMesh(); + ~QgsTriangularMesh() = default; /** * Construct triangular mesh from layer's native mesh and context From d1240119ef88abbd4a28aaa795e46515293cb879 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 09:38:39 +0200 Subject: [PATCH 4/6] use QStringLiteral --- python/core/core_auto.sip | 5 ++--- python/core/mesh/qgsmeshdataprovider.sip.in | 2 ++ src/core/mesh/qgsmeshdataprovider.h | 2 +- src/core/mesh/qgsmeshmemorydataprovider.cpp | 15 +++++++-------- src/core/mesh/qgsmeshmemorydataprovider.h | 2 +- src/core/mesh/qgstriangularmesh.h | 3 +++ 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index fa21ee1df5c..8718ae6eb9a 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -376,6 +376,8 @@ %Include raster/qgsrasterdataprovider.sip %Include raster/qgsrasterinterface.sip %Include raster/qgsrasterprojector.sip +%Include mesh/qgsmeshdataprovider.sip +%Include mesh/qgsmeshlayer.sip %Include geometry/qgsabstractgeometry.sip %Include geometry/qgsgeometry.sip %Include geometry/qgspoint.sip @@ -433,6 +435,3 @@ %Include qgsuserprofilemanager.sip %Include symbology/qgsarrowsymbollayer.sip %Include qgsuserprofile.sip -%Include mesh/qgsmeshdataprovider.sip -%Include mesh/qgsmeshlayer.sip - diff --git a/python/core/mesh/qgsmeshdataprovider.sip.in b/python/core/mesh/qgsmeshdataprovider.sip.in index 7d7652d80d3..e408efd9451 100644 --- a/python/core/mesh/qgsmeshdataprovider.sip.in +++ b/python/core/mesh/qgsmeshdataprovider.sip.in @@ -69,6 +69,8 @@ Base class for providing data for :py:class:`QgsMeshLayer` Responsible for reading native mesh data .. seealso:: :py:class:`QgsMeshSource` + +.. versionadded:: 3.2 %End %TypeHeaderCode diff --git a/src/core/mesh/qgsmeshdataprovider.h b/src/core/mesh/qgsmeshdataprovider.h index 89d96cc74cb..8fc669094ea 100644 --- a/src/core/mesh/qgsmeshdataprovider.h +++ b/src/core/mesh/qgsmeshdataprovider.h @@ -78,7 +78,7 @@ class CORE_EXPORT QgsMeshSource SIP_ABSTRACT * Responsible for reading native mesh data * * \see QgsMeshSource - * + * \since QGIS 3.2 */ class CORE_EXPORT QgsMeshDataProvider: public QgsDataProvider, public QgsMeshSource { diff --git a/src/core/mesh/qgsmeshmemorydataprovider.cpp b/src/core/mesh/qgsmeshmemorydataprovider.cpp index 6347a70fe9b..e7d9e53a52b 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.cpp +++ b/src/core/mesh/qgsmeshmemorydataprovider.cpp @@ -26,12 +26,12 @@ bool QgsMeshMemoryDataProvider::isValid() const QString QgsMeshMemoryDataProvider::name() const { - return "mesh_memory"; + return TEXT_PROVIDER_KEY; } QString QgsMeshMemoryDataProvider::description() const { - return "memory data provider for mesh layer"; + return TEXT_PROVIDER_DESCRIPTION; } QgsCoordinateReferenceSystem QgsMeshMemoryDataProvider::crs() const @@ -41,7 +41,6 @@ QgsCoordinateReferenceSystem QgsMeshMemoryDataProvider::crs() const QgsMeshMemoryDataProvider::QgsMeshMemoryDataProvider( const QString &uri ) : QgsMeshDataProvider( uri ) - , mIsValid( false ) { mIsValid = splitSections( uri ); } @@ -67,7 +66,7 @@ QgsMeshMemoryDataProvider *QgsMeshMemoryDataProvider::createProvider( const QStr bool QgsMeshMemoryDataProvider::splitSections( const QString &uri ) { - QStringList sections = uri.split( "---", QString::SkipEmptyParts ); + const QStringList sections = uri.split( QStringLiteral( "---" ), QString::SkipEmptyParts ); if ( sections.size() != 2 ) { setError( QgsError( QStringLiteral( "Invalid mesh definition, does not contain 2 sections" ), @@ -85,10 +84,10 @@ bool QgsMeshMemoryDataProvider::addVertices( const QString &def ) { QVector vertices; - QStringList verticesCoords = def.split( "\n", QString::SkipEmptyParts ); + const QStringList verticesCoords = def.split( '\n', QString::SkipEmptyParts ); for ( int i = 0; i < verticesCoords.size(); ++i ) { - QStringList coords = verticesCoords[i].split( ",", QString::SkipEmptyParts ); + const QStringList coords = verticesCoords[i].split( ',', QString::SkipEmptyParts ); if ( coords.size() != 2 ) { setError( QgsError( QStringLiteral( "Invalid mesh definition, vertex definition does not contain x, y" ), @@ -109,10 +108,10 @@ bool QgsMeshMemoryDataProvider::addFaces( const QString &def ) { QVector faces; - QStringList facesVertices = def.split( "\n", QString::SkipEmptyParts ); + const QStringList facesVertices = def.split( '\n', QString::SkipEmptyParts ); for ( int i = 0; i < facesVertices.size(); ++i ) { - QStringList vertices = facesVertices[i].split( ",", QString::SkipEmptyParts ); + const QStringList vertices = facesVertices[i].split( ',', QString::SkipEmptyParts ); if ( vertices.size() < 3 ) { setError( QgsError( QStringLiteral( "Invalid mesh definition, face must contain at least 3 vertices" ), diff --git a/src/core/mesh/qgsmeshmemorydataprovider.h b/src/core/mesh/qgsmeshmemorydataprovider.h index 15f46a86afc..a3bdcf570bf 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.h +++ b/src/core/mesh/qgsmeshmemorydataprovider.h @@ -90,7 +90,7 @@ class QgsMeshMemoryDataProvider: public QgsMeshDataProvider QVector mVertices; QVector mFaces; - bool mIsValid; + bool mIsValid = false; }; ///@endcond diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h index 26ee2f27218..18297c8f157 100644 --- a/src/core/mesh/qgstriangularmesh.h +++ b/src/core/mesh/qgstriangularmesh.h @@ -38,7 +38,10 @@ struct CORE_EXPORT QgsMesh }; /** + * \ingroup core + * * Triangular/Derived Mesh + * * \since QGIS 3.2 */ class CORE_EXPORT QgsTriangularMesh From 5b8a341e04d708da3256d3cd7eace8abe974eda5 Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 10:39:28 +0200 Subject: [PATCH 5/6] fix Doxygen warnings --- python/core/mesh/qgsmeshlayer.sip.in | 4 ++-- src/core/mesh/qgsmeshlayer.h | 4 ++-- src/core/mesh/qgsmeshmemorydataprovider.cpp | 4 +++- src/core/mesh/qgstriangularmesh.h | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/python/core/mesh/qgsmeshlayer.sip.in b/python/core/mesh/qgsmeshlayer.sip.in index b7ace1c386a..2560646250e 100644 --- a/python/core/mesh/qgsmeshlayer.sip.in +++ b/python/core/mesh/qgsmeshlayer.sip.in @@ -28,9 +28,9 @@ yet support editing transactions. The main data providers supported by QGIS are listed below. -\section providers Mesh data providers +\section mesh_providers Mesh data providers -\subsection memory Memory data providerType (mesh_memory) +\subsection mesh_memory Memory data providerType (mesh_memory) The memory data provider is used to construct in memory data, for example scratch data. There is no inherent persistent storage of the data. The data source uri is constructed. diff --git a/src/core/mesh/qgsmeshlayer.h b/src/core/mesh/qgsmeshlayer.h index b185010e864..1359ce2380e 100644 --- a/src/core/mesh/qgsmeshlayer.h +++ b/src/core/mesh/qgsmeshlayer.h @@ -46,9 +46,9 @@ struct QgsMesh; * * The main data providers supported by QGIS are listed below. * - * \section providers Mesh data providers + * \section mesh_providers Mesh data providers * - * \subsection memory Memory data providerType (mesh_memory) + * \subsection mesh_memory Memory data providerType (mesh_memory) * * The memory data provider is used to construct in memory data, for example scratch * data. There is no inherent persistent storage of the data. The data source uri is constructed. diff --git a/src/core/mesh/qgsmeshmemorydataprovider.cpp b/src/core/mesh/qgsmeshmemorydataprovider.cpp index e7d9e53a52b..8143251fe78 100644 --- a/src/core/mesh/qgsmeshmemorydataprovider.cpp +++ b/src/core/mesh/qgsmeshmemorydataprovider.cpp @@ -14,6 +14,8 @@ * (at your option) any later version. * * * ***************************************************************************/ +///@cond PRIVATE + #include "qgsmeshmemorydataprovider.h" static const QString TEXT_PROVIDER_KEY = QStringLiteral( "mesh_memory" ); @@ -164,4 +166,4 @@ QgsMeshFace QgsMeshMemoryDataProvider::face( int index ) const return mFaces[index]; } - +///@endcond diff --git a/src/core/mesh/qgstriangularmesh.h b/src/core/mesh/qgstriangularmesh.h index 18297c8f157..79a851ba890 100644 --- a/src/core/mesh/qgstriangularmesh.h +++ b/src/core/mesh/qgstriangularmesh.h @@ -54,7 +54,7 @@ class CORE_EXPORT QgsTriangularMesh /** * Construct triangular mesh from layer's native mesh and context - * \param layer QgsMeshLayer to get native mesh data + * \param nativeMesh QgsMesh to access native vertices and faces * \param context Rendering context to estimate number of triagles to create for an face */ void update( QgsMesh *nativeMesh, QgsRenderContext *context ); From 5b1918f5f36441a5df874de1d9ff00af99cee2bb Mon Sep 17 00:00:00 2001 From: Peter Petrik Date: Fri, 20 Apr 2018 11:56:16 +0200 Subject: [PATCH 6/6] use unique_ptrs for tmp pointers --- src/gui/qgsbrowserdockwidget_p.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/gui/qgsbrowserdockwidget_p.cpp b/src/gui/qgsbrowserdockwidget_p.cpp index 6db14611582..6e626db538c 100644 --- a/src/gui/qgsbrowserdockwidget_p.cpp +++ b/src/gui/qgsbrowserdockwidget_p.cpp @@ -19,6 +19,8 @@ ***************************************************************************/ #include "qgsbrowserdockwidget_p.h" +#include + #include #include #include @@ -143,7 +145,7 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) { QgsDebugMsg( "creating raster layer" ); // should copy code from addLayer() to split uri ? - QgsRasterLayer *layer = new QgsRasterLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ); + std::unique_ptr layer( new QgsRasterLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) ); if ( layer ) { if ( layer->isValid() ) @@ -151,13 +153,12 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) layerCrs = layer->crs(); layerMetadata = layer->htmlMetadata(); } - delete layer; } } else if ( type == QgsMapLayer::MeshLayer ) { QgsDebugMsg( "creating mesh layer" ); - QgsMeshLayer *layer = new QgsMeshLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ); + std::unique_ptr layer( new QgsMeshLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) ); if ( layer ) { if ( layer->isValid() ) @@ -165,13 +166,12 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) layerCrs = layer->crs(); layerMetadata = layer->htmlMetadata(); } - delete layer; } } else if ( type == QgsMapLayer::VectorLayer ) { QgsDebugMsg( "creating vector layer" ); - QgsVectorLayer *layer = new QgsVectorLayer( layerItem->uri(), layerItem->name(), layerItem->providerKey() ); + std::unique_ptr layer( new QgsVectorLayer( layerItem->uri(), layerItem->name(), layerItem->providerKey() ) ); if ( layer ) { if ( layer->isValid() ) @@ -179,7 +179,6 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item ) layerCrs = layer->crs(); layerMetadata = layer->htmlMetadata(); } - delete layer; } } else if ( type == QgsMapLayer::PluginLayer )