Merge pull request #6820 from PeterPetrik/QgsMeshLayer_1_mesh

[FEATURE] QgsMeshLayer part 1: Reading raw mesh
This commit is contained in:
Martin Dobias 2018-04-25 09:02:24 +02:00 committed by GitHub
commit ade216d02d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 2897 additions and 12 deletions

View File

@ -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)

32
cmake/FindMDAL.cmake Normal file
View File

@ -0,0 +1,32 @@
# Find MDAL
# ~~~~~~~~~
# Copyright (c) 2018, Peter Petrik <zilolv at gmail dot com>
# 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} )

View File

@ -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

89
external/mdal/api/mdal.h vendored Normal file
View File

@ -0,0 +1,89 @@
/*
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
/* Statuses */
enum MDAL_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 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 );
//! Close mesh, free the memory
MDAL_EXPORT void MDAL_CloseMesh( MeshH mesh );
//! Return vertex count for the mesh
MDAL_EXPORT int MDAL_M_vertexCount( MeshH mesh );
//! Return vertex X coord for the mesh
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, int index );
//! Return face count for the mesh
MDAL_EXPORT int MDAL_M_faceCount( MeshH mesh );
//! Return number of vertices face consist of, e.g. 3 for triangle
MDAL_EXPORT int MDAL_M_faceVerticesCountAt( MeshH mesh, int index );
//! Return vertex index for face
MDAL_EXPORT int MDAL_M_faceVerticesIndexAt( MeshH mesh, int face_index, int vertex_index );
#ifdef __cplusplus
}
#endif
#endif //MDAL_H

204
external/mdal/frmts/mdal_2dm.cpp vendored Normal file
View File

@ -0,0 +1,204 @@
/*
MDAL - Mesh Data Abstraction Library (MIT License)
Copyright (C) 2018 Peter Petrik (zilolv at gmail dot com)
*/
#include <stddef.h>
#include <iosfwd>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <map>
#include <cassert>
#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( MDAL_Status *status )
{
if ( status ) *status = MDAL_Status::None;
if ( !MDAL::fileExists( mMeshFile ) )
{
if ( status ) *status = MDAL_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 = MDAL_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 = MDAL_Status::Warn_UnsupportedElement;
elemCount += 1; // We still count them as elements
}
}
// Allocate memory
std::vector<Vertex> vertices( nodeCount );
std::vector<Face> faces( elemCount );
in.clear();
in.seekg( 0, std::ios::beg );
std::vector<std::string> chunks;
size_t elemIndex = 0;
size_t nodeIndex = 0;
std::map<size_t, size_t> elemIDtoIndex;
std::map<size_t, size_t> 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<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = MDAL_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<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = MDAL_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<size_t, size_t>::iterator search = elemIDtoIndex.find( elemID );
if ( search != elemIDtoIndex.end() )
{
if ( status ) *status = MDAL_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<size_t, size_t>::iterator search = nodeIDtoIndex.find( nodeID );
if ( search != nodeIDtoIndex.end() )
{
if ( status ) *status = MDAL_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<Face>::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<size_t, size_t>::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 = MDAL_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;
}

28
external/mdal/frmts/mdal_2dm.hpp vendored Normal file
View File

@ -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 <string>
#include "mdal_defines.hpp"
#include "mdal.h"
namespace MDAL
{
class Loader2dm
{
public:
Loader2dm( const std::string &meshFile );
Mesh *load( MDAL_Status *status );
private:
std::string mMeshFile;
};
} // namespace MDAL
#endif //MDAL_2DM_HPP

100
external/mdal/mdal.cpp vendored Normal file
View File

@ -0,0 +1,100 @@
#include <string>
#include <cassert>
#include <stddef.h>
#include "mdal.h"
#include "mdal_loader.hpp"
#include "mdal_defines.hpp"
static MDAL_Status sLastStatus;
const char *MDAL_Version()
{
return "0.0.2";
}
MDAL_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;
}
}
int MDAL_M_vertexCount( MeshH mesh )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
int len = static_cast<int>( m->vertices.size() );
return len;
}
double MDAL_M_vertexXCoordinatesAt( MeshH mesh, int index )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
assert( index > -1 );
size_t i = static_cast<size_t>( index );
assert( m->vertices.size() > i );
return m->vertices[i].x;
}
double MDAL_M_vertexYCoordinatesAt( MeshH mesh, int index )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
assert( index > -1 );
size_t i = static_cast<size_t>( index );
assert( m->vertices.size() > i );
return m->vertices[i].y;
}
int MDAL_M_faceCount( MeshH mesh )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
int len = static_cast<int>( m->faces.size() );
return len;
}
int MDAL_M_faceVerticesCountAt( MeshH mesh, int index )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
assert( index > -1 );
size_t i = static_cast<size_t>( index );
assert( m->faces.size() > i );
int len = static_cast<int>( m->faces[i].size() );
return len;
}
int MDAL_M_faceVerticesIndexAt( MeshH mesh, int face_index, int vertex_index )
{
assert( mesh );
MDAL::Mesh *m = ( MDAL::Mesh * ) mesh;
assert( face_index > -1 );
size_t fi = static_cast<size_t>( face_index );
assert( m->faces.size() > fi );
assert( vertex_index > -1 );
size_t vi = static_cast<size_t>( vertex_index );
assert( m->faces[fi].size() > vi );
int len = static_cast<int>( m->faces[fi][vi] );
return len;
}

