[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.
This commit is contained in:
Peter Petrik 2018-04-06 16:11:50 +02:00
parent 3b59ccc7ce
commit 50422a1165
62 changed files with 2943 additions and 8 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} )

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

@ -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 <stddef.h>
/* 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

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( 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<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 = 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 = 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 = 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 = 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 = 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( Status *status );
private:
std::string mMeshFile;
};
} // namespace MDAL
#endif //MDAL_2DM_HPP

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

@ -0,0 +1,86 @@
#include <string>
#include <cassert>
#include <stddef.h>
#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];
}

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

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

@ -433,3 +433,6 @@
%Include qgsuserprofilemanager.sip
%Include symbology/qgsarrowsymbollayer.sip
%Include qgsuserprofile.sip
%Include mesh/qgsmeshdataprovider.sip
%Include mesh/qgsmeshlayer.sip

View File

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

View File

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

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

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

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

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

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

View File

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

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

View File

@ -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 <cstddef>
#include "qgis_core.h"
#include "qgis.h"
#include "qgspoint.h"
#include "qgsrectangle.h"
#include "qgsdataprovider.h"
#include "qgscoordinatereferencesystem.h"
#include <QVector>
#include <QHash>
#include <QString>
#include <QVariant>
typedef QgsPoint QgsMeshVertex; //xyz coords of vertex
typedef QVector<size_t> 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

View File

@ -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 <QUuid>
#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<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,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 <QMap>
#include <QSet>
#include <QSharedPointer>
#include <QList>
#include <QStringList>
#include <QFont>
#include <QMutex>
#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

View File

@ -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 <QPicture>
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<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,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 <QList>
#include <QPainter>
#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<QgsMeshFace> &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

View File

@ -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 <QFile>
#include <QJsonDocument>
#include <limits>
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<QgsMeshVertex> 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<QgsMeshFace> 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];
}

View File

@ -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 <cstddef>
#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;
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<QgsMeshVertex> mVertices;
QVector<QgsMeshFace> mFaces;
bool mIsValid;
};
///@endcond
#endif // QGSMESHMEMORYDATAPROVIDER_H

View File

@ -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<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,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 <QList>
#include <QPainter>
#include <QVector>
#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<QgsMeshVertex> vertices;
//! faces
QVector<QgsMeshFace> 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<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,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;

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,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(); }

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

@ -35,7 +35,7 @@
#include "qgsvectorlayer.h"
#include "qgsproject.h"
#include "qgssettings.h"
#include "qgsmeshlayer.h"
#include <QDragEnterEvent>
@ -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" );

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

View File

@ -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 <QFile>
#include <QJsonDocument>
#include <limits>
#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()
{
}

View File

@ -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 <cstddef>
#include "qgscoordinatereferencesystem.h"
#include "qgsdataitem.h"
#include "qgsmeshdataprovider.h"
#include "qgsrectangle.h"
#include <QString>
#include <QStringList>
#include <QDomElement>
#include <QMap>
#include <QVector>
#include <mdal.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;
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

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

View File

@ -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 <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"
/**
* \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( ( 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"

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