From e878ff2141ee4a082d8876d71947bac81fcbf2ef Mon Sep 17 00:00:00 2001 From: dengwirda Date: Fri, 15 Aug 2025 13:16:37 +1000 Subject: [PATCH 1/4] Add support to read *.geojson files into jigsaw_msh_t --- jigsawpy/__init__.py | 3 +- jigsawpy/parse/loadgeo.py | 184 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 jigsawpy/parse/loadgeo.py diff --git a/jigsawpy/__init__.py b/jigsawpy/__init__.py index 4fb3d5d..574034a 100644 --- a/jigsawpy/__init__.py +++ b/jigsawpy/__init__.py @@ -13,7 +13,7 @@ * JIGSAW: Interface to the JIGSAW meshing library. ------------------------------------------------------------ * - * Last updated: 10 Aug., 2025 + * Last updated: 13 Aug., 2025 * * Copyright 2019-2025 * Darren Engwirda @@ -92,6 +92,7 @@ from jigsawpy.parse.saveoff import saveoff from jigsawpy.parse.savewav import savewav from jigsawpy.parse.savevtk import savevtk +from jigsawpy.parse.loadgeo import loadgeo class cmd: diff --git a/jigsawpy/parse/loadgeo.py b/jigsawpy/parse/loadgeo.py new file mode 100644 index 0000000..5723ceb --- /dev/null +++ b/jigsawpy/parse/loadgeo.py @@ -0,0 +1,184 @@ + +import numpy as np +import json +import argparse + +from jigsawpy import jigsaw_msh_t, savemsh + + +def linegeo(line, nset, eset, nobj, last): + """ + LINEGEO: read a geoJSON line into a jigsaw msh_t object. + + """ + + npts = len(line) - 0 + + if (npts > 0): +#----------------------------------------- read LINE dataset + temp = jigsaw_msh_t() + temp.vert2 = np.zeros( + (npts + 0), dtype=temp.VERT2_t) + temp.edge2 = np.zeros( + (npts - 1), dtype=temp.EDGE2_t) + + temp.edge2["IDtag"][:] = nobj + + nobj = nobj + 1 + + indx = np.arange(0, npts - 1) + last + + last = last + npts + + temp.vert2["coord"] = line[+0::] + + temp.edge2["index"][:, 0] = indx + 0 + temp.edge2["index"][:, 1] = indx + 1 + + nset.append(temp.vert2) + eset.append(temp.edge2) + + return nobj, last + + +def polygeo(loop, nset, eset, nobj, last): + """ + POLYGEO: read a geoJSON poly into a jigsaw msh_t object. + + """ + + npts = len(loop) - 1 + + if (npts > +0): +#----------------------------------------- read LOOP dataset + temp = jigsaw_msh_t() + temp.vert2 = np.zeros( + (npts + 0), dtype=temp.VERT2_t) + temp.edge2 = np.zeros( + (npts - 0), dtype=temp.EDGE2_t) + + temp.edge2["IDtag"][:] = nobj + + nobj = nobj + 1 + + indx = np.arange(0, npts - 1) + last + + itop = last + npts - 1 + + idx1 = np.full( + npts, itop, dtype=np.int32) + idx1[:-1] = indx + 0 + + idx2 = np.full( + npts, last, dtype=np.int32) + idx2[:-1] = indx + 1 + + last = last + npts + + temp.vert2["coord"] = loop[:-1:] + + temp.edge2["index"][:, 0] = idx1 + temp.edge2["index"][:, 1] = idx2 + + nset.append(temp.vert2) + eset.append(temp.edge2) + + return nobj, last + + +def readgeo(geom, nset, eset, nobj, last): + """ + READGEO: read a geoJSON part into a jigsaw msh_t object. + + """ + + if (geom["type"] == "LineString"): + + line = geom["coordinates"] + + nobj, last = linegeo( + line, nset, eset, nobj, last) + + elif (geom["type"] == "MultiLineString"): + + for line in geom["coordinates"]: + + nobj, last = linegeo( + line, nset, eset, nobj, last) + + elif (geom["type"] == "Polygon"): + + for loop in geom["coordinates"]: + + nobj, last = polygeo( + loop, nset, eset, nobj, last) + + elif (geom["type"] == "MultiPolygon"): + + for poly in geom["coordinates"]: + for loop in poly: + + nobj, last = polygeo( + loop, nset, eset, nobj, last) + + return nobj, last + + +def loadgeo(name, mesh): + """ + LOADGEO: load a geoJSON file into a jigsaw msh_t object. + + """ + + if (not isinstance(name, str)): + raise Exception("Incorrect type: NAME.") + + if (not isinstance(mesh, jigsaw_msh_t)): + raise Exception("Incorrect type: MESH.") + + nobj = +0; last = +0; nset = []; eset = [] + + with open(name) as f: geoj = json.load(f) + + for feat in geoj["features"]: + + geom = (feat["geometry"]) + + if (geom["type"] != "GeometryCollection"): + + nobj, last = readgeo( + geom, nset, eset, nobj, last) + + else: + + for next in geom["geometries"]: + + nobj, last = readgeo( + next, nset, eset, nobj, last) + + mesh.vert2 = np.concatenate(nset, axis=0) + mesh.edge2 = np.concatenate(eset, axis=0) + + return + + +if (__name__ == "__main__"): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument( + "--src-file", dest="src_file", type=str, + required=True, help="src geoJSON file to load") + + parser.add_argument( + "--dst-file", dest="dst_file", type=str, + required=True, help="Output .msh file to save") + + args = parser.parse_args() + + mesh = jigsaw_msh_t() + + loadgeo(name=args.src_file, mesh=mesh) + savemsh(name=args.dst_file, mesh=mesh) + From b2ad5984a32d02b176c7b761e7bdf328663caa22 Mon Sep 17 00:00:00 2001 From: dengwirda Date: Fri, 15 Aug 2025 13:19:12 +1000 Subject: [PATCH 2/4] Simplify README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 03f1411..5ea3c00 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,6 @@ See `jigsawpy` for a description of the various functions available. project.py - apply cartographic projection operators to mesh obj. bisect.py - refine a mesh obj. via bisection. - extrude.py - create a mesh obj. via extrusion. ### `Example Problems` From dea8a0d0d71d26fb3c8e121bca5609ff0f889c11 Mon Sep 17 00:00:00 2001 From: dengwirda Date: Fri, 15 Aug 2025 13:22:50 +1000 Subject: [PATCH 3/4] Squashed 'external/jigsaw/' changes from a3afb93..d7859d7 d7859d7 Merge pull request #57 from dengwirda/cmake-paths e2d9a88 Use OPENMP_USER_PATH to set OpenMP__INCLUDE_DIR a4c9e44 Allow user-defined paths to NetCDF + OpenMP libs git-subtree-dir: external/jigsaw git-subtree-split: d7859d7e1b71e4ee1a33663a617ada9ffb4777b8 --- src/CMakeLists.txt | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 16922f2..abfaf75 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,9 +4,15 @@ function (cfg_compile_options OPT CFG) endfunction () include (CheckCXXCompilerFlag) +include (CheckIPOSupported) set (CMAKE_CXX_STANDARD 17) -set (CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) + +check_ipo_supported (RESULT IPO OUTPUT IPO_ERR LANGUAGES C CXX) + +if (IPO) + set (CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE) +endif () if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR @@ -59,7 +65,13 @@ endif () # try to find netcdf support -find_library (NETCDF_LIBRARY NAMES netcdf) +if (DEFINED NETCDF_USER_PATH) + message (STATUS "Testing user NetCDF path") + find_library (NETCDF_LIBRARY NAMES netcdf + PATHS ${NETCDF_USER_PATH} NO_DEFAULT_PATH) +else () + find_library (NETCDF_LIBRARY NAMES netcdf) +endif () if (NETCDF_LIBRARY) message (STATUS "NetCDF library found") @@ -70,7 +82,14 @@ endif () # try to find openmp support -find_package (OpenMP) +if (DEFINED OPENMP_USER_PATH) + message (STATUS "Testing user OpenMP path") + set ( OpenMP_C_INCLUDE_DIR ${OPENMP_USER_PATH}) + set (OpenMP_CXX_INCLUDE_DIR ${OPENMP_USER_PATH}) + find_package (OpenMP) +else () + find_package (OpenMP) +endif () if (OpenMP_CXX_FOUND AND (OpenMP_CXX_VERSION LESS 3)) set (OpenMP_CXX_FOUND FALSE) @@ -80,6 +99,7 @@ endif () if (OpenMP_CXX_FOUND) message (STATUS "OpenMP library found") message (STATUS "OpenMP inc. lib: ${OpenMP_CXX_LIB_NAMES}") + message (STATUS "OpenMP inc. lib: ${OpenMP_CXX_LIBRARIES}") else () message (STATUS "OpenMP library not found") endif () From 4e56419ce0bc8d377a69774194d12f4a40aa738c Mon Sep 17 00:00:00 2001 From: dengwirda Date: Fri, 15 Aug 2025 13:24:37 +1000 Subject: [PATCH 4/4] Add support for cmd-line cmake params to control jigsaw build --- build.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/build.py b/build.py index 2131bfc..1bebdce 100644 --- a/build.py +++ b/build.py @@ -2,10 +2,14 @@ import os import subprocess import shutil +import argparse HERE = os.path.abspath(os.path.dirname(__file__)) -def build_external(): +def build_external(build_type="Release", + netcdf_user_path=None, + openmp_user_path=None + ): #-- The actual cmake-based build steps for JIGSAW cwd_pointer = os.getcwd() @@ -41,9 +45,18 @@ def build_external(): os.chdir(builds_path) config_call = [ - "cmake", - "..", "-DCMAKE_BUILD_TYPE=Release"] + "cmake", "..", + "-DCMAKE_BUILD_TYPE=" + build_type] + if (netcdf_user_path is not None): + config_call+= [ + "-DNETCDF_USER_PATH="+netcdf_user_path] + + if (openmp_user_path is not None): + config_call+= [ + "-DOPENMP_USER_PATH="+openmp_user_path] + + print(config_call) subprocess.run(config_call, check=True) print("cmake compile for jigsaw...") @@ -51,7 +64,7 @@ def build_external(): try: compilecall = [ "cmake", "--build", ".", - "--config", "Release", + "--config", build_type, "--target", "install", "--parallel", "4" ] @@ -61,7 +74,7 @@ def build_external(): except: compilecall = [ "cmake", "--build", ".", - "--config", "Release", + "--config", build_type, "--target", "install" ] subprocess.run( @@ -78,5 +91,32 @@ def build_external(): shutil.rmtree(builds_path) -if (__name__ == "__main__"): build_external() +if (__name__ == "__main__"): + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawTextHelpFormatter) + + parser.add_argument( + "--cmake-build-type", dest="cmake_build_type", + required=False, + type=str, default="Release", + help="Build JIGSAW in {Release}, Debug mode.") + + parser.add_argument( + "--netcdf-user-path", dest="netcdf_user_path", + required=False, + type=str, default=None, + help="(Optional) dir. containing netcdf lib.") + + parser.add_argument( + "--openmp-user-path", dest="openmp_user_path", + required=False, + type=str, default=None, + help="(Optional) dir. containing openmp lib.") + + args = parser.parse_args() + + build_external(args.cmake_build_type, + args.netcdf_user_path, + args.openmp_user_path)