31
external/mdal/mdal_defines.hpp vendored Normal file
View File

@ -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 <stddef.h>
#include <vector>
namespace MDAL
{
typedef struct
{
double x;
double y;
} Vertex;
typedef std::vector<size_t> Face;
struct Mesh
{
std::vector<Vertex> vertices;
std::vector<Face> faces;
};
} // namespace MDAL
#endif //MDAL_DEFINES_HPP

13
external/mdal/mdal_loader.cpp vendored Normal file
View File

@ -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, MDAL_Status *status )
{
MDAL::Loader2dm loader( meshFile );
return loader.load( status );
}

24
external/mdal/mdal_loader.hpp vendored Normal file
View File

@ -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 <string>
#include "mdal.h"
#include "mdal_defines.hpp"
namespace MDAL
{
class Loader
{
public:
static Mesh *load( const std::string &meshFile, MDAL_Status *status );
};
} // namespace MDAL
#endif //MDAL_LOADER_HPP

56
external/mdal/mdal_utils.cpp vendored Normal file
View File

@ -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 <fstream>
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<std::string> MDAL::split( const std::string &str, const std::string &delimiter, SplitBehaviour behaviour )
{
std::string remaining( str );
std::vector<std::string> 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() );
}

34
external/mdal/mdal_utils.hpp vendored Normal file
View File

@ -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 <string>
#include <vector>
#include <stddef.h>
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<std::string> split( const std::string &str, const std::string &delimiter, SplitBehaviour behaviour );
} // namespace MDAL
#endif //MDAL_UTILS_HPP

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="16"
width="16"
version="1.1"
id="svg10"
sodipodi:docname="mIconMeshLayer.svg"
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)">
<metadata
id="metadata16">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs14" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="1021"
id="namedview12"
showgrid="false"
inkscape:zoom="14.75"
inkscape:cx="8"
inkscape:cy="8"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="g8" />
<g
transform="translate(0.06779661,-15.457627)"
id="g8"
style="fill:#eeeeec;fill-rule:evenodd;stroke:#888a85;stroke-linecap:round;stroke-linejoin:round">
<rect
id="rect30"
width="5.9661016"
height="4.7457619"
x="1.6949153"
y="18.508474" />
<rect
id="rect32"
width="5.762712"
height="5.0847459"
x="1.7627119"
y="23.254236" />
<rect
id="rect34"
width="5.6949148"
height="4.7457619"
x="7.525424"
y="18.508474" />
<rect
id="rect36"
width="5.5593219"
height="5.0847468"
x="7.6610169"
y="23.254236" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -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

View File

@ -378,6 +378,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

