Skip to content
Permalink
Browse files

Merge pull request #6820 from PeterPetrik/QgsMeshLayer_1_mesh

[FEATURE] QgsMeshLayer part 1: Reading raw mesh
  • Loading branch information
wonder-sk committed Apr 25, 2018
2 parents ae53f26 + 5b1918f commit ade216d02d686f1fa704ce891b700d59b22a2538
Showing with 2,897 additions and 12 deletions.
  1. +10 −0 CMakeLists.txt
  2. +32 −0 cmake/FindMDAL.cmake
  3. +1 −0 doc/CMakeLists.txt
  4. +89 −0 external/mdal/api/mdal.h
  5. +204 −0 external/mdal/frmts/mdal_2dm.cpp
  6. +28 −0 external/mdal/frmts/mdal_2dm.hpp
  7. +100 −0 external/mdal/mdal.cpp
  8. +31 −0 external/mdal/mdal_defines.hpp
  9. +13 −0 external/mdal/mdal_loader.cpp
  10. +24 −0 external/mdal/mdal_loader.hpp
  11. +56 −0 external/mdal/mdal_utils.cpp
  12. +34 −0 external/mdal/mdal_utils.hpp
  13. +78 −0 images/themes/default/mIconMeshLayer.svg
  14. +1 −0 python/CMakeLists.txt
  15. +2 −0 python/core/core_auto.sip
  16. +99 −0 python/core/mesh/qgsmeshdataprovider.sip.in
  17. +138 −0 python/core/mesh/qgsmeshlayer.sip.in
  18. +28 −0 python/core/mesh/qgsnativemesh.sip.in
  19. +6 −1 python/core/qgsdataitem.sip.in
  20. +4 −0 python/core/qgsdataprovider.sip.in
  21. +5 −1 python/core/qgsmaplayer.sip.in
  22. +8 −0 python/core/qgsmimedatautils.sip.in
  23. +1 −0 python/core/qgsproviderregistry.sip.in
  24. +6 −0 src/analysis/processing/qgsalgorithmpackage.cpp
  25. +1 −0 src/app/CMakeLists.txt
  26. +6 −0 src/app/qgisapp.cpp
  27. +14 −0 src/core/CMakeLists.txt
  28. +2 −0 src/core/expression/qgsexpressionfunction.cpp
  29. +40 −0 src/core/mesh/qgsmeshdataprovider.cpp
  30. +98 −0 src/core/mesh/qgsmeshdataprovider.h
  31. +214 −0 src/core/mesh/qgsmeshlayer.cpp
  32. +179 −0 src/core/mesh/qgsmeshlayer.h
  33. +93 −0 src/core/mesh/qgsmeshlayerrenderer.cpp
  34. +72 −0 src/core/mesh/qgsmeshlayerrenderer.h
  35. +169 −0 src/core/mesh/qgsmeshmemorydataprovider.cpp
  36. +98 −0 src/core/mesh/qgsmeshmemorydataprovider.h
  37. +98 −0 src/core/mesh/qgstriangularmesh.cpp
  38. +82 −0 src/core/mesh/qgstriangularmesh.h
  39. +2 −0 src/core/processing/qgsprocessingutils.cpp
  40. +12 −0 src/core/qgsdataitem.cpp
  41. +4 −1 src/core/qgsdataitem.h
  42. +4 −0 src/core/qgsdataprovider.h
  43. +5 −1 src/core/qgsmaplayer.h
  44. +5 −0 src/core/qgsmaplayermodel.cpp
  45. +13 −0 src/core/qgsmimedatautils.cpp
  46. +9 −1 src/core/qgsmimedatautils.h
  47. +2 −1 src/core/qgsproviderregistry.cpp
  48. +1 −0 src/core/qgsproviderregistry.h
  49. +1 −0 src/gui/CMakeLists.txt
  50. +18 −5 src/gui/qgsbrowserdockwidget_p.cpp
  51. +1 −0 src/providers/CMakeLists.txt
  52. +80 −0 src/providers/mdal/CMakeLists.txt
  53. +76 −0 src/providers/mdal/qgsmdaldataitems.cpp
  54. +31 −0 src/providers/mdal/qgsmdaldataitems.h
  55. +133 −0 src/providers/mdal/qgsmdalprovider.cpp
  56. +63 −0 src/providers/mdal/qgsmdalprovider.h
  57. +6 −0 src/providers/ogr/qgsgeopackagedataitems.cpp
  58. +4 −1 tests/src/core/CMakeLists.txt
  59. +124 −0 tests/src/core/testqgsmeshlayer.cpp
  60. +123 −0 tests/src/core/testqgsmeshlayerrenderer.cpp
  61. BIN ...rol_images/mesh/expected_quad_and_triangle_native_mesh/expected_quad_and_triangle_native_mesh.png
  62. BIN ...es/mesh/expected_quad_and_triangle_triangular_mesh/expected_quad_and_triangle_triangular_mesh.png
  63. +8 −0 tests/testdata/mesh/quad_and_triangle.2dm
  64. +8 −0 tests/testdata/mesh/quad_and_triangle.txt
@@ -95,6 +95,12 @@ IF(WITH_CORE)
SET (HAVE_GUI TRUE) # used in qgsconfig.h
ENDIF()

# try to configure and build MDAL support
SET (WITH_INTERNAL_MDAL TRUE CACHE BOOL "Determines whether MDAL support should be built")
IF (NOT WITH_INTERNAL_MDAL)
SET (MDAL_PREFIX "" CACHE PATH "Path to MDAL base directory")
ENDIF (NOT WITH_INTERNAL_MDAL)

# try to configure and build POSTGRESQL support
SET (WITH_POSTGRESQL TRUE CACHE BOOL "Determines whether POSTGRESQL support should be built")
IF (WITH_POSTGRESQL)
@@ -258,6 +264,10 @@ IF(WITH_CORE)
FIND_PACKAGE(Postgres) # PostgreSQL provider
ENDIF (WITH_POSTGRESQL)

IF (NOT WITH_INTERNAL_MDAL)
FIND_PACKAGE(MDAL REQUIRED) # MDAL provider
ENDIF (NOT WITH_INTERNAL_MDAL)

FIND_PACKAGE(SpatiaLite REQUIRED)

IF (NOT PROJ_FOUND OR NOT GEOS_FOUND OR NOT GDAL_FOUND)
@@ -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} )
@@ -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
@@ -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
@@ -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;
}
@@ -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

0 comments on commit ade216d

Please sign in to comment.
You can’t perform that action at this time.