View File

@ -0,0 +1,99 @@
/************************************************************************
* 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<int> 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 int vertexCount() const = 0;
%Docstring
Return number of vertices in the native mesh
:return: Number of vertices in the mesh
%End
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( int index ) const = 0;
%Docstring
Factory for mesh vertex with index
:return: new mesh vertex on index
%End
virtual QgsMeshFace face( int 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`
.. versionadded:: 3.2
%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 *
************************************************************************/

View File

@ -0,0 +1,138 @@
/************************************************************************
* 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 mesh_providers Mesh data providers
\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.
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
QgsMeshLayer cannot be copied.
%End
virtual QgsMeshLayer *clone() const /Factory/;
virtual QgsRectangle extent() const;
virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) /Factory/;
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
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
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 *
************************************************************************/

View File

@ -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<int> QgsMeshFace; //list of vertex indexes
struct QgsNativeMesh
{
std::vector<QgsMeshVertex> vertices;
std::vector<QgsMeshFace> 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 *
************************************************************************/

View File

@ -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,10 @@ Returns the icon name of the given ``layerType``
static QIcon iconTable();
static QIcon iconRaster();
static QIcon iconDefault();
static QIcon iconMesh();
%Docstring
Return icon for mesh layer type
%End
virtual QString layerName() const;
%Docstring

View File

@ -38,6 +38,10 @@ to generic QgsVectorDataProvider's) depends on it.
{
sipType = sipType_QgsRasterDataProvider;
}
else if ( qobject_cast<QgsMeshDataProvider *>( sipCpp ) )
{
sipType = sipType_QgsMeshDataProvider;
}
else
{
sipType = 0;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -142,6 +142,12 @@ QVariantMap QgsPackageAlgorithm::processAlgorithm( const QVariantMap &parameters
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;
}
}

View File

@ -599,6 +599,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

View File

@ -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 );

View File

@ -447,6 +447,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
@ -684,6 +690,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
@ -1061,6 +1071,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
@ -1181,6 +1194,7 @@ INCLUDE_DIRECTORIES(
scalebar
symbology
metadata
mesh
${CMAKE_SOURCE_DIR}/external/nmea
)
IF (WITH_INTERNAL_QEXTSERIALPORT)

View File

@ -3642,6 +3642,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" );
}

View File

@ -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 ( int 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;
}

View File

@ -0,0 +1,98 @@
/***************************************************************************
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 "qgis_core.h"
#include "qgspoint.h"
#include "qgsrectangle.h"
#include "qgsdataprovider.h"
#include <QVector>
#include <QString>
typedef QgsPoint QgsMeshVertex; //xyz coords of vertex
typedef QVector<int> 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 vertices in the native mesh
* \returns Number of vertices in the mesh
*/
virtual int vertexCount() const = 0;
/**
* \brief Return number of faces in the native mesh
* \returns Number of faces in the mesh
*/
virtual int faceCount() const = 0;
/**
* \brief Factory for mesh vertex with index
* \returns new mesh vertex on index
*/
virtual QgsMeshVertex vertex( int index ) const = 0;
/**
* \brief Factory for mesh face with index
* \returns new mesh face on index
*/
virtual QgsMeshFace face( int index ) const = 0;
};
/**
* \ingroup core
* Base class for providing data for QgsMeshLayer
*
* Responsible for reading native mesh data
*
* \see QgsMeshSource
* \since QGIS 3.2
*/
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

View File

@ -0,0 +1,214 @@
/***************************************************************************
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 <cstddef>
#include <QUuid>
#include "qgsfillsymbollayer.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,
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.reset( new QgsFillSymbol( l1 ) );
toggleTriangularMeshRendering( false );
} // QgsMeshLayer ctor
QgsMeshLayer::~QgsMeshLayer()
{
if ( mDataProvider )
delete mDataProvider;
}
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;
}
QgsMesh *QgsMeshLayer::nativeMesh() SIP_SKIP
{
return mNativeMesh.get();
}
QgsTriangularMesh *QgsMeshLayer::triangularMesh() SIP_SKIP
{
return mTriangularMesh.get();
}
QgsSymbol *QgsMeshLayer::nativeMeshSymbol()
{
return mNativeMeshSymbol.get();
}
QgsSymbol *QgsMeshLayer::triangularMeshSymbol()
{
return mTriangularMeshSymbol.get();
}
void QgsMeshLayer::toggleTriangularMeshRendering( bool toggle )
{
if ( toggle && mTriangularMeshSymbol )
return;
if ( toggle )
{
QgsSymbolLayerList l2;
l2 << new QgsSimpleFillSymbolLayer( Qt::white, Qt::NoBrush, Qt::red, Qt::SolidLine, 0.26 );
mTriangularMeshSymbol.reset( new QgsFillSymbol( l2 ) );
}
else
{
mTriangularMeshSymbol.reset();
}
triggerRepaint();
}
void QgsMeshLayer::fillNativeMesh()
{
Q_ASSERT( !mNativeMesh );
mNativeMesh.reset( new QgsMesh() );
if ( !( dataProvider() && dataProvider()->isValid() ) )
return;
mNativeMesh->vertices.resize( dataProvider()->vertexCount() );
for ( int i = 0; i < dataProvider()->vertexCount(); ++i )
{
mNativeMesh->vertices[i] = dataProvider()->vertex( i );
}
mNativeMesh->faces.resize( dataProvider()->faceCount() );
for ( int i = 0; i < dataProvider()->faceCount(); ++i )
{
mNativeMesh->faces[i] = dataProvider()->face( i );
}
}
QgsMapLayerRenderer *QgsMeshLayer::createMapRenderer( QgsRenderContext &rendererContext )
{
if ( !mNativeMesh )
{
// lazy loading of mesh data
fillNativeMesh();
}
if ( !mTriangularMesh )
mTriangularMesh.reset( new QgsTriangularMesh() );
mTriangularMesh->update( mNativeMesh.get(), &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;
}
bool QgsMeshLayer::setDataProvider( QString const &provider )
{
Q_ASSERT( !mDataProvider ); //called from ctor
mProviderKey = provider;
QString dataSource = mDataSource;
mDataProvider = qobject_cast<QgsMeshDataProvider *>( 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

View File

@ -0,0 +1,179 @@
/***************************************************************************
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 <memory>
#include "qgis_core.h"
#include "qgsmaplayer.h"
#include "qgsrendercontext.h"
#include "qgsmeshdataprovider.h"
class QgsMapLayerRenderer;
class QgsSymbol;
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 mesh_providers Mesh data providers
*
* \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.
* 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" );
~QgsMeshLayer() override;
//! QgsMeshLayer cannot be copied.
QgsMeshLayer( const QgsMeshLayer &rhs ) = delete;
//! QgsMeshLayer cannot be copied.
QgsMeshLayer &operator=( QgsMeshLayer const &rhs ) = delete;
QgsMeshDataProvider *dataProvider() override;
const QgsMeshDataProvider *dataProvider() const override SIP_SKIP;
QgsMeshLayer *clone() const override SIP_FACTORY;
QgsRectangle extent() const override;
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 triangular mesh (nullprt before rendering)
QgsTriangularMesh *triangularMesh() SIP_SKIP;
//! Returns a line symbol used for rendering native mesh.
QgsSymbol *nativeMeshSymbol();
/**
* Returns a line symbol used for rendering of triangular (derived) mesh.
* \see toggleTriangularMeshRendering
*/
QgsSymbol *triangularMeshSymbol();
//! Toggle rendering of triangular (derived) mesh. Off by default
void toggleTriangularMeshRendering( bool toggle );
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:
void fillNativeMesh();
private:
//! 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<QgsMesh> mNativeMesh;
//! Pointer to derived mesh structure
std::unique_ptr<QgsTriangularMesh> mTriangularMesh;
//! rendering native mesh
std::unique_ptr<QgsSymbol> mNativeMeshSymbol;
//! rendering triangular mesh
std::unique_ptr<QgsSymbol> mTriangularMeshSymbol;
};
#endif //QGSMESHLAYER_H

View File

@ -0,0 +1,93 @@
/***************************************************************************
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 "qgsfield.h"
#include "qgslogger.h"
#include "qgsmeshlayer.h"
#include "qgspointxy.h"
#include "qgsrenderer.h"
#include "qgssinglesymbolrenderer.h"
#include "qgssymbol.h"
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.reset( layer->nativeMeshSymbol()->clone() );
}
if ( layer->triangularMeshSymbol() )
{
mTriangularMeshSymbol.reset( layer->triangularMeshSymbol()->clone() );
}
}
bool QgsMeshLayerRenderer::render()
{
renderMesh( mNativeMeshSymbol, mNativeMesh.faces ); // native mesh
renderMesh( mTriangularMeshSymbol, mTriangularMesh.triangles() ); // triangular mesh
return true;
}
void QgsMeshLayerRenderer::renderMesh( const std::unique_ptr<QgsSymbol> &symbol, const QVector<QgsMeshFace> &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<QgsPointXY> 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 );
}

View File

@ -0,0 +1,72 @@
/***************************************************************************
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 QgsMeshLayer;
class QgsSymbol;
#define SIP_NO_FILE
#include <memory>
#include "qgis.h"
#include "qgsmaplayerrenderer.h"
#include "qgsrendercontext.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:
//! Ctor
QgsMeshLayerRenderer( QgsMeshLayer *layer, QgsRenderContext &context );
~QgsMeshLayerRenderer() override = default;
bool render() override;
private:
void renderMesh( const std::unique_ptr<QgsSymbol> &symbol, const QVector<QgsMeshFace> &faces );
protected:
// copy from mesh layer
QgsMesh mNativeMesh;
// copy from mesh layer
QgsTriangularMesh mTriangularMesh;
// copy from mesh layer
std::unique_ptr<QgsSymbol> mNativeMeshSymbol = nullptr;
// copy from mesh layer
std::unique_ptr<QgsSymbol> mTriangularMeshSymbol = nullptr;
// rendering context
QgsRenderContext &mContext;
};
#endif // QGSMESHLAYERRENDERER_H

View File

@ -0,0 +1,169 @@
/***************************************************************************
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. *
* *
***************************************************************************/
///@cond PRIVATE
#include "qgsmeshmemorydataprovider.h"
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 TEXT_PROVIDER_KEY;
}
QString QgsMeshMemoryDataProvider::description() const
{
return TEXT_PROVIDER_DESCRIPTION;
}
QgsCoordinateReferenceSystem QgsMeshMemoryDataProvider::crs() const
{
return QgsCoordinateReferenceSystem();
}
QgsMeshMemoryDataProvider::QgsMeshMemoryDataProvider( const QString &uri )
: QgsMeshDataProvider( uri )
{
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 )
{
const QStringList sections = uri.split( QStringLiteral( "---" ), 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<QgsMeshVertex> vertices;
const QStringList verticesCoords = def.split( '\n', QString::SkipEmptyParts );
for ( int i = 0; i < verticesCoords.size(); ++i )
{
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" ),
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<QgsMeshFace> faces;
const QStringList facesVertices = def.split( '\n', QString::SkipEmptyParts );
for ( int i = 0; i < facesVertices.size(); ++i )
{
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" ),
QStringLiteral( "Mesh Memory Provider" ) ) );
return false;
}
QgsMeshFace face;
for ( int j = 0; j < vertices.size(); ++j )
{
int vertex_id = vertices[j].toInt();
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 );
}
mFaces = faces;
return true;
}
int QgsMeshMemoryDataProvider::vertexCount() const
{
return mVertices.size();
}
int QgsMeshMemoryDataProvider::faceCount() const
{
return mFaces.size();
}
QgsMeshVertex QgsMeshMemoryDataProvider::vertex( int index ) const
{
Q_ASSERT( vertexCount() > index );
return mVertices[index];
}
QgsMeshFace QgsMeshMemoryDataProvider::face( int index ) const
{
Q_ASSERT( faceCount() > index );
return mFaces[index];
}
///@endcond

View File

@ -0,0 +1,98 @@
/***************************************************************************
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 <QString>
#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;
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();
//! 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<QgsMeshVertex> mVertices;
QVector<QgsMeshFace> mFaces;
bool mIsValid = false;
};
///@endcond
#endif // QGSMESHMEMORYDATAPROVIDER_H

View File

@ -0,0 +1,98 @@
/***************************************************************************
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 "qgsrendercontext.h"
#include "qgscoordinatetransform.h"
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 )
{
const QgsMeshVertex &vertex = nativeMesh->vertices.at( 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 )
{
const QgsMeshFace &face = nativeMesh->faces.at( 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<QgsMeshVertex> &QgsTriangularMesh::vertices() const
{
return mTriangularMesh.vertices;
}
const QVector<QgsMeshFace> &QgsTriangularMesh::triangles() const
{
return mTriangularMesh.faces;
}
const QVector<int> &QgsTriangularMesh::trianglesToNativeFaces() const
{
return mTrianglesToNativeFaces;
}

View File

@ -0,0 +1,82 @@
/***************************************************************************
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 <QVector>
#include "qgis_core.h"
#include "qgsmeshdataprovider.h"
class QgsRenderContext;
//! Mesh - vertices and faces
struct CORE_EXPORT QgsMesh
{
//! vertices
QVector<QgsMeshVertex> vertices;
//! faces
QVector<QgsMeshFace> faces;
};
/**
* \ingroup core
*
* Triangular/Derived Mesh
*
* \since QGIS 3.2
*/
class CORE_EXPORT QgsTriangularMesh
{
public:
//! Ctor
QgsTriangularMesh() = default;
//! Dtor
~QgsTriangularMesh() = default;
/**
* Construct triangular mesh from layer's native mesh and context
* \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 );
/**
* 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<QgsMeshVertex> &vertices() const ;
//! Return triangles
const QVector<QgsMeshFace> &triangles() const ;
//! Return mapping between triangles and original faces
const QVector<int> &trianglesToNativeFaces() const ;
private:
// vertices: map CRS; 0-N ... native vertices, N+1 - len ... extra vertices
// faces are derived triangles
QgsMesh mTriangularMesh;
QVector<int> mTrianglesToNativeFaces; //len(mTrianglesToNativeFaces) == len(mTriangles). Mapping derived -> native
};
#endif // QGSTRIANGULARMESH_H

View File

@ -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() );

View File

@ -74,6 +74,11 @@ QIcon QgsLayerItem::iconRaster()
return QgsApplication::getThemeIcon( QStringLiteral( "/mIconRaster.svg" ) );
}
QIcon QgsLayerItem::iconMesh()
{
return QgsApplication::getThemeIcon( QStringLiteral( "/mIconPointLayer.svg" ) );
}
QIcon QgsLayerItem::iconDefault()
{
return QgsApplication::getThemeIcon( QStringLiteral( "/mIconLayer.png" ) );
@ -602,6 +607,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 +644,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 +679,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;

View File

@ -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,8 @@ 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
virtual QString layerName() const { return name(); }

View File

@ -59,6 +59,10 @@ class CORE_EXPORT QgsDataProvider : public QObject
{
sipType = sipType_QgsRasterDataProvider;
}
else if ( qobject_cast<QgsMeshDataProvider *>( sipCpp ) )
{
sipType = sipType_QgsMeshDataProvider;
}
else
{
sipType = 0;

View File

@ -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
};
/**

View File

@ -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<QgsVectorLayer *>( layer );

View File

@ -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 )

View File

@ -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;
/**

View File

@ -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 );

View File

@ -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

View File

@ -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

View File

@ -19,6 +19,8 @@
***************************************************************************/
#include "qgsbrowserdockwidget_p.h"
#include <memory>
#include <QAbstractTextDocumentLayout>
#include <QHeaderView>
#include <QTreeView>
@ -35,7 +37,7 @@
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgssettings.h"
#include "qgsmeshlayer.h"
#include <QDragEnterEvent>
@ -143,7 +145,20 @@ 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<QgsRasterLayer> layer( new QgsRasterLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) );
if ( layer )
{
if ( layer->isValid() )
{
layerCrs = layer->crs();
layerMetadata = layer->htmlMetadata();
}
}
}
else if ( type == QgsMapLayer::MeshLayer )
{
QgsDebugMsg( "creating mesh layer" );
std::unique_ptr<QgsMeshLayer> layer( new QgsMeshLayer( layerItem->uri(), layerItem->uri(), layerItem->providerKey() ) );
if ( layer )
{
if ( layer->isValid() )
@ -151,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<QgsVectorLayer> layer( new QgsVectorLayer( layerItem->uri(), layerItem->name(), layerItem->providerKey() ) );
if ( layer )
{
if ( layer->isValid() )
@ -165,7 +179,6 @@ void QgsBrowserLayerProperties::setItem( QgsDataItem *item )
layerCrs = layer->crs();
layerMetadata = layer->htmlMetadata();
}
delete layer;
}
}
else if ( type == QgsMapLayer::PluginLayer )

View File

@ -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)

View File

@ -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})

View File

@ -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 <QFileInfo>
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 );
}

View File

@ -0,0 +1,31 @@
/***************************************************************************
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"
#include <QString>
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

View File

@ -0,0 +1,133 @@
/***************************************************************************
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"
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 );
}
int QgsMdalProvider::vertexCount() const
{
if ( mMeshH )
return MDAL_M_vertexCount( mMeshH );
else
return 0;
}
int QgsMdalProvider::faceCount() const
{
if ( mMeshH )
return MDAL_M_faceCount( mMeshH );
else
return 0;
}
QgsMeshVertex QgsMdalProvider::vertex( int 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( int index ) const
{
Q_ASSERT( index < faceCount() );
QgsMeshFace face;
int n_face_vertices = MDAL_M_faceVerticesCountAt( mMeshH, index );
for ( int 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()
{
}

View File

@ -0,0 +1,63 @@
/***************************************************************************
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 <QString>
#include <mdal.h>
#include "qgscoordinatereferencesystem.h"
#include "qgsmeshdataprovider.h"
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;
int vertexCount() const override;
int faceCount() const override;
QgsMeshVertex vertex( int index ) const override;
QgsMeshFace face( int index ) const override;
private:
MeshH mMeshH;
};
#endif

View File

@ -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" ) )
{
// unsupported
hasError = true;
continue;
}
else
{
srcLayer = dropUri.rasterLayer( owner, error );

View File

@ -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
)
@ -188,7 +189,9 @@ SET(TESTS
testqgsvectorlayerjoinbuffer.cpp
testqgsvectorlayer.cpp
testziplayer.cpp
)
testqgsmeshlayer.cpp
testqgsmeshlayerrenderer.cpp
)
IF(WITH_QTWEBKIT)
SET(TESTS ${TESTS}

View File

@ -0,0 +1,124 @@
/***************************************************************************
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 <QObject>
#include <QString>
//qgis includes...
#include "qgsmaplayer.h"
#include "qgsmeshlayer.h"
#include "qgsapplication.h"
#include "qgsproviderregistry.h"
#include "qgsproject.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<QgsMapLayer *>() << 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<QgsMapLayer *>() << mMdalLayer );
}
void TestQgsMeshLayer::cleanupTestCase()
{
QgsApplication::exitQgis();
}
void TestQgsMeshLayer::test_data_provider()
{
QList<const QgsMeshDataProvider *> 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( 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( 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"

View File

@ -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 <QObject>
#include <QString>
#include <QLabel>
#include <QStringList>
#include <QApplication>
#include <QFileInfo>
#include <QDir>
#include <QDesktopServices>
//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<QgsMapLayer *>() << mMemoryLayer );
mMapSettings->setLayers(
QList<QgsMapLayer *>() << 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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

View File

@ -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

View File

@ -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