From 5f2156a48b2a82a67ee31a3bf9b6110b50edcf8c Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 21 Sep 2020 16:39:07 -1000 Subject: [PATCH 01/45] Initial upload of files and setting of CMake par guesses for building gmtmex First goal is to have this no break regular GMT when GMT_BUILD_GMTMEX is not set. --- cmake/ConfigUserAdvancedTemplate.cmake | 24 + src/gmtmex/CMakeLists.txt | 37 + src/gmtmex/README.gmtmex | 9 + src/gmtmex/gmt.m | 223 ++++ src/gmtmex/gmtmex.c | 415 ++++++++ src/gmtmex/gmtmex.h | 182 ++++ src/gmtmex/gmtmex_parser.c | 1345 ++++++++++++++++++++++++ 7 files changed, 2235 insertions(+) create mode 100644 src/gmtmex/CMakeLists.txt create mode 100644 src/gmtmex/README.gmtmex create mode 100644 src/gmtmex/gmt.m create mode 100644 src/gmtmex/gmtmex.c create mode 100644 src/gmtmex/gmtmex.h create mode 100644 src/gmtmex/gmtmex_parser.c diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index 3cb95b25c11..bcda2661149 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -57,6 +57,9 @@ #set (GMT_EXCLUDE_BLAS TRUE) #set (GMT_EXCLUDE_ZLIB TRUE) +# Include special gmtmex supplement for the GMT/MEX toolbox [which requires MATLAB] +#set (GMT_BUILD_GMTMEX TRUE) + # ============================================================================ # Advanced configuration begins here. Usually it is not necessary to edit any # settings below. You should know what you are doing if you do though. Note: @@ -247,6 +250,27 @@ # set (CMAKE_C_FLAGS_RELEASE "-ggdb3 -O2 -Wuninitialized") # check uninitialized variables #endif (HAVE_OPENMP) +# +# Building the GMT/MEX Toolbox +# +# Please export an environmental variable MATLAB that points to your Matlab application + +if (GMT_BUILD_GMTMEX) + set (SUPPL_EXTRA_DIRS ${SUPPL_EXTRA_DIRS} gmtmex) + if (APPLE) + set (MATLAB "$ENV{MATLAB}") + set (MEX_EXT "mexmaci64") + set (MATLAB_MEX maci64") + set (MATLAB_FLAGS="-g") + add_definitions(-DGMT_MATLAB) + #set (MEX_BLD "xcrun clang -undefined error -arch x86_64 -bundle -DGMT_MATLAB ${MATLAB_FLAGS}) + set (MEX_INC "-I${MATLAB}/extern/include") + set (MEX_LIB "-L${MATLAB}/bin/${MATLAB_MEX} -lmx -lmex") + include_directories (${MEX_INC}) + list (APPEND GMT_OPTIONAL_LIBRARIES ${MEX_LIB}) + endif (APPLE) +endif (GMT_BUILD_GMTMEX) + # # System specific tweaks # diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt new file mode 100644 index 00000000000..53d047e0ca1 --- /dev/null +++ b/src/gmtmex/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# +# Copyright (c) 1991-2020 by the GMT Team (https://www.generic-mapping-tools.org/team.html) +# See LICENSE.TXT file for copying and redistribution conditions. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; version 3 or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# Contact info: www.generic-mapping-tools.org +#------------------------------------------------------------------------------- +# +# CMake settings for supplemental package: +# +# SUPPL_NAME: name of the supplemental package +# SUPPL_HEADERS: header files. Will be installed if BUILD_DEVELOPER is TRUE +# SUPPL_PROGS_SRCS: list of C source codes for supplemental modules +# SUPPL_LIB_SRCS: list of C source codes for supplemental library +# SUPPL_EXAMPLE_FILES: README and other example files +# SUPPL_EXAMPLE_PROGS: Example scripts +# + +set (SUPPL_NAME gmtmex) +set (SUPPL_PROGS_SRCS gmtmex.c gmtmex_parser.c) +set (SUPPL_HEADERS gmtmex_parser.h) +set (SUPPL_LIB_SRCS ${SUPPL_PROGS_SRCS}) +set (SUPPL_EXAMPLE_FILES README.gmtmex) + +# install gmt/mex external function interface and driver shell script +install (PROGRAMS gmt.m gmtmex.${MEX_EXT} + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) diff --git a/src/gmtmex/README.gmtmex b/src/gmtmex/README.gmtmex new file mode 100644 index 00000000000..839932f09ed --- /dev/null +++ b/src/gmtmex/README.gmtmex @@ -0,0 +1,9 @@ +# +Distributed under the GNU Lesser Public License; see file +LICENSE.TXT in main GMT directory. + +GMT 5 Release introduced a full set of Matlab interface functions +for all GMT modules. GMT 6 continued this work, and GMT 6.1 made +some internal changes, necessitating GMTMEX 2.0.0 + +See documentation for usage. diff --git a/src/gmtmex/gmt.m b/src/gmtmex/gmt.m new file mode 100644 index 00000000000..0741f489a98 --- /dev/null +++ b/src/gmtmex/gmt.m @@ -0,0 +1,223 @@ +function varargout = gmt(cmd, varargin) +% Helper function to call the gmtmex MEX function + + if (nargin == 0) + fprintf(sprintf('\n\t\tGMT - The Generic Mapping Tools, Version 6.1 API\n')) + fprintf(sprintf('Copyright 1991-2020 The GMT Team (https://www.generic-mapping-tools.org/team.html\n\n')) + + fprintf(sprintf('Usage:\tTo call a GMT module:\n\t output = gmt (''module_name'', ''options'', numeric_input)\n\n')) + fprintf(sprintf(['\tTo create a Grid structure from a 2-D Z array and a 1x9 header vector:\n\t' ... + ' G = gmt (''wrapgrid'', Z, head)\n' ... + '\theader is a vector with [x_min x_max, y_min y_max z_min z_max reg x_inc y_inc]\n\n'])) + fprintf(sprintf(['\tTo create an Image structure from a 2-D img array and a 1x9 header vector:\n\t' ... + ' I = gmt (''wrapimage'', img, header [, cmap])\n' ... + '\theader is a vector with [x_min x_max, y_min y_max z_min z_max reg x_inc y_inc].\n' ... + '\tcmap is an optional color palette structure or a Matlab Mx3 cmap array (not yet).\n\n'])) + fprintf(sprintf(['\tTo create a structure for a multi-segment dataset:\n\t' ... + ' D = gmt (''wrapseg'', {[1 0; 1 1], [5 5; 56]}, {''Seg1'', ''Seg2''})\n\n'])) + fprintf(sprintf(['\tTo create a structure with numeric and text good for use in pstext:\n\t' ... + ' R = gmt (''record'', [1 0; 1 1], {''Text1'', ''Text2''})\n\n'])) + fprintf(sprintf(['\tTo join two color palette structures:\n\t' ... + ' cpt = gmt (''catcpt'', cpt1, cpt2)\n\n'])) + fprintf(sprintf(['\tTo merge all data segments from an array of Data structures:\n\t' ... + ' all = gmt (''catseg'', segments[, 1])\n\t' ... + 'The optional 2nd argument will insert a NaN-record at the start of each segment.\n'])) + return + end + + if (strcmp(cmd,'wrapgrid') || strcmp(cmd,'wrapimage') || strcmp(cmd,'catcpt') || strncmp(cmd,'catseg',6) || ... + strcmp(cmd,'wrapseg') || strcmp(cmd,'record')) + [varargout{1:nargout}] = feval (cmd, varargin{:}); + else + [varargout{1:nargout}] = gmtmex (cmd, varargin{:}); + end + +% ------------------------------------------------------------------------------------------------- +function all = catseg(varargin) + all = catsegment(varargin{:}); + +function all = catsegment(A, header) +% MERGE Combine all segment arrays to a single array +% all = catsegment (A, opt) +% +% Concatenate all data segment arrays in the structures A +% into a single array. If the optional argument opt is given +% the we start each segment with a NaN record. + + n_segments = length(A); n = 0; + [nr, nc] = size (A(1).data); % Get # columns from first segment + for k = 1:n_segments % Count total rows + n = n + length(A(k).data); + end + if nargin == 2 % Need to add a NaN-record per segment + all = zeros (n+n_segments, nc); + else + all = zeros (n, nc); + end + n = 1; + for k = 1:n_segments + [nr, nc] = size (A(k).data); + if nargin == 2 % Add NaN-record + all(n,:) = NaN; + n = n + 1; + end + all(n:(n+nr-1),:) = A(k).data; + n = n + nr; + end + +% ------------------------------------------------------------------------------------------------- +function cpt = catcpt(cpt1, cpt2) +% Join two CPT1 and CPT2 color palette structures. +% Note, the two palettes must be continuous across its common border. No testing on that is done here. +% NOT COMPLETE. NEEDS THE CPT, MODEL & COMMENT FIELDS + + if (nargin ~= 2) + error(' Must provide 2 input arguments.') + elseif (cpt1.depth ~= cpt2.depth) + error(' Cannot join two palettes that have different bit depths.') + end + if (size(cpt1.colormap,1) ~= size(cpt1.range)) + % A continuous palette so the join would have one color in excess. We could average + % the top cpt1 color and bottom cpt2 but that would blur the transition. + %cpt.colormap = [cpt1.colormap(1:end-1,:); (cpt1.colormap(end,:)+cpt2.colormap(1,:))/2; cpt2.colormap(2:end,:)]; + cpt.colormap = [cpt1.colormap(1:end-1,:); cpt2.colormap]; + cpt.alpha = [cpt1.alpha(1:end-1,:); cpt2.alpha]; + else + cpt.colormap = [cpt1.colormap; cpt2.colormap]; + cpt.alpha = [cpt1.alpha; cpt2.alpha]; + end + cpt.range = [cpt1.range; cpt2.range]; + cpt.minmax = [cpt1.minmax(1) cpt2.minmax(2)]; + cpt.bfn = cpt1.bfn; % Just keep the first one + cpt.depth = cpt1.depth; + +% ------------------------------------------------------------------------------------------------- +function G = wrapgrid(Z, head) +% Fill the Grid struct used in gmtmex. HEAD is the old 1x9 header vector. + + if (nargin ~= 2) + error(' Must provide 2 input arguments.') + elseif (size(Z,1) < 2 || size(Z,2) < 2) + error(' First argument must be a decent 2D array.') + elseif (any(size(head) ~= [1 9])) + error(' Second argument must be a 1x9 header vector.') + end + + if (~isa(head, 'double')), head = double(head); end + G.proj4 = ''; + G.wkt = ''; + G.range = head(1:6); + G.inc = head(8:9); + G.registration = head(7); + G.nodata = NaN; + G.title = ''; + G.comment = ''; + G.command = ''; + G.datatype = 'float32'; + G.x = linspace(head(1), head(2), size(Z,2)); + G.y = linspace(head(3), head(4), size(Z,1)); + G.z = Z; + G.x_unit = ''; + G.y_unit = ''; + G.z_unit = ''; + +% ------------------------------------------------------------------------------------------------- +function I = wrapimage(img, head, cmap) +% Fill the Image struct used in gmtmex. HEAD is the old 1x9 header vector. + + if (nargin < 2) + error(' Must provide at least 2 input arguments.') + end + if (size(img,1) < 2 || size(img,2) < 2) + error(' First argument must be a decent 2D image array.') + elseif (any(size(head) ~= [1 9])) + error(' Second argument must be a 1x9 header vector.') + end + + if (~isa(head, 'double')), head = double(head); end + I.proj4 = ''; + I.wkt = ''; + I.range = head(1:6); + I.inc = head(8:9); + I.nodata = NaN; + I.registration = head(7); + I.title = ''; + I.comment = ''; + I.command = ''; + I.datatype = 'uint8'; + I.x = linspace(head(1), head(2), size(img,2)); + I.y = linspace(head(3), head(4), size(img,1)); + I.image = img; + I.x_unit = ''; + I.y_unit = ''; + I.z_unit = ''; + if (nargin == 3) + if (~isa(cmap, 'struct')) + % TODO: write a function that converts from Mx3 Matlab cmap to color struct used in MEX + error('The third argin (cmap) must be a colormap struct.') + end + I.colormap = cmap; + else + I.colormap = []; + end + if (size(img,3) == 4) % Not obvious that this is the best choice + I.alpha = img(:,:,4); + else + I.alpha = []; + end + I.layout = 'TCBa'; + +% ------------------------------------------------------------------------------------------------- +function D = wrapseg(in, headers, text, comm, proj_s, wkt_s) +% Fill the Dataset struct used in gmtmex. +% IN -> A cell array of matrices where each matrix is a segment. +% HEADERS, TEXT, COMM, PROJ_S & WKT_S are all optional and can either be empty, char strings or cell +% arrays of text with the exact same size of input data IN which must also be a cell array (numeric). +% HEADERS -> multi-segment header info (a cell array of strings o a single text line) +% TEXT -> any text that may follow numeric columns (a cell array of strings or a single text line) +% COMM -> Commentary describing this datase (a text string) +% PROJ_S -> A text string with a PROJ4 projection string +% WKT_S -> A text string with a WKT projection string + + if (nargin == 0), error('Wrong number of input arguments'), end + if (~isa(in, 'cell')), error('Only cell arrays of matrices are accepted in first argument'), end + if (~exist('headers', 'var') || isempty(headers)), headers = cell(size(in)); end + if (~exist('text', 'var') || isempty(text)), text = cell(size(in)); end + if (~exist('comm', 'var') || isempty(comm)), comm = cell(size(in)); end + if (~exist('proj_s', 'var') || isempty(proj_s)), proj_s = cell(size(in)); end + if (~exist('wkt_s', 'var') || isempty(wkt_s)), wkt_s = cell(size(in)); end + if (isa(comm, 'char')) % If allocated above this test is false + comment = cell(size(in)); comment{1} = comm; + else + comment = comm; + end + if (isa(proj_s, 'char')) + proj4 = cell(size(in)); proj4{1} = proj_s; + else + proj4 = proj_s; + end + if (isa(wkt_s, 'char')) + wkt = cell(size(in)); wkt{1} = wkt_s; + else + wkt = wkt_s; + end + if (~isequal(size(in), size(headers)) || ~isequal(size(headers), size(text)) || ~isequal(size(text), size(comm)) || ... + ~isequal(size(comm), size(proj_s)) || ~isequal(size(proj_s), size(wkt_s))) + error('All cell arrays must be of the same size/shape. Can''t mix row and column cell vectors') + end + D = struct('data',in, 'header',headers, 'text',text, 'comment',comment, 'proj4',proj4, 'wkt',wkt); + +% ------------------------------------------------------------------------------------------------- +function R = record(data, text) +% Simplifies creating one or more GMT records on the fly + R.data = data; + if (ischar(text)) + R.text = text; + else + [n,m] = size(text); + if (n == 1) + R.text = text'; + else + R.text = text; + end + end diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c new file mode 100644 index 00000000000..1a15c0cacb3 --- /dev/null +++ b/src/gmtmex/gmtmex.c @@ -0,0 +1,415 @@ +/*-------------------------------------------------------------------- + * $Id$ + * + * Copyright (c) 2015-2020 by P. Wessel and J. Luis + * See LICENSE.TXT file for copying and redistribution conditions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3 or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * Contact info: gmt.soest.hawaii.edu + *--------------------------------------------------------------------*/ +/* + * This is the MATLAB/Octave(mex) GMT application, which can do the following: + * 1) Create a new session and optionally return the API pointer. We provide for + * storing the pointer as a global variable (persistent) between calls. + * 2) Destroy a GMT session, either given the API pointer or by fetching it from + * the global (persistent) variable. + * 3) Call any of the GMT modules while passing data in and out of GMT. + * + * First argument to the gmt function is the API pointer, but it is optional once created. + * Next argument is the module name + * Thrid argument is the option string + * Finally, there are optional comma-separated MATLAB array entities required by the command. + * Information about the options of each program is provided via GMT_Encode_Options. + * + * GMT Version: 6.x + * Created: 20-OCT-2017 + * Updated: 1-JUL-2020 requires GMT 6.1.x + * + */ + +#include "gmtmex.h" + +extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ + +#ifndef SINGLE_SESSION +/* Being declared external we can access it between MEX calls */ +static uintptr_t *pPersistent; /* To store API address back and forth within a single MATLAB session */ + +/* Here is the exit function, which gets run when the MEX-file is + cleared and when the user exits MATLAB. The mexAtExit function + should always be declared as static. */ +static void force_Destroy_Session (void) { + void *API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API != NULL) { /* Otherwise just silently ignore this call */ + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("Failure to destroy GMT session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ + } +} +#endif + +static void usage (int nlhs, int nrhs) { + /* Basic usage message */ + if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ + mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", + MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); + mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); + mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); + mexPrintf("You may redistribute copies of this program under the terms of the\n"); + mexPrintf("GNU Lesser General Public License.\n"); + mexPrintf("For more information about these matters, see the file named LICENSE.TXT.\n"); + mexPrintf("For a brief description of GMT modules, type gmt ('help')\n\n"); + } + else { + mexPrintf("Usage is:\n\tgmt ('module_name', 'options'[, ]); %% Run a GMT module\n"); + if (nlhs != 0) + mexErrMsgTxt ("But meanwhile you already made an error by asking help and an output.\n"); + } +} + +static void *Initiate_Session (unsigned int verbose) { + /* Initialize the GMT Session and store the API pointer in a persistent variable */ + void *API = NULL; + /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ + /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ + if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + + GMT_SESSION_COLMAJOR, GMTMEX_print_func)) == NULL) + mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); + +#ifndef SINGLE_SESSION + if (!pPersistent) pPersistent = mxMalloc(sizeof(uintptr_t)); + pPersistent[0] = (uintptr_t)(API); + mexMakeMemoryPersistent (pPersistent); +#endif + return (API); +} + +static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { + /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior + when we do for example (i.e. no lhs): sqrt([4 9]) + */ + void *ptr = NULL; + switch (X->family) { + case GMT_IS_GRID: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); + break; + case GMT_IS_IMAGE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); + break; + case GMT_IS_DATASET: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + break; + case GMT_IS_PALETTE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + break; + case GMT_IS_POSTSCRIPT: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + break; + default: + break; + } + return ptr; +} + +/* This is the function that is called when we type gmt in MATLAB/Octave */ +void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + int status = 0; /* Status code from GMT API */ + int n_in_objects = 0; /* Number of input objects passed to module */ + unsigned int first = 0; /* Array ID of first command argument (not 0 when API-ID is first) */ + unsigned int verbose = 0; /* Default verbose setting */ + unsigned int n_items = 0, pos = 0; /* Number of MATLAB arguments (left and right) */ + size_t str_length = 0, k = 0; /* Misc. counters */ + void *API = NULL; /* GMT API control structure */ + struct GMT_OPTION *options = NULL; /* Linked list of module options */ + struct GMT_RESOURCE *X = NULL; /* Array of information about MATLAB args */ + char *cmd = NULL; /* Pointer used to get the user's MATLAB command */ + char *gtxt = NULL; /* For debug printing of revised command */ + char *opt_args = NULL; /* Pointer to the user's module options */ + char module[MODULE_LEN] = {""}; /* Name of GMT module to call */ + char opt_buffer[BUFSIZ] = {""}; /* Local copy of command line options */ + void *ptr = NULL; +#ifndef SINGLE_SESSION + uintptr_t *pti = NULL; /* To locally store the API address */ +#endif + + /* -1. Check that the GMT library is of a suitable version for this GMTMEX version */ + + if (GMT_MAJOR_VERSION > GMTMEX_GMT_MAJOR_VERSION) { /* This may not work if, for instance, we want >= 6.1.x but is using GMT 7.0.0 */ + char message[128] = {""}; + sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); + mexPrintf (message); + } + else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { + char message[128] = {""}; + sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", + GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); + mexErrMsgTxt (message); + } + + /* 0. No arguments at all results in the GMT banner message */ + if (nrhs == 0) { + usage (nlhs, nrhs); + return; + } + + /* 1. Check for the special commands create and help */ + + if (nrhs == 1) { /* This may be create or help */ + cmd = mxArrayToString (prhs[0]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); + if (!strncmp (cmd, "help", 4U) || !strncmp (cmd, "--help", 6U)) { + usage (nlhs, 1); + return; + } +#ifndef SINGLE_SESSION + if (!strncmp (cmd, "create", 6U)) { /* Asked to create a new GMT session */ + if (nlhs > 1) /* Asked for too much output, only 1 or 0 is allowed */ + mexErrMsgTxt ("GMT: Usage: gmt ('create') or API = gmt ('create');\n"); + if (pPersistent) /* See if have a GMT API pointer */ + API = (void *)pPersistent[0]; + if (API != NULL) { /* If another session still exists */ + GMT_Report (API, GMT_MSG_VERBOSE, + "GMT: A previous GMT session is still active. Ignoring your 'create' request.\n"); + if (nlhs) /* Return nothing */ + plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); + return; + } + if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = GMT_get_V (gtxt[2]); + API = Initiate_Session (verbose); /* Initializing a new GMT session */ + + if (nlhs) { /* Return the API adress as an integer (nlhs == 1 here) )*/ + plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); + pti = mxGetData(plhs[0]); + *pti = *pPersistent; + } + + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + return; + } + + /* OK, neither create nor help, must be a single command with no arguments nor the API. So get it: */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { /* No session yet, create one under the hood */ + API = Initiate_Session(verbose); /* Initializing a new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } + else + API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is currupted. Better to start from scratch.\n"); + } + else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { + /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ + pti = (uintptr_t *)mxGetData(prhs[0]); + API = (void *)pti[0]; /* Get the GMT API pointer */ + first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ + } + else { /* We still don't have the API, so we must get it from the past or initiate a new session */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) + API = Initiate_Session (verbose); /* Initializing new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ +#endif + } + +#ifdef SINGLE_SESSION + /* Initiate a new session */ + API = Initiate_Session (verbose); /* Initializing new GMT session */ +#endif + + if (!cmd) { /* First argument is the command string, e.g., 'blockmean -R0/5/0/5 -I1' or just 'destroy' */ + cmd = mxArrayToString(prhs[first]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string but is probably a cell array of strings.\n"); + } + + if (!strncmp (cmd, "destroy", 7U)) { /* Destroy the session */ +#ifndef SINGLE_SESSION + if (nlhs != 0) + mexErrMsgTxt ("GMT: Usage is gmt ('destroy');\n"); + + if (GMT_Destroy_Options (API, &options)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ +#endif + return; + } + + /* 2. Get module name and separate out args */ + + /* Here we have a GMT module call. The documented use is to give the module name separately from + * the module options, but users may forget and combine the two. So we check both cases. */ + + n_in_objects = nrhs - 1; + str_length = strlen (cmd); /* Length of module (or command) argument */ + for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ + + if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ + strcpy (module, cmd); /* Isolate the module name in this string */ + if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ + first++; /* Since we have a 2nd string to skip now */ + opt_args = mxArrayToString (prhs[first]); + n_in_objects--; + } + /* Else we got no options, just input objects */ + } + else { /* Case b2. Get mex arguments, if any, and extract the GMT module name */ + if (k >= MODULE_LEN) + mexErrMsgTxt ("GMT: Module name in command is too long\n"); + strncpy (module, cmd, k); /* Isolate the module name in this string */ + + while (cmd[k] == ' ') k++; /* Skip any spaces between module name and start of options */ + if (cmd[k]) opt_args = &cmd[k]; + } + + + /* See if info about instalation is required */ + if (!strcmp(module, "gmt")) { + char t[256] = {""}; + if (!opt_args) { + mexPrintf("Warning: calling the 'gmt' program by itself does nothing here.\n"); + return; + } + if (!strcmp(opt_args, "--show-bindir")) /* Show the directory that contains the 'gmt' executable */ + GMT_Get_Default (API, "BINDIR", t); + else if (!strcmp(opt_args, "--show-sharedir")) /* Show share directory */ + GMT_Get_Default (API, "SHAREDIR", t); + else if (!strcmp(opt_args, "--show-datadir")) /* Show the data directory */ + GMT_Get_Default (API, "DATADIR", t); + else if (!strcmp(opt_args, "--show-plugindir")) /* Show the plugin directory */ + GMT_Get_Default (API, "PLUGINDIR", t); + else if (!strcmp(opt_args, "--show-cores")) /* Show number of cores */ + GMT_Get_Default (API, "CORES", t); + + if (t[0] != '\0') { + if (nlhs) + plhs[0] = mxCreateString (t); + else + mexPrintf ("%s\n", t); + } + else + mexPrintf ("Warning: called the 'gmt' program with unknown option.\n"); + return; + } + + /* Make sure this is a valid module */ + if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ + mexErrMsgTxt ("GMT: No module by that name was found.\n"); + + /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to + * another string with enough space. */ + + if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ + /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ + if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ + if (opt_args) + strcat (opt_buffer, " -F"); + else + strcpy (opt_buffer, "-F"); + } + + /* 2++ If gmtwrite then add -T? with correct object type */ + if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ + char targ[5] = {" -T?"}; + targ[3] = GMTMEX_objecttype (prhs[nrhs-1]); + strcat (opt_buffer, targ); + } + /* 2+++ If gmtread -Ti then temporarily set pad to 0 since we don't want padding in image arrays */ + else if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default(API, "API_PAD", "0"); + + /* 3. Convert mex command line arguments to a linked GMT option list */ + if (opt_buffer[0] && (options = GMT_Create_Options (API, 0, opt_buffer)) == NULL) + mexErrMsgTxt ("GMT: Failure to parse GMT5 command options\n"); + + if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ + options = GMT_Create_Options (API, 0, "-?"); + + /* 4. Preprocess to update GMT option lists and return info array X */ + if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { + if (n_items == UINT_MAX) /* Just got usage/synopsis option */ + n_items = 0; + else + mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); + } + + if (options) { /* Only for debugging - remove this section when stable */ + gtxt = GMT_Create_Cmd (API, options); + GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); + GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ + } + + /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ + + for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ + if (X[k].direction == GMT_IN) { + if ((X[k].pos+first+1) < (unsigned int)nrhs) + ptr = (void *)prhs[X[k].pos+first+1]; + else + mexErrMsgTxt ("GMT: Attempting to address a prhs entry that does not exist\n"); + } + else { + if ((X[k].pos) < nlhs) + ptr = (void *)plhs[X[k].pos]; + else { + //mexErrMsgTxt ("GMT: Attempting to address a plhs entry that does not exist\n"); + ptr = alloc_default_plhs (API, &X[k]); + } + } + GMTMEX_Set_Object (API, &X[k], ptr); /* Set object pointer */ + } + + /* 6. Run GMT module; give usage message if errors arise during parsing */ + status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); + if (status != GMT_NOERROR) { + if (status == GMT_MODULE_USAGE || status == GMT_MODULE_SYNOPSIS || status == GMT_MODULE_LIST || + status == GMT_MODULE_EXIST || status == GMT_MODULE_PURPOSE) { + return; + } + else { + mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); + mexErrMsgTxt("GMT: exiting\n"); + } + } + + /* 7. Hook up any GMT outputs to MATLAB plhs array */ + + for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ + if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ + pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ + plhs[pos] = GMTMEX_Get_Object (API, &X[k]); /* Hook mex object onto rhs list */ + } + + /* 2++- If gmtread -Ti then reset the sessions pad value that was temporarily changed above (2+++) */ + if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default (API, "API_PAD", "2"); + + /* 8. Free all GMT containers involved in this module call */ + + for (k = 0; k < n_items; k++) { + void *ppp = X[k].object; + if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to close virtual file\n"); + if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); + else { /* Success, now make sure we dont destroy the same pointer more than once */ + for (size_t kk = k+1; kk < n_items; kk++) + if (X[kk].object == ppp) X[kk].object = NULL; + } + } + + /* 9. Destroy linked option list */ + + if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); +#ifdef SINGLE_SESSION + if (GMT_Destroy_Session (API)) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); +#endif + return; +} diff --git a/src/gmtmex/gmtmex.h b/src/gmtmex/gmtmex.h new file mode 100644 index 00000000000..32f470cbfe9 --- /dev/null +++ b/src/gmtmex/gmtmex.h @@ -0,0 +1,182 @@ +/* + * $Id$ + * + * Copyright (c) 2015-2020 by P. Wessel and J. Luis + * See LICENSE.TXT file for copying and redistribution conditions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3 or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * Contact info: www.soest.hawaii.edu/pwessel + *--------------------------------------------------------------------*/ +/* GMT convenience functions used by MATLAB/OCTAVE mex/oct API + * We also define the MEX structures used to pass data in/out of GMT. + */ + +#ifndef GMTMEX_H +#define GMTMEX_H + +#define GMTMEX_MAJOR_VERSION 2 +#define GMTMEX_MINOR_VERSION 0 +#define GMTMEX_PATCH_VERSION 0 + +/* Define the minimum GMT version suitable for this GMTMEX version */ + +#define GMTMEX_GMT_MAJOR_VERSION 6 +#define GMTMEX_GMT_MINOR_VERSION 1 +#define GMTMEX_GMT_PATCH_VERSION 0 + +#include "gmt.h" +#include "gmt_version.h" +#include +#include +#include + +#ifdef GMT_OCTOCT +# include +#else +# include +# define mxIsScalar_(mx) \ + ( (2 == mxGetNumberOfDimensions(mx)) \ + && (1 == mxGetM(mx))&& (1 == mxGetN(mx)) ) +#endif /* Matlab and Octave(mex) */ + +/* Matlab and Octave (in -mex mode) are identical, oct files are different and not yet tested */ + +/* Older Ml versions don't have mwSize */ +#ifndef GMT_OCTMEX +#if !defined(MATLAB_VERSION) +#if !defined(MWSIZE_MAX) +# define MATLAB_VERSION 0x2006a /* R2006a or earlier */ +#elif MX_API_VER < 0x07040000 +# define MATLAB_VERSION 0x2006b /* R2006b */ +#elif !defined(FMT_PTRDIFF_T) +# define MATLAB_VERSION 0x2007a /* R2007a */ +#elif !defined(CUINT64_T) +# define MATLAB_VERSION 0x2007b /* R2007b */ +#elif defined(mxSetLogical) +# define MATLAB_VERSION 0x2008a /* R2008a */ +#else +# if !defined(blas_h) +# include "blas.h" +# endif +# if !defined(lapack_h) +# include "lapack.h" +# endif +# if !defined(MATHWORKS_MATRIX_MATRIX_PUB_FWD_H) +# if defined(CHAR16_T) +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2008b /* R2008b */ +# elif !defined(cgeqr2p) +# define MATLAB_VERSION 0x2010b /* R2010b */ +# else +# define MATLAB_VERSION 0x2011a /* R2011a */ +# endif +# else +# include "emlrt.h" +# define MATLAB_VERSION EMLRT_VERSION_INFO /* R2011b or later */ +# endif +# else +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2009a /* R2009a */ +# elif !defined(cgbequb) +# define MATLAB_VERSION 0x2009b /* R2009b */ +# else +# define MATLAB_VERSION 0x2010a /* R2010a */ +# endif +# endif +#endif +#endif /* if !defined(MATLAB_VERSION) */ + +#if MATLAB_VERSION < 0x2006b +typedef int mwSize; +#endif +#endif + +#ifdef GMT_OCTOCT /* Octave oct files only */ +# define MEX_PROG "Octave(oct)" +# define MEX_COL_ORDER GMT_IS_ROW_FORMAT + /* Macros for getting the Octave(oct) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) +#else /* Here we go for Matlab or Octave(mex) */ +# ifdef GMT_MATLAB +# define MEX_PROG "Matlab" +# else +# define MEX_PROG "Octave(mex)" +# endif +# define MEX_COL_ORDER GMT_IS_COL_FORMAT + /* Macros for getting the Matlab/Octave(mex) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) +#endif + +/* Definitions of MEX structures used to hold GMT objects. + * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ + +/* GMT_IS_DATASET: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. Pure datasets will only set the data + * matrix and leave the text cell array empty, while textsets + * will fill out both. Only the first segment will have any + * information in the info and projection_ref_* items. */ + +#define N_MEX_FIELDNAMES_DATASET 6 +static const char *GMTMEX_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = + {"data", "text", "header", "comment", "proj4", "wkt"}; + +/* GMT_IS_GRID: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_GRID 17 +static const char *GMTMEX_fieldname_grid[N_MEX_FIELDNAMES_GRID] = + {"z", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", + "command", "datatype", "x_unit", "y_unit", "z_unit", "layout", "proj4", "wkt"}; + +/* GMT_IS_IMAGE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_IMAGE 19 +static const char *GMTMEX_fieldname_image[N_MEX_FIELDNAMES_IMAGE] = + {"image", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", "command", + "datatype", "x_unit", "y_unit", "z_unit", "colormap", "alpha", "layout", "proj4", "wkt"}; + +/* GMT_IS_PALETTE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_CPT 11 +static const char *GMTMEX_fieldname_cpt[N_MEX_FIELDNAMES_CPT] = + {"colormap", "alpha", "range", "minmax", "bfn", "depth", "hinge", "cpt", "model", "mode", "comment"}; + +/* GMT_IS_POSTSCRIPT: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_PS 4 +static const char *GMTMEX_fieldname_ps[N_MEX_FIELDNAMES_PS] = + {"postscript", "length", "mode", "comment"}; + +/* Macro for indexing into a GMT grid [with pad] */ +#define GMT_IJP(h,row,col) ((uint64_t)(((int64_t)(row)+(int64_t)h->pad[GMT_YHI])*((int64_t)h->mx)+(int64_t)(col)+(int64_t)h->pad[GMT_XLO])) + +#define MODULE_LEN 32 /* Max length of a GMT module name */ + +/* These 4 functions are used by gmtmex.c: */ +EXTERN_MSC char GMTMEX_objecttype (const mxArray *ptr); +EXTERN_MSC int GMTMEX_print_func (FILE *fp, const char *message); +EXTERN_MSC void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr); +EXTERN_MSC void * GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X); +#endif diff --git a/src/gmtmex/gmtmex_parser.c b/src/gmtmex/gmtmex_parser.c new file mode 100644 index 00000000000..1612e49eda4 --- /dev/null +++ b/src/gmtmex/gmtmex_parser.c @@ -0,0 +1,1345 @@ +/* + * $Id$ + * + * Copyright (c) 2015-2020 by P. Wessel and J. Luis + * See LICENSE.TXT file for copying and redistribution conditions. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3 or any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * Contact info: www.soest.hawaii.edu/gmt + *--------------------------------------------------------------------*/ +/* This layer of code handles the interface between GMT objects and how + * we represent them in Matlab/Octave. + */ + +#define STDC_FORMAT_MACROS +#define GMTMEX_LIB +#include "gmtmex.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef rint + #define rint(x) (floor((x)+0.5f)) //does not work reliable. +#endif + +#if defined(WIN32) +#if !defined(lrint) +# define lrint (int64_t)rint +#endif +#endif + +enum MEX_dim { + DIM_COL = 0, /* Holds the number of columns for vectors and x-nodes for matrix */ + DIM_ROW = 1}; /* Holds the number of rows for vectors and y-nodes for matrix */ + +/* Note: Wherever we say "MATLAB" below we mean "MATLAB or Octave" + * + * Reading and writing occur inside gmt_api.c via the standard GMT containers. + * The packing up of GMT structures into MATLAB structures and vice versa happens after getting the + * results out of the GMT API and before passing them back to MATLAB. + * + * All 5 GMT Resources are supported in this API, according to these rules: + * GMT_GRID: Handled with a MATLAB grid structure and we use GMT's GMT_GRID for the passing + * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] + * + The 2-D grid array z (single precision) + * + An x-array of coordinates + * + An y-array of coordinates + * + Various Proj4 strings + * GMT_IMAGE: Handled with a MATLAB image structure and we use GMT's GMT_IMAGE for the passing + * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] + * + The 2-D grid array z (single precision) + * + An x-array of coordinates + * + An y-array of coordinates + * + Various Proj4 strings + * GMT_DATASET: Handled with an array of MATLAB data structures and we use GMT's GMT_DATASET for passing in and out of GMT. + * Each MEX structure contain data for one segment: + * + A text string for the segment header + * + A 2-D matrix with rows and columns (double precision) + * + An optional cell array with strings from trailing columns that could not be deciphered as data. + * + First segment may also have dataset comment and proj4/wtk strings + * GMT_PALETTE: Handled with a MATLAB structure and we use GMT's native GMT_PALETTE for the passing. + * + colormap is the N*3 matrix for MATLAB colormaps + * + range is a N-element array with z-values at color changes + * + alpha is a N-element array with transparencies + * + minmax is a 2-element array with zmin and zmax + * + bnf is a 3x3-element matrix with back,fore,NaN colors + * + depth is a 1-element matrix with color depth (1, 8, 24 bits) + * + hinge is a 1-element matrix with hinge z-value (or NaN) + * + cpt is a N*6-element matrix with original CPT slice values + * + comment holds any Palette comments + * GMT_POSTSCRIPT: Handled with a MATLAB structure and we use GMT's native GMT_POSTSCRIPT for the passing. + * + postscript is the single string with all the PostScript code + * + length is the number of bytes in the string + * + mode is the overlay/trailer indicator + * + comment holds any PostScript comments + */ + +int GMTMEX_print_func (FILE *fp, const char *message) { + /* Replacement for GMT's gmt_print_func. It is being used indirectly via + * API->print_func. Purpose of this is to allow MATLAB (which cannot use + * printf) to reset API->print_func to this function via GMT_Create_Session. + * This allows GMT's errors and warnings to appear in MATLAB console. */ + + mexPrintf (message); + return 0; +} + +static uint64_t gmtmex_getMNK (const mxArray *p, int which) { + /* Get number of columns or number of bands of a mxArray. + which = 0 to inquire n_rows + = 1 to inquire n_columns + = 2 to inquire n_bands + = ? ERROR + */ + uint64_t nx, ny, nBands, nDims; + const mwSize *dim_array = NULL; + + nDims = mxGetNumberOfDimensions(p); + dim_array = mxGetDimensions(p); + ny = dim_array[0]; + nx = dim_array[1]; + nBands = dim_array[2]; + if (nDims == 2) /* Otherwise it would stay undefined */ + nBands = 1; + + if (which == 0) + return ny; + else if (which == 1) + return nx; + else if (which == 2) + return nBands; + else + mexErrMsgTxt("gmtmex_getMNK: Bad dimension number!"); + return 0; +} + +static void gmtmex_quit_if_missing (const char *function, const char *field) { + char buffer[128] = {""}; + sprintf (buffer , "%s: Could not find structure field %s\n", function, field); + mexErrMsgTxt (buffer); +} + +static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { + /* Given an incoming GMT grid G, build a MATLAB structure and assign the output components. + * Note: Incoming GMT grid has standard padding while MATLAB grid has none. */ + + unsigned int k; + uint64_t row, col, gmt_ij; + float *f = NULL; + double *d = NULL, *G_x = NULL, *G_y = NULL, *x = NULL, *y = NULL; + mxArray *G_struct = NULL, *mxptr[N_MEX_FIELDNAMES_GRID]; + + if (!G->data) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_grid: programming error, output matrix G is empty\n"); + + /* Create a MATLAB struct to hold this grid [matrix will be a float (mxSINGLE_CLASS)]. */ + G_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); + + /* Get pointers and populate structure from the information in G */ + mxptr[0] = mxCreateNumericMatrix (G->header->n_rows, G->header->n_columns, mxSINGLE_CLASS, mxREAL); + mxptr[1] = mxCreateNumericMatrix (1, G->header->n_columns, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, G->header->n_rows, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateDoubleScalar ((double)G->header->registration); + mxptr[6] = mxCreateDoubleScalar ((double)G->header->nan_value); + mxptr[7] = mxCreateString (G->header->title); + mxptr[8] = mxCreateString (G->header->remark); + mxptr[9] = mxCreateString (G->header->command); + mxptr[10] = mxCreateString ("float32"); + mxptr[11] = mxCreateString (G->header->x_units); + mxptr[12] = mxCreateString (G->header->y_units); + mxptr[13] = mxCreateString (G->header->z_units); + mxptr[14] = (G->header->mem_layout[0]) ? mxCreateString(G->header->mem_layout) : mxCreateString ("TCF"); + mxptr[15] = mxCreateString (G->header->ProjRefPROJ4); + mxptr[16] = mxCreateString (G->header->ProjRefWKT); + + d = mxGetPr (mxptr[3]); /* Range */ + for (k = 0; k < 4; k++) d[k] = G->header->wesn[k]; + d[4] = G->header->z_min; d[5] = G->header->z_max; + + d = mxGetPr (mxptr[4]); /* Increments */ + for (k = 0; k < 2; k++) d[k] = G->header->inc[k]; + + /* Load the real grd array into a float MATLAB array by transposing + from padded GMT grd format to unpadded MATLAB format */ + f = mxGetData (mxptr[0]); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + f[MEXG_IJ(G,row,col)] = G->data[gmt_ij]; + } + } + + /* Also return the convenient x and y arrays */ + G_x = GMT_Get_Coord (API, GMT_IS_GRID, GMT_X, G); /* Get array of x coordinates */ + G_y = GMT_Get_Coord (API, GMT_IS_GRID, GMT_Y, G); /* Get array of y coordinates */ + x = mxGetData (mxptr[1]); + y = mxGetData (mxptr[2]); + memcpy (x, G_x, G->header->n_columns * sizeof (double)); + for (k = 0; k < G->header->n_rows; k++) + y[G->header->n_rows-1-k] = G_y[k]; /* Must reverse the y-array */ + if (GMT_Destroy_Data (API, &G_x)) + mexPrintf("Warning: Failure to delete G_x (x coordinate vector)\n"); + if (GMT_Destroy_Data (API, &G_y)) + mexPrintf("Warning: Failure to delete G_y (y coordinate vector)\n"); + for (k = 0; k < N_MEX_FIELDNAMES_GRID; k++) + mxSetField (G_struct, 0, GMTMEX_fieldname_grid[k], mxptr[k]); + return (G_struct); +} + +static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { + /* Given a GMT DATASET D, build a MATLAB array of segment structure and assign values. + * Each segment will have 6 items: + * header: Text string with the segment header (could be empty) + * data: Matrix with the data for this segment (n_rows by n_columns) + * text: Optional cell array for trailing text + * comment: Cell array with any comments + * proj4: String with any proj4 information + * wkt: String with any WKT information + */ + + int n_headers; + uint64_t tbl, seg, seg_out, col, row, start, k, n_items = 1; + double *data = NULL; + struct GMT_DATASEGMENT *S = NULL; + mxArray *D_struct = NULL, *mxheader = NULL, *mxdata = NULL, *mxtext = NULL, *mxstring = NULL; + + if (D == NULL) { /* No output produced (?) - return a null data set */ + D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + return (D_struct); + } + + for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) /* Count non-zero segments */ + for (seg = 0; seg < D->table[tbl]->n_segments; seg++) + if (D->table[tbl]->segment[seg]->n_rows) + seg_out++; + if (seg_out == 0) n_items = 0; + D_struct = mxCreateStructMatrix ((mwSize)seg_out, (mwSize)n_items, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + + n_headers = (D->n_tables) ? D->table[0]->n_headers : 0; /* Number of header records in first table */ + for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) { + for (seg = 0; seg < D->table[tbl]->n_segments; seg++) { + S = D->table[tbl]->segment[seg]; /* Shorthand */ + if (S->n_rows == 0) continue; /* Skip empty segments */ + if (S->header) { /* Has segment header */ + mxheader = mxCreateString (S->header); + mxSetField (D_struct, (mwSize)seg_out, "header", mxheader); + } + if (S->text) { /* Has trailing text */ + mxtext = mxCreateCellMatrix (S->n_rows, 1); + for (row = 0; row < S->n_rows; row++) { + mxstring = mxCreateString (S->text[row]); + mxSetCell (mxtext, (int)row, mxstring); + } + mxSetField (D_struct, (mwSize)seg_out, "text", mxtext); + } + if (S->n_columns) { /* Has numerical data */ + mxdata = mxCreateNumericMatrix ((mwSize)S->n_rows, (mwSize)S->n_columns, mxDOUBLE_CLASS, mxREAL); + data = mxGetPr (mxdata); + for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ + memcpy (&data[start], S->data[col], S->n_rows * sizeof (double)); + mxSetField (D_struct, (mwSize)seg_out, "data", mxdata); + } + if (n_headers) { /* First segment will get any headers, the rest nothing */ + mxtext = mxCreateCellMatrix (n_headers, n_headers ? 1 : 0); + for (k = 0; k < n_headers; k++) { + mxstring = mxCreateString (D->table[0]->header[k]); + mxSetCell (mxtext, (int)k, mxstring); + } + mxSetField (D_struct, (mwSize)seg_out, "comment", mxtext); + n_headers = 0; /* No other segment will have a non-empty comment cell array */ + } + seg_out++; + } + } + return (D_struct); +} + +static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { + /* Given a GMT GMT_POSTSCRIPT P, build a MATLAB array of segment structure and assign values. + * Each segment will have 4 items: + * postscript: Text string with the entire PostScript plot + * length: Byte length of postscript + * mode: 1 has header, 2 has trailer, 3 is complete + * comment: Cell array with any comments + */ + uint64_t k, *length = NULL; + unsigned int *mode = NULL; + mxArray *P_struct = NULL, *mxptr[N_MEX_FIELDNAMES_PS], *mxstring = NULL; + + if (P == NULL) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_postscript: programming error, input POSTSCRIPT struct P is NULL or data string is empty\n"); + + if (!P->data) { + /* Return empty PS struct */ + P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + return P_struct; + } + + /* Return PS with postscript and length in a struct */ + P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + + mxptr[0] = mxCreateString (P->data); + mxptr[1] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); + mxptr[3] = mxCreateCellMatrix (P->n_headers, P->n_headers ? 1 : 0); + length = (uint64_t *)mxGetData(mxptr[1]); + mode = (uint32_t *)mxGetData(mxptr[2]); + + length[0] = (uint64_t)P->n_bytes; /* Set length of the PS string */ + mode[0] = (uint32_t)P->mode; /* Set mode of the PS string */ + + for (k = 0; k < P->n_headers; k++) { + mxstring = mxCreateString (P->header[k]); + mxSetCell (mxptr[3], (int)k, mxstring); + } + + for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) + mxSetField (P_struct, 0, GMTMEX_fieldname_ps[k], mxptr[k]); + + return P_struct; +} + +static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { + /* Given a GMT GMT_PALETTE C, build a MATLAB structure and assign values. + * Each segment will have 10 items: + * colormap: Nx3 array of colors usable in Matlab' colormap + * alpha: Nx1 array with transparency values + * range: Nx1 arran with z-values at color changes + * minmax: 2x1 array with min/max zvalues + * bfn: 3x3 array with colors for background, forground, nan + * depth Color depth 24, 8, 1 + * hinge: Z-value at discontinuous color break, or NaN + * cpt: Nx6 full GMT CPT array + * model: String with color model rgb, hsv, or cmyk [rgb] + * comment: Cell array with any comments + * + * Limitation: MATLAB's colormap format can either hold discrete + * or continuous colormaps, but not a mixture of these, which GMT + * can do. Thus, mixed-mode GMT cpts being used in MATLAB or passed + * out from MATLAB cannot represent these changes accurately. */ + + unsigned int k, j, n_colors, *depth = NULL; + double *color = NULL, *cpt = NULL, *alpha = NULL, *minmax = NULL, *range = NULL, *hinge = NULL, *cyclic = NULL, *bfn = NULL; + mxArray *C_struct = NULL, *mxptr[N_MEX_FIELDNAMES_CPT], *mxstring = NULL; + + if (C == NULL) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_palette: programming error, output CPT C is empty\n"); + + if (!C->data) { /* Return empty struct */ + C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + return (C_struct); + } + + /* Return CPT via colormap, range, and alpha arrays in a struct */ + /* Create a MATLAB struct for this CPT */ + C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + + n_colors = (C->is_continuous) ? C->n_colors + 1 : C->n_colors; + mxptr[0] = mxCreateNumericMatrix (n_colors, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[1] = mxCreateNumericMatrix (n_colors, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (C->n_colors, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (2, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (3, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); + mxptr[6] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[7] = mxCreateNumericMatrix (C->n_colors, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[8] = NULL; /* Set below */ + mxptr[9] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[10] = mxCreateCellMatrix (C->n_headers, C->n_headers ? 1 : 0); + + color = mxGetPr (mxptr[0]); + alpha = mxGetPr (mxptr[1]); + range = mxGetPr (mxptr[2]); + minmax = mxGetPr (mxptr[3]); + bfn = mxGetPr (mxptr[4]); + depth = (uint32_t *)mxGetData (mxptr[5]); + hinge = mxGetPr (mxptr[6]); + cpt = mxGetPr (mxptr[7]); + cyclic = mxGetPr (mxptr[9]); + depth[0] = (C->is_bw) ? 1 : ((C->is_gray) ? 8 : 24); + hinge[0] = (C->has_hinge) ? C->hinge : mxGetNaN (); + cyclic[0] = (C->is_wrapping) ? 1.0 : 0.0; + for (j = 0; j < 3; j++) /* Copy r/g/b from palette bfn to MATLAB array */ + for (k = 0; k < 3; k++) bfn[j+3*k] = C->bfn[j].rgb[k]; + for (j = 0; j < C->n_colors; j++) { /* Copy r/g/b from palette to MATLAB colormap and cpt */ + for (k = 0; k < 3; k++) { + color[j+k*n_colors] = cpt[j+k*C->n_colors] = C->data[j].rgb_low[k]; + cpt[j+(k+3)*C->n_colors] = C->data[j].rgb_high[k]; + } + alpha[j] = C->data[j].rgb_low[3]; + range[j] = C->data[j].z_low; + range[j+C->n_colors] = C->data[j].z_high; + } + if (C->is_continuous) { /* Add last color/alpha to colormap */ + for (k = 0; k < 3; k++) color[j+k*n_colors] = C->data[C->n_colors-1].rgb_high[k]; + alpha[j] = C->data[j].rgb_low[3]; + } + minmax[0] = C->data[0].z_low; /* Set min/max limits */ + minmax[1] = C->data[C->n_colors-1].z_high; + if (C->n_headers) { + for (k = 0; k < C->n_headers; k++) { + mxstring = mxCreateString (C->header[k]); + mxSetCell (mxptr[10], (int)k, mxstring); + } + } + if (C->model & GMT_HSV) + mxptr[8] = mxCreateString ("hsv"); + else if (C->model & GMT_CMYK) + mxptr[8] = mxCreateString ("cmyk"); + else + mxptr[8] = mxCreateString ("rgb"); + + for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) /* Update all fields */ + mxSetField (C_struct, 0, GMTMEX_fieldname_cpt[k], mxptr[k]); + return (C_struct); +} + +static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { + unsigned int k; + mwSize dim[3]; + uint8_t *u = NULL, *alpha = NULL; + double *d = NULL, *I_x = NULL, *I_y = NULL, *x = NULL, *y = NULL, *color = NULL; + mxArray *I_struct = NULL, *mxptr[N_MEX_FIELDNAMES_IMAGE]; + + if (I == NULL || !I->data) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_image: programming error, output image I is empty\n"); + + /* Return image via a uint8_t (mxUINT8_CLASS) matrix in a struct */ + /* Create a MATLAB struct for this image */ + I_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); + /* Create the various fields with information from I */ + mxptr[0] = NULL; /* Set below */ + mxptr[1] = mxCreateNumericMatrix (1, I->header->n_columns, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, I->header->n_rows, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateDoubleScalar ((double)I->header->registration); + mxptr[6] = mxCreateDoubleScalar ((double)I->header->nan_value); + mxptr[7] = mxCreateString (I->header->title); + mxptr[8] = mxCreateString (I->header->remark); + mxptr[9] = mxCreateString (I->header->command); + mxptr[10] = mxCreateString ("uint8"); + mxptr[11] = mxCreateString (I->header->x_units); + mxptr[12] = mxCreateString (I->header->y_units); + mxptr[13] = mxCreateString (I->header->z_units); + mxptr[14] = mxptr[15] = NULL; /* Set below */ + mxptr[16] = (I->header->mem_layout[0]) ? mxCreateString(I->header->mem_layout) : mxCreateString ("TCBa"); + mxptr[17] = mxCreateString (I->header->ProjRefPROJ4); + mxptr[18] = mxCreateString (I->header->ProjRefWKT); + + /* Fill in values */ + d = mxGetPr (mxptr[3]); /* Range */ + for (k = 0; k < 4; k++) d[k] = I->header->wesn[k]; + d[4] = I->header->z_min; d[5] = I->header->z_max; + + d = mxGetPr(mxptr[4]); /* Increments */ + for (k = 0; k < 2; k++) d[k] = I->header->inc[k]; + + if (I->colormap != NULL) { /* Indexed image has a color map */ + mxptr[14] = mxCreateNumericMatrix (I->n_indexed_colors, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + color = mxGetPr (mxptr[14]); + for (k = 0; k < 4 * (unsigned int)I->n_indexed_colors && I->colormap[k] >= 0; k++) + color[k] = (uint8_t)I->colormap[k]; + k /= 4; + memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); + } + else if (I->header->n_bands == 1) { /* gray image */ + mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); + } + else if (I->header->n_bands == 3) { /* RGB image */ + dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; + mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + if (!strncmp(I->header->mem_layout, "TCBa", 4)) + memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); + else if (!strncmp(I->header->mem_layout, "TRPa", 4)) { + GMT_Change_Layout (API, GMT_IS_IMAGE, "TCB", 0, I, u, alpha); /* Convert from TRP to TCB */ + mxptr[16] = mxCreateString ("TCBa"); /* Because we just converted to it above */ + } + else { + mexPrintf("WarnError: this image's' memory layout, %s, is not implemented. Expect random art.\n"); + } + if (I->alpha) { + mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + alpha = mxGetData (mxptr[15]); + memcpy (alpha, I->alpha, I->header->nm * sizeof (uint8_t)); + } + } + else if (I->header->n_bands == 4) { /* RGBA image, with a color map */ + dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; + mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + alpha = mxGetData (mxptr[15]); + memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); + memcpy (alpha, &(I->data)[3 * I->header->nm], I->header->nm * sizeof (uint8_t)); + /* + for (k = 0; k < I->header->nm; k++) { + for (m = 0; m < 3; m++) + u[k+m*I->header->nm] = (uint8_t)I->data[4*k+m]; + alpha[k] = (uint8_t)I->data[4*k+3]; + } + */ + } + + /* Also return the convenient x and y arrays */ + I_x = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_X, I); /* Get array of x coordinates */ + I_y = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_Y, I); /* Get array of y coordinates */ + x = mxGetData (mxptr[1]); + y = mxGetData (mxptr[2]); + memcpy (x, I_x, I->header->n_columns * sizeof (double)); + for (k = 0; k < I->header->n_rows; k++) /* Must reverse the y-array */ + y[I->header->n_rows-1-k] = I_y[k]; + if (GMT_Destroy_Data (API, &I_x)) + mexPrintf("Warning: Failure to delete I_x (x coordinate vector)\n"); + if (GMT_Destroy_Data (API, &I_y)) + mexPrintf("Warning: Failure to delete I_y (y coordinate vector)\n"); + for (k = 0; k < N_MEX_FIELDNAMES_IMAGE; k++) { /* Update fields */ + if (mxptr[k]) mxSetField (I_struct, 0, GMTMEX_fieldname_image[k], mxptr[k]); + } + return (I_struct); +} + +static struct GMT_GRID *gmtmex_grid_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty Grid container to hold a GMT grid. + * If direction is GMT_IN then we are given a MATLAB grid and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT grid as a destination. */ + unsigned int row, col; + uint64_t gmt_ij; + struct GMT_GRID *G = NULL; + + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + unsigned int registration, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + mxArray *mx_ptr = NULL, *mxGrid = NULL, *mxHdr = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_grid_init: The input that was supposed to contain the Grid, is empty\n"); + if (!mxIsStruct (ptr)) { + if (!mxIsCell (ptr)) + mexErrMsgTxt ("gmtmex_grid_init: Expected a Grid structure or Cell array for input\n"); + else { /* Test that we have a {MxN,1x9} cell array */ + if (mxGetM(ptr) != 2 && mxGetN(ptr) != 2) + mexErrMsgTxt ("gmtmex_grid_init: Cell array must contain two elements\n"); + else { + mxGrid = mxGetCell(ptr, 0); + mxHdr = mxGetCell(ptr, 1); + if (mxGetM(mxGrid) < 2 || mxGetN(mxGrid) < 2) + mexErrMsgTxt ("gmtmex_grid_init: First element of grid's cell array must contain a decent matrix\n"); + if (mxGetM(mxHdr) != 1 || mxGetN(mxHdr) != 9) + mexErrMsgTxt ("gmtmex_grid_init: grid's cell array second element must contain a 1x9 vector\n"); + if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) + mexErrMsgTxt ("gmtmex_grid_init: grid's cell matrix must be either single or double.\n"); + } + } + } + + if (mxIsStruct(ptr)) { /* Passed a regular MEX Grid structure */ + double *inc = NULL, *range = NULL, *reg = NULL; + unsigned int pad = (unsigned int)GMT_NOTSET; + char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, + z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[3]; + mx_ptr = mxGetField (ptr, 0, "inc"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find inc array with Grid increments\n"); + inc = mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "range"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find range array for Grid range\n"); + range = mxGetData (mx_ptr); + + mxGrid = mxGetField(ptr, 0, "z"); + if (mxGrid == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find data array for Grid\n"); + if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) + mexErrMsgTxt ("gmtmex_grid_init: data array must be either single or double.\n"); + + mx_ptr = mxGetField (ptr, 0, "registration"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find registration array for Grid registration\n"); + reg = mxGetData (mx_ptr); + registration = (unsigned int)lrint(reg[0]); + + + mx_ptr = mxGetField(ptr, 0, "pad"); + if (mx_ptr != NULL) { + double *dpad = mxGetData(mx_ptr); + pad = (unsigned int)dpad[0]; + if (pad > 2) + mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); + } + + if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, + NULL, range, inc, registration, pad, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); + + G->header->z_min = range[4]; + G->header->z_max = range[5]; + + G->header->registration = registration; + + mx_ptr = mxGetField (ptr, 0, "nodata"); + if (mx_ptr != NULL) + G->header->nan_value = *(float *)mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "proj4"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + G->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "wkt"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + G->header->ProjRefWKT = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "title"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "command"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "comment"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr)+2); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "x_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "y_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "z_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "layout"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->mem_layout, layout, 3); + } + else + strncpy(G->header->mem_layout, "TRS", 3); + } + else { /* Passed header and grid separately */ + double *h = mxGetData(mxHdr); + registration = (unsigned int)lrint(h[6]); + if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, + NULL, h, &h[7], registration, GMT_NOTSET, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); + G->header->z_min = h[4]; + G->header->z_max = h[5]; + } + + if (mxIsSingle(mxGrid)) { + float *f4 = mxGetData(mxGrid); + if (f4 == NULL) + mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + G->data[gmt_ij] = f4[MEXG_IJ(G,row,col)]; + } + } + } + else { + double *f8 = mxGetData(mxGrid); + if (f8 == NULL) + mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + G->data[gmt_ij] = (float)f8[MEXG_IJ(G,row,col)]; + } + } + } + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_grid_init: Allocated GMT Grid %lx\n", (long)G); + GMT_Report (API, GMT_MSG_DEBUG, + "gmtmex_grid_init: Registered GMT Grid array %lx via memory reference from MATLAB\n", + (long)G->data); + } + else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((G = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_IS_OUTPUT, + NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT blank grid container for holding output grid\n"); + } + return (G); +} + +static struct GMT_IMAGE *gmtmex_image_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty Image container to hold a GMT image. + * If direction is GMT_IN then we are given a MATLAB image and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT image as a destination. */ + struct GMT_IMAGE *I = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + uint64_t dim[3]; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0, pad = 0; + char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, + z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[4]; + double *reg = NULL, *inc = NULL, *range = NULL; + mxArray *mx_ptr = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_image_init: The input that was supposed to contain the Image, is empty\n"); + + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_image_init: Expected a Image structure for input\n"); + + mx_ptr = mxGetField (ptr, 0, "range"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find range array for Image range\n"); + range = mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "inc"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find inc array with Image increments\n"); + inc = mxGetData (mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "registration"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find registration info in Image struct\n"); + reg = mxGetData(mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "pad"); + if (mx_ptr != NULL) { + double *dpad = mxGetData(mx_ptr); + pad = (unsigned int)dpad[0]; + if (pad > 2) + mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); + } + + mx_ptr = mxGetField (ptr, 0, "image"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find data array for Image\n"); + + if (!mxIsUint8(mx_ptr)) + mexErrMsgTxt("gmtmex_image_init: The only data type supported by now is UInt8, and this image is not.\n"); + + dim[0] = gmtmex_getMNK (mx_ptr, 1); dim[1] = gmtmex_getMNK (mx_ptr, 0); dim[2] = gmtmex_getMNK (mx_ptr, 2); + if ((I = GMT_Create_Data (API, GMT_IS_IMAGE|flag, GMT_IS_SURFACE, GMT_GRID_HEADER_ONLY, dim, + range, inc, (unsigned int)reg[0], pad, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT source image for input\n"); + + I->data = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ + GMT_Set_AllocMode (API, GMT_IS_IMAGE, I); + //I->alloc_mode = GMT_ALLOC_EXTERNALLY; + +/* + memcpy (I->data, (unsigned char *)mxGetData (mx_ptr), I->header->nm * I->header->n_bands * sizeof (char)); + for (row = 0; row < I->header->n_rows; row++) { + for (col = 0; col < I->header->n_columns; col++) { + gmt_ij = GMT_IJP (I->header, row, col); + I->data [gmt_ij] = f[MEXG_IJ(I,row,col)]; + } + } +*/ + mx_ptr = mxGetField (ptr, 0, "alpha"); + I->alpha = NULL; + if (mx_ptr != NULL) { + if (mxGetNumberOfDimensions(mx_ptr) == 2) + I->alpha = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ + } + + I->header->z_min = range[4]; + I->header->z_max = range[5]; + + mx_ptr = mxGetField(ptr, 0, "x"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find x-coords vector for Image\n"); + I->x = mxGetData(mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "y"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find y-coords vector for Image\n"); + I->y = mxGetData(mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "nodata"); + if (mx_ptr != NULL) + I->header->nan_value = *(float *)mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "proj4"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + I->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "wkt"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + I->header->ProjRefWKT = GMT_Duplicate_String (API, str); + free (str); + } + + mx_ptr = mxGetField (ptr, 0, "title"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "command"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "comment"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "x_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "y_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "z_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "layout"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->mem_layout, layout, 4); + } + else + strncpy(I->header->mem_layout, "TCBa", 4); + + I->colormap = NULL; /* BUT IT SHOULD BE PROPERLY ASSIGNED IF IMAGE IS INDEXED */ + if (dim[2] == 1) + I->color_interp = "Gray"; + else + I->color_interp = "Unknown"; /* BUT WE CAN */ + + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_image_init: Allocated GMT Image %lx\n", (long)I); + GMT_Report (API, GMT_MSG_DEBUG, + "gmtmex_image_init: Registered GMT Image array %lx via memory reference from MATLAB\n", + (long)I->data); + } + else { /* Just allocate an empty container to hold an output image (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((I = GMT_Create_Data (API, GMT_IS_IMAGE, GMT_IS_SURFACE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT blank image container for holding output image\n"); + + GMT_Set_Default (API, "API_IMAGE_LAYOUT", "TCBa"); /* State how we wish to receive images from GDAL */ + } + return (I); +} + +static void *gmtmex_dataset_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr, unsigned int *actual_family) { + /* Create containers to hold or receive data tables: + * direction == GMT_IN: Create empty GMT_DATASET container, fill from Mex, and use as GMT input. + * Input from MATLAB may be a MEX data structure, a plain matrix, a cell array of strings or a single string. + * direction == GMT_OUT: Create empty GMT_DATASET container, let GMT fill it out, and use for Mex output. + * If direction is GMT_IN then we are given a MATLAB struct and can determine dimension. + * If output then we don't know size so we set dimensions to zero. */ + struct GMT_DATASET *D = NULL; + + *actual_family = GMT_IS_DATASET; /* Default but may change to matrix below */ + if (direction == GMT_IN) { /* Data given, dimensions are know, create container for GMT */ + uint64_t seg, col, row, start, k, n_headers, dim[4] = {1, 0, 0, 0}, n, m, n_rows; /* We only return a single table */ + size_t length = 0; + bool got_single_record = false; + unsigned int mode; + char buffer[BUFSIZ] = {""}, *txt = NULL; + mxArray *mx_ptr = NULL, *mx_ptr_d = NULL, *mx_ptr_t = NULL; + double *data = NULL; + struct GMT_DATASEGMENT *S = NULL; + + if (!ptr) mexErrMsgTxt ("gmtmex_dataset_init: Input is empty where it can't be.\n"); + if (mxIsNumeric (ptr)) { /* Got a MATLAB matrix as input - pass data pointers via MATRIX to save memory */ + struct GMT_MATRIX *M = NULL; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + flag |= GMT_VIA_MATRIX; + *actual_family |= GMT_VIA_MATRIX; + mxClassID type = mxGetClassID (ptr); /* Storage type for this matrix */ + dim[DIM_ROW] = mxGetM (ptr); /* Number of rows */ + dim[DIM_COL] = mxGetN (ptr); /* Number of columns */ + /* Create matrix container but do not allocate any matrix memory */ + if ((M = GMT_Create_Data (API, GMT_IS_DATASET|flag, GMT_IS_PLP, GMT_CONTAINER_ONLY, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source matrix\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Matrix %lx\n", (long)M); + switch (type) { /* Assign ML type pointer to the corresponding GMT matrix union pointer */ + case mxDOUBLE_CLASS: M->type = GMT_DOUBLE; M->data.f8 = mxGetData (ptr); break; + case mxSINGLE_CLASS: M->type = GMT_FLOAT; M->data.f4 = (float *)mxGetData (ptr); break; + case mxUINT64_CLASS: M->type = GMT_ULONG; M->data.ui8 = (uint64_t *)mxGetData (ptr); break; + case mxINT64_CLASS: M->type = GMT_LONG; M->data.si8 = (int64_t *)mxGetData (ptr); break; + case mxUINT32_CLASS: M->type = GMT_UINT; M->data.ui4 = (uint32_t *)mxGetData (ptr); break; + case mxINT32_CLASS: M->type = GMT_INT; M->data.si4 = (int32_t *)mxGetData (ptr); break; + case mxUINT16_CLASS: M->type = GMT_USHORT; M->data.ui2 = (uint16_t *)mxGetData (ptr); break; + case mxINT16_CLASS: M->type = GMT_SHORT; M->data.si2 = (int16_t *)mxGetData (ptr); break; + case mxUINT8_CLASS: M->type = GMT_UCHAR; M->data.uc1 = (uint8_t *)mxGetData (ptr); break; + case mxINT8_CLASS: M->type = GMT_CHAR; M->data.sc1 = (int8_t *)mxGetData (ptr); break; + default: + mexErrMsgTxt ("gmtmex_dataset_init: Unsupported MATLAB data type in GMT matrix input."); + break; + } + /* Data from MATLAB and Octave(mex) is in col format and data from Octave(oct) is in row format */ +#ifdef GMT_OCTOCT + M->dim = M->n_columns; +#else + M->dim = M->n_rows; +#endif + //M->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Since matrix was allocated by MATLAB/Octave we cannot free it in GMT */ + GMT_Set_AllocMode (API, GMT_IS_MATRIX, M); + M->shape = MEX_COL_ORDER; /* Either col or row order, depending on MATLAB/Octave setting in gmtmex.h */ + return (M); + } + /* We come here if we did not receive a matrix, There are three options: */ + /* 1. A dataset MATLAB structure or array of structures. + * 2. A Cell array of plain text strings for a text-only file. + * 3. A single text string instead of a one-item cell array of strings. */ + + if (mxIsStruct (ptr)) { /* Got the dataset structure */ + dim[GMT_SEG] = mxGetM (ptr); /* Number of segments */ + if (dim[GMT_SEG] == 0) mexErrMsgTxt ("gmtmex_dataset_init: Input has zero segments where it can't be.\n"); + mx_ptr_d = mxGetField (ptr, 0, "data"); /* Get first segment's data matrix [if available] */ + if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ + mx_ptr_t = mxGetField (ptr, 0, "text"); /* Get first segment's text matrix [if available] */ + if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ + + if (mx_ptr_d == NULL && mx_ptr_t == NULL) + mexErrMsgTxt("gmtmex_dataset_init: Both 'data' array and 'text' array are NULL!\n"); + if (mx_ptr_d) + dim[GMT_COL] = mxGetN (mx_ptr_d); /* Number of columns */ + if (dim[GMT_COL] == 0 && mx_ptr_t == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Input has zero columns where it can't be.\n"); + + if (mx_ptr_t) /* This segment also has a cell array of strings */ + mode = GMT_WITH_STRINGS; + else + mode = GMT_NO_STRINGS; + + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + + for (seg = 0; seg < dim[GMT_SEG]; seg++) { /* Each incoming structure is a new data segment */ + mx_ptr = mxGetField (ptr, (mwSize)seg, "header"); /* Get pointer to MEX segment header */ + buffer[0] = 0; /* Reset our temporary text buffer */ + if (mx_ptr && (length = mxGetN (mx_ptr)) != 0) /* These is a non-empty segment header to keep */ + mxGetString (mx_ptr, buffer, (mwSize)(length+1)); + mx_ptr_d = mxGetField (ptr, (mwSize)seg, "data"); /* Data matrix for this segment */ + if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ + mx_ptr_t = mxGetField (ptr, (mwSize)seg, "text"); /* text cell array for this segment */ + if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ + + if (mx_ptr_t) { /* This segment also has a cell array of strings or possibly a single string (if n_rows == 1) */ + got_single_record = false; + m = mxGetM (mx_ptr_t); n = mxGetN (mx_ptr_t); + if (!mxIsCell (mx_ptr_t) && (m == 1 || n == 1)) { + got_single_record = true; + m = n = 1; + } + } + else /* No trailing text */ + m = n = 0; + dim[GMT_ROW] = (mx_ptr_d == NULL) ? m : mxGetM (mx_ptr_d); /* Number of rows in matrix (or strings if no data) */ + if ((m == dim[GMT_ROW] && n == 1) || (n == dim[GMT_ROW] && m == 1)) + mode = GMT_WITH_STRINGS; + else + mode = GMT_NO_STRINGS; + /* Allocate a new data segment and hook up to to our single table */ + S = GMT_Alloc_Segment (API, mode, dim[GMT_ROW], dim[GMT_COL], buffer, D->table[0]->segment[seg]); + if (mx_ptr_d != NULL) data = mxGetData (mx_ptr_d); + for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ + memcpy (S->data[col], &data[start], S->n_rows * sizeof (double)); + if (mode == GMT_WITH_STRINGS) { /* Add in the trailing strings */ + if (got_single_record) { /* Only true when we got a single row with a single string instead of a cell array */ + txt = mxArrayToString (mx_ptr_t); + S->text[0] = GMT_Duplicate_String (API, txt); + } + else { /* Must extract text from the cell array */ + for (row = 0; row < S->n_rows; row++) { + mx_ptr = mxGetCell (mx_ptr_t, (mwSize)row); + txt = mxArrayToString (mx_ptr); + S->text[row] = GMT_Duplicate_String (API, txt); + } + } + } + D->table[0]->n_records += S->n_rows; /* Must manually keep track of totals */ + if (seg == 0) { /* First segment may have table information */ + mx_ptr_t = mxGetField (ptr, (mwSize)seg, "comment"); /* Table headers */ + if (mx_ptr_t && (n_headers = mxGetM (mx_ptr_t)) != 0) { /* Number of headers found */ + for (k = 0; k < n_headers; k++) { /* Extract the headers and insert into dataset */ + mx_ptr = mxGetCell (mx_ptr_t, (mwSize)k); + txt = mxArrayToString (mx_ptr); + if (GMT_Set_Comment (API, GMT_IS_DATASET, GMT_COMMENT_IS_TEXT, txt, D)) + mexErrMsgTxt("gmtmex_dataset_init: Failed to set a dataset header\n"); + } + } + } + if (mode == GMT_WITH_STRINGS) D->type = (D->n_columns) ? GMT_READ_MIXED : GMT_READ_TEXT; + else D->type = GMT_READ_DATA; + } + } + else if (mxIsCell (ptr)) { /* Got a cell array of strings and no numerical data */ + uint64_t k2 = 0; + m = mxGetM (ptr); n = mxGetN (ptr); + n_rows = (m > n) ? m : n; /* Number of items in cell array */ + mode = GMT_WITH_STRINGS; /* Since that is all we have */ + /* Determine number of segments up front since user may use '>' to indicate segment header */ + for (k = 0; k < n_rows; k++) { + mx_ptr = mxGetCell (ptr, (mwSize)k); + txt = mxArrayToString (mx_ptr); + if (txt[0] == '>') dim[GMT_SEG]++; /* Found start of a new segment */ + } + if (dim[GMT_SEG] == 0) dim[GMT_SEG] = 1; /* No segment headers given a single segment */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, GMT_WITH_STRINGS, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + k = seg = 0; + while (k < n_rows) { /* Examine the input records and look for segment breaks */ + mx_ptr = mxGetCell (ptr, (mwSize)k); + txt = mxArrayToString (mx_ptr); + buffer[0] = '\0'; + if (txt[0] == '>' || (k == 0 && txt[0] != '>')) { /* Found start of a new (or first and only) segment */ + if (txt[0] == '>') strcpy (buffer, txt), k++; /* Segment header */ + k2 = k; /* k and k2 initially point to the first row of the current segment */ + dim[GMT_ROW] = 0; /* Have no rows so far */ + while (k2 < n_rows && dim[GMT_ROW] == 0) { /* While not reached end of current segment */ + mx_ptr = mxGetCell (ptr, (mwSize)k2); + txt = mxArrayToString (mx_ptr); + if (txt[0] == '>') /* Got next segment header, must end current segment */ + dim[GMT_ROW] = k2 - k; + else /* Keep going */ + k2++; + } + if (dim[GMT_ROW] == 0) dim[GMT_ROW] = k2 - k; /* Happens for last segment when there is no "next" segment header */ + } + /* Now we have the length of this segment */ + S = GMT_Alloc_Segment (API, GMT_WITH_STRINGS, dim[GMT_ROW], 0, buffer, D->table[0]->segment[seg]); + for (row = 0; row < S->n_rows; row++) { /* Hook up the string records */ + mx_ptr = mxGetCell (ptr, (mwSize)(k+row)); /* k is the offset to 1st record of current segment in inpu cell array */ + txt = mxArrayToString (mx_ptr); + S->text[row] = GMT_Duplicate_String (API, txt); + } + D->table[0]->n_records += S->n_rows; /* Must manually keep track of total records */ + seg++; /* Got ourselves a new segment, increment counter */ + /* Move to next unused record or we have reached the end */ + k = k2; + } + D->type = GMT_READ_TEXT; + } + else if (mxIsChar (ptr)) { /* Got a single string only */ + dim[GMT_ROW] = dim[GMT_SEG] = 1; /* Put string into a single segment */ + mode = GMT_WITH_STRINGS; /* Since that is all we have */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + S = D->table[0]->segment[0]; /* The lone segment */ + txt = mxArrayToString (ptr); /* The lone string */ + S->text[0] = GMT_Duplicate_String (API, txt); + D->type = GMT_READ_TEXT; + } + else + mexErrMsgTxt ("gmtmex_dataset_init: Expected a data structure, cell array with strings, or a single string for input\n"); + D->n_records = D->table[0]->n_records; + } + else { /* Here we set up an empty container to receive data from GMT (signal this by passing 0s and NULLs) */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Dataset %lx\n", (long)D); + } + return (D); +} + +static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to create an empty CPT container to hold a GMT Color Palette. + * If direction is GMT_IN then we are given a MATLAB CPT struct and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT CPT as a destination. */ + struct GMT_PALETTE *P = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + unsigned int k, j, one = 1, n_headers, *depth = NULL; + uint64_t dim[2] = {0, 0}; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + char model[8] = {""}; + mxArray *mx_ptr[N_MEX_FIELDNAMES_CPT]; + double *colormap = NULL, *range = NULL, *minmax = NULL, *alpha = NULL, *bfn = NULL, *hinge = NULL, *cpt = NULL, *cyclic = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_palette_init: The input that was supposed to contain the CPT, is empty\n"); + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_palette_init: Expected a CPT structure for input\n"); + for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) { + if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_cpt[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_palette_init", GMTMEX_fieldname_cpt[k]); + } + + dim[0] = mxGetM (mx_ptr[0]); /* Number of rows in colormap */ + if (dim[0] < 1) + mexErrMsgTxt ("gmtmex_palette_init: Colormap array has no CPT values\n"); + colormap = mxGetData (mx_ptr[0]); + alpha = mxGetData (mx_ptr[1]); + range = mxGetData (mx_ptr[2]); + minmax = mxGetData (mx_ptr[3]); + bfn = mxGetData (mx_ptr[4]); + depth = mxGetData (mx_ptr[5]); + hinge = mxGetData (mx_ptr[6]); + cpt = mxGetData (mx_ptr[7]); + cyclic = mxGetData (mx_ptr[9]); + + dim[1] = mxGetM (mx_ptr[2]); /* Length of range array */ + if (dim[0] > dim[1]) { /* This only happens when we have a continuous color table */ + dim[1] = dim[0]; /* Actual length of colormap array */ + dim[0]--; /* Number of CPT slices */ + } + else /* Discrete, so the one offset needs to be zero */ + one = 0; + + if ((P = GMT_Create_Data (API, GMT_IS_PALETTE|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT source CPT for input\n"); + + if ((n_headers = (unsigned int)mxGetM (mx_ptr[10])) != 0) { /* Number of headers found */ + char *txt = NULL; + mxArray *ptr = NULL; + for (k = 0; k < n_headers; k++) { + ptr = mxGetCell (mx_ptr[10], (mwSize)k); + txt = mxArrayToString (ptr); + if (GMT_Set_Comment (API, GMT_IS_PALETTE, GMT_COMMENT_IS_TEXT, txt, P)) + mexErrMsgTxt("gmtmex_palette_init: Failed to set a CPT header\n"); + } + } + for (j = 0; j < 3; j++) { /* Do the bfn first */ + for (k = 0; k < 3; k++) + P->bfn[j].rgb[k] = bfn[j+k*3]; + } + for (j = 0; j < P->n_colors; j++) { /* OK to access j+1'th elemenent since length of colormap is P->n_colors+1 */ + for (k = 0; k < 3; k++) { + P->data[j].rgb_low[k] = cpt[j+k*dim[0]]; + P->data[j].rgb_high[k] = cpt[j+(k+3)*dim[0]]; + } + P->data[j].rgb_low[3] = alpha[j]; + P->data[j].rgb_high[3] = alpha[j+one]; + P->data[j].z_low = range[j]; + P->data[j].z_high = range[j+P->n_colors]; + P->data[j].annot = 3; /* Enforce annotations for now */ + } + P->is_continuous = one; + P->is_bw = P->is_gray = 0; + P->is_wrapping = (unsigned int)rint (cyclic[0]); + if (depth[0] == 1) + P->is_bw = 1; + else if (depth[0] == 8) + P->is_gray = 1; + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_palette_init: Allocated GMT CPT %lx\n", (long)P); + if (!mxIsNaN (hinge[0])) { + P->has_hinge = 1; + P->mode |= GMT_CPT_HINGED; + P->hinge = hinge[0]; + } + mxGetString (mx_ptr[8], model, (mwSize)mxGetN(mx_ptr[8])+1); + if (!strncmp (model, "hsv", 3U)) + P->model = GMT_HSV; + else if (!strncmp (model, "cmyk", 4U)) + P->model = GMT_CMYK; + else + P->model = GMT_RGB; + } + else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((P = GMT_Create_Data (API, GMT_IS_PALETTE, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT blank CPT container for holding output CPT\n"); + } + return (P); +} + +static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty POSTSCRIPT container to hold a GMT POSTSCRIPT object. + * If direction is GMT_IN then we are given a MATLAB structure with known sizes. + * If direction is GMT_OUT then we allocate an empty GMT POSTSCRIPT as a destination. */ + struct GMT_POSTSCRIPT *P = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the MATLAB input pointer */ + uint64_t dim[1] = {0}, *length = NULL; + unsigned int k, n_headers, *mode = NULL, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + mxArray *mx_ptr[N_MEX_FIELDNAMES_PS]; + char *PS = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_ps_init: The input that was supposed to contain the PostScript structure is empty\n"); + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_ps_init: Expected a MATLAB PostScript structure for input\n"); + for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) { + if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_ps[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_ps_init", GMTMEX_fieldname_ps[k]); + } + + length = mxGetData (mx_ptr[1]); + if (length[0] == 0) + mexErrMsgTxt ("gmtmex_ps_init: Dimension of PostScript given as zero\n"); + PS = malloc (mxGetN(mx_ptr[0])+1); + mxGetString (mx_ptr[0], PS, (mwSize)mxGetN(mx_ptr[0])); + mode = mxGetData (mx_ptr[2]); + /* Passing dim[0] = 0 since we dont want any allocation of a PS string */ + if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT source for input\n"); + P->data = PS; /* PostScript string instead is coming from MATLAB */ + GMT_Set_AllocMode (API, GMT_IS_POSTSCRIPT, P); + //P->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Hence we are not allowed to free it */ + P->n_bytes = length[0]; /* Length of the actual PS string */ + //P->n_alloc = 0; /* But nothing was actually allocated here - just passing pointer from MATLAB */ + P->mode = mode[0]; /* Inherit the mode */ + if ((n_headers = (unsigned int)mxGetM (mx_ptr[3])) != 0) { /* Number of headers found */ + char *txt = NULL; + mxArray *ptr = NULL; + for (k = 0; k < n_headers; k++) { + ptr = mxGetCell (mx_ptr[3], (mwSize)k); + txt = mxArrayToString (ptr); + if (GMT_Set_Comment (API, GMT_IS_POSTSCRIPT, GMT_COMMENT_IS_TEXT, txt, P)) + mexErrMsgTxt("gmtmex_ps_init: Failed to set a PostScript header\n"); + } + } + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_ps_init: Allocated GMT POSTSCRIPT %lx\n", (long)P); + } + else { /* Just allocate an empty container to hold an output PS object (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT container for holding output PostScript\n"); + } + return (P); +} + +char GMTMEX_objecttype (const mxArray *ptr) { + /* Determine what we are returning so gmt write can pass the correct -T? flag */ + mxArray *mx_ptr = NULL; + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("GMTMEX_objecttype: Pointer is empty\n"); + if (mxIsStruct (ptr)) { /* This means either a dataset, grid, image, cpt, or PS, so must check for fields */ + mx_ptr = mxGetField (ptr, 0, "data"); + if (mx_ptr) return 'd'; + mx_ptr = mxGetField (ptr, 0, "postscript"); + if (mx_ptr) return 'p'; + mx_ptr = mxGetField (ptr, 0, "hinge"); + if (mx_ptr) return 'c'; + mx_ptr = mxGetField (ptr, 0, "image"); + if (mx_ptr) return 'i'; + mx_ptr = mxGetField (ptr, 0, "z"); + if (mx_ptr) return 'g'; + mexErrMsgTxt ("GMTMEX_objecttype: Could not recognize the structure\n"); + } + else if (mxIsCell (ptr)) /* This is a dataset with text only */ + return 'd'; + else /* We have to assume it is a numerical matrix */ + return 'd'; + return '-'; /* Can never get here you would think */ +} + +void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { + /* Create the GMT container and hook onto resource array as X->object */ + unsigned int module_input = (X->option->option == GMT_OPT_INFILE), actual_family = X->family; + + switch (X->family) { + case GMT_IS_GRID: /* Get a grid from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_grid_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got Grid\n"); + break; + case GMT_IS_IMAGE: /* Get an image from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_image_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got Image\n"); + break; + case GMT_IS_DATASET: /* Get a dataset from Matlab or a dummy one to hold GMT output */ + /* Because a GMT_DATASET may appears as a GMT_MATRIX or GMT_VECTOR we need the actual_family to open the virtual file later */ + X->object = gmtmex_dataset_init (API, X->direction, module_input, ptr, &actual_family); + break; + case GMT_IS_PALETTE: /* Get a palette from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_palette_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got CPT\n"); + break; + case GMT_IS_POSTSCRIPT: /* Get a PostScript struct from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_ps_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got POSTSCRIPT\n"); + break; + default: + GMT_Report (API, GMT_MSG_NORMAL, "GMTMEX_Set_Object: Bad data type (%d)\n", X->family); + break; + } + if (X->object == NULL) + mexErrMsgTxt("GMT: Failure to register the resource\n"); + if (GMT_Open_VirtualFile (API, actual_family, X->geometry, X->direction|GMT_IS_REFERENCE, X->object, X->name) != GMT_NOERROR) /* Make filename with embedded object ID */ + mexErrMsgTxt ("GMT: Failure to open virtual file\n"); + if (GMT_Expand_Option (API, X->option, X->name) != GMT_NOERROR) /* Replace ? in argument with name */ + mexErrMsgTxt ("GMT: Failure to expand filename marker (?)\n"); +} + +void *GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X) { + mxArray *ptr = NULL; + /* In line-by-line modules it is possible no output is produced, hence we make an exception for DATASET: */ + if ((X->object = GMT_Read_VirtualFile (API, X->name)) == NULL && X->family != GMT_IS_DATASET) + mexErrMsgTxt ("GMT: Error reading virtual file from GMT\n"); + switch (X->family) { /* Determine what container we got */ + case GMT_IS_GRID: /* A GMT grid; make it the pos'th output item */ + ptr = gmtmex_get_grid (API, X->object); + break; + case GMT_IS_DATASET: /* A GMT table; make it a data structure and the pos'th output item */ + ptr = gmtmex_get_dataset (API, X->object); + break; + case GMT_IS_PALETTE: /* A GMT CPT; make it a colormap and the pos'th output item */ + ptr = gmtmex_get_palette (API, X->object); + break; + case GMT_IS_IMAGE: /* A GMT Image; make it the pos'th output item */ + ptr = gmtmex_get_image (API, X->object); + break; + case GMT_IS_POSTSCRIPT: /* A GMT PostScript string; make it the pos'th output item */ + ptr = gmtmex_get_postscript (API, X->object); +#if 0 + { + char cmd[32] = {""}; + strcpy(cmd, name); strcat(cmd, " -A -Tf"); + GMT_Call_Module(API, "psconvert", GMT_MODULE_CMD, cmd); + } +#endif + break; + default: + mexErrMsgTxt ("GMT: Internal Error - unsupported data type\n"); + break; + } + return ptr; +} From 017cb8e91d81f648367bbc8e23eca81a3cb8a0ba Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 21 Sep 2020 17:15:41 -1000 Subject: [PATCH 02/45] Report what we find for MATLAB lits etc. --- CMakeLists.txt | 24 ++++++++++++++++++++---- cmake/ConfigUserAdvancedTemplate.cmake | 10 ++++++---- src/gmtmex/CMakeLists.txt | 2 +- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e622f2db72..9bad47ce0d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,11 +174,23 @@ else (SUPPL_EXTRA_DIRS) set (PROTO "none") endif (SUPPL_EXTRA_DIRS) -if (GMT_INSTALL_MODULE_LINKS) - set (LINKS "yes") +if (SUPPL_EXTRA_DIRS) + set (PROTO ${SUPPL_EXTRA_DIRS}) +else (SUPPL_EXTRA_DIRS) + set (PROTO "none") +endif (SUPPL_EXTRA_DIRS) + +if (GMT_BUILD_GMTMEX) + set (MATLAB_APP "${MATLAB}") + set (MATLAB_INC "${MEX_INC}") + set (MATLAB_LIB1 "${MEX_LIB}") + set (MATLAB_LIB2 "${MX_LIB}") else (GMT_INSTALL_MODULE_LINKS) - set (LINKS "no") -endif (GMT_INSTALL_MODULE_LINKS) + set (MATLAB_APP "not used") + set (MATLAB_INC "not used") + set (MATLAB_LIB1 "not used") + set (MATLAB_LIB2 "not used") +endif (GMT_BUILD_GMTMEX) # Configure header file to pass some of the CMake settings to the source code configure_file (src/config.h.in src/config.h) @@ -231,6 +243,10 @@ message( "* Build GMT for developers : ${DEVEL}\n" "* Build proto supplements : ${PROTO}\n" "* Build module links : ${LINKS}\n" + "* MATLAB Application : ${MATLAB_APP}\n" + "* MATLAB include dir : ${MATLAB_INC}\n" + "* MATLAB MEX library : ${MATLAB_LIB1}\n" + "* MATLAB MX library : ${MATLAB_LIB2}\n" "* Found Ghostscript (gs) : ${GMT_CONFIG_GS_MESSAGE}\n" "* Found GraphicsMagick (gm) : ${GMT_CONFIG_GM_MESSAGE}\n" "* Found ffmpeg : ${GMT_CONFIG_FFMPEG_MESSAGE}\n" diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index bcda2661149..b91b30089b9 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -260,14 +260,16 @@ if (GMT_BUILD_GMTMEX) if (APPLE) set (MATLAB "$ENV{MATLAB}") set (MEX_EXT "mexmaci64") - set (MATLAB_MEX maci64") + set (MATLAB_MEX "maci64") set (MATLAB_FLAGS="-g") add_definitions(-DGMT_MATLAB) - #set (MEX_BLD "xcrun clang -undefined error -arch x86_64 -bundle -DGMT_MATLAB ${MATLAB_FLAGS}) - set (MEX_INC "-I${MATLAB}/extern/include") - set (MEX_LIB "-L${MATLAB}/bin/${MATLAB_MEX} -lmx -lmex") + #set (MEX_BLD "xcrun clang -undefined error -arch x86_64 -bundle -DGMT_MATLAB ${MATLAB_FLAGS}") + set (MEX_INC "${MATLAB}/extern/include") + set (MEX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmex.dylib") + set (MX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmx.dylib") include_directories (${MEX_INC}) list (APPEND GMT_OPTIONAL_LIBRARIES ${MEX_LIB}) + list (APPEND GMT_OPTIONAL_LIBRARIES ${MX_LIB}) endif (APPLE) endif (GMT_BUILD_GMTMEX) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index 53d047e0ca1..65f32d986a3 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -27,7 +27,7 @@ set (SUPPL_NAME gmtmex) set (SUPPL_PROGS_SRCS gmtmex.c gmtmex_parser.c) -set (SUPPL_HEADERS gmtmex_parser.h) +set (SUPPL_HEADERS gmtmex.h) set (SUPPL_LIB_SRCS ${SUPPL_PROGS_SRCS}) set (SUPPL_EXAMPLE_FILES README.gmtmex) From 78e63ab78c9c7da9c49f2457ee31d88c08570f44 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 21 Sep 2020 17:49:18 -1000 Subject: [PATCH 03/45] Update CMakeLists.txt --- src/gmtmex/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index 65f32d986a3..a33a473c89e 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -26,12 +26,12 @@ # set (SUPPL_NAME gmtmex) -set (SUPPL_PROGS_SRCS gmtmex.c gmtmex_parser.c) set (SUPPL_HEADERS gmtmex.h) -set (SUPPL_LIB_SRCS ${SUPPL_PROGS_SRCS}) +set (SUPPL_LIB_SRCS gmtmex_parser.c) set (SUPPL_EXAMPLE_FILES README.gmtmex) # install gmt/mex external function interface and driver shell script -install (PROGRAMS gmt.m gmtmex.${MEX_EXT} +# install (PROGRAMS gmt.m gmtmex.${MEX_EXT} +install (PROGRAMS gmt.m DESTINATION ${GMT_BINDIR} COMPONENT Runtime) From 8a5691b2a1d435174c948f73827644fe3732f8a6 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 22 Sep 2020 14:28:19 -1000 Subject: [PATCH 04/45] testing --- src/gmtmex/CMakeLists.txt | 11 ++++++++++- src/gmtmex/gmtmex.h | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index a33a473c89e..5942d6e8b2f 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -27,11 +27,20 @@ set (SUPPL_NAME gmtmex) set (SUPPL_HEADERS gmtmex.h) -set (SUPPL_LIB_SRCS gmtmex_parser.c) +#set (SUPPL_LIB_SRCS gmtmex_parser.c) +set (GMT_LIB_SRCS ${GMT_LIB_SRCS} gmtmex_parser.c) set (SUPPL_EXAMPLE_FILES README.gmtmex) +# Add the standalone mexFunction +add_executable (gmtmex gmtmex.c) +#target_link_libraries (gmtmex gmtlib ${GMT_SUPPL_LIB_NAME}) +target_link_libraries (gmtmex gmtlib) +# Rename gmt target to prevent version clash +set_target_properties (gmtmex PROPERTIES OUTPUT_NAME gmtmex.${MEX_EXT}) + # install gmt/mex external function interface and driver shell script # install (PROGRAMS gmt.m gmtmex.${MEX_EXT} install (PROGRAMS gmt.m DESTINATION ${GMT_BINDIR} COMPONENT Runtime) + diff --git a/src/gmtmex/gmtmex.h b/src/gmtmex/gmtmex.h index 32f470cbfe9..bf7acd72d74 100644 --- a/src/gmtmex/gmtmex.h +++ b/src/gmtmex/gmtmex.h @@ -32,8 +32,8 @@ #define GMTMEX_GMT_MINOR_VERSION 1 #define GMTMEX_GMT_PATCH_VERSION 0 -#include "gmt.h" -#include "gmt_version.h" +#include "../gmt.h" +#include "../gmt_version.h" #include #include #include From 8149a021dc77992ff06c2651bed442f4c9bc76e9 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Thu, 24 Sep 2020 16:43:29 -1000 Subject: [PATCH 05/45] eliminate cmakelist in gmtmex --- cmake/ConfigUserAdvancedTemplate.cmake | 2 -- src/CMakeLists.txt | 20 ++++++++++- src/gmtmex/CMakeLists.txt | 46 -------------------------- 3 files changed, 19 insertions(+), 49 deletions(-) delete mode 100644 src/gmtmex/CMakeLists.txt diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index b91b30089b9..d10a6142065 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -256,14 +256,12 @@ # Please export an environmental variable MATLAB that points to your Matlab application if (GMT_BUILD_GMTMEX) - set (SUPPL_EXTRA_DIRS ${SUPPL_EXTRA_DIRS} gmtmex) if (APPLE) set (MATLAB "$ENV{MATLAB}") set (MEX_EXT "mexmaci64") set (MATLAB_MEX "maci64") set (MATLAB_FLAGS="-g") add_definitions(-DGMT_MATLAB) - #set (MEX_BLD "xcrun clang -undefined error -arch x86_64 -bundle -DGMT_MATLAB ${MATLAB_FLAGS}") set (MEX_INC "${MATLAB}/extern/include") set (MEX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmex.dylib") set (MX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmx.dylib") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 08e4c572851..4c95c65320c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -372,6 +372,10 @@ set (GMT_PSL_SRCS postscriptlight.c declspec.h psl_config.h PSL_Standard+.h set (GMT_MBSYSTEM_SRCS gmt_mbsystem_glue.c) +if (GMT_BUILD_GMTMEX) + set (GMT_GMTMEX_SRCS gmtmex/gmtmex_parser.c gmtmex/gmtmex.c gmtmex/gmtmex.h) +endif (GMT_BUILD_GMTMEX) + # libgmt set (GMT_LIB_SRCS block_subs.h gmt_common_byteswap.h gmt_common_math.h gmt_common_runpath.h gmt_common_sighandler.c gmt_common_string.h clear.c begin.c @@ -398,7 +402,7 @@ set (GMT_LIB_SRCS block_subs.h gmt_common_byteswap.h gmt_common_math.h ${CMAKE_CURRENT_BINARY_DIR}/gmt_${SHARED_LIB_NAME}_glue.c kiss_fft/_kiss_fft_guts.h kiss_fft/kiss_fft.c kiss_fft/kiss_fft.h kiss_fft/kiss_fftnd.c kiss_fft/kiss_fftnd.h - ${GMT_TRIANGLE_SRCS} ${GMT_MBSYSTEM_SRCS}) + ${GMT_TRIANGLE_SRCS} ${GMT_MBSYSTEM_SRCS} ${GMT_GMTMEX_SRCS}) # source codes for testing API if (DO_API_TESTS) @@ -562,6 +566,20 @@ endforeach (_gmt_prog) # Rename gmt target to prevent version clash set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) +if (GMT_BUILD_GMTMEX) + # Just need to add a symbolic link with the right name to libgmt + set( gmtmex_link ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/gmtmex.${MEX_EXT}) + set( gmtmex_target ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/${LIB_PREFIX}gmt.${GMT_LIB_SOVERSION}.${CMAKE_SHARED_LIBRARY_SUFFIX}) + add_custom_command ( OUTPUT ${gmtmex_link} POST_BUILD + COMMAND ln -s ${gmtmex_target} ${gmtmex_link} + DEPENDS install ${gmtmex_target} + COMMENT "Generating gmtmex symbolic link") + # Install the driver script + install (PROGRAMS gmtmex/gmt.m + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) +endif (GMT_BUILD_GMTMEX) + # psldemo add_executable (psldemo psldemo.h psldemo.c) target_link_libraries (psldemo pslib) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt deleted file mode 100644 index 5942d6e8b2f..00000000000 --- a/src/gmtmex/CMakeLists.txt +++ /dev/null @@ -1,46 +0,0 @@ -# -# -# Copyright (c) 1991-2020 by the GMT Team (https://www.generic-mapping-tools.org/team.html) -# See LICENSE.TXT file for copying and redistribution conditions. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; version 3 or any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# Contact info: www.generic-mapping-tools.org -#------------------------------------------------------------------------------- -# -# CMake settings for supplemental package: -# -# SUPPL_NAME: name of the supplemental package -# SUPPL_HEADERS: header files. Will be installed if BUILD_DEVELOPER is TRUE -# SUPPL_PROGS_SRCS: list of C source codes for supplemental modules -# SUPPL_LIB_SRCS: list of C source codes for supplemental library -# SUPPL_EXAMPLE_FILES: README and other example files -# SUPPL_EXAMPLE_PROGS: Example scripts -# - -set (SUPPL_NAME gmtmex) -set (SUPPL_HEADERS gmtmex.h) -#set (SUPPL_LIB_SRCS gmtmex_parser.c) -set (GMT_LIB_SRCS ${GMT_LIB_SRCS} gmtmex_parser.c) -set (SUPPL_EXAMPLE_FILES README.gmtmex) - -# Add the standalone mexFunction -add_executable (gmtmex gmtmex.c) -#target_link_libraries (gmtmex gmtlib ${GMT_SUPPL_LIB_NAME}) -target_link_libraries (gmtmex gmtlib) -# Rename gmt target to prevent version clash -set_target_properties (gmtmex PROPERTIES OUTPUT_NAME gmtmex.${MEX_EXT}) - -# install gmt/mex external function interface and driver shell script -# install (PROGRAMS gmt.m gmtmex.${MEX_EXT} -install (PROGRAMS gmt.m - DESTINATION ${GMT_BINDIR} - COMPONENT Runtime) - From 4f598d16555d686f44026e02c674097f71918c2c Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 2 Apr 2021 16:50:59 -1000 Subject: [PATCH 06/45] Update emx files --- src/gmtmex/gmtmex.c | 66 ++++++++++++++++++++------------------ src/gmtmex/gmtmex.h | 10 +++--- src/gmtmex/gmtmex_parser.c | 50 +++++++++++++++-------------- 3 files changed, 64 insertions(+), 62 deletions(-) diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index 1a15c0cacb3..b27c371855d 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -1,6 +1,4 @@ /*-------------------------------------------------------------------- - * $Id$ - * * Copyright (c) 2015-2020 by P. Wessel and J. Luis * See LICENSE.TXT file for copying and redistribution conditions. * @@ -13,7 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * Contact info: gmt.soest.hawaii.edu + * Contact info: www.generic-mapping-tools.org *--------------------------------------------------------------------*/ /* * This is the MATLAB/Octave(mex) GMT application, which can do the following: @@ -25,7 +23,7 @@ * * First argument to the gmt function is the API pointer, but it is optional once created. * Next argument is the module name - * Thrid argument is the option string + * Third argument is the option string * Finally, there are optional comma-separated MATLAB array entities required by the command. * Information about the options of each program is provided via GMT_Encode_Options. * @@ -37,7 +35,12 @@ #include "gmtmex.h" +#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 +extern int gmt_get_V (char arg); /* Temporary here to allow full debug messaging */ +#else extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ +#define gmt_get_V GMT_get_V /* Old name */ +#endif #ifndef SINGLE_SESSION /* Being declared external we can access it between MEX calls */ @@ -93,7 +96,7 @@ static void *Initiate_Session (unsigned int verbose) { static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior - when we do for example (i.e. no lhs): sqrt([4 9]) + when we do for example (i.e. no lhs): sqrt([4 9]) */ void *ptr = NULL; switch (X->family) { @@ -145,14 +148,14 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { char message[128] = {""}; sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); - mexPrintf (message); + mexPrintf (message); } else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { char message[128] = {""}; sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); - mexErrMsgTxt (message); + mexErrMsgTxt (message); } /* 0. No arguments at all results in the GMT banner message */ @@ -162,7 +165,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } /* 1. Check for the special commands create and help */ - + if (nrhs == 1) { /* This may be create or help */ cmd = mxArrayToString (prhs[0]); if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); @@ -183,10 +186,10 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); return; } - if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = GMT_get_V (gtxt[2]); + if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = gmt_get_V (gtxt[2]); API = Initiate_Session (verbose); /* Initializing a new GMT session */ - if (nlhs) { /* Return the API adress as an integer (nlhs == 1 here) )*/ + if (nlhs) { /* Return the API address as an integer (nlhs == 1 here) )*/ plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); pti = mxGetData(plhs[0]); *pti = *pPersistent; @@ -203,7 +206,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } else API = (void *)pPersistent[0]; /* Get the GMT API pointer */ - if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is currupted. Better to start from scratch.\n"); + if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is corrupted. Better to start from scratch.\n"); } else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ @@ -212,9 +215,10 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ } else { /* We still don't have the API, so we must get it from the past or initiate a new session */ - if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { API = Initiate_Session (verbose); /* Initializing new GMT session */ mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } #endif } @@ -241,14 +245,14 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } /* 2. Get module name and separate out args */ - + /* Here we have a GMT module call. The documented use is to give the module name separately from * the module options, but users may forget and combine the two. So we check both cases. */ - + n_in_objects = nrhs - 1; str_length = strlen (cmd); /* Length of module (or command) argument */ for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ - + if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ strcpy (module, cmd); /* Isolate the module name in this string */ if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ @@ -268,7 +272,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } - /* See if info about instalation is required */ + /* See if info about installation is required */ if (!strcmp(module, "gmt")) { char t[256] = {""}; if (!opt_args) { @@ -299,11 +303,11 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { /* Make sure this is a valid module */ if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ - mexErrMsgTxt ("GMT: No module by that name was found.\n"); - + mexErrMsgTxt ("GMT: No module by that name was found.\n"); + /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to * another string with enough space. */ - + if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ @@ -312,7 +316,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { else strcpy (opt_buffer, "-F"); } - + /* 2++ If gmtwrite then add -T? with correct object type */ if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ char targ[5] = {" -T?"}; @@ -329,7 +333,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ options = GMT_Create_Options (API, 0, "-?"); - + /* 4. Preprocess to update GMT option lists and return info array X */ if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { if (n_items == UINT_MAX) /* Just got usage/synopsis option */ @@ -337,15 +341,15 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { else mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); } - + if (options) { /* Only for debugging - remove this section when stable */ gtxt = GMT_Create_Cmd (API, options); GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ } - + /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ - + for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ if (X[k].direction == GMT_IN) { if ((X[k].pos+first+1) < (unsigned int)nrhs) @@ -363,14 +367,12 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } GMTMEX_Set_Object (API, &X[k], ptr); /* Set object pointer */ } - + /* 6. Run GMT module; give usage message if errors arise during parsing */ status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); if (status != GMT_NOERROR) { - if (status == GMT_MODULE_USAGE || status == GMT_MODULE_SYNOPSIS || status == GMT_MODULE_LIST || - status == GMT_MODULE_EXIST || status == GMT_MODULE_PURPOSE) { + if (status <= GMT_MODULE_PURPOSE) return; - } else { mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); mexErrMsgTxt("GMT: exiting\n"); @@ -378,7 +380,7 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { } /* 7. Hook up any GMT outputs to MATLAB plhs array */ - + for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ @@ -390,21 +392,21 @@ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { GMT_Set_Default (API, "API_PAD", "2"); /* 8. Free all GMT containers involved in this module call */ - + for (k = 0; k < n_items; k++) { void *ppp = X[k].object; if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) mexErrMsgTxt ("GMT: Failed to close virtual file\n"); if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); - else { /* Success, now make sure we dont destroy the same pointer more than once */ + else { /* Success, now make sure we don't destroy the same pointer more than once */ for (size_t kk = k+1; kk < n_items; kk++) if (X[kk].object == ppp) X[kk].object = NULL; } } /* 9. Destroy linked option list */ - + if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); #ifdef SINGLE_SESSION diff --git a/src/gmtmex/gmtmex.h b/src/gmtmex/gmtmex.h index bf7acd72d74..ade887e5201 100644 --- a/src/gmtmex/gmtmex.h +++ b/src/gmtmex/gmtmex.h @@ -1,6 +1,4 @@ /* - * $Id$ - * * Copyright (c) 2015-2020 by P. Wessel and J. Luis * See LICENSE.TXT file for copying and redistribution conditions. * @@ -13,7 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * Contact info: www.soest.hawaii.edu/pwessel + * Contact info: www.generic-mapping-tools.org *--------------------------------------------------------------------*/ /* GMT convenience functions used by MATLAB/OCTAVE mex/oct API * We also define the MEX structures used to pass data in/out of GMT. @@ -30,10 +28,10 @@ #define GMTMEX_GMT_MAJOR_VERSION 6 #define GMTMEX_GMT_MINOR_VERSION 1 -#define GMTMEX_GMT_PATCH_VERSION 0 +#define GMTMEX_GMT_PATCH_VERSION 1 -#include "../gmt.h" -#include "../gmt_version.h" +#include "gmt.h" +#include "gmt_version.h" #include #include #include diff --git a/src/gmtmex/gmtmex_parser.c b/src/gmtmex/gmtmex_parser.c index 1612e49eda4..089cda8f2c8 100644 --- a/src/gmtmex/gmtmex_parser.c +++ b/src/gmtmex/gmtmex_parser.c @@ -1,6 +1,4 @@ /* - * $Id$ - * * Copyright (c) 2015-2020 by P. Wessel and J. Luis * See LICENSE.TXT file for copying and redistribution conditions. * @@ -13,7 +11,7 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * - * Contact info: www.soest.hawaii.edu/gmt + * Contact info: www.generic-mapping-tools.org *--------------------------------------------------------------------*/ /* This layer of code handles the interface between GMT objects and how * we represent them in Matlab/Octave. @@ -224,7 +222,7 @@ static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); return (D_struct); } - + for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) /* Count non-zero segments */ for (seg = 0; seg < D->table[tbl]->n_segments; seg++) if (D->table[tbl]->segment[seg]->n_rows) @@ -282,7 +280,7 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { uint64_t k, *length = NULL; unsigned int *mode = NULL; mxArray *P_struct = NULL, *mxptr[N_MEX_FIELDNAMES_PS], *mxstring = NULL; - + if (P == NULL) /* Safety valve */ mexErrMsgTxt ("gmtmex_get_postscript: programming error, input POSTSCRIPT struct P is NULL or data string is empty\n"); @@ -291,7 +289,7 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); return P_struct; } - + /* Return PS with postscript and length in a struct */ P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); @@ -301,15 +299,15 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { mxptr[3] = mxCreateCellMatrix (P->n_headers, P->n_headers ? 1 : 0); length = (uint64_t *)mxGetData(mxptr[1]); mode = (uint32_t *)mxGetData(mxptr[2]); - + length[0] = (uint64_t)P->n_bytes; /* Set length of the PS string */ mode[0] = (uint32_t)P->mode; /* Set mode of the PS string */ - + for (k = 0; k < P->n_headers; k++) { mxstring = mxCreateString (P->header[k]); mxSetCell (mxptr[3], (int)k, mxstring); } - + for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) mxSetField (P_struct, 0, GMTMEX_fieldname_ps[k], mxptr[k]); @@ -346,7 +344,7 @@ static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); return (C_struct); } - + /* Return CPT via colormap, range, and alpha arrays in a struct */ /* Create a MATLAB struct for this CPT */ C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); @@ -363,7 +361,7 @@ static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { mxptr[8] = NULL; /* Set below */ mxptr[9] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); mxptr[10] = mxCreateCellMatrix (C->n_headers, C->n_headers ? 1 : 0); - + color = mxGetPr (mxptr[0]); alpha = mxGetPr (mxptr[1]); range = mxGetPr (mxptr[2]); @@ -431,7 +429,7 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); mxptr[5] = mxCreateDoubleScalar ((double)I->header->registration); - mxptr[6] = mxCreateDoubleScalar ((double)I->header->nan_value); + mxptr[6] = mxCreateDoubleScalar ((double)I->header->nan_value); mxptr[7] = mxCreateString (I->header->title); mxptr[8] = mxCreateString (I->header->remark); mxptr[9] = mxCreateString (I->header->command); @@ -461,7 +459,7 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { color[k] = (uint8_t)I->colormap[k]; k /= 4; memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); - } + } else if (I->header->n_bands == 1) { /* gray image */ mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); u = mxGetData (mxptr[0]); @@ -483,7 +481,7 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { if (I->alpha) { mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); alpha = mxGetData (mxptr[15]); - memcpy (alpha, I->alpha, I->header->nm * sizeof (uint8_t)); + memcpy (alpha, I->alpha, I->header->nm * sizeof (uint8_t)); } } else if (I->header->n_bands == 4) { /* RGBA image, with a color map */ @@ -492,8 +490,8 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { u = mxGetData (mxptr[0]); mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); alpha = mxGetData (mxptr[15]); - memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); - memcpy (alpha, &(I->data)[3 * I->header->nm], I->header->nm * sizeof (uint8_t)); + memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); + memcpy (alpha, &(I->data)[3 * I->header->nm], I->header->nm * sizeof (uint8_t)); /* for (k = 0; k < I->header->nm; k++) { for (m = 0; m < 3; m++) @@ -611,7 +609,7 @@ static struct GMT_GRID *gmtmex_grid_init (void *API, unsigned int direction, uns free (str); } mx_ptr = mxGetField (ptr, 0, "wkt"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ char *str = malloc(mxGetN(mx_ptr) + 1); mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); G->header->ProjRefWKT = GMT_Duplicate_String (API, str); @@ -798,14 +796,14 @@ static struct GMT_IMAGE *gmtmex_image_init (void *API, unsigned int direction, u I->header->nan_value = *(float *)mxGetData (mx_ptr); mx_ptr = mxGetField (ptr, 0, "proj4"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this lenght */ + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this length */ char *str = malloc(mxGetN(mx_ptr) + 1); mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); I->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); free (str); } mx_ptr = mxGetField (ptr, 0, "wkt"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more than this length */ char *str = malloc(mxGetN(mx_ptr) + 1); mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); I->header->ProjRefWKT = GMT_Duplicate_String (API, str); @@ -939,7 +937,7 @@ static void *gmtmex_dataset_init (void *API, unsigned int direction, unsigned in /* 1. A dataset MATLAB structure or array of structures. * 2. A Cell array of plain text strings for a text-only file. * 3. A single text string instead of a one-item cell array of strings. */ - + if (mxIsStruct (ptr)) { /* Got the dataset structure */ dim[GMT_SEG] = mxGetM (ptr); /* Number of segments */ if (dim[GMT_SEG] == 0) mexErrMsgTxt ("gmtmex_dataset_init: Input has zero segments where it can't be.\n"); @@ -1060,7 +1058,7 @@ static void *gmtmex_dataset_init (void *API, unsigned int direction, unsigned in /* Now we have the length of this segment */ S = GMT_Alloc_Segment (API, GMT_WITH_STRINGS, dim[GMT_ROW], 0, buffer, D->table[0]->segment[seg]); for (row = 0; row < S->n_rows; row++) { /* Hook up the string records */ - mx_ptr = mxGetCell (ptr, (mwSize)(k+row)); /* k is the offset to 1st record of current segment in inpu cell array */ + mx_ptr = mxGetCell (ptr, (mwSize)(k+row)); /* k is the offset to 1st record of current segment in input cell array */ txt = mxArrayToString (mx_ptr); S->text[row] = GMT_Duplicate_String (API, txt); } @@ -1115,7 +1113,7 @@ static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int directio if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_cpt[k])) == NULL) gmtmex_quit_if_missing ("gmtmex_palette_init", GMTMEX_fieldname_cpt[k]); } - + dim[0] = mxGetM (mx_ptr[0]); /* Number of rows in colormap */ if (dim[0] < 1) mexErrMsgTxt ("gmtmex_palette_init: Colormap array has no CPT values\n"); @@ -1129,6 +1127,10 @@ static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int directio cpt = mxGetData (mx_ptr[7]); cyclic = mxGetData (mx_ptr[9]); + /* Disable unused-but-set-variable warnings */ + (void)(minmax); + (void)(colormap); + dim[1] = mxGetM (mx_ptr[2]); /* Length of range array */ if (dim[0] > dim[1]) { /* This only happens when we have a continuous color table */ dim[1] = dim[0]; /* Actual length of colormap array */ @@ -1154,7 +1156,7 @@ static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int directio for (k = 0; k < 3; k++) P->bfn[j].rgb[k] = bfn[j+k*3]; } - for (j = 0; j < P->n_colors; j++) { /* OK to access j+1'th elemenent since length of colormap is P->n_colors+1 */ + for (j = 0; j < P->n_colors; j++) { /* OK to access j+1'th element since length of colormap is P->n_colors+1 */ for (k = 0; k < 3; k++) { P->data[j].rgb_low[k] = cpt[j+k*dim[0]]; P->data[j].rgb_high[k] = cpt[j+(k+3)*dim[0]]; @@ -1212,7 +1214,7 @@ static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_ps[k])) == NULL) gmtmex_quit_if_missing ("gmtmex_ps_init", GMTMEX_fieldname_ps[k]); } - + length = mxGetData (mx_ptr[1]); if (length[0] == 0) mexErrMsgTxt ("gmtmex_ps_init: Dimension of PostScript given as zero\n"); From c1ee290ec648af633326109e5c37ea4e5bc35893 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 2 Apr 2021 17:51:54 -1000 Subject: [PATCH 07/45] Update ConfigReleaseBuild.cmake --- admin/ConfigReleaseBuild.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/admin/ConfigReleaseBuild.cmake b/admin/ConfigReleaseBuild.cmake index dde5289c61c..b9674e3d435 100644 --- a/admin/ConfigReleaseBuild.cmake +++ b/admin/ConfigReleaseBuild.cmake @@ -13,6 +13,8 @@ set (DCW_ROOT "$ENV{GMT_DCW_SOURCE}") set (GMT_ENABLE_OPENMP TRUE) set (GMT_PUBLIC_RELEASE TRUE) +# Include special gmtmex supplement for the GMT/MEX toolbox [which requires MATLAB] +set (GMT_BUILD_GMTMEX TRUE) # recommended even for release build set (CMAKE_C_FLAGS "-Wall -Wdeclaration-after-statement ${CMAKE_C_FLAGS}") From 2b203c50e25911f48293201b4577542bc72570c6 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 10:41:16 -1000 Subject: [PATCH 08/45] Consolidate gmtmex as supplement --- src/CMakeLists.txt | 14 +- src/gmtmex/gmtmex.c | 425 +------------ src/gmtmex/gmtmex.h | 180 ------ src/gmtmex/{gmtmex_parser.c => gmtmex_api.c} | 611 ++++++++++++++++++- 4 files changed, 604 insertions(+), 626 deletions(-) delete mode 100644 src/gmtmex/gmtmex.h rename src/gmtmex/{gmtmex_parser.c => gmtmex_api.c} (70%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a325d74837a..9fd037867b6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -379,7 +379,7 @@ set (GMT_PSL_SRCS postscriptlight.c declspec.h psl_config.h PSL_Standard+.h set (GMT_MBSYSTEM_SRCS gmt_mbsystem_glue.c) if (GMT_BUILD_GMTMEX) - set (GMT_GMTMEX_SRCS gmtmex/gmtmex_parser.c gmtmex/gmtmex.c gmtmex/gmtmex.h) + set (GMT_GMTMEX_SRCS gmtmex/gmtmex_api.c) endif (GMT_BUILD_GMTMEX) # libgmt @@ -574,12 +574,12 @@ set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) if (GMT_BUILD_GMTMEX) # Just need to add a symbolic link with the right name to libgmt - set( gmtmex_link ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/gmtmex.${MEX_EXT}) - set( gmtmex_target ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/${LIB_PREFIX}gmt.${GMT_LIB_SOVERSION}.${CMAKE_SHARED_LIBRARY_SUFFIX}) - add_custom_command ( OUTPUT ${gmtmex_link} POST_BUILD - COMMAND ln -s ${gmtmex_target} ${gmtmex_link} - DEPENDS install ${gmtmex_target} - COMMENT "Generating gmtmex symbolic link") + #set( gmtmex_link ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/gmtmex.${MEX_EXT}) + #set( gmtmex_target ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/${LIB_PREFIX}gmt.${GMT_LIB_SOVERSION}.${CMAKE_SHARED_LIBRARY_SUFFIX}) + #add_custom_command ( OUTPUT ${gmtmex_link} POST_BUILD + # COMMAND ln -s ${gmtmex_target} ${gmtmex_link} + # DEPENDS install ${gmtmex_target} + # COMMENT "Generating gmtmex symbolic link") # Install the driver script install (PROGRAMS gmtmex/gmt.m DESTINATION ${GMT_BINDIR} diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index b27c371855d..38f907c6a7f 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -1,417 +1,24 @@ -/*-------------------------------------------------------------------- - * Copyright (c) 2015-2020 by P. Wessel and J. Luis - * See LICENSE.TXT file for copying and redistribution conditions. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 3 or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * Contact info: www.generic-mapping-tools.org - *--------------------------------------------------------------------*/ /* - * This is the MATLAB/Octave(mex) GMT application, which can do the following: - * 1) Create a new session and optionally return the API pointer. We provide for - * storing the pointer as a global variable (persistent) between calls. - * 2) Destroy a GMT session, either given the API pointer or by fetching it from - * the global (persistent) variable. - * 3) Call any of the GMT modules while passing data in and out of GMT. + * Copyright (c) 2015-2021 by P. Wessel and J. Luis + * See LICENSE.TXT file for copying and redistribution conditions. * - * First argument to the gmt function is the API pointer, but it is optional once created. - * Next argument is the module name - * Third argument is the option string - * Finally, there are optional comma-separated MATLAB array entities required by the command. - * Information about the options of each program is provided via GMT_Encode_Options. + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; version 3 or any later version. * - * GMT Version: 6.x - * Created: 20-OCT-2017 - * Updated: 1-JUL-2020 requires GMT 6.1.x + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. * + * Contact info: www.generic-mapping-tools.org */ -#include "gmtmex.h" - -#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 -extern int gmt_get_V (char arg); /* Temporary here to allow full debug messaging */ -#else -extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ -#define gmt_get_V GMT_get_V /* Old name */ -#endif - -#ifndef SINGLE_SESSION -/* Being declared external we can access it between MEX calls */ -static uintptr_t *pPersistent; /* To store API address back and forth within a single MATLAB session */ - -/* Here is the exit function, which gets run when the MEX-file is - cleared and when the user exits MATLAB. The mexAtExit function - should always be declared as static. */ -static void force_Destroy_Session (void) { - void *API = (void *)pPersistent[0]; /* Get the GMT API pointer */ - if (API != NULL) { /* Otherwise just silently ignore this call */ - if (GMT_Destroy_Session (API)) mexErrMsgTxt ("Failure to destroy GMT session\n"); - *pPersistent = 0; /* Wipe the persistent memory */ - } -} -#endif - -static void usage (int nlhs, int nrhs) { - /* Basic usage message */ - if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ - mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", - MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); - mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); - mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); - mexPrintf("You may redistribute copies of this program under the terms of the\n"); - mexPrintf("GNU Lesser General Public License.\n"); - mexPrintf("For more information about these matters, see the file named LICENSE.TXT.\n"); - mexPrintf("For a brief description of GMT modules, type gmt ('help')\n\n"); - } - else { - mexPrintf("Usage is:\n\tgmt ('module_name', 'options'[, ]); %% Run a GMT module\n"); - if (nlhs != 0) - mexErrMsgTxt ("But meanwhile you already made an error by asking help and an output.\n"); - } -} - -static void *Initiate_Session (unsigned int verbose) { - /* Initialize the GMT Session and store the API pointer in a persistent variable */ - void *API = NULL; - /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ - /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ - if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + - GMT_SESSION_COLMAJOR, GMTMEX_print_func)) == NULL) - mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); - -#ifndef SINGLE_SESSION - if (!pPersistent) pPersistent = mxMalloc(sizeof(uintptr_t)); - pPersistent[0] = (uintptr_t)(API); - mexMakeMemoryPersistent (pPersistent); -#endif - return (API); -} - -static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { - /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior - when we do for example (i.e. no lhs): sqrt([4 9]) - */ - void *ptr = NULL; - switch (X->family) { - case GMT_IS_GRID: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); - break; - case GMT_IS_IMAGE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); - break; - case GMT_IS_DATASET: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); - break; - case GMT_IS_PALETTE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); - break; - case GMT_IS_POSTSCRIPT: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); - break; - default: - break; - } - return ptr; -} - -/* This is the function that is called when we type gmt in MATLAB/Octave */ +/* 1. Must include mex.h since we use mxArray */ +#include "mex.h" +/* 2. Must list extern GMTMEX mexfunction available from libgmt */ +extern void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]); +/* 3. The minimal gateway function called by Matlab */ void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { - int status = 0; /* Status code from GMT API */ - int n_in_objects = 0; /* Number of input objects passed to module */ - unsigned int first = 0; /* Array ID of first command argument (not 0 when API-ID is first) */ - unsigned int verbose = 0; /* Default verbose setting */ - unsigned int n_items = 0, pos = 0; /* Number of MATLAB arguments (left and right) */ - size_t str_length = 0, k = 0; /* Misc. counters */ - void *API = NULL; /* GMT API control structure */ - struct GMT_OPTION *options = NULL; /* Linked list of module options */ - struct GMT_RESOURCE *X = NULL; /* Array of information about MATLAB args */ - char *cmd = NULL; /* Pointer used to get the user's MATLAB command */ - char *gtxt = NULL; /* For debug printing of revised command */ - char *opt_args = NULL; /* Pointer to the user's module options */ - char module[MODULE_LEN] = {""}; /* Name of GMT module to call */ - char opt_buffer[BUFSIZ] = {""}; /* Local copy of command line options */ - void *ptr = NULL; -#ifndef SINGLE_SESSION - uintptr_t *pti = NULL; /* To locally store the API address */ -#endif - - /* -1. Check that the GMT library is of a suitable version for this GMTMEX version */ - - if (GMT_MAJOR_VERSION > GMTMEX_GMT_MAJOR_VERSION) { /* This may not work if, for instance, we want >= 6.1.x but is using GMT 7.0.0 */ - char message[128] = {""}; - sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", - GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); - mexPrintf (message); - } - else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { - char message[128] = {""}; - sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", - GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, - GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); - mexErrMsgTxt (message); - } - - /* 0. No arguments at all results in the GMT banner message */ - if (nrhs == 0) { - usage (nlhs, nrhs); - return; - } - - /* 1. Check for the special commands create and help */ - - if (nrhs == 1) { /* This may be create or help */ - cmd = mxArrayToString (prhs[0]); - if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); - if (!strncmp (cmd, "help", 4U) || !strncmp (cmd, "--help", 6U)) { - usage (nlhs, 1); - return; - } -#ifndef SINGLE_SESSION - if (!strncmp (cmd, "create", 6U)) { /* Asked to create a new GMT session */ - if (nlhs > 1) /* Asked for too much output, only 1 or 0 is allowed */ - mexErrMsgTxt ("GMT: Usage: gmt ('create') or API = gmt ('create');\n"); - if (pPersistent) /* See if have a GMT API pointer */ - API = (void *)pPersistent[0]; - if (API != NULL) { /* If another session still exists */ - GMT_Report (API, GMT_MSG_VERBOSE, - "GMT: A previous GMT session is still active. Ignoring your 'create' request.\n"); - if (nlhs) /* Return nothing */ - plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); - return; - } - if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = gmt_get_V (gtxt[2]); - API = Initiate_Session (verbose); /* Initializing a new GMT session */ - - if (nlhs) { /* Return the API address as an integer (nlhs == 1 here) )*/ - plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); - pti = mxGetData(plhs[0]); - *pti = *pPersistent; - } - - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - return; - } - - /* OK, neither create nor help, must be a single command with no arguments nor the API. So get it: */ - if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { /* No session yet, create one under the hood */ - API = Initiate_Session(verbose); /* Initializing a new GMT session */ - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - } - else - API = (void *)pPersistent[0]; /* Get the GMT API pointer */ - if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is corrupted. Better to start from scratch.\n"); - } - else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { - /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ - pti = (uintptr_t *)mxGetData(prhs[0]); - API = (void *)pti[0]; /* Get the GMT API pointer */ - first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ - } - else { /* We still don't have the API, so we must get it from the past or initiate a new session */ - if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { - API = Initiate_Session (verbose); /* Initializing new GMT session */ - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - } -#endif - } - -#ifdef SINGLE_SESSION - /* Initiate a new session */ - API = Initiate_Session (verbose); /* Initializing new GMT session */ -#endif - - if (!cmd) { /* First argument is the command string, e.g., 'blockmean -R0/5/0/5 -I1' or just 'destroy' */ - cmd = mxArrayToString(prhs[first]); - if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string but is probably a cell array of strings.\n"); - } - - if (!strncmp (cmd, "destroy", 7U)) { /* Destroy the session */ -#ifndef SINGLE_SESSION - if (nlhs != 0) - mexErrMsgTxt ("GMT: Usage is gmt ('destroy');\n"); - - if (GMT_Destroy_Options (API, &options)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); - if (GMT_Destroy_Session (API)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); - *pPersistent = 0; /* Wipe the persistent memory */ -#endif - return; - } - - /* 2. Get module name and separate out args */ - - /* Here we have a GMT module call. The documented use is to give the module name separately from - * the module options, but users may forget and combine the two. So we check both cases. */ - - n_in_objects = nrhs - 1; - str_length = strlen (cmd); /* Length of module (or command) argument */ - for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ - - if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ - strcpy (module, cmd); /* Isolate the module name in this string */ - if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ - first++; /* Since we have a 2nd string to skip now */ - opt_args = mxArrayToString (prhs[first]); - n_in_objects--; - } - /* Else we got no options, just input objects */ - } - else { /* Case b2. Get mex arguments, if any, and extract the GMT module name */ - if (k >= MODULE_LEN) - mexErrMsgTxt ("GMT: Module name in command is too long\n"); - strncpy (module, cmd, k); /* Isolate the module name in this string */ - - while (cmd[k] == ' ') k++; /* Skip any spaces between module name and start of options */ - if (cmd[k]) opt_args = &cmd[k]; - } - - - /* See if info about installation is required */ - if (!strcmp(module, "gmt")) { - char t[256] = {""}; - if (!opt_args) { - mexPrintf("Warning: calling the 'gmt' program by itself does nothing here.\n"); - return; - } - if (!strcmp(opt_args, "--show-bindir")) /* Show the directory that contains the 'gmt' executable */ - GMT_Get_Default (API, "BINDIR", t); - else if (!strcmp(opt_args, "--show-sharedir")) /* Show share directory */ - GMT_Get_Default (API, "SHAREDIR", t); - else if (!strcmp(opt_args, "--show-datadir")) /* Show the data directory */ - GMT_Get_Default (API, "DATADIR", t); - else if (!strcmp(opt_args, "--show-plugindir")) /* Show the plugin directory */ - GMT_Get_Default (API, "PLUGINDIR", t); - else if (!strcmp(opt_args, "--show-cores")) /* Show number of cores */ - GMT_Get_Default (API, "CORES", t); - - if (t[0] != '\0') { - if (nlhs) - plhs[0] = mxCreateString (t); - else - mexPrintf ("%s\n", t); - } - else - mexPrintf ("Warning: called the 'gmt' program with unknown option.\n"); - return; - } - - /* Make sure this is a valid module */ - if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ - mexErrMsgTxt ("GMT: No module by that name was found.\n"); - - /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to - * another string with enough space. */ - - if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ - /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ - if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ - if (opt_args) - strcat (opt_buffer, " -F"); - else - strcpy (opt_buffer, "-F"); - } - - /* 2++ If gmtwrite then add -T? with correct object type */ - if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ - char targ[5] = {" -T?"}; - targ[3] = GMTMEX_objecttype (prhs[nrhs-1]); - strcat (opt_buffer, targ); - } - /* 2+++ If gmtread -Ti then temporarily set pad to 0 since we don't want padding in image arrays */ - else if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) - GMT_Set_Default(API, "API_PAD", "0"); - - /* 3. Convert mex command line arguments to a linked GMT option list */ - if (opt_buffer[0] && (options = GMT_Create_Options (API, 0, opt_buffer)) == NULL) - mexErrMsgTxt ("GMT: Failure to parse GMT5 command options\n"); - - if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ - options = GMT_Create_Options (API, 0, "-?"); - - /* 4. Preprocess to update GMT option lists and return info array X */ - if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { - if (n_items == UINT_MAX) /* Just got usage/synopsis option */ - n_items = 0; - else - mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); - } - - if (options) { /* Only for debugging - remove this section when stable */ - gtxt = GMT_Create_Cmd (API, options); - GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); - GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ - } - - /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ - - for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ - if (X[k].direction == GMT_IN) { - if ((X[k].pos+first+1) < (unsigned int)nrhs) - ptr = (void *)prhs[X[k].pos+first+1]; - else - mexErrMsgTxt ("GMT: Attempting to address a prhs entry that does not exist\n"); - } - else { - if ((X[k].pos) < nlhs) - ptr = (void *)plhs[X[k].pos]; - else { - //mexErrMsgTxt ("GMT: Attempting to address a plhs entry that does not exist\n"); - ptr = alloc_default_plhs (API, &X[k]); - } - } - GMTMEX_Set_Object (API, &X[k], ptr); /* Set object pointer */ - } - - /* 6. Run GMT module; give usage message if errors arise during parsing */ - status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); - if (status != GMT_NOERROR) { - if (status <= GMT_MODULE_PURPOSE) - return; - else { - mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); - mexErrMsgTxt("GMT: exiting\n"); - } - } - - /* 7. Hook up any GMT outputs to MATLAB plhs array */ - - for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ - if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ - pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ - plhs[pos] = GMTMEX_Get_Object (API, &X[k]); /* Hook mex object onto rhs list */ - } - - /* 2++- If gmtread -Ti then reset the sessions pad value that was temporarily changed above (2+++) */ - if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) - GMT_Set_Default (API, "API_PAD", "2"); - - /* 8. Free all GMT containers involved in this module call */ - - for (k = 0; k < n_items; k++) { - void *ppp = X[k].object; - if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failed to close virtual file\n"); - if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); - else { /* Success, now make sure we don't destroy the same pointer more than once */ - for (size_t kk = k+1; kk < n_items; kk++) - if (X[kk].object == ppp) X[kk].object = NULL; - } - } - - /* 9. Destroy linked option list */ - - if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); -#ifdef SINGLE_SESSION - if (GMT_Destroy_Session (API)) - mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); -#endif - return; + GMT_mexFunction (nlhs, plhs, nrhs, prhs); } diff --git a/src/gmtmex/gmtmex.h b/src/gmtmex/gmtmex.h deleted file mode 100644 index ade887e5201..00000000000 --- a/src/gmtmex/gmtmex.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (c) 2015-2020 by P. Wessel and J. Luis - * See LICENSE.TXT file for copying and redistribution conditions. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 3 or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * Contact info: www.generic-mapping-tools.org - *--------------------------------------------------------------------*/ -/* GMT convenience functions used by MATLAB/OCTAVE mex/oct API - * We also define the MEX structures used to pass data in/out of GMT. - */ - -#ifndef GMTMEX_H -#define GMTMEX_H - -#define GMTMEX_MAJOR_VERSION 2 -#define GMTMEX_MINOR_VERSION 0 -#define GMTMEX_PATCH_VERSION 0 - -/* Define the minimum GMT version suitable for this GMTMEX version */ - -#define GMTMEX_GMT_MAJOR_VERSION 6 -#define GMTMEX_GMT_MINOR_VERSION 1 -#define GMTMEX_GMT_PATCH_VERSION 1 - -#include "gmt.h" -#include "gmt_version.h" -#include -#include -#include - -#ifdef GMT_OCTOCT -# include -#else -# include -# define mxIsScalar_(mx) \ - ( (2 == mxGetNumberOfDimensions(mx)) \ - && (1 == mxGetM(mx))&& (1 == mxGetN(mx)) ) -#endif /* Matlab and Octave(mex) */ - -/* Matlab and Octave (in -mex mode) are identical, oct files are different and not yet tested */ - -/* Older Ml versions don't have mwSize */ -#ifndef GMT_OCTMEX -#if !defined(MATLAB_VERSION) -#if !defined(MWSIZE_MAX) -# define MATLAB_VERSION 0x2006a /* R2006a or earlier */ -#elif MX_API_VER < 0x07040000 -# define MATLAB_VERSION 0x2006b /* R2006b */ -#elif !defined(FMT_PTRDIFF_T) -# define MATLAB_VERSION 0x2007a /* R2007a */ -#elif !defined(CUINT64_T) -# define MATLAB_VERSION 0x2007b /* R2007b */ -#elif defined(mxSetLogical) -# define MATLAB_VERSION 0x2008a /* R2008a */ -#else -# if !defined(blas_h) -# include "blas.h" -# endif -# if !defined(lapack_h) -# include "lapack.h" -# endif -# if !defined(MATHWORKS_MATRIX_MATRIX_PUB_FWD_H) -# if defined(CHAR16_T) -# if !defined(COMPLEX_TYPES) -# define MATLAB_VERSION 0x2008b /* R2008b */ -# elif !defined(cgeqr2p) -# define MATLAB_VERSION 0x2010b /* R2010b */ -# else -# define MATLAB_VERSION 0x2011a /* R2011a */ -# endif -# else -# include "emlrt.h" -# define MATLAB_VERSION EMLRT_VERSION_INFO /* R2011b or later */ -# endif -# else -# if !defined(COMPLEX_TYPES) -# define MATLAB_VERSION 0x2009a /* R2009a */ -# elif !defined(cgbequb) -# define MATLAB_VERSION 0x2009b /* R2009b */ -# else -# define MATLAB_VERSION 0x2010a /* R2010a */ -# endif -# endif -#endif -#endif /* if !defined(MATLAB_VERSION) */ - -#if MATLAB_VERSION < 0x2006b -typedef int mwSize; -#endif -#endif - -#ifdef GMT_OCTOCT /* Octave oct files only */ -# define MEX_PROG "Octave(oct)" -# define MEX_COL_ORDER GMT_IS_ROW_FORMAT - /* Macros for getting the Octave(oct) ij that correspond to (row,col) [no pad involved] */ - /* This one operates on GMT_MATRIX */ -# define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) - /* And this on GMT_GRID */ -# define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) -#else /* Here we go for Matlab or Octave(mex) */ -# ifdef GMT_MATLAB -# define MEX_PROG "Matlab" -# else -# define MEX_PROG "Octave(mex)" -# endif -# define MEX_COL_ORDER GMT_IS_COL_FORMAT - /* Macros for getting the Matlab/Octave(mex) ij that correspond to (row,col) [no pad involved] */ - /* This one operates on GMT_MATRIX */ -# define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) - /* And this on GMT_GRID */ -# define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) -#endif - -/* Definitions of MEX structures used to hold GMT objects. - * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ - -/* GMT_IS_DATASET: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. Pure datasets will only set the data - * matrix and leave the text cell array empty, while textsets - * will fill out both. Only the first segment will have any - * information in the info and projection_ref_* items. */ - -#define N_MEX_FIELDNAMES_DATASET 6 -static const char *GMTMEX_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = - {"data", "text", "header", "comment", "proj4", "wkt"}; - -/* GMT_IS_GRID: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_GRID 17 -static const char *GMTMEX_fieldname_grid[N_MEX_FIELDNAMES_GRID] = - {"z", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", - "command", "datatype", "x_unit", "y_unit", "z_unit", "layout", "proj4", "wkt"}; - -/* GMT_IS_IMAGE: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_IMAGE 19 -static const char *GMTMEX_fieldname_image[N_MEX_FIELDNAMES_IMAGE] = - {"image", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", "command", - "datatype", "x_unit", "y_unit", "z_unit", "colormap", "alpha", "layout", "proj4", "wkt"}; - -/* GMT_IS_PALETTE: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_CPT 11 -static const char *GMTMEX_fieldname_cpt[N_MEX_FIELDNAMES_CPT] = - {"colormap", "alpha", "range", "minmax", "bfn", "depth", "hinge", "cpt", "model", "mode", "comment"}; - -/* GMT_IS_POSTSCRIPT: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_PS 4 -static const char *GMTMEX_fieldname_ps[N_MEX_FIELDNAMES_PS] = - {"postscript", "length", "mode", "comment"}; - -/* Macro for indexing into a GMT grid [with pad] */ -#define GMT_IJP(h,row,col) ((uint64_t)(((int64_t)(row)+(int64_t)h->pad[GMT_YHI])*((int64_t)h->mx)+(int64_t)(col)+(int64_t)h->pad[GMT_XLO])) - -#define MODULE_LEN 32 /* Max length of a GMT module name */ - -/* These 4 functions are used by gmtmex.c: */ -EXTERN_MSC char GMTMEX_objecttype (const mxArray *ptr); -EXTERN_MSC int GMTMEX_print_func (FILE *fp, const char *message); -EXTERN_MSC void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr); -EXTERN_MSC void * GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X); -#endif diff --git a/src/gmtmex/gmtmex_parser.c b/src/gmtmex/gmtmex_api.c similarity index 70% rename from src/gmtmex/gmtmex_parser.c rename to src/gmtmex/gmtmex_api.c index 089cda8f2c8..a69f29835aa 100644 --- a/src/gmtmex/gmtmex_parser.c +++ b/src/gmtmex/gmtmex_api.c @@ -13,13 +13,44 @@ * * Contact info: www.generic-mapping-tools.org *--------------------------------------------------------------------*/ -/* This layer of code handles the interface between GMT objects and how - * we represent them in Matlab/Octave. +/* + * This is the MATLAB/Octave(mex) GMT application, which can do the following: + * 1) Create a new session and optionally return the API pointer. We provide for + * storing the pointer as a global variable (persistent) between calls. + * 2) Destroy a GMT session, either given the API pointer or by fetching it from + * the global (persistent) variable. + * 3) Call any of the GMT modules while passing data in and out of GMT. + * + * First argument to the gmt function is the API pointer, but it is optional once created. + * Next argument is the module name + * Third argument is the option string + * Finally, there are optional comma-separated MATLAB array entities required by the command. + * Information about the options of each program is provided via GMT_Encode_Options. + * + * GMT Version: 6.x + * Created: 20-OCT-2017 + * Updated: 1-APR-2021 requires GMT 6.2.x + * + * + * GMT convenience functions used by MATLAB/OCTAVE mex/oct API + * We also define the MEX structures used to pass data in/out of GMT. */ +#define GMTMEX_MAJOR_VERSION 2 +#define GMTMEX_MINOR_VERSION 0 +#define GMTMEX_PATCH_VERSION 0 + +/* Define the minimum GMT version suitable for this GMTMEX version */ + +#define GMTMEX_GMT_MAJOR_VERSION 6 +#define GMTMEX_GMT_MINOR_VERSION 1 +#define GMTMEX_GMT_PATCH_VERSION 1 + #define STDC_FORMAT_MACROS #define GMTMEX_LIB -#include "gmtmex.h" + +#include "gmt.h" +#include "gmt_version.h" #include #include #include @@ -30,6 +61,143 @@ #include #include #include +#include + +#ifdef GMT_OCTOCT +# include +#else +# include +# define mxIsScalar_(mx) \ + ( (2 == mxGetNumberOfDimensions(mx)) \ + && (1 == mxGetM(mx))&& (1 == mxGetN(mx)) ) +#endif /* Matlab and Octave(mex) */ + +/* Matlab and Octave (in -mex mode) are identical, oct files are different and not yet tested */ + +/* Older Ml versions don't have mwSize */ +#ifndef GMT_OCTMEX +#if !defined(MATLAB_VERSION) +#if !defined(MWSIZE_MAX) +# define MATLAB_VERSION 0x2006a /* R2006a or earlier */ +#elif MX_API_VER < 0x07040000 +# define MATLAB_VERSION 0x2006b /* R2006b */ +#elif !defined(FMT_PTRDIFF_T) +# define MATLAB_VERSION 0x2007a /* R2007a */ +#elif !defined(CUINT64_T) +# define MATLAB_VERSION 0x2007b /* R2007b */ +#elif defined(mxSetLogical) +# define MATLAB_VERSION 0x2008a /* R2008a */ +#else +# if !defined(blas_h) +# include "blas.h" +# endif +# if !defined(lapack_h) +# include "lapack.h" +# endif +# if !defined(MATHWORKS_MATRIX_MATRIX_PUB_FWD_H) +# if defined(CHAR16_T) +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2008b /* R2008b */ +# elif !defined(cgeqr2p) +# define MATLAB_VERSION 0x2010b /* R2010b */ +# else +# define MATLAB_VERSION 0x2011a /* R2011a */ +# endif +# else +# include "emlrt.h" +# define MATLAB_VERSION EMLRT_VERSION_INFO /* R2011b or later */ +# endif +# else +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2009a /* R2009a */ +# elif !defined(cgbequb) +# define MATLAB_VERSION 0x2009b /* R2009b */ +# else +# define MATLAB_VERSION 0x2010a /* R2010a */ +# endif +# endif +#endif +#endif /* if !defined(MATLAB_VERSION) */ + +#if MATLAB_VERSION < 0x2006b +typedef int mwSize; +#endif +#endif + +#ifdef GMT_OCTOCT /* Octave oct files only */ +# define MEX_PROG "Octave(oct)" +# define MEX_COL_ORDER GMT_IS_ROW_FORMAT + /* Macros for getting the Octave(oct) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) +#else /* Here we go for Matlab or Octave(mex) */ +# ifdef GMT_MATLAB +# define MEX_PROG "Matlab" +# else +# define MEX_PROG "Octave(mex)" +# endif +# define MEX_COL_ORDER GMT_IS_COL_FORMAT + /* Macros for getting the Matlab/Octave(mex) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) +#endif + +/* Definitions of MEX structures used to hold GMT objects. + * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ + +/* GMT_IS_DATASET: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. Pure datasets will only set the data + * matrix and leave the text cell array empty, while textsets + * will fill out both. Only the first segment will have any + * information in the info and projection_ref_* items. */ + +#define N_MEX_FIELDNAMES_DATASET 6 +static const char *gmtmex_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = + {"data", "text", "header", "comment", "proj4", "wkt"}; + +/* GMT_IS_GRID: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_GRID 17 +static const char *gmtmex_fieldname_grid[N_MEX_FIELDNAMES_GRID] = + {"z", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", + "command", "datatype", "x_unit", "y_unit", "z_unit", "layout", "proj4", "wkt"}; + +/* GMT_IS_IMAGE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_IMAGE 19 +static const char *gmtmex_fieldname_image[N_MEX_FIELDNAMES_IMAGE] = + {"image", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", "command", + "datatype", "x_unit", "y_unit", "z_unit", "colormap", "alpha", "layout", "proj4", "wkt"}; + +/* GMT_IS_PALETTE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_CPT 11 +static const char *gmtmex_fieldname_cpt[N_MEX_FIELDNAMES_CPT] = + {"colormap", "alpha", "range", "minmax", "bfn", "depth", "hinge", "cpt", "model", "mode", "comment"}; + +/* GMT_IS_POSTSCRIPT: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_PS 4 +static const char *gmtmex_fieldname_ps[N_MEX_FIELDNAMES_PS] = + {"postscript", "length", "mode", "comment"}; + +/* Macro for indexing into a GMT grid [with pad] */ +#define GMT_IJP(h,row,col) ((uint64_t)(((int64_t)(row)+(int64_t)h->pad[GMT_YHI])*((int64_t)h->mx)+(int64_t)(col)+(int64_t)h->pad[GMT_XLO])) + +#define MODULE_LEN 32 /* Max length of a GMT module name */ #ifndef rint #define rint(x) (floor((x)+0.5f)) //does not work reliable. @@ -87,7 +255,7 @@ enum MEX_dim { * + comment holds any PostScript comments */ -int GMTMEX_print_func (FILE *fp, const char *message) { +static int gmtmex_print_func (FILE *fp, const char *message) { /* Replacement for GMT's gmt_print_func. It is being used indirectly via * API->print_func. Purpose of this is to allow MATLAB (which cannot use * printf) to reset API->print_func to this function via GMT_Create_Session. @@ -146,7 +314,7 @@ static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { mexErrMsgTxt ("gmtmex_get_grid: programming error, output matrix G is empty\n"); /* Create a MATLAB struct to hold this grid [matrix will be a float (mxSINGLE_CLASS)]. */ - G_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); + G_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); /* Get pointers and populate structure from the information in G */ mxptr[0] = mxCreateNumericMatrix (G->header->n_rows, G->header->n_columns, mxSINGLE_CLASS, mxREAL); @@ -197,7 +365,7 @@ static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { if (GMT_Destroy_Data (API, &G_y)) mexPrintf("Warning: Failure to delete G_y (y coordinate vector)\n"); for (k = 0; k < N_MEX_FIELDNAMES_GRID; k++) - mxSetField (G_struct, 0, GMTMEX_fieldname_grid[k], mxptr[k]); + mxSetField (G_struct, 0, gmtmex_fieldname_grid[k], mxptr[k]); return (G_struct); } @@ -219,7 +387,7 @@ static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { mxArray *D_struct = NULL, *mxheader = NULL, *mxdata = NULL, *mxtext = NULL, *mxstring = NULL; if (D == NULL) { /* No output produced (?) - return a null data set */ - D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); return (D_struct); } @@ -228,7 +396,7 @@ static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { if (D->table[tbl]->segment[seg]->n_rows) seg_out++; if (seg_out == 0) n_items = 0; - D_struct = mxCreateStructMatrix ((mwSize)seg_out, (mwSize)n_items, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + D_struct = mxCreateStructMatrix ((mwSize)seg_out, (mwSize)n_items, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); n_headers = (D->n_tables) ? D->table[0]->n_headers : 0; /* Number of header records in first table */ for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) { @@ -286,12 +454,12 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { if (!P->data) { /* Return empty PS struct */ - P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); return P_struct; } /* Return PS with postscript and length in a struct */ - P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); mxptr[0] = mxCreateString (P->data); mxptr[1] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); @@ -309,7 +477,7 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { } for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) - mxSetField (P_struct, 0, GMTMEX_fieldname_ps[k], mxptr[k]); + mxSetField (P_struct, 0, gmtmex_fieldname_ps[k], mxptr[k]); return P_struct; } @@ -341,13 +509,13 @@ static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { mexErrMsgTxt ("gmtmex_get_palette: programming error, output CPT C is empty\n"); if (!C->data) { /* Return empty struct */ - C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); return (C_struct); } /* Return CPT via colormap, range, and alpha arrays in a struct */ /* Create a MATLAB struct for this CPT */ - C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); n_colors = (C->is_continuous) ? C->n_colors + 1 : C->n_colors; mxptr[0] = mxCreateNumericMatrix (n_colors, 3, mxDOUBLE_CLASS, mxREAL); @@ -405,7 +573,7 @@ static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { mxptr[8] = mxCreateString ("rgb"); for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) /* Update all fields */ - mxSetField (C_struct, 0, GMTMEX_fieldname_cpt[k], mxptr[k]); + mxSetField (C_struct, 0, gmtmex_fieldname_cpt[k], mxptr[k]); return (C_struct); } @@ -421,7 +589,7 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { /* Return image via a uint8_t (mxUINT8_CLASS) matrix in a struct */ /* Create a MATLAB struct for this image */ - I_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); + I_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); /* Create the various fields with information from I */ mxptr[0] = NULL; /* Set below */ mxptr[1] = mxCreateNumericMatrix (1, I->header->n_columns, mxDOUBLE_CLASS, mxREAL); @@ -514,7 +682,7 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { if (GMT_Destroy_Data (API, &I_y)) mexPrintf("Warning: Failure to delete I_y (y coordinate vector)\n"); for (k = 0; k < N_MEX_FIELDNAMES_IMAGE; k++) { /* Update fields */ - if (mxptr[k]) mxSetField (I_struct, 0, GMTMEX_fieldname_image[k], mxptr[k]); + if (mxptr[k]) mxSetField (I_struct, 0, gmtmex_fieldname_image[k], mxptr[k]); } return (I_struct); } @@ -1110,8 +1278,8 @@ static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int directio if (!mxIsStruct (ptr)) mexErrMsgTxt ("gmtmex_palette_init: Expected a CPT structure for input\n"); for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) { - if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_cpt[k])) == NULL) - gmtmex_quit_if_missing ("gmtmex_palette_init", GMTMEX_fieldname_cpt[k]); + if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_cpt[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_palette_init", gmtmex_fieldname_cpt[k]); } dim[0] = mxGetM (mx_ptr[0]); /* Number of rows in colormap */ @@ -1211,8 +1379,8 @@ static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, if (!mxIsStruct (ptr)) mexErrMsgTxt ("gmtmex_ps_init: Expected a MATLAB PostScript structure for input\n"); for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) { - if ((mx_ptr[k] = mxGetField (ptr, 0, GMTMEX_fieldname_ps[k])) == NULL) - gmtmex_quit_if_missing ("gmtmex_ps_init", GMTMEX_fieldname_ps[k]); + if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_ps[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_ps_init", gmtmex_fieldname_ps[k]); } length = mxGetData (mx_ptr[1]); @@ -1249,11 +1417,11 @@ static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, return (P); } -char GMTMEX_objecttype (const mxArray *ptr) { +static char gmtmex_objecttype (const mxArray *ptr) { /* Determine what we are returning so gmt write can pass the correct -T? flag */ mxArray *mx_ptr = NULL; if (mxIsEmpty (ptr)) - mexErrMsgTxt ("GMTMEX_objecttype: Pointer is empty\n"); + mexErrMsgTxt ("gmtmex_objecttype: Pointer is empty\n"); if (mxIsStruct (ptr)) { /* This means either a dataset, grid, image, cpt, or PS, so must check for fields */ mx_ptr = mxGetField (ptr, 0, "data"); if (mx_ptr) return 'd'; @@ -1265,7 +1433,7 @@ char GMTMEX_objecttype (const mxArray *ptr) { if (mx_ptr) return 'i'; mx_ptr = mxGetField (ptr, 0, "z"); if (mx_ptr) return 'g'; - mexErrMsgTxt ("GMTMEX_objecttype: Could not recognize the structure\n"); + mexErrMsgTxt ("gmtmex_objecttype: Could not recognize the structure\n"); } else if (mxIsCell (ptr)) /* This is a dataset with text only */ return 'd'; @@ -1274,18 +1442,18 @@ char GMTMEX_objecttype (const mxArray *ptr) { return '-'; /* Can never get here you would think */ } -void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { +static void gmtmex_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { /* Create the GMT container and hook onto resource array as X->object */ unsigned int module_input = (X->option->option == GMT_OPT_INFILE), actual_family = X->family; switch (X->family) { case GMT_IS_GRID: /* Get a grid from Matlab or a dummy one to hold GMT output */ X->object = gmtmex_grid_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got Grid\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Grid\n"); break; case GMT_IS_IMAGE: /* Get an image from Matlab or a dummy one to hold GMT output */ X->object = gmtmex_image_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got Image\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Image\n"); break; case GMT_IS_DATASET: /* Get a dataset from Matlab or a dummy one to hold GMT output */ /* Because a GMT_DATASET may appears as a GMT_MATRIX or GMT_VECTOR we need the actual_family to open the virtual file later */ @@ -1293,14 +1461,14 @@ void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { break; case GMT_IS_PALETTE: /* Get a palette from Matlab or a dummy one to hold GMT output */ X->object = gmtmex_palette_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got CPT\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got CPT\n"); break; case GMT_IS_POSTSCRIPT: /* Get a PostScript struct from Matlab or a dummy one to hold GMT output */ X->object = gmtmex_ps_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "GMTMEX_Set_Object: Got POSTSCRIPT\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got POSTSCRIPT\n"); break; default: - GMT_Report (API, GMT_MSG_NORMAL, "GMTMEX_Set_Object: Bad data type (%d)\n", X->family); + GMT_Report (API, GMT_MSG_NORMAL, "gmtmex_Set_Object: Bad data type (%d)\n", X->family); break; } if (X->object == NULL) @@ -1311,7 +1479,7 @@ void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { mexErrMsgTxt ("GMT: Failure to expand filename marker (?)\n"); } -void *GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X) { +static void *gmtmex_Get_Object (void *API, struct GMT_RESOURCE *X) { mxArray *ptr = NULL; /* In line-by-line modules it is possible no output is produced, hence we make an exception for DATASET: */ if ((X->object = GMT_Read_VirtualFile (API, X->name)) == NULL && X->family != GMT_IS_DATASET) @@ -1345,3 +1513,386 @@ void *GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X) { } return ptr; } + +#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 +extern int gmt_get_V (char arg); /* Temporary here to allow full debug messaging */ +#else +extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ +#define gmt_get_V GMT_get_V /* Old name */ +#endif + +#ifndef SINGLE_SESSION +/* Being declared external we can access it between MEX calls */ +static uintptr_t *pPersistent; /* To store API address back and forth within a single MATLAB session */ + +/* Here is the exit function, which gets run when the MEX-file is + cleared and when the user exits MATLAB. The mexAtExit function + should always be declared as static. */ +static void force_Destroy_Session (void) { + void *API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API != NULL) { /* Otherwise just silently ignore this call */ + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("Failure to destroy GMT session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ + } +} +#endif + +static void usage (int nlhs, int nrhs) { + /* Basic usage message */ + if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ + mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", + MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); + mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); + mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); + mexPrintf("You may redistribute copies of this program under the terms of the\n"); + mexPrintf("GNU Lesser General Public License.\n"); + mexPrintf("For more information about these matters, see the file named LICENSE.TXT.\n"); + mexPrintf("For a brief description of GMT modules, type gmt ('help')\n\n"); + } + else { + mexPrintf("Usage is:\n\tgmt ('module_name', 'options'[, ]); %% Run a GMT module\n"); + if (nlhs != 0) + mexErrMsgTxt ("But meanwhile you already made an error by asking help and an output.\n"); + } +} + +static void *Initiate_Session (unsigned int verbose) { + /* Initialize the GMT Session and store the API pointer in a persistent variable */ + void *API = NULL; + /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ + /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ + if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + + GMT_SESSION_COLMAJOR, gmtmex_print_func)) == NULL) + mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); + +#ifndef SINGLE_SESSION + if (!pPersistent) pPersistent = mxMalloc(sizeof(uintptr_t)); + pPersistent[0] = (uintptr_t)(API); + mexMakeMemoryPersistent (pPersistent); +#endif + return (API); +} + +static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { + /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior + when we do for example (i.e. no lhs): sqrt([4 9]) + */ + void *ptr = NULL; + switch (X->family) { + case GMT_IS_GRID: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); + break; + case GMT_IS_IMAGE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); + break; + case GMT_IS_DATASET: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); + break; + case GMT_IS_PALETTE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); + break; + case GMT_IS_POSTSCRIPT: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); + break; + default: + break; + } + return ptr; +} + +/* This is the function that is called when we type gmt in MATLAB/Octave. + * It is the only function experted by the GMT API library. + */ + +void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + int status = 0; /* Status code from GMT API */ + int n_in_objects = 0; /* Number of input objects passed to module */ + unsigned int first = 0; /* Array ID of first command argument (not 0 when API-ID is first) */ + unsigned int verbose = 0; /* Default verbose setting */ + unsigned int n_items = 0, pos = 0; /* Number of MATLAB arguments (left and right) */ + size_t str_length = 0, k = 0; /* Misc. counters */ + void *API = NULL; /* GMT API control structure */ + struct GMT_OPTION *options = NULL; /* Linked list of module options */ + struct GMT_RESOURCE *X = NULL; /* Array of information about MATLAB args */ + char *cmd = NULL; /* Pointer used to get the user's MATLAB command */ + char *gtxt = NULL; /* For debug printing of revised command */ + char *opt_args = NULL; /* Pointer to the user's module options */ + char module[MODULE_LEN] = {""}; /* Name of GMT module to call */ + char opt_buffer[BUFSIZ] = {""}; /* Local copy of command line options */ + void *ptr = NULL; +#ifndef SINGLE_SESSION + uintptr_t *pti = NULL; /* To locally store the API address */ +#endif + + /* -1. Check that the GMT library is of a suitable version for this GMTMEX version */ + + if (GMT_MAJOR_VERSION > GMTMEX_GMT_MAJOR_VERSION) { /* This may not work if, for instance, we want >= 6.1.x but is using GMT 7.0.0 */ + char message[128] = {""}; + sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); + mexPrintf (message); + } + else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { + char message[128] = {""}; + sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", + GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); + mexErrMsgTxt (message); + } + + /* 0. No arguments at all results in the GMT banner message */ + if (nrhs == 0) { + usage (nlhs, nrhs); + return; + } + + /* 1. Check for the special commands create and help */ + + if (nrhs == 1) { /* This may be create or help */ + cmd = mxArrayToString (prhs[0]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); + if (!strncmp (cmd, "help", 4U) || !strncmp (cmd, "--help", 6U)) { + usage (nlhs, 1); + return; + } +#ifndef SINGLE_SESSION + if (!strncmp (cmd, "create", 6U)) { /* Asked to create a new GMT session */ + if (nlhs > 1) /* Asked for too much output, only 1 or 0 is allowed */ + mexErrMsgTxt ("GMT: Usage: gmt ('create') or API = gmt ('create');\n"); + if (pPersistent) /* See if have a GMT API pointer */ + API = (void *)pPersistent[0]; + if (API != NULL) { /* If another session still exists */ + GMT_Report (API, GMT_MSG_VERBOSE, + "GMT: A previous GMT session is still active. Ignoring your 'create' request.\n"); + if (nlhs) /* Return nothing */ + plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); + return; + } + if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = gmt_get_V (gtxt[2]); + API = Initiate_Session (verbose); /* Initializing a new GMT session */ + + if (nlhs) { /* Return the API address as an integer (nlhs == 1 here) )*/ + plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); + pti = mxGetData(plhs[0]); + *pti = *pPersistent; + } + + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + return; + } + + /* OK, neither create nor help, must be a single command with no arguments nor the API. So get it: */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { /* No session yet, create one under the hood */ + API = Initiate_Session(verbose); /* Initializing a new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } + else + API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is corrupted. Better to start from scratch.\n"); + } + else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { + /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ + pti = (uintptr_t *)mxGetData(prhs[0]); + API = (void *)pti[0]; /* Get the GMT API pointer */ + first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ + } + else { /* We still don't have the API, so we must get it from the past or initiate a new session */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { + API = Initiate_Session (verbose); /* Initializing new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } +#endif + } + +#ifdef SINGLE_SESSION + /* Initiate a new session */ + API = Initiate_Session (verbose); /* Initializing new GMT session */ +#endif + + if (!cmd) { /* First argument is the command string, e.g., 'blockmean -R0/5/0/5 -I1' or just 'destroy' */ + cmd = mxArrayToString(prhs[first]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string but is probably a cell array of strings.\n"); + } + + if (!strncmp (cmd, "destroy", 7U)) { /* Destroy the session */ +#ifndef SINGLE_SESSION + if (nlhs != 0) + mexErrMsgTxt ("GMT: Usage is gmt ('destroy');\n"); + + if (GMT_Destroy_Options (API, &options)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ +#endif + return; + } + + /* 2. Get module name and separate out args */ + + /* Here we have a GMT module call. The documented use is to give the module name separately from + * the module options, but users may forget and combine the two. So we check both cases. */ + + n_in_objects = nrhs - 1; + str_length = strlen (cmd); /* Length of module (or command) argument */ + for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ + + if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ + strcpy (module, cmd); /* Isolate the module name in this string */ + if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ + first++; /* Since we have a 2nd string to skip now */ + opt_args = mxArrayToString (prhs[first]); + n_in_objects--; + } + /* Else we got no options, just input objects */ + } + else { /* Case b2. Get mex arguments, if any, and extract the GMT module name */ + if (k >= MODULE_LEN) + mexErrMsgTxt ("GMT: Module name in command is too long\n"); + strncpy (module, cmd, k); /* Isolate the module name in this string */ + + while (cmd[k] == ' ') k++; /* Skip any spaces between module name and start of options */ + if (cmd[k]) opt_args = &cmd[k]; + } + + /* See if info about installation is required */ + if (!strcmp(module, "gmt")) { + char t[256] = {""}; + if (!opt_args) { + mexPrintf("Warning: calling the 'gmt' program by itself does nothing here.\n"); + return; + } + if (!strcmp(opt_args, "--show-bindir")) /* Show the directory that contains the 'gmt' executable */ + GMT_Get_Default (API, "BINDIR", t); + else if (!strcmp(opt_args, "--show-sharedir")) /* Show share directory */ + GMT_Get_Default (API, "SHAREDIR", t); + else if (!strcmp(opt_args, "--show-datadir")) /* Show the data directory */ + GMT_Get_Default (API, "DATADIR", t); + else if (!strcmp(opt_args, "--show-plugindir")) /* Show the plugin directory */ + GMT_Get_Default (API, "PLUGINDIR", t); + else if (!strcmp(opt_args, "--show-cores")) /* Show number of cores */ + GMT_Get_Default (API, "CORES", t); + + if (t[0] != '\0') { + if (nlhs) + plhs[0] = mxCreateString (t); + else + mexPrintf ("%s\n", t); + } + else + mexPrintf ("Warning: called the 'gmt' program with unknown option.\n"); + return; + } + + /* Make sure this is a valid module */ + if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ + mexErrMsgTxt ("GMT: No module by that name was found.\n"); + + /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to + * another string with enough space. */ + + if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ + /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ + if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ + if (opt_args) + strcat (opt_buffer, " -F"); + else + strcpy (opt_buffer, "-F"); + } + + /* 2++ If gmtwrite then add -T? with correct object type */ + if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ + char targ[5] = {" -T?"}; + targ[3] = gmtmex_objecttype (prhs[nrhs-1]); + strcat (opt_buffer, targ); + } + /* 2+++ If gmtread -Ti then temporarily set pad to 0 since we don't want padding in image arrays */ + else if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default(API, "API_PAD", "0"); + + /* 3. Convert mex command line arguments to a linked GMT option list */ + if (opt_buffer[0] && (options = GMT_Create_Options (API, 0, opt_buffer)) == NULL) + mexErrMsgTxt ("GMT: Failure to parse GMT5 command options\n"); + + if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ + options = GMT_Create_Options (API, 0, "-?"); + + /* 4. Preprocess to update GMT option lists and return info array X */ + if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { + if (n_items == UINT_MAX) /* Just got usage/synopsis option */ + n_items = 0; + else + mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); + } + + if (options) { /* Only for debugging - remove this section when stable */ + gtxt = GMT_Create_Cmd (API, options); + GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); + GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ + } + + /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ + + for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ + if (X[k].direction == GMT_IN) { + if ((X[k].pos+first+1) < (unsigned int)nrhs) + ptr = (void *)prhs[X[k].pos+first+1]; + else + mexErrMsgTxt ("GMT: Attempting to address a prhs entry that does not exist\n"); + } + else { + if ((X[k].pos) < nlhs) + ptr = (void *)plhs[X[k].pos]; + else { + //mexErrMsgTxt ("GMT: Attempting to address a plhs entry that does not exist\n"); + ptr = alloc_default_plhs (API, &X[k]); + } + } + gmtmex_Set_Object (API, &X[k], ptr); /* Set object pointer */ + } + + /* 6. Run GMT module; give usage message if errors arise during parsing */ + status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); + if (status != GMT_NOERROR) { + if (status <= GMT_MODULE_PURPOSE) + return; + else { + mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); + mexErrMsgTxt("GMT: exiting\n"); + } + } + + /* 7. Hook up any GMT outputs to MATLAB plhs array */ + + for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ + if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ + pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ + plhs[pos] = gmtmex_Get_Object (API, &X[k]); /* Hook mex object onto rhs list */ + } + + /* 2++- If gmtread -Ti then reset the sessions pad value that was temporarily changed above (2+++) */ + if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default (API, "API_PAD", "2"); + + /* 8. Free all GMT containers involved in this module call */ + + for (k = 0; k < n_items; k++) { + void *ppp = X[k].object; + if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to close virtual file\n"); + if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); + else { /* Success, now make sure we don't destroy the same pointer more than once */ + for (size_t kk = k+1; kk < n_items; kk++) + if (X[kk].object == ppp) X[kk].object = NULL; + } + } + + /* 9. Destroy linked option list */ + + if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); +#ifdef SINGLE_SESSION + if (GMT_Destroy_Session (API)) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); +#endif + return; +} From 65cd6f2992ce6060059fea6036323015b401c2bc Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 10:47:44 -1000 Subject: [PATCH 09/45] Update gmt.m --- src/gmtmex/gmt.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmtmex/gmt.m b/src/gmtmex/gmt.m index 0741f489a98..bf0bcec436a 100644 --- a/src/gmtmex/gmt.m +++ b/src/gmtmex/gmt.m @@ -2,7 +2,7 @@ % Helper function to call the gmtmex MEX function if (nargin == 0) - fprintf(sprintf('\n\t\tGMT - The Generic Mapping Tools, Version 6.1 API\n')) + fprintf(sprintf('\n\t\tGMT - The Generic Mapping Tools, Version 6.2 API\n')) fprintf(sprintf('Copyright 1991-2020 The GMT Team (https://www.generic-mapping-tools.org/team.html\n\n')) fprintf(sprintf('Usage:\tTo call a GMT module:\n\t output = gmt (''module_name'', ''options'', numeric_input)\n\n')) From 56c694f6a043132a5be8e5c01262c82119056da0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 11:14:47 -1000 Subject: [PATCH 10/45] Add CMakeLists.txt file for gmtmex Only has contents if GMT_BUILD_GMTMEX is defined. --- src/CMakeLists.txt | 2 +- src/gmtmex/CMakeLists.txt | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 src/gmtmex/CMakeLists.txt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 9fd037867b6..d034fc54e02 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -871,7 +871,7 @@ if (BUILD_SUPPLEMENTS) DESTINATION ${GMT_DOCDIR}/${GMT_SUPPL_LIB_NAME}/${_suppl_name} COMPONENT Documentation) - # install example scriptss + # install example scripts install (PROGRAMS ${_suppl_example_progs} DESTINATION ${GMT_DOCDIR}/${GMT_SUPPL_LIB_NAME}/${_suppl_name} COMPONENT Documentation) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt new file mode 100644 index 00000000000..7f017a8062b --- /dev/null +++ b/src/gmtmex/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html) +# See LICENSE.TXT file for copying and redistribution conditions. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; version 3 or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# Contact info: www.generic-mapping-tools.org +#------------------------------------------------------------------------------- +## +## Settings for GMT MexFunction shared library +## +# Note: Only activated if GMT_BUILD_GMTMEX is set in ConfigUserAdvanced.cmake + +if (GMT_BUILD_GMTMEX) + # Add the mexfunction as a shared library + add_library (GMTmexfunction SHARED gmtmex.c) + # Set the Matlab include file directory + target_include_directories (GMTmexfunction ${MATLAB_INC}) + # Link with the Matlab and mex libraries and gmt library + target_link_libraries (GMTmexfunction gmtlib ${MATLAB_LIB1} ${MATLAB_LIB2}) + # Set output properties of shared library + set_target_properties (GMTmexfunction + PROPERTIES + OUTPUT_NAME gmtmex + RUNTIME_OUTPUT_NAME gmtmex + PREFIX "") + # Add the install target + install (TARGETS GMTmexfunction + RUNTIME DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) +endif (GMT_BUILD_GMTMEX) From 2bc96a8eea38fd0b11a2cb5f22bdcfaf6ecd1412 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 11:17:38 -1000 Subject: [PATCH 11/45] Update CMakeLists.txt --- src/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d034fc54e02..22f09500158 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -379,6 +379,7 @@ set (GMT_PSL_SRCS postscriptlight.c declspec.h psl_config.h PSL_Standard+.h set (GMT_MBSYSTEM_SRCS gmt_mbsystem_glue.c) if (GMT_BUILD_GMTMEX) + # Includes GMT_MexFunction in the GMT API set (GMT_GMTMEX_SRCS gmtmex/gmtmex_api.c) endif (GMT_BUILD_GMTMEX) From e81ab9a2177a21565d219847ad26ca0a1271271e Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 12:29:46 -1000 Subject: [PATCH 12/45] Use FindMatlab --- src/CMakeLists.txt | 14 ++------------ src/gmtmex/CMakeLists.txt | 8 ++++++-- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 22f09500158..c0fb3e2a6f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -574,17 +574,8 @@ endforeach (_gmt_prog) set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) if (GMT_BUILD_GMTMEX) - # Just need to add a symbolic link with the right name to libgmt - #set( gmtmex_link ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/gmtmex.${MEX_EXT}) - #set( gmtmex_target ${CMAKE_INSTALL_PREFIX}/${GMT_LIBDIR}/${LIB_PREFIX}gmt.${GMT_LIB_SOVERSION}.${CMAKE_SHARED_LIBRARY_SUFFIX}) - #add_custom_command ( OUTPUT ${gmtmex_link} POST_BUILD - # COMMAND ln -s ${gmtmex_target} ${gmtmex_link} - # DEPENDS install ${gmtmex_target} - # COMMENT "Generating gmtmex symbolic link") - # Install the driver script - install (PROGRAMS gmtmex/gmt.m - DESTINATION ${GMT_BINDIR} - COMPONENT Runtime) + find_package (Matlab) + add_subdirectory (gmtmex) endif (GMT_BUILD_GMTMEX) # psldemo @@ -886,7 +877,6 @@ if (BUILD_SUPPLEMENTS) endforeach (_dir) endif (BUILD_SUPPLEMENTS) - ## ## Print out CFLAGS ## diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index 7f017a8062b..a65961482c2 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -22,16 +22,20 @@ if (GMT_BUILD_GMTMEX) # Add the mexfunction as a shared library add_library (GMTmexfunction SHARED gmtmex.c) # Set the Matlab include file directory - target_include_directories (GMTmexfunction ${MATLAB_INC}) + target_include_directories (GMTmexfunction PUBLIC ${Matlab_INCLUDE_DIRS}) # Link with the Matlab and mex libraries and gmt library - target_link_libraries (GMTmexfunction gmtlib ${MATLAB_LIB1} ${MATLAB_LIB2}) + target_link_libraries (GMTmexfunction gmtlib ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY}) # Set output properties of shared library set_target_properties (GMTmexfunction PROPERTIES OUTPUT_NAME gmtmex RUNTIME_OUTPUT_NAME gmtmex + SUFFIX ".mexmaci64" PREFIX "") # Add the install target + install (PROGRAMS gmt.m + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) install (TARGETS GMTmexfunction RUNTIME DESTINATION ${GMT_BINDIR} COMPONENT Runtime) From 8051bb2c1dbdcfd2d8b7f747c20803413dd7bb97 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 13:47:07 -1000 Subject: [PATCH 13/45] More updates --- CMakeLists.txt | 11 +- src/CMakeLists.txt | 7 +- src/gmt_init.c | 4 - src/gmtmex/CMakeLists.txt | 8 +- src/gmtmex/gmtmex.c | 1888 +++++++++++++++++++++++++++++++++++- src/gmtmex/gmtmex_api.c | 1898 ------------------------------------- 6 files changed, 1896 insertions(+), 1920 deletions(-) delete mode 100644 src/gmtmex/gmtmex_api.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 99293981b88..7e9bfbd1b29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,15 +186,17 @@ else (SUPPL_EXTRA_DIRS) endif (SUPPL_EXTRA_DIRS) if (GMT_BUILD_GMTMEX) - set (MATLAB_APP "${MATLAB}") - set (MATLAB_INC "${MEX_INC}") - set (MATLAB_LIB1 "${MEX_LIB}") - set (MATLAB_LIB2 "${MX_LIB}") + set (MATLAB_APP "$ENV{MATLAB}") + set (MATLAB_INC "${Matlab_INCLUDE_DIRS}") + set (MATLAB_LIB1 "${Matlab_MEX_LIBRARY}") + set (MATLAB_LIB2 "${Matlab_MX_LIBRARY}") + set (MATLAB_EXT "${Matlab_MEX_EXTENSION}") else (GMT_INSTALL_MODULE_LINKS) set (MATLAB_APP "not used") set (MATLAB_INC "not used") set (MATLAB_LIB1 "not used") set (MATLAB_LIB2 "not used") + set (MATLAB_EXT "not used") endif (GMT_BUILD_GMTMEX) # Configure header file to pass some of the CMake settings to the source code @@ -252,6 +254,7 @@ message( "* MATLAB include dir : ${MATLAB_INC}\n" "* MATLAB MEX library : ${MATLAB_LIB1}\n" "* MATLAB MX library : ${MATLAB_LIB2}\n" + "* MATLAB mex extension : ${MATLAB_EXT}\n" "* Found Ghostscript (gs) : ${GMT_CONFIG_GS_MESSAGE}\n" "* Found GraphicsMagick (gm) : ${GMT_CONFIG_GM_MESSAGE}\n" "* Found ffmpeg : ${GMT_CONFIG_FFMPEG_MESSAGE}\n" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index c0fb3e2a6f5..d417dd418e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -378,11 +378,6 @@ set (GMT_PSL_SRCS postscriptlight.c declspec.h psl_config.h PSL_Standard+.h set (GMT_MBSYSTEM_SRCS gmt_mbsystem_glue.c) -if (GMT_BUILD_GMTMEX) - # Includes GMT_MexFunction in the GMT API - set (GMT_GMTMEX_SRCS gmtmex/gmtmex_api.c) -endif (GMT_BUILD_GMTMEX) - # libgmt set (GMT_LIB_SRCS block_subs.h gmt_common_byteswap.h gmt_common_math.h gmt_common_runpath.h gmt_common_sighandler.c gmt_common_string.h clear.c begin.c @@ -409,7 +404,7 @@ set (GMT_LIB_SRCS block_subs.h gmt_common_byteswap.h gmt_common_math.h ${CMAKE_CURRENT_BINARY_DIR}/gmt_${SHARED_LIB_NAME}_glue.c kiss_fft/_kiss_fft_guts.h kiss_fft/kiss_fft.c kiss_fft/kiss_fft.h kiss_fft/kiss_fftnd.c kiss_fft/kiss_fftnd.h - ${GMT_TRIANGLE_SRCS} ${GMT_MBSYSTEM_SRCS} ${GMT_GMTMEX_SRCS}) + ${GMT_TRIANGLE_SRCS} ${GMT_MBSYSTEM_SRCS}) # source codes for testing API if (DO_API_TESTS) diff --git a/src/gmt_init.c b/src/gmt_init.c index e9df8355b1b..50694e47a5a 100644 --- a/src/gmt_init.c +++ b/src/gmt_init.c @@ -97,10 +97,6 @@ #include "gmt_internals.h" #include "gmt_common_runpath.h" -#ifdef GMT_MATLAB -# include -#endif - #define USER_MEDIA_OFFSET 1000 diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index a65961482c2..aad5207e014 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -19,18 +19,20 @@ # Note: Only activated if GMT_BUILD_GMTMEX is set in ConfigUserAdvanced.cmake if (GMT_BUILD_GMTMEX) + # Add the flag to select Matlab vs Octave + add_definitions(-DGMT_MATLAB) # Add the mexfunction as a shared library add_library (GMTmexfunction SHARED gmtmex.c) # Set the Matlab include file directory - target_include_directories (GMTmexfunction PUBLIC ${Matlab_INCLUDE_DIRS}) + target_include_directories (GMTmexfunction PUBLIC ${GMT_SOURCE_DIR}/src /Users/pwessel/GMTdev/gmt-dev/rbuild/src ${Matlab_INCLUDE_DIRS}) # Link with the Matlab and mex libraries and gmt library - target_link_libraries (GMTmexfunction gmtlib ${Matlab_MEX_LIBRARY} ${Matlab_MX_LIBRARY}) + target_link_libraries (GMTmexfunction gmtlib ${Matlab_MX_LIBRARY} ${Matlab_MEX_LIBRARY}) # Set output properties of shared library set_target_properties (GMTmexfunction PROPERTIES OUTPUT_NAME gmtmex RUNTIME_OUTPUT_NAME gmtmex - SUFFIX ".mexmaci64" + SUFFIX ".${Matlab_MEX_EXTENSION}" PREFIX "") # Add the install target install (PROGRAMS gmt.m diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index 38f907c6a7f..ddddb4f0793 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -12,13 +12,1891 @@ * GNU Lesser General Public License for more details. * * Contact info: www.generic-mapping-tools.org + *--------------------------------------------------------------------*/ +/* + * This is the MATLAB/Octave(mex) GMT application, which can do the following: + * 1) Create a new session and optionally return the API pointer. We provide for + * storing the pointer as a global variable (persistent) between calls. + * 2) Destroy a GMT session, either given the API pointer or by fetching it from + * the global (persistent) variable. + * 3) Call any of the GMT modules while passing data in and out of GMT. + * + * First argument to the gmt function is the API pointer, but it is optional once created. + * Next argument is the module name + * Third argument is the option string + * Finally, there are optional comma-separated MATLAB array entities required by the command. + * Information about the options of each program is provided via GMT_Encode_Options. + * + * GMT Version: 6.x + * Created: 20-OCT-2017 + * Updated: 1-APR-2021 requires GMT 6.2.x + * + * + * GMT convenience functions used by MATLAB/OCTAVE mex/oct API + * We also define the MEX structures used to pass data in/out of GMT. + */ + +#define GMTMEX_MAJOR_VERSION 2 +#define GMTMEX_MINOR_VERSION 0 +#define GMTMEX_PATCH_VERSION 0 + +/* Define the minimum GMT version suitable for this GMTMEX version */ + +#define GMTMEX_GMT_MAJOR_VERSION 6 +#define GMTMEX_GMT_MINOR_VERSION 2 +#define GMTMEX_GMT_PATCH_VERSION 0 + +#define STDC_FORMAT_MACROS +#define GMTMEX_LIB + +#include "gmt.h" +#include "gmt_version.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef GMT_OCTOCT +# include +#else +# include +# define mxIsScalar_(mx) \ + ( (2 == mxGetNumberOfDimensions(mx)) \ + && (1 == mxGetM(mx))&& (1 == mxGetN(mx)) ) +#endif /* Matlab and Octave(mex) */ + +/* Matlab and Octave (in -mex mode) are identical, oct files are different and not yet tested */ + +/* Older Ml versions don't have mwSize */ +#ifndef GMT_OCTMEX +#if !defined(MATLAB_VERSION) +#if !defined(MWSIZE_MAX) +# define MATLAB_VERSION 0x2006a /* R2006a or earlier */ +#elif MX_API_VER < 0x07040000 +# define MATLAB_VERSION 0x2006b /* R2006b */ +#elif !defined(FMT_PTRDIFF_T) +# define MATLAB_VERSION 0x2007a /* R2007a */ +#elif !defined(CUINT64_T) +# define MATLAB_VERSION 0x2007b /* R2007b */ +#elif defined(mxSetLogical) +# define MATLAB_VERSION 0x2008a /* R2008a */ +#else +# if !defined(blas_h) +# include "blas.h" +# endif +# if !defined(lapack_h) +# include "lapack.h" +# endif +# if !defined(MATHWORKS_MATRIX_MATRIX_PUB_FWD_H) +# if defined(CHAR16_T) +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2008b /* R2008b */ +# elif !defined(cgeqr2p) +# define MATLAB_VERSION 0x2010b /* R2010b */ +# else +# define MATLAB_VERSION 0x2011a /* R2011a */ +# endif +# else +# include "emlrt.h" +# define MATLAB_VERSION EMLRT_VERSION_INFO /* R2011b or later */ +# endif +# else +# if !defined(COMPLEX_TYPES) +# define MATLAB_VERSION 0x2009a /* R2009a */ +# elif !defined(cgbequb) +# define MATLAB_VERSION 0x2009b /* R2009b */ +# else +# define MATLAB_VERSION 0x2010a /* R2010a */ +# endif +# endif +#endif +#endif /* if !defined(MATLAB_VERSION) */ + +#if MATLAB_VERSION < 0x2006b +typedef int mwSize; +#endif +#endif + +#ifdef GMT_OCTOCT /* Octave oct files only */ +# define MEX_PROG "Octave(oct)" +# define MEX_COL_ORDER GMT_IS_ROW_FORMAT + /* Macros for getting the Octave(oct) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) +#else /* Here we go for Matlab or Octave(mex) */ +# ifdef GMT_MATLAB +# define MEX_PROG "Matlab" +# else +# define MEX_PROG "Octave(mex)" +# endif +# define MEX_COL_ORDER GMT_IS_COL_FORMAT + /* Macros for getting the Matlab/Octave(mex) ij that correspond to (row,col) [no pad involved] */ + /* This one operates on GMT_MATRIX */ +# define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) + /* And this on GMT_GRID */ +# define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) +#endif + +/* Definitions of MEX structures used to hold GMT objects. + * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ + +/* GMT_IS_DATASET: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. Pure datasets will only set the data + * matrix and leave the text cell array empty, while textsets + * will fill out both. Only the first segment will have any + * information in the info and projection_ref_* items. */ + +#define N_MEX_FIELDNAMES_DATASET 6 +static const char *gmtmex_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = + {"data", "text", "header", "comment", "proj4", "wkt"}; + +/* GMT_IS_GRID: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_GRID 17 +static const char *gmtmex_fieldname_grid[N_MEX_FIELDNAMES_GRID] = + {"z", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", + "command", "datatype", "x_unit", "y_unit", "z_unit", "layout", "proj4", "wkt"}; + +/* GMT_IS_IMAGE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_IMAGE 19 +static const char *gmtmex_fieldname_image[N_MEX_FIELDNAMES_IMAGE] = + {"image", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", "command", + "datatype", "x_unit", "y_unit", "z_unit", "colormap", "alpha", "layout", "proj4", "wkt"}; + +/* GMT_IS_PALETTE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_CPT 11 +static const char *gmtmex_fieldname_cpt[N_MEX_FIELDNAMES_CPT] = + {"colormap", "alpha", "range", "minmax", "bfn", "depth", "hinge", "cpt", "model", "mode", "comment"}; + +/* GMT_IS_POSTSCRIPT: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_PS 4 +static const char *gmtmex_fieldname_ps[N_MEX_FIELDNAMES_PS] = + {"postscript", "length", "mode", "comment"}; + +/* Macro for indexing into a GMT grid [with pad] */ +#define GMT_IJP(h,row,col) ((uint64_t)(((int64_t)(row)+(int64_t)h->pad[GMT_YHI])*((int64_t)h->mx)+(int64_t)(col)+(int64_t)h->pad[GMT_XLO])) + +#define MODULE_LEN 32 /* Max length of a GMT module name */ + +#ifndef rint + #define rint(x) (floor((x)+0.5f)) //does not work reliable. +#endif + +#if defined(WIN32) +#if !defined(lrint) +# define lrint (int64_t)rint +#endif +#endif + +enum MEX_dim { + DIM_COL = 0, /* Holds the number of columns for vectors and x-nodes for matrix */ + DIM_ROW = 1}; /* Holds the number of rows for vectors and y-nodes for matrix */ + +/* Note: Wherever we say "MATLAB" below we mean "MATLAB or Octave" + * + * Reading and writing occur inside gmt_api.c via the standard GMT containers. + * The packing up of GMT structures into MATLAB structures and vice versa happens after getting the + * results out of the GMT API and before passing them back to MATLAB. + * + * All 5 GMT Resources are supported in this API, according to these rules: + * GMT_GRID: Handled with a MATLAB grid structure and we use GMT's GMT_GRID for the passing + * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] + * + The 2-D grid array z (single precision) + * + An x-array of coordinates + * + An y-array of coordinates + * + Various Proj4 strings + * GMT_IMAGE: Handled with a MATLAB image structure and we use GMT's GMT_IMAGE for the passing + * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] + * + The 2-D grid array z (single precision) + * + An x-array of coordinates + * + An y-array of coordinates + * + Various Proj4 strings + * GMT_DATASET: Handled with an array of MATLAB data structures and we use GMT's GMT_DATASET for passing in and out of GMT. + * Each MEX structure contain data for one segment: + * + A text string for the segment header + * + A 2-D matrix with rows and columns (double precision) + * + An optional cell array with strings from trailing columns that could not be deciphered as data. + * + First segment may also have dataset comment and proj4/wtk strings + * GMT_PALETTE: Handled with a MATLAB structure and we use GMT's native GMT_PALETTE for the passing. + * + colormap is the N*3 matrix for MATLAB colormaps + * + range is a N-element array with z-values at color changes + * + alpha is a N-element array with transparencies + * + minmax is a 2-element array with zmin and zmax + * + bnf is a 3x3-element matrix with back,fore,NaN colors + * + depth is a 1-element matrix with color depth (1, 8, 24 bits) + * + hinge is a 1-element matrix with hinge z-value (or NaN) + * + cpt is a N*6-element matrix with original CPT slice values + * + comment holds any Palette comments + * GMT_POSTSCRIPT: Handled with a MATLAB structure and we use GMT's native GMT_POSTSCRIPT for the passing. + * + postscript is the single string with all the PostScript code + * + length is the number of bytes in the string + * + mode is the overlay/trailer indicator + * + comment holds any PostScript comments + */ + +static int gmtmex_print_func (FILE *fp, const char *message) { + /* Replacement for GMT's gmt_print_func. It is being used indirectly via + * API->print_func. Purpose of this is to allow MATLAB (which cannot use + * printf) to reset API->print_func to this function via GMT_Create_Session. + * This allows GMT's errors and warnings to appear in MATLAB console. */ + + mexPrintf (message); + return 0; +} + +static uint64_t gmtmex_getMNK (const mxArray *p, int which) { + /* Get number of columns or number of bands of a mxArray. + which = 0 to inquire n_rows + = 1 to inquire n_columns + = 2 to inquire n_bands + = ? ERROR + */ + uint64_t nx, ny, nBands, nDims; + const mwSize *dim_array = NULL; + + nDims = mxGetNumberOfDimensions(p); + dim_array = mxGetDimensions(p); + ny = dim_array[0]; + nx = dim_array[1]; + nBands = dim_array[2]; + if (nDims == 2) /* Otherwise it would stay undefined */ + nBands = 1; + + if (which == 0) + return ny; + else if (which == 1) + return nx; + else if (which == 2) + return nBands; + else + mexErrMsgTxt("gmtmex_getMNK: Bad dimension number!"); + return 0; +} + +static void gmtmex_quit_if_missing (const char *function, const char *field) { + char buffer[128] = {""}; + sprintf (buffer , "%s: Could not find structure field %s\n", function, field); + mexErrMsgTxt (buffer); +} + +static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { + /* Given an incoming GMT grid G, build a MATLAB structure and assign the output components. + * Note: Incoming GMT grid has standard padding while MATLAB grid has none. */ + + unsigned int k; + uint64_t row, col, gmt_ij; + float *f = NULL; + double *d = NULL, *G_x = NULL, *G_y = NULL, *x = NULL, *y = NULL; + mxArray *G_struct = NULL, *mxptr[N_MEX_FIELDNAMES_GRID]; + + if (!G->data) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_grid: programming error, output matrix G is empty\n"); + + /* Create a MATLAB struct to hold this grid [matrix will be a float (mxSINGLE_CLASS)]. */ + G_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); + + /* Get pointers and populate structure from the information in G */ + mxptr[0] = mxCreateNumericMatrix (G->header->n_rows, G->header->n_columns, mxSINGLE_CLASS, mxREAL); + mxptr[1] = mxCreateNumericMatrix (1, G->header->n_columns, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, G->header->n_rows, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateDoubleScalar ((double)G->header->registration); + mxptr[6] = mxCreateDoubleScalar ((double)G->header->nan_value); + mxptr[7] = mxCreateString (G->header->title); + mxptr[8] = mxCreateString (G->header->remark); + mxptr[9] = mxCreateString (G->header->command); + mxptr[10] = mxCreateString ("float32"); + mxptr[11] = mxCreateString (G->header->x_units); + mxptr[12] = mxCreateString (G->header->y_units); + mxptr[13] = mxCreateString (G->header->z_units); + mxptr[14] = (G->header->mem_layout[0]) ? mxCreateString(G->header->mem_layout) : mxCreateString ("TCF"); + mxptr[15] = mxCreateString (G->header->ProjRefPROJ4); + mxptr[16] = mxCreateString (G->header->ProjRefWKT); + + d = mxGetPr (mxptr[3]); /* Range */ + for (k = 0; k < 4; k++) d[k] = G->header->wesn[k]; + d[4] = G->header->z_min; d[5] = G->header->z_max; + + d = mxGetPr (mxptr[4]); /* Increments */ + for (k = 0; k < 2; k++) d[k] = G->header->inc[k]; + + /* Load the real grd array into a float MATLAB array by transposing + from padded GMT grd format to unpadded MATLAB format */ + f = mxGetData (mxptr[0]); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + f[MEXG_IJ(G,row,col)] = G->data[gmt_ij]; + } + } + + /* Also return the convenient x and y arrays */ + G_x = GMT_Get_Coord (API, GMT_IS_GRID, GMT_X, G); /* Get array of x coordinates */ + G_y = GMT_Get_Coord (API, GMT_IS_GRID, GMT_Y, G); /* Get array of y coordinates */ + x = mxGetData (mxptr[1]); + y = mxGetData (mxptr[2]); + memcpy (x, G_x, G->header->n_columns * sizeof (double)); + for (k = 0; k < G->header->n_rows; k++) + y[G->header->n_rows-1-k] = G_y[k]; /* Must reverse the y-array */ + if (GMT_Destroy_Data (API, &G_x)) + mexPrintf("Warning: Failure to delete G_x (x coordinate vector)\n"); + if (GMT_Destroy_Data (API, &G_y)) + mexPrintf("Warning: Failure to delete G_y (y coordinate vector)\n"); + for (k = 0; k < N_MEX_FIELDNAMES_GRID; k++) + mxSetField (G_struct, 0, gmtmex_fieldname_grid[k], mxptr[k]); + return (G_struct); +} + +static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { + /* Given a GMT DATASET D, build a MATLAB array of segment structure and assign values. + * Each segment will have 6 items: + * header: Text string with the segment header (could be empty) + * data: Matrix with the data for this segment (n_rows by n_columns) + * text: Optional cell array for trailing text + * comment: Cell array with any comments + * proj4: String with any proj4 information + * wkt: String with any WKT information + */ + + int n_headers; + uint64_t tbl, seg, seg_out, col, row, start, k, n_items = 1; + double *data = NULL; + struct GMT_DATASEGMENT *S = NULL; + mxArray *D_struct = NULL, *mxheader = NULL, *mxdata = NULL, *mxtext = NULL, *mxstring = NULL; + + if (D == NULL) { /* No output produced (?) - return a null data set */ + D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); + return (D_struct); + } + + for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) /* Count non-zero segments */ + for (seg = 0; seg < D->table[tbl]->n_segments; seg++) + if (D->table[tbl]->segment[seg]->n_rows) + seg_out++; + if (seg_out == 0) n_items = 0; + D_struct = mxCreateStructMatrix ((mwSize)seg_out, (mwSize)n_items, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); + + n_headers = (D->n_tables) ? D->table[0]->n_headers : 0; /* Number of header records in first table */ + for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) { + for (seg = 0; seg < D->table[tbl]->n_segments; seg++) { + S = D->table[tbl]->segment[seg]; /* Shorthand */ + if (S->n_rows == 0) continue; /* Skip empty segments */ + if (S->header) { /* Has segment header */ + mxheader = mxCreateString (S->header); + mxSetField (D_struct, (mwSize)seg_out, "header", mxheader); + } + if (S->text) { /* Has trailing text */ + mxtext = mxCreateCellMatrix (S->n_rows, 1); + for (row = 0; row < S->n_rows; row++) { + mxstring = mxCreateString (S->text[row]); + mxSetCell (mxtext, (int)row, mxstring); + } + mxSetField (D_struct, (mwSize)seg_out, "text", mxtext); + } + if (S->n_columns) { /* Has numerical data */ + mxdata = mxCreateNumericMatrix ((mwSize)S->n_rows, (mwSize)S->n_columns, mxDOUBLE_CLASS, mxREAL); + data = mxGetPr (mxdata); + for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ + memcpy (&data[start], S->data[col], S->n_rows * sizeof (double)); + mxSetField (D_struct, (mwSize)seg_out, "data", mxdata); + } + if (n_headers) { /* First segment will get any headers, the rest nothing */ + mxtext = mxCreateCellMatrix (n_headers, n_headers ? 1 : 0); + for (k = 0; k < n_headers; k++) { + mxstring = mxCreateString (D->table[0]->header[k]); + mxSetCell (mxtext, (int)k, mxstring); + } + mxSetField (D_struct, (mwSize)seg_out, "comment", mxtext); + n_headers = 0; /* No other segment will have a non-empty comment cell array */ + } + seg_out++; + } + } + return (D_struct); +} + +static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { + /* Given a GMT GMT_POSTSCRIPT P, build a MATLAB array of segment structure and assign values. + * Each segment will have 4 items: + * postscript: Text string with the entire PostScript plot + * length: Byte length of postscript + * mode: 1 has header, 2 has trailer, 3 is complete + * comment: Cell array with any comments + */ + uint64_t k, *length = NULL; + unsigned int *mode = NULL; + mxArray *P_struct = NULL, *mxptr[N_MEX_FIELDNAMES_PS], *mxstring = NULL; + + if (P == NULL) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_postscript: programming error, input POSTSCRIPT struct P is NULL or data string is empty\n"); + + if (!P->data) { + /* Return empty PS struct */ + P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); + return P_struct; + } + + /* Return PS with postscript and length in a struct */ + P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); + + mxptr[0] = mxCreateString (P->data); + mxptr[1] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); + mxptr[3] = mxCreateCellMatrix (P->n_headers, P->n_headers ? 1 : 0); + length = (uint64_t *)mxGetData(mxptr[1]); + mode = (uint32_t *)mxGetData(mxptr[2]); + + length[0] = (uint64_t)P->n_bytes; /* Set length of the PS string */ + mode[0] = (uint32_t)P->mode; /* Set mode of the PS string */ + + for (k = 0; k < P->n_headers; k++) { + mxstring = mxCreateString (P->header[k]); + mxSetCell (mxptr[3], (int)k, mxstring); + } + + for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) + mxSetField (P_struct, 0, gmtmex_fieldname_ps[k], mxptr[k]); + + return P_struct; +} + +static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { + /* Given a GMT GMT_PALETTE C, build a MATLAB structure and assign values. + * Each segment will have 10 items: + * colormap: Nx3 array of colors usable in Matlab' colormap + * alpha: Nx1 array with transparency values + * range: Nx1 arran with z-values at color changes + * minmax: 2x1 array with min/max zvalues + * bfn: 3x3 array with colors for background, forground, nan + * depth Color depth 24, 8, 1 + * hinge: Z-value at discontinuous color break, or NaN + * cpt: Nx6 full GMT CPT array + * model: String with color model rgb, hsv, or cmyk [rgb] + * comment: Cell array with any comments + * + * Limitation: MATLAB's colormap format can either hold discrete + * or continuous colormaps, but not a mixture of these, which GMT + * can do. Thus, mixed-mode GMT cpts being used in MATLAB or passed + * out from MATLAB cannot represent these changes accurately. */ + + unsigned int k, j, n_colors, *depth = NULL; + double *color = NULL, *cpt = NULL, *alpha = NULL, *minmax = NULL, *range = NULL, *hinge = NULL, *cyclic = NULL, *bfn = NULL; + mxArray *C_struct = NULL, *mxptr[N_MEX_FIELDNAMES_CPT], *mxstring = NULL; + + if (C == NULL) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_palette: programming error, output CPT C is empty\n"); + + if (!C->data) { /* Return empty struct */ + C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); + return (C_struct); + } + + /* Return CPT via colormap, range, and alpha arrays in a struct */ + /* Create a MATLAB struct for this CPT */ + C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); + + n_colors = (C->is_continuous) ? C->n_colors + 1 : C->n_colors; + mxptr[0] = mxCreateNumericMatrix (n_colors, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[1] = mxCreateNumericMatrix (n_colors, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (C->n_colors, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (2, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (3, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); + mxptr[6] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[7] = mxCreateNumericMatrix (C->n_colors, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[8] = NULL; /* Set below */ + mxptr[9] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); + mxptr[10] = mxCreateCellMatrix (C->n_headers, C->n_headers ? 1 : 0); + + color = mxGetPr (mxptr[0]); + alpha = mxGetPr (mxptr[1]); + range = mxGetPr (mxptr[2]); + minmax = mxGetPr (mxptr[3]); + bfn = mxGetPr (mxptr[4]); + depth = (uint32_t *)mxGetData (mxptr[5]); + hinge = mxGetPr (mxptr[6]); + cpt = mxGetPr (mxptr[7]); + cyclic = mxGetPr (mxptr[9]); + depth[0] = (C->is_bw) ? 1 : ((C->is_gray) ? 8 : 24); + hinge[0] = (C->has_hinge) ? C->hinge : mxGetNaN (); + cyclic[0] = (C->is_wrapping) ? 1.0 : 0.0; + for (j = 0; j < 3; j++) /* Copy r/g/b from palette bfn to MATLAB array */ + for (k = 0; k < 3; k++) bfn[j+3*k] = C->bfn[j].rgb[k]; + for (j = 0; j < C->n_colors; j++) { /* Copy r/g/b from palette to MATLAB colormap and cpt */ + for (k = 0; k < 3; k++) { + color[j+k*n_colors] = cpt[j+k*C->n_colors] = C->data[j].rgb_low[k]; + cpt[j+(k+3)*C->n_colors] = C->data[j].rgb_high[k]; + } + alpha[j] = C->data[j].rgb_low[3]; + range[j] = C->data[j].z_low; + range[j+C->n_colors] = C->data[j].z_high; + } + if (C->is_continuous) { /* Add last color/alpha to colormap */ + for (k = 0; k < 3; k++) color[j+k*n_colors] = C->data[C->n_colors-1].rgb_high[k]; + alpha[j] = C->data[j].rgb_low[3]; + } + minmax[0] = C->data[0].z_low; /* Set min/max limits */ + minmax[1] = C->data[C->n_colors-1].z_high; + if (C->n_headers) { + for (k = 0; k < C->n_headers; k++) { + mxstring = mxCreateString (C->header[k]); + mxSetCell (mxptr[10], (int)k, mxstring); + } + } + if (C->model & GMT_HSV) + mxptr[8] = mxCreateString ("hsv"); + else if (C->model & GMT_CMYK) + mxptr[8] = mxCreateString ("cmyk"); + else + mxptr[8] = mxCreateString ("rgb"); + + for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) /* Update all fields */ + mxSetField (C_struct, 0, gmtmex_fieldname_cpt[k], mxptr[k]); + return (C_struct); +} + +static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { + unsigned int k; + mwSize dim[3]; + uint8_t *u = NULL, *alpha = NULL; + double *d = NULL, *I_x = NULL, *I_y = NULL, *x = NULL, *y = NULL, *color = NULL; + mxArray *I_struct = NULL, *mxptr[N_MEX_FIELDNAMES_IMAGE]; + + if (I == NULL || !I->data) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_image: programming error, output image I is empty\n"); + + /* Return image via a uint8_t (mxUINT8_CLASS) matrix in a struct */ + /* Create a MATLAB struct for this image */ + I_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); + /* Create the various fields with information from I */ + mxptr[0] = NULL; /* Set below */ + mxptr[1] = mxCreateNumericMatrix (1, I->header->n_columns, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, I->header->n_rows, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateDoubleScalar ((double)I->header->registration); + mxptr[6] = mxCreateDoubleScalar ((double)I->header->nan_value); + mxptr[7] = mxCreateString (I->header->title); + mxptr[8] = mxCreateString (I->header->remark); + mxptr[9] = mxCreateString (I->header->command); + mxptr[10] = mxCreateString ("uint8"); + mxptr[11] = mxCreateString (I->header->x_units); + mxptr[12] = mxCreateString (I->header->y_units); + mxptr[13] = mxCreateString (I->header->z_units); + mxptr[14] = mxptr[15] = NULL; /* Set below */ + mxptr[16] = (I->header->mem_layout[0]) ? mxCreateString(I->header->mem_layout) : mxCreateString ("TCBa"); + mxptr[17] = mxCreateString (I->header->ProjRefPROJ4); + mxptr[18] = mxCreateString (I->header->ProjRefWKT); + + /* Fill in values */ + d = mxGetPr (mxptr[3]); /* Range */ + for (k = 0; k < 4; k++) d[k] = I->header->wesn[k]; + d[4] = I->header->z_min; d[5] = I->header->z_max; + + d = mxGetPr(mxptr[4]); /* Increments */ + for (k = 0; k < 2; k++) d[k] = I->header->inc[k]; + + if (I->colormap != NULL) { /* Indexed image has a color map */ + mxptr[14] = mxCreateNumericMatrix (I->n_indexed_colors, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + color = mxGetPr (mxptr[14]); + for (k = 0; k < 4 * (unsigned int)I->n_indexed_colors && I->colormap[k] >= 0; k++) + color[k] = (uint8_t)I->colormap[k]; + k /= 4; + memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); + } + else if (I->header->n_bands == 1) { /* gray image */ + mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); + } + else if (I->header->n_bands == 3) { /* RGB image */ + dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; + mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + if (!strncmp(I->header->mem_layout, "TCBa", 4)) + memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); + else if (!strncmp(I->header->mem_layout, "TRPa", 4)) { + GMT_Change_Layout (API, GMT_IS_IMAGE, "TCB", 0, I, u, alpha); /* Convert from TRP to TCB */ + mxptr[16] = mxCreateString ("TCBa"); /* Because we just converted to it above */ + } + else { + mexPrintf("WarnError: this image's' memory layout, %s, is not implemented. Expect random art.\n"); + } + if (I->alpha) { + mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + alpha = mxGetData (mxptr[15]); + memcpy (alpha, I->alpha, I->header->nm * sizeof (uint8_t)); + } + } + else if (I->header->n_bands == 4) { /* RGBA image, with a color map */ + dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; + mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); + u = mxGetData (mxptr[0]); + mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); + alpha = mxGetData (mxptr[15]); + memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); + memcpy (alpha, &(I->data)[3 * I->header->nm], I->header->nm * sizeof (uint8_t)); + /* + for (k = 0; k < I->header->nm; k++) { + for (m = 0; m < 3; m++) + u[k+m*I->header->nm] = (uint8_t)I->data[4*k+m]; + alpha[k] = (uint8_t)I->data[4*k+3]; + } + */ + } + + /* Also return the convenient x and y arrays */ + I_x = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_X, I); /* Get array of x coordinates */ + I_y = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_Y, I); /* Get array of y coordinates */ + x = mxGetData (mxptr[1]); + y = mxGetData (mxptr[2]); + memcpy (x, I_x, I->header->n_columns * sizeof (double)); + for (k = 0; k < I->header->n_rows; k++) /* Must reverse the y-array */ + y[I->header->n_rows-1-k] = I_y[k]; + if (GMT_Destroy_Data (API, &I_x)) + mexPrintf("Warning: Failure to delete I_x (x coordinate vector)\n"); + if (GMT_Destroy_Data (API, &I_y)) + mexPrintf("Warning: Failure to delete I_y (y coordinate vector)\n"); + for (k = 0; k < N_MEX_FIELDNAMES_IMAGE; k++) { /* Update fields */ + if (mxptr[k]) mxSetField (I_struct, 0, gmtmex_fieldname_image[k], mxptr[k]); + } + return (I_struct); +} + +static struct GMT_GRID *gmtmex_grid_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty Grid container to hold a GMT grid. + * If direction is GMT_IN then we are given a MATLAB grid and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT grid as a destination. */ + unsigned int row, col; + uint64_t gmt_ij; + struct GMT_GRID *G = NULL; + + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + unsigned int registration, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + mxArray *mx_ptr = NULL, *mxGrid = NULL, *mxHdr = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_grid_init: The input that was supposed to contain the Grid, is empty\n"); + if (!mxIsStruct (ptr)) { + if (!mxIsCell (ptr)) + mexErrMsgTxt ("gmtmex_grid_init: Expected a Grid structure or Cell array for input\n"); + else { /* Test that we have a {MxN,1x9} cell array */ + if (mxGetM(ptr) != 2 && mxGetN(ptr) != 2) + mexErrMsgTxt ("gmtmex_grid_init: Cell array must contain two elements\n"); + else { + mxGrid = mxGetCell(ptr, 0); + mxHdr = mxGetCell(ptr, 1); + if (mxGetM(mxGrid) < 2 || mxGetN(mxGrid) < 2) + mexErrMsgTxt ("gmtmex_grid_init: First element of grid's cell array must contain a decent matrix\n"); + if (mxGetM(mxHdr) != 1 || mxGetN(mxHdr) != 9) + mexErrMsgTxt ("gmtmex_grid_init: grid's cell array second element must contain a 1x9 vector\n"); + if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) + mexErrMsgTxt ("gmtmex_grid_init: grid's cell matrix must be either single or double.\n"); + } + } + } + + if (mxIsStruct(ptr)) { /* Passed a regular MEX Grid structure */ + double *inc = NULL, *range = NULL, *reg = NULL; + unsigned int pad = (unsigned int)GMT_NOTSET; + char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, + z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[3]; + mx_ptr = mxGetField (ptr, 0, "inc"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find inc array with Grid increments\n"); + inc = mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "range"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find range array for Grid range\n"); + range = mxGetData (mx_ptr); + + mxGrid = mxGetField(ptr, 0, "z"); + if (mxGrid == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find data array for Grid\n"); + if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) + mexErrMsgTxt ("gmtmex_grid_init: data array must be either single or double.\n"); + + mx_ptr = mxGetField (ptr, 0, "registration"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Could not find registration array for Grid registration\n"); + reg = mxGetData (mx_ptr); + registration = (unsigned int)lrint(reg[0]); + + + mx_ptr = mxGetField(ptr, 0, "pad"); + if (mx_ptr != NULL) { + double *dpad = mxGetData(mx_ptr); + pad = (unsigned int)dpad[0]; + if (pad > 2) + mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); + } + + if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, + NULL, range, inc, registration, pad, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); + + G->header->z_min = range[4]; + G->header->z_max = range[5]; + + G->header->registration = registration; + + mx_ptr = mxGetField (ptr, 0, "nodata"); + if (mx_ptr != NULL) + G->header->nan_value = *(float *)mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "proj4"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + G->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "wkt"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + G->header->ProjRefWKT = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "title"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "command"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "comment"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr)+2); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "x_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "y_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "z_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "layout"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(G->header->mem_layout, layout, 3); + } + else + strncpy(G->header->mem_layout, "TRS", 3); + } + else { /* Passed header and grid separately */ + double *h = mxGetData(mxHdr); + registration = (unsigned int)lrint(h[6]); + if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, + NULL, h, &h[7], registration, GMT_NOTSET, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); + G->header->z_min = h[4]; + G->header->z_max = h[5]; + } + + if (mxIsSingle(mxGrid)) { + float *f4 = mxGetData(mxGrid); + if (f4 == NULL) + mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + G->data[gmt_ij] = f4[MEXG_IJ(G,row,col)]; + } + } + } + else { + double *f8 = mxGetData(mxGrid); + if (f8 == NULL) + mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); + for (row = 0; row < G->header->n_rows; row++) { + for (col = 0; col < G->header->n_columns; col++) { + gmt_ij = GMT_IJP (G->header, row, col); + G->data[gmt_ij] = (float)f8[MEXG_IJ(G,row,col)]; + } + } + } + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_grid_init: Allocated GMT Grid %lx\n", (long)G); + GMT_Report (API, GMT_MSG_DEBUG, + "gmtmex_grid_init: Registered GMT Grid array %lx via memory reference from MATLAB\n", + (long)G->data); + } + else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((G = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_IS_OUTPUT, + NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT blank grid container for holding output grid\n"); + } + return (G); +} + +static struct GMT_IMAGE *gmtmex_image_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty Image container to hold a GMT image. + * If direction is GMT_IN then we are given a MATLAB image and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT image as a destination. */ + struct GMT_IMAGE *I = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + uint64_t dim[3]; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0, pad = 0; + char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, + z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[4]; + double *reg = NULL, *inc = NULL, *range = NULL; + mxArray *mx_ptr = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_image_init: The input that was supposed to contain the Image, is empty\n"); + + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_image_init: Expected a Image structure for input\n"); + + mx_ptr = mxGetField (ptr, 0, "range"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find range array for Image range\n"); + range = mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "inc"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find inc array with Image increments\n"); + inc = mxGetData (mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "registration"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find registration info in Image struct\n"); + reg = mxGetData(mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "pad"); + if (mx_ptr != NULL) { + double *dpad = mxGetData(mx_ptr); + pad = (unsigned int)dpad[0]; + if (pad > 2) + mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); + } + + mx_ptr = mxGetField (ptr, 0, "image"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_image_init: Could not find data array for Image\n"); + + if (!mxIsUint8(mx_ptr)) + mexErrMsgTxt("gmtmex_image_init: The only data type supported by now is UInt8, and this image is not.\n"); + + dim[0] = gmtmex_getMNK (mx_ptr, 1); dim[1] = gmtmex_getMNK (mx_ptr, 0); dim[2] = gmtmex_getMNK (mx_ptr, 2); + if ((I = GMT_Create_Data (API, GMT_IS_IMAGE|flag, GMT_IS_SURFACE, GMT_GRID_HEADER_ONLY, dim, + range, inc, (unsigned int)reg[0], pad, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT source image for input\n"); + + I->data = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ + GMT_Set_AllocMode (API, GMT_IS_IMAGE, I); + //I->alloc_mode = GMT_ALLOC_EXTERNALLY; + +/* + memcpy (I->data, (unsigned char *)mxGetData (mx_ptr), I->header->nm * I->header->n_bands * sizeof (char)); + for (row = 0; row < I->header->n_rows; row++) { + for (col = 0; col < I->header->n_columns; col++) { + gmt_ij = GMT_IJP (I->header, row, col); + I->data [gmt_ij] = f[MEXG_IJ(I,row,col)]; + } + } +*/ + mx_ptr = mxGetField (ptr, 0, "alpha"); + I->alpha = NULL; + if (mx_ptr != NULL) { + if (mxGetNumberOfDimensions(mx_ptr) == 2) + I->alpha = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ + } + + I->header->z_min = range[4]; + I->header->z_max = range[5]; + + mx_ptr = mxGetField(ptr, 0, "x"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find x-coords vector for Image\n"); + I->x = mxGetData(mx_ptr); + + mx_ptr = mxGetField(ptr, 0, "y"); + if (mx_ptr == NULL) + mexErrMsgTxt("gmtmex_image_init: Could not find y-coords vector for Image\n"); + I->y = mxGetData(mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "nodata"); + if (mx_ptr != NULL) + I->header->nan_value = *(float *)mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "proj4"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this length */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + I->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "wkt"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more than this length */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + I->header->ProjRefWKT = GMT_Duplicate_String (API, str); + free (str); + } + + mx_ptr = mxGetField (ptr, 0, "title"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "command"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "comment"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "x_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "y_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "z_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "layout"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(I->header->mem_layout, layout, 4); + } + else + strncpy(I->header->mem_layout, "TCBa", 4); + + I->colormap = NULL; /* BUT IT SHOULD BE PROPERLY ASSIGNED IF IMAGE IS INDEXED */ + if (dim[2] == 1) + I->color_interp = "Gray"; + else + I->color_interp = "Unknown"; /* BUT WE CAN */ + + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_image_init: Allocated GMT Image %lx\n", (long)I); + GMT_Report (API, GMT_MSG_DEBUG, + "gmtmex_image_init: Registered GMT Image array %lx via memory reference from MATLAB\n", + (long)I->data); + } + else { /* Just allocate an empty container to hold an output image (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((I = GMT_Create_Data (API, GMT_IS_IMAGE, GMT_IS_SURFACE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT blank image container for holding output image\n"); + + GMT_Set_Default (API, "API_IMAGE_LAYOUT", "TCBa"); /* State how we wish to receive images from GDAL */ + } + return (I); +} + +static void *gmtmex_dataset_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr, unsigned int *actual_family) { + /* Create containers to hold or receive data tables: + * direction == GMT_IN: Create empty GMT_DATASET container, fill from Mex, and use as GMT input. + * Input from MATLAB may be a MEX data structure, a plain matrix, a cell array of strings or a single string. + * direction == GMT_OUT: Create empty GMT_DATASET container, let GMT fill it out, and use for Mex output. + * If direction is GMT_IN then we are given a MATLAB struct and can determine dimension. + * If output then we don't know size so we set dimensions to zero. */ + struct GMT_DATASET *D = NULL; + + *actual_family = GMT_IS_DATASET; /* Default but may change to matrix below */ + if (direction == GMT_IN) { /* Data given, dimensions are know, create container for GMT */ + uint64_t seg, col, row, start, k, n_headers, dim[4] = {1, 0, 0, 0}, n, m, n_rows; /* We only return a single table */ + size_t length = 0; + bool got_single_record = false; + unsigned int mode; + char buffer[BUFSIZ] = {""}, *txt = NULL; + mxArray *mx_ptr = NULL, *mx_ptr_d = NULL, *mx_ptr_t = NULL; + double *data = NULL; + struct GMT_DATASEGMENT *S = NULL; + + if (!ptr) mexErrMsgTxt ("gmtmex_dataset_init: Input is empty where it can't be.\n"); + if (mxIsNumeric (ptr)) { /* Got a MATLAB matrix as input - pass data pointers via MATRIX to save memory */ + struct GMT_MATRIX *M = NULL; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + flag |= GMT_VIA_MATRIX; + *actual_family |= GMT_VIA_MATRIX; + mxClassID type = mxGetClassID (ptr); /* Storage type for this matrix */ + dim[DIM_ROW] = mxGetM (ptr); /* Number of rows */ + dim[DIM_COL] = mxGetN (ptr); /* Number of columns */ + /* Create matrix container but do not allocate any matrix memory */ + if ((M = GMT_Create_Data (API, GMT_IS_DATASET|flag, GMT_IS_PLP, GMT_CONTAINER_ONLY, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source matrix\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Matrix %lx\n", (long)M); + switch (type) { /* Assign ML type pointer to the corresponding GMT matrix union pointer */ + case mxDOUBLE_CLASS: M->type = GMT_DOUBLE; M->data.f8 = mxGetData (ptr); break; + case mxSINGLE_CLASS: M->type = GMT_FLOAT; M->data.f4 = (float *)mxGetData (ptr); break; + case mxUINT64_CLASS: M->type = GMT_ULONG; M->data.ui8 = (uint64_t *)mxGetData (ptr); break; + case mxINT64_CLASS: M->type = GMT_LONG; M->data.si8 = (int64_t *)mxGetData (ptr); break; + case mxUINT32_CLASS: M->type = GMT_UINT; M->data.ui4 = (uint32_t *)mxGetData (ptr); break; + case mxINT32_CLASS: M->type = GMT_INT; M->data.si4 = (int32_t *)mxGetData (ptr); break; + case mxUINT16_CLASS: M->type = GMT_USHORT; M->data.ui2 = (uint16_t *)mxGetData (ptr); break; + case mxINT16_CLASS: M->type = GMT_SHORT; M->data.si2 = (int16_t *)mxGetData (ptr); break; + case mxUINT8_CLASS: M->type = GMT_UCHAR; M->data.uc1 = (uint8_t *)mxGetData (ptr); break; + case mxINT8_CLASS: M->type = GMT_CHAR; M->data.sc1 = (int8_t *)mxGetData (ptr); break; + default: + mexErrMsgTxt ("gmtmex_dataset_init: Unsupported MATLAB data type in GMT matrix input."); + break; + } + /* Data from MATLAB and Octave(mex) is in col format and data from Octave(oct) is in row format */ +#ifdef GMT_OCTOCT + M->dim = M->n_columns; +#else + M->dim = M->n_rows; +#endif + //M->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Since matrix was allocated by MATLAB/Octave we cannot free it in GMT */ + GMT_Set_AllocMode (API, GMT_IS_MATRIX, M); + M->shape = MEX_COL_ORDER; /* Either col or row order, depending on MATLAB/Octave setting in gmtmex.h */ + return (M); + } + /* We come here if we did not receive a matrix, There are three options: */ + /* 1. A dataset MATLAB structure or array of structures. + * 2. A Cell array of plain text strings for a text-only file. + * 3. A single text string instead of a one-item cell array of strings. */ + + if (mxIsStruct (ptr)) { /* Got the dataset structure */ + dim[GMT_SEG] = mxGetM (ptr); /* Number of segments */ + if (dim[GMT_SEG] == 0) mexErrMsgTxt ("gmtmex_dataset_init: Input has zero segments where it can't be.\n"); + mx_ptr_d = mxGetField (ptr, 0, "data"); /* Get first segment's data matrix [if available] */ + if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ + mx_ptr_t = mxGetField (ptr, 0, "text"); /* Get first segment's text matrix [if available] */ + if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ + + if (mx_ptr_d == NULL && mx_ptr_t == NULL) + mexErrMsgTxt("gmtmex_dataset_init: Both 'data' array and 'text' array are NULL!\n"); + if (mx_ptr_d) + dim[GMT_COL] = mxGetN (mx_ptr_d); /* Number of columns */ + if (dim[GMT_COL] == 0 && mx_ptr_t == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Input has zero columns where it can't be.\n"); + + if (mx_ptr_t) /* This segment also has a cell array of strings */ + mode = GMT_WITH_STRINGS; + else + mode = GMT_NO_STRINGS; + + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + + for (seg = 0; seg < dim[GMT_SEG]; seg++) { /* Each incoming structure is a new data segment */ + mx_ptr = mxGetField (ptr, (mwSize)seg, "header"); /* Get pointer to MEX segment header */ + buffer[0] = 0; /* Reset our temporary text buffer */ + if (mx_ptr && (length = mxGetN (mx_ptr)) != 0) /* These is a non-empty segment header to keep */ + mxGetString (mx_ptr, buffer, (mwSize)(length+1)); + mx_ptr_d = mxGetField (ptr, (mwSize)seg, "data"); /* Data matrix for this segment */ + if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ + mx_ptr_t = mxGetField (ptr, (mwSize)seg, "text"); /* text cell array for this segment */ + if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ + + if (mx_ptr_t) { /* This segment also has a cell array of strings or possibly a single string (if n_rows == 1) */ + got_single_record = false; + m = mxGetM (mx_ptr_t); n = mxGetN (mx_ptr_t); + if (!mxIsCell (mx_ptr_t) && (m == 1 || n == 1)) { + got_single_record = true; + m = n = 1; + } + } + else /* No trailing text */ + m = n = 0; + dim[GMT_ROW] = (mx_ptr_d == NULL) ? m : mxGetM (mx_ptr_d); /* Number of rows in matrix (or strings if no data) */ + if ((m == dim[GMT_ROW] && n == 1) || (n == dim[GMT_ROW] && m == 1)) + mode = GMT_WITH_STRINGS; + else + mode = GMT_NO_STRINGS; + /* Allocate a new data segment and hook up to to our single table */ + S = GMT_Alloc_Segment (API, mode, dim[GMT_ROW], dim[GMT_COL], buffer, D->table[0]->segment[seg]); + if (mx_ptr_d != NULL) data = mxGetData (mx_ptr_d); + for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ + memcpy (S->data[col], &data[start], S->n_rows * sizeof (double)); + if (mode == GMT_WITH_STRINGS) { /* Add in the trailing strings */ + if (got_single_record) { /* Only true when we got a single row with a single string instead of a cell array */ + txt = mxArrayToString (mx_ptr_t); + S->text[0] = GMT_Duplicate_String (API, txt); + } + else { /* Must extract text from the cell array */ + for (row = 0; row < S->n_rows; row++) { + mx_ptr = mxGetCell (mx_ptr_t, (mwSize)row); + txt = mxArrayToString (mx_ptr); + S->text[row] = GMT_Duplicate_String (API, txt); + } + } + } + D->table[0]->n_records += S->n_rows; /* Must manually keep track of totals */ + if (seg == 0) { /* First segment may have table information */ + mx_ptr_t = mxGetField (ptr, (mwSize)seg, "comment"); /* Table headers */ + if (mx_ptr_t && (n_headers = mxGetM (mx_ptr_t)) != 0) { /* Number of headers found */ + for (k = 0; k < n_headers; k++) { /* Extract the headers and insert into dataset */ + mx_ptr = mxGetCell (mx_ptr_t, (mwSize)k); + txt = mxArrayToString (mx_ptr); + if (GMT_Set_Comment (API, GMT_IS_DATASET, GMT_COMMENT_IS_TEXT, txt, D)) + mexErrMsgTxt("gmtmex_dataset_init: Failed to set a dataset header\n"); + } + } + } + if (mode == GMT_WITH_STRINGS) D->type = (D->n_columns) ? GMT_READ_MIXED : GMT_READ_TEXT; + else D->type = GMT_READ_DATA; + } + } + else if (mxIsCell (ptr)) { /* Got a cell array of strings and no numerical data */ + uint64_t k2 = 0; + m = mxGetM (ptr); n = mxGetN (ptr); + n_rows = (m > n) ? m : n; /* Number of items in cell array */ + mode = GMT_WITH_STRINGS; /* Since that is all we have */ + /* Determine number of segments up front since user may use '>' to indicate segment header */ + for (k = 0; k < n_rows; k++) { + mx_ptr = mxGetCell (ptr, (mwSize)k); + txt = mxArrayToString (mx_ptr); + if (txt[0] == '>') dim[GMT_SEG]++; /* Found start of a new segment */ + } + if (dim[GMT_SEG] == 0) dim[GMT_SEG] = 1; /* No segment headers given a single segment */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, GMT_WITH_STRINGS, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + k = seg = 0; + while (k < n_rows) { /* Examine the input records and look for segment breaks */ + mx_ptr = mxGetCell (ptr, (mwSize)k); + txt = mxArrayToString (mx_ptr); + buffer[0] = '\0'; + if (txt[0] == '>' || (k == 0 && txt[0] != '>')) { /* Found start of a new (or first and only) segment */ + if (txt[0] == '>') strcpy (buffer, txt), k++; /* Segment header */ + k2 = k; /* k and k2 initially point to the first row of the current segment */ + dim[GMT_ROW] = 0; /* Have no rows so far */ + while (k2 < n_rows && dim[GMT_ROW] == 0) { /* While not reached end of current segment */ + mx_ptr = mxGetCell (ptr, (mwSize)k2); + txt = mxArrayToString (mx_ptr); + if (txt[0] == '>') /* Got next segment header, must end current segment */ + dim[GMT_ROW] = k2 - k; + else /* Keep going */ + k2++; + } + if (dim[GMT_ROW] == 0) dim[GMT_ROW] = k2 - k; /* Happens for last segment when there is no "next" segment header */ + } + /* Now we have the length of this segment */ + S = GMT_Alloc_Segment (API, GMT_WITH_STRINGS, dim[GMT_ROW], 0, buffer, D->table[0]->segment[seg]); + for (row = 0; row < S->n_rows; row++) { /* Hook up the string records */ + mx_ptr = mxGetCell (ptr, (mwSize)(k+row)); /* k is the offset to 1st record of current segment in input cell array */ + txt = mxArrayToString (mx_ptr); + S->text[row] = GMT_Duplicate_String (API, txt); + } + D->table[0]->n_records += S->n_rows; /* Must manually keep track of total records */ + seg++; /* Got ourselves a new segment, increment counter */ + /* Move to next unused record or we have reached the end */ + k = k2; + } + D->type = GMT_READ_TEXT; + } + else if (mxIsChar (ptr)) { /* Got a single string only */ + dim[GMT_ROW] = dim[GMT_SEG] = 1; /* Put string into a single segment */ + mode = GMT_WITH_STRINGS; /* Since that is all we have */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); + S = D->table[0]->segment[0]; /* The lone segment */ + txt = mxArrayToString (ptr); /* The lone string */ + S->text[0] = GMT_Duplicate_String (API, txt); + D->type = GMT_READ_TEXT; + } + else + mexErrMsgTxt ("gmtmex_dataset_init: Expected a data structure, cell array with strings, or a single string for input\n"); + D->n_records = D->table[0]->n_records; + } + else { /* Here we set up an empty container to receive data from GMT (signal this by passing 0s and NULLs) */ + if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source dataset\n"); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Dataset %lx\n", (long)D); + } + return (D); +} + +static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to create an empty CPT container to hold a GMT Color Palette. + * If direction is GMT_IN then we are given a MATLAB CPT struct and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT CPT as a destination. */ + struct GMT_PALETTE *P = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + unsigned int k, j, one = 1, n_headers, *depth = NULL; + uint64_t dim[2] = {0, 0}; + unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + char model[8] = {""}; + mxArray *mx_ptr[N_MEX_FIELDNAMES_CPT]; + double *colormap = NULL, *range = NULL, *minmax = NULL, *alpha = NULL, *bfn = NULL, *hinge = NULL, *cpt = NULL, *cyclic = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_palette_init: The input that was supposed to contain the CPT, is empty\n"); + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_palette_init: Expected a CPT structure for input\n"); + for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) { + if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_cpt[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_palette_init", gmtmex_fieldname_cpt[k]); + } + + dim[0] = mxGetM (mx_ptr[0]); /* Number of rows in colormap */ + if (dim[0] < 1) + mexErrMsgTxt ("gmtmex_palette_init: Colormap array has no CPT values\n"); + colormap = mxGetData (mx_ptr[0]); + alpha = mxGetData (mx_ptr[1]); + range = mxGetData (mx_ptr[2]); + minmax = mxGetData (mx_ptr[3]); + bfn = mxGetData (mx_ptr[4]); + depth = mxGetData (mx_ptr[5]); + hinge = mxGetData (mx_ptr[6]); + cpt = mxGetData (mx_ptr[7]); + cyclic = mxGetData (mx_ptr[9]); + + /* Disable unused-but-set-variable warnings */ + (void)(minmax); + (void)(colormap); + + dim[1] = mxGetM (mx_ptr[2]); /* Length of range array */ + if (dim[0] > dim[1]) { /* This only happens when we have a continuous color table */ + dim[1] = dim[0]; /* Actual length of colormap array */ + dim[0]--; /* Number of CPT slices */ + } + else /* Discrete, so the one offset needs to be zero */ + one = 0; + + if ((P = GMT_Create_Data (API, GMT_IS_PALETTE|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT source CPT for input\n"); + + if ((n_headers = (unsigned int)mxGetM (mx_ptr[10])) != 0) { /* Number of headers found */ + char *txt = NULL; + mxArray *ptr = NULL; + for (k = 0; k < n_headers; k++) { + ptr = mxGetCell (mx_ptr[10], (mwSize)k); + txt = mxArrayToString (ptr); + if (GMT_Set_Comment (API, GMT_IS_PALETTE, GMT_COMMENT_IS_TEXT, txt, P)) + mexErrMsgTxt("gmtmex_palette_init: Failed to set a CPT header\n"); + } + } + for (j = 0; j < 3; j++) { /* Do the bfn first */ + for (k = 0; k < 3; k++) + P->bfn[j].rgb[k] = bfn[j+k*3]; + } + for (j = 0; j < P->n_colors; j++) { /* OK to access j+1'th element since length of colormap is P->n_colors+1 */ + for (k = 0; k < 3; k++) { + P->data[j].rgb_low[k] = cpt[j+k*dim[0]]; + P->data[j].rgb_high[k] = cpt[j+(k+3)*dim[0]]; + } + P->data[j].rgb_low[3] = alpha[j]; + P->data[j].rgb_high[3] = alpha[j+one]; + P->data[j].z_low = range[j]; + P->data[j].z_high = range[j+P->n_colors]; + P->data[j].annot = 3; /* Enforce annotations for now */ + } + P->is_continuous = one; + P->is_bw = P->is_gray = 0; + P->is_wrapping = (unsigned int)rint (cyclic[0]); + if (depth[0] == 1) + P->is_bw = 1; + else if (depth[0] == 8) + P->is_gray = 1; + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_palette_init: Allocated GMT CPT %lx\n", (long)P); + if (!mxIsNaN (hinge[0])) { + P->has_hinge = 1; + P->mode |= GMT_CPT_HINGED; + P->hinge = hinge[0]; + } + mxGetString (mx_ptr[8], model, (mwSize)mxGetN(mx_ptr[8])+1); + if (!strncmp (model, "hsv", 3U)) + P->model = GMT_HSV; + else if (!strncmp (model, "cmyk", 4U)) + P->model = GMT_CMYK; + else + P->model = GMT_RGB; + } + else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((P = GMT_Create_Data (API, GMT_IS_PALETTE, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT blank CPT container for holding output CPT\n"); + } + return (P); +} + +static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty POSTSCRIPT container to hold a GMT POSTSCRIPT object. + * If direction is GMT_IN then we are given a MATLAB structure with known sizes. + * If direction is GMT_OUT then we allocate an empty GMT POSTSCRIPT as a destination. */ + struct GMT_POSTSCRIPT *P = NULL; + if (direction == GMT_IN) { /* Dimensions are known from the MATLAB input pointer */ + uint64_t dim[1] = {0}, *length = NULL; + unsigned int k, n_headers, *mode = NULL, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + mxArray *mx_ptr[N_MEX_FIELDNAMES_PS]; + char *PS = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_ps_init: The input that was supposed to contain the PostScript structure is empty\n"); + if (!mxIsStruct (ptr)) + mexErrMsgTxt ("gmtmex_ps_init: Expected a MATLAB PostScript structure for input\n"); + for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) { + if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_ps[k])) == NULL) + gmtmex_quit_if_missing ("gmtmex_ps_init", gmtmex_fieldname_ps[k]); + } + + length = mxGetData (mx_ptr[1]); + if (length[0] == 0) + mexErrMsgTxt ("gmtmex_ps_init: Dimension of PostScript given as zero\n"); + PS = malloc (mxGetN(mx_ptr[0])+1); + mxGetString (mx_ptr[0], PS, (mwSize)mxGetN(mx_ptr[0])); + mode = mxGetData (mx_ptr[2]); + /* Passing dim[0] = 0 since we dont want any allocation of a PS string */ + if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT source for input\n"); + P->data = PS; /* PostScript string instead is coming from MATLAB */ + GMT_Set_AllocMode (API, GMT_IS_POSTSCRIPT, P); + //P->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Hence we are not allowed to free it */ + P->n_bytes = length[0]; /* Length of the actual PS string */ + //P->n_alloc = 0; /* But nothing was actually allocated here - just passing pointer from MATLAB */ + P->mode = mode[0]; /* Inherit the mode */ + if ((n_headers = (unsigned int)mxGetM (mx_ptr[3])) != 0) { /* Number of headers found */ + char *txt = NULL; + mxArray *ptr = NULL; + for (k = 0; k < n_headers; k++) { + ptr = mxGetCell (mx_ptr[3], (mwSize)k); + txt = mxArrayToString (ptr); + if (GMT_Set_Comment (API, GMT_IS_POSTSCRIPT, GMT_COMMENT_IS_TEXT, txt, P)) + mexErrMsgTxt("gmtmex_ps_init: Failed to set a PostScript header\n"); + } + } + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_ps_init: Allocated GMT POSTSCRIPT %lx\n", (long)P); + } + else { /* Just allocate an empty container to hold an output PS object (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT container for holding output PostScript\n"); + } + return (P); +} + +static char gmtmex_objecttype (const mxArray *ptr) { + /* Determine what we are returning so gmt write can pass the correct -T? flag */ + mxArray *mx_ptr = NULL; + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_objecttype: Pointer is empty\n"); + if (mxIsStruct (ptr)) { /* This means either a dataset, grid, image, cpt, or PS, so must check for fields */ + mx_ptr = mxGetField (ptr, 0, "data"); + if (mx_ptr) return 'd'; + mx_ptr = mxGetField (ptr, 0, "postscript"); + if (mx_ptr) return 'p'; + mx_ptr = mxGetField (ptr, 0, "hinge"); + if (mx_ptr) return 'c'; + mx_ptr = mxGetField (ptr, 0, "image"); + if (mx_ptr) return 'i'; + mx_ptr = mxGetField (ptr, 0, "z"); + if (mx_ptr) return 'g'; + mexErrMsgTxt ("gmtmex_objecttype: Could not recognize the structure\n"); + } + else if (mxIsCell (ptr)) /* This is a dataset with text only */ + return 'd'; + else /* We have to assume it is a numerical matrix */ + return 'd'; + return '-'; /* Can never get here you would think */ +} + +static void gmtmex_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { + /* Create the GMT container and hook onto resource array as X->object */ + unsigned int module_input = (X->option->option == GMT_OPT_INFILE), actual_family = X->family; + + switch (X->family) { + case GMT_IS_GRID: /* Get a grid from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_grid_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Grid\n"); + break; + case GMT_IS_IMAGE: /* Get an image from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_image_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Image\n"); + break; + case GMT_IS_DATASET: /* Get a dataset from Matlab or a dummy one to hold GMT output */ + /* Because a GMT_DATASET may appears as a GMT_MATRIX or GMT_VECTOR we need the actual_family to open the virtual file later */ + X->object = gmtmex_dataset_init (API, X->direction, module_input, ptr, &actual_family); + break; + case GMT_IS_PALETTE: /* Get a palette from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_palette_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got CPT\n"); + break; + case GMT_IS_POSTSCRIPT: /* Get a PostScript struct from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_ps_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got POSTSCRIPT\n"); + break; + default: + GMT_Report (API, GMT_MSG_NORMAL, "gmtmex_Set_Object: Bad data type (%d)\n", X->family); + break; + } + if (X->object == NULL) + mexErrMsgTxt("GMT: Failure to register the resource\n"); + if (GMT_Open_VirtualFile (API, actual_family, X->geometry, X->direction|GMT_IS_REFERENCE, X->object, X->name) != GMT_NOERROR) /* Make filename with embedded object ID */ + mexErrMsgTxt ("GMT: Failure to open virtual file\n"); + if (GMT_Expand_Option (API, X->option, X->name) != GMT_NOERROR) /* Replace ? in argument with name */ + mexErrMsgTxt ("GMT: Failure to expand filename marker (?)\n"); +} + +static void *gmtmex_Get_Object (void *API, struct GMT_RESOURCE *X) { + mxArray *ptr = NULL; + /* In line-by-line modules it is possible no output is produced, hence we make an exception for DATASET: */ + if ((X->object = GMT_Read_VirtualFile (API, X->name)) == NULL && X->family != GMT_IS_DATASET) + mexErrMsgTxt ("GMT: Error reading virtual file from GMT\n"); + switch (X->family) { /* Determine what container we got */ + case GMT_IS_GRID: /* A GMT grid; make it the pos'th output item */ + ptr = gmtmex_get_grid (API, X->object); + break; + case GMT_IS_DATASET: /* A GMT table; make it a data structure and the pos'th output item */ + ptr = gmtmex_get_dataset (API, X->object); + break; + case GMT_IS_PALETTE: /* A GMT CPT; make it a colormap and the pos'th output item */ + ptr = gmtmex_get_palette (API, X->object); + break; + case GMT_IS_IMAGE: /* A GMT Image; make it the pos'th output item */ + ptr = gmtmex_get_image (API, X->object); + break; + case GMT_IS_POSTSCRIPT: /* A GMT PostScript string; make it the pos'th output item */ + ptr = gmtmex_get_postscript (API, X->object); +#if 0 + { + char cmd[32] = {""}; + strcpy(cmd, name); strcat(cmd, " -A -Tf"); + GMT_Call_Module(API, "psconvert", GMT_MODULE_CMD, cmd); + } +#endif + break; + default: + mexErrMsgTxt ("GMT: Internal Error - unsupported data type\n"); + break; + } + return ptr; +} + +#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 +extern int gmt_get_V (char arg); /* Temporary here to allow full debug messaging */ +#else +extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ +#define gmt_get_V GMT_get_V /* Old name */ +#endif + +#ifndef SINGLE_SESSION +/* Being declared external we can access it between MEX calls */ +static uintptr_t *pPersistent; /* To store API address back and forth within a single MATLAB session */ + +/* Here is the exit function, which gets run when the MEX-file is + cleared and when the user exits MATLAB. The mexAtExit function + should always be declared as static. */ +static void force_Destroy_Session (void) { + void *API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API != NULL) { /* Otherwise just silently ignore this call */ + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("Failure to destroy GMT session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ + } +} +#endif + +static void usage (int nlhs, int nrhs) { + /* Basic usage message */ + if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ + mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", + MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); + mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); + mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); + mexPrintf("You may redistribute copies of this program under the terms of the\n"); + mexPrintf("GNU Lesser General Public License.\n"); + mexPrintf("For more information about these matters, see the file named LICENSE.TXT.\n"); + mexPrintf("For a brief description of GMT modules, type gmt ('help')\n\n"); + } + else { + mexPrintf("Usage is:\n\tgmt ('module_name', 'options'[, ]); %% Run a GMT module\n"); + if (nlhs != 0) + mexErrMsgTxt ("But meanwhile you already made an error by asking help and an output.\n"); + } +} + +static void *Initiate_Session (unsigned int verbose) { + /* Initialize the GMT Session and store the API pointer in a persistent variable */ + void *API = NULL; + /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ + /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ + if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + + GMT_SESSION_COLMAJOR, gmtmex_print_func)) == NULL) + mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); + +#ifndef SINGLE_SESSION + if (!pPersistent) pPersistent = mxMalloc(sizeof(uintptr_t)); + pPersistent[0] = (uintptr_t)(API); + mexMakeMemoryPersistent (pPersistent); +#endif + return (API); +} + +static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { + /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior + when we do for example (i.e. no lhs): sqrt([4 9]) + */ + void *ptr = NULL; + switch (X->family) { + case GMT_IS_GRID: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); + break; + case GMT_IS_IMAGE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); + break; + case GMT_IS_DATASET: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); + break; + case GMT_IS_PALETTE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); + break; + case GMT_IS_POSTSCRIPT: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); + break; + default: + break; + } + return ptr; +} + +/* This is the function that is called when we type gmt in MATLAB/Octave. + * It is the only function experted by the GMT API library. */ -/* 1. Must include mex.h since we use mxArray */ -#include "mex.h" -/* 2. Must list extern GMTMEX mexfunction available from libgmt */ -extern void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]); -/* 3. The minimal gateway function called by Matlab */ +void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + int status = 0; /* Status code from GMT API */ + int n_in_objects = 0; /* Number of input objects passed to module */ + unsigned int first = 0; /* Array ID of first command argument (not 0 when API-ID is first) */ + unsigned int verbose = 0; /* Default verbose setting */ + unsigned int n_items = 0, pos = 0; /* Number of MATLAB arguments (left and right) */ + size_t str_length = 0, k = 0; /* Misc. counters */ + void *API = NULL; /* GMT API control structure */ + struct GMT_OPTION *options = NULL; /* Linked list of module options */ + struct GMT_RESOURCE *X = NULL; /* Array of information about MATLAB args */ + char *cmd = NULL; /* Pointer used to get the user's MATLAB command */ + char *gtxt = NULL; /* For debug printing of revised command */ + char *opt_args = NULL; /* Pointer to the user's module options */ + char module[MODULE_LEN] = {""}; /* Name of GMT module to call */ + char opt_buffer[BUFSIZ] = {""}; /* Local copy of command line options */ + void *ptr = NULL; +#ifndef SINGLE_SESSION + uintptr_t *pti = NULL; /* To locally store the API address */ +#endif + + /* -1. Check that the GMT library is of a suitable version for this GMTMEX version */ + + if (GMT_MAJOR_VERSION > GMTMEX_GMT_MAJOR_VERSION) { /* This may not work if, for instance, we want >= 6.1.x but is using GMT 7.0.0 */ + char message[128] = {""}; + sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); + mexPrintf (message); + } + else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { + char message[128] = {""}; + sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", + GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, + GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); + mexErrMsgTxt (message); + } + + /* 0. No arguments at all results in the GMT banner message */ + if (nrhs == 0) { + usage (nlhs, nrhs); + return; + } + + /* 1. Check for the special commands create and help */ + + if (nrhs == 1) { /* This may be create or help */ + cmd = mxArrayToString (prhs[0]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); + if (!strncmp (cmd, "help", 4U) || !strncmp (cmd, "--help", 6U)) { + usage (nlhs, 1); + return; + } +#ifndef SINGLE_SESSION + if (!strncmp (cmd, "create", 6U)) { /* Asked to create a new GMT session */ + if (nlhs > 1) /* Asked for too much output, only 1 or 0 is allowed */ + mexErrMsgTxt ("GMT: Usage: gmt ('create') or API = gmt ('create');\n"); + if (pPersistent) /* See if have a GMT API pointer */ + API = (void *)pPersistent[0]; + if (API != NULL) { /* If another session still exists */ + GMT_Report (API, GMT_MSG_VERBOSE, + "GMT: A previous GMT session is still active. Ignoring your 'create' request.\n"); + if (nlhs) /* Return nothing */ + plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); + return; + } + if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = gmt_get_V (gtxt[2]); + API = Initiate_Session (verbose); /* Initializing a new GMT session */ + + if (nlhs) { /* Return the API address as an integer (nlhs == 1 here) )*/ + plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); + pti = mxGetData(plhs[0]); + *pti = *pPersistent; + } + + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + return; + } + + /* OK, neither create nor help, must be a single command with no arguments nor the API. So get it: */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { /* No session yet, create one under the hood */ + API = Initiate_Session(verbose); /* Initializing a new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } + else + API = (void *)pPersistent[0]; /* Get the GMT API pointer */ + if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is corrupted. Better to start from scratch.\n"); + } + else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { + /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ + pti = (uintptr_t *)mxGetData(prhs[0]); + API = (void *)pti[0]; /* Get the GMT API pointer */ + first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ + } + else { /* We still don't have the API, so we must get it from the past or initiate a new session */ + if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { + API = Initiate_Session (verbose); /* Initializing new GMT session */ + mexAtExit(force_Destroy_Session); /* Register an exit function. */ + } +#endif + } + +#ifdef SINGLE_SESSION + /* Initiate a new session */ + API = Initiate_Session (verbose); /* Initializing new GMT session */ +#endif + + if (!cmd) { /* First argument is the command string, e.g., 'blockmean -R0/5/0/5 -I1' or just 'destroy' */ + cmd = mxArrayToString(prhs[first]); + if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string but is probably a cell array of strings.\n"); + } + + if (!strncmp (cmd, "destroy", 7U)) { /* Destroy the session */ +#ifndef SINGLE_SESSION + if (nlhs != 0) + mexErrMsgTxt ("GMT: Usage is gmt ('destroy');\n"); + + if (GMT_Destroy_Options (API, &options)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); + if (GMT_Destroy_Session (API)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); + *pPersistent = 0; /* Wipe the persistent memory */ +#endif + return; + } + + /* 2. Get module name and separate out args */ + + /* Here we have a GMT module call. The documented use is to give the module name separately from + * the module options, but users may forget and combine the two. So we check both cases. */ + + n_in_objects = nrhs - 1; + str_length = strlen (cmd); /* Length of module (or command) argument */ + for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ + + if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ + strcpy (module, cmd); /* Isolate the module name in this string */ + if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ + first++; /* Since we have a 2nd string to skip now */ + opt_args = mxArrayToString (prhs[first]); + n_in_objects--; + } + /* Else we got no options, just input objects */ + } + else { /* Case b2. Get mex arguments, if any, and extract the GMT module name */ + if (k >= MODULE_LEN) + mexErrMsgTxt ("GMT: Module name in command is too long\n"); + strncpy (module, cmd, k); /* Isolate the module name in this string */ + + while (cmd[k] == ' ') k++; /* Skip any spaces between module name and start of options */ + if (cmd[k]) opt_args = &cmd[k]; + } + + /* See if info about installation is required */ + if (!strcmp(module, "gmt")) { + char t[256] = {""}; + if (!opt_args) { + mexPrintf("Warning: calling the 'gmt' program by itself does nothing here.\n"); + return; + } + if (!strcmp(opt_args, "--show-bindir")) /* Show the directory that contains the 'gmt' executable */ + GMT_Get_Default (API, "BINDIR", t); + else if (!strcmp(opt_args, "--show-sharedir")) /* Show share directory */ + GMT_Get_Default (API, "SHAREDIR", t); + else if (!strcmp(opt_args, "--show-datadir")) /* Show the data directory */ + GMT_Get_Default (API, "DATADIR", t); + else if (!strcmp(opt_args, "--show-plugindir")) /* Show the plugin directory */ + GMT_Get_Default (API, "PLUGINDIR", t); + else if (!strcmp(opt_args, "--show-cores")) /* Show number of cores */ + GMT_Get_Default (API, "CORES", t); + + if (t[0] != '\0') { + if (nlhs) + plhs[0] = mxCreateString (t); + else + mexPrintf ("%s\n", t); + } + else + mexPrintf ("Warning: called the 'gmt' program with unknown option.\n"); + return; + } + + /* Make sure this is a valid module */ + if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ + mexErrMsgTxt ("GMT: No module by that name was found.\n"); + + /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to + * another string with enough space. */ + + if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ + /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ + if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ + if (opt_args) + strcat (opt_buffer, " -F"); + else + strcpy (opt_buffer, "-F"); + } + + /* 2++ If gmtwrite then add -T? with correct object type */ + if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ + char targ[5] = {" -T?"}; + targ[3] = gmtmex_objecttype (prhs[nrhs-1]); + strcat (opt_buffer, targ); + } + /* 2+++ If gmtread -Ti then temporarily set pad to 0 since we don't want padding in image arrays */ + else if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default(API, "API_PAD", "0"); + + /* 3. Convert mex command line arguments to a linked GMT option list */ + if (opt_buffer[0] && (options = GMT_Create_Options (API, 0, opt_buffer)) == NULL) + mexErrMsgTxt ("GMT: Failure to parse GMT5 command options\n"); + + if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ + options = GMT_Create_Options (API, 0, "-?"); + + /* 4. Preprocess to update GMT option lists and return info array X */ + if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { + if (n_items == UINT_MAX) /* Just got usage/synopsis option */ + n_items = 0; + else + mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); + } + + if (options) { /* Only for debugging - remove this section when stable */ + gtxt = GMT_Create_Cmd (API, options); + GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); + GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ + } + + /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ + + for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ + if (X[k].direction == GMT_IN) { + if ((X[k].pos+first+1) < (unsigned int)nrhs) + ptr = (void *)prhs[X[k].pos+first+1]; + else + mexErrMsgTxt ("GMT: Attempting to address a prhs entry that does not exist\n"); + } + else { + if ((X[k].pos) < nlhs) + ptr = (void *)plhs[X[k].pos]; + else { + //mexErrMsgTxt ("GMT: Attempting to address a plhs entry that does not exist\n"); + ptr = alloc_default_plhs (API, &X[k]); + } + } + gmtmex_Set_Object (API, &X[k], ptr); /* Set object pointer */ + } + + /* 6. Run GMT module; give usage message if errors arise during parsing */ + status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); + if (status != GMT_NOERROR) { + if (status <= GMT_MODULE_PURPOSE) + return; + else { + mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); + mexErrMsgTxt("GMT: exiting\n"); + } + } + + /* 7. Hook up any GMT outputs to MATLAB plhs array */ + + for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ + if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ + pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ + plhs[pos] = gmtmex_Get_Object (API, &X[k]); /* Hook mex object onto rhs list */ + } + + /* 2++- If gmtread -Ti then reset the sessions pad value that was temporarily changed above (2+++) */ + if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) + GMT_Set_Default (API, "API_PAD", "2"); + + /* 8. Free all GMT containers involved in this module call */ + + for (k = 0; k < n_items; k++) { + void *ppp = X[k].object; + if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to close virtual file\n"); + if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); + else { /* Success, now make sure we don't destroy the same pointer more than once */ + for (size_t kk = k+1; kk < n_items; kk++) + if (X[kk].object == ppp) X[kk].object = NULL; + } + } + + /* 9. Destroy linked option list */ + + if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); +#ifdef SINGLE_SESSION + if (GMT_Destroy_Session (API)) + mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); +#endif + return; +} + void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { GMT_mexFunction (nlhs, plhs, nrhs, prhs); } diff --git a/src/gmtmex/gmtmex_api.c b/src/gmtmex/gmtmex_api.c deleted file mode 100644 index a69f29835aa..00000000000 --- a/src/gmtmex/gmtmex_api.c +++ /dev/null @@ -1,1898 +0,0 @@ -/* - * Copyright (c) 2015-2020 by P. Wessel and J. Luis - * See LICENSE.TXT file for copying and redistribution conditions. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation; version 3 or any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * Contact info: www.generic-mapping-tools.org - *--------------------------------------------------------------------*/ -/* - * This is the MATLAB/Octave(mex) GMT application, which can do the following: - * 1) Create a new session and optionally return the API pointer. We provide for - * storing the pointer as a global variable (persistent) between calls. - * 2) Destroy a GMT session, either given the API pointer or by fetching it from - * the global (persistent) variable. - * 3) Call any of the GMT modules while passing data in and out of GMT. - * - * First argument to the gmt function is the API pointer, but it is optional once created. - * Next argument is the module name - * Third argument is the option string - * Finally, there are optional comma-separated MATLAB array entities required by the command. - * Information about the options of each program is provided via GMT_Encode_Options. - * - * GMT Version: 6.x - * Created: 20-OCT-2017 - * Updated: 1-APR-2021 requires GMT 6.2.x - * - * - * GMT convenience functions used by MATLAB/OCTAVE mex/oct API - * We also define the MEX structures used to pass data in/out of GMT. - */ - -#define GMTMEX_MAJOR_VERSION 2 -#define GMTMEX_MINOR_VERSION 0 -#define GMTMEX_PATCH_VERSION 0 - -/* Define the minimum GMT version suitable for this GMTMEX version */ - -#define GMTMEX_GMT_MAJOR_VERSION 6 -#define GMTMEX_GMT_MINOR_VERSION 1 -#define GMTMEX_GMT_PATCH_VERSION 1 - -#define STDC_FORMAT_MACROS -#define GMTMEX_LIB - -#include "gmt.h" -#include "gmt_version.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef GMT_OCTOCT -# include -#else -# include -# define mxIsScalar_(mx) \ - ( (2 == mxGetNumberOfDimensions(mx)) \ - && (1 == mxGetM(mx))&& (1 == mxGetN(mx)) ) -#endif /* Matlab and Octave(mex) */ - -/* Matlab and Octave (in -mex mode) are identical, oct files are different and not yet tested */ - -/* Older Ml versions don't have mwSize */ -#ifndef GMT_OCTMEX -#if !defined(MATLAB_VERSION) -#if !defined(MWSIZE_MAX) -# define MATLAB_VERSION 0x2006a /* R2006a or earlier */ -#elif MX_API_VER < 0x07040000 -# define MATLAB_VERSION 0x2006b /* R2006b */ -#elif !defined(FMT_PTRDIFF_T) -# define MATLAB_VERSION 0x2007a /* R2007a */ -#elif !defined(CUINT64_T) -# define MATLAB_VERSION 0x2007b /* R2007b */ -#elif defined(mxSetLogical) -# define MATLAB_VERSION 0x2008a /* R2008a */ -#else -# if !defined(blas_h) -# include "blas.h" -# endif -# if !defined(lapack_h) -# include "lapack.h" -# endif -# if !defined(MATHWORKS_MATRIX_MATRIX_PUB_FWD_H) -# if defined(CHAR16_T) -# if !defined(COMPLEX_TYPES) -# define MATLAB_VERSION 0x2008b /* R2008b */ -# elif !defined(cgeqr2p) -# define MATLAB_VERSION 0x2010b /* R2010b */ -# else -# define MATLAB_VERSION 0x2011a /* R2011a */ -# endif -# else -# include "emlrt.h" -# define MATLAB_VERSION EMLRT_VERSION_INFO /* R2011b or later */ -# endif -# else -# if !defined(COMPLEX_TYPES) -# define MATLAB_VERSION 0x2009a /* R2009a */ -# elif !defined(cgbequb) -# define MATLAB_VERSION 0x2009b /* R2009b */ -# else -# define MATLAB_VERSION 0x2010a /* R2010a */ -# endif -# endif -#endif -#endif /* if !defined(MATLAB_VERSION) */ - -#if MATLAB_VERSION < 0x2006b -typedef int mwSize; -#endif -#endif - -#ifdef GMT_OCTOCT /* Octave oct files only */ -# define MEX_PROG "Octave(oct)" -# define MEX_COL_ORDER GMT_IS_ROW_FORMAT - /* Macros for getting the Octave(oct) ij that correspond to (row,col) [no pad involved] */ - /* This one operates on GMT_MATRIX */ -# define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) - /* And this on GMT_GRID */ -# define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) -#else /* Here we go for Matlab or Octave(mex) */ -# ifdef GMT_MATLAB -# define MEX_PROG "Matlab" -# else -# define MEX_PROG "Octave(mex)" -# endif -# define MEX_COL_ORDER GMT_IS_COL_FORMAT - /* Macros for getting the Matlab/Octave(mex) ij that correspond to (row,col) [no pad involved] */ - /* This one operates on GMT_MATRIX */ -# define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) - /* And this on GMT_GRID */ -# define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) -#endif - -/* Definitions of MEX structures used to hold GMT objects. - * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ - -/* GMT_IS_DATASET: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. Pure datasets will only set the data - * matrix and leave the text cell array empty, while textsets - * will fill out both. Only the first segment will have any - * information in the info and projection_ref_* items. */ - -#define N_MEX_FIELDNAMES_DATASET 6 -static const char *gmtmex_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = - {"data", "text", "header", "comment", "proj4", "wkt"}; - -/* GMT_IS_GRID: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_GRID 17 -static const char *gmtmex_fieldname_grid[N_MEX_FIELDNAMES_GRID] = - {"z", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", - "command", "datatype", "x_unit", "y_unit", "z_unit", "layout", "proj4", "wkt"}; - -/* GMT_IS_IMAGE: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_IMAGE 19 -static const char *gmtmex_fieldname_image[N_MEX_FIELDNAMES_IMAGE] = - {"image", "x", "y", "range", "inc", "registration", "nodata", "title", "comment", "command", - "datatype", "x_unit", "y_unit", "z_unit", "colormap", "alpha", "layout", "proj4", "wkt"}; - -/* GMT_IS_PALETTE: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_CPT 11 -static const char *gmtmex_fieldname_cpt[N_MEX_FIELDNAMES_CPT] = - {"colormap", "alpha", "range", "minmax", "bfn", "depth", "hinge", "cpt", "model", "mode", "comment"}; - -/* GMT_IS_POSTSCRIPT: - * Returned by GMT via the parser as a MEX structure with the - * fields listed below. */ - -#define N_MEX_FIELDNAMES_PS 4 -static const char *gmtmex_fieldname_ps[N_MEX_FIELDNAMES_PS] = - {"postscript", "length", "mode", "comment"}; - -/* Macro for indexing into a GMT grid [with pad] */ -#define GMT_IJP(h,row,col) ((uint64_t)(((int64_t)(row)+(int64_t)h->pad[GMT_YHI])*((int64_t)h->mx)+(int64_t)(col)+(int64_t)h->pad[GMT_XLO])) - -#define MODULE_LEN 32 /* Max length of a GMT module name */ - -#ifndef rint - #define rint(x) (floor((x)+0.5f)) //does not work reliable. -#endif - -#if defined(WIN32) -#if !defined(lrint) -# define lrint (int64_t)rint -#endif -#endif - -enum MEX_dim { - DIM_COL = 0, /* Holds the number of columns for vectors and x-nodes for matrix */ - DIM_ROW = 1}; /* Holds the number of rows for vectors and y-nodes for matrix */ - -/* Note: Wherever we say "MATLAB" below we mean "MATLAB or Octave" - * - * Reading and writing occur inside gmt_api.c via the standard GMT containers. - * The packing up of GMT structures into MATLAB structures and vice versa happens after getting the - * results out of the GMT API and before passing them back to MATLAB. - * - * All 5 GMT Resources are supported in this API, according to these rules: - * GMT_GRID: Handled with a MATLAB grid structure and we use GMT's GMT_GRID for the passing - * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] - * + The 2-D grid array z (single precision) - * + An x-array of coordinates - * + An y-array of coordinates - * + Various Proj4 strings - * GMT_IMAGE: Handled with a MATLAB image structure and we use GMT's GMT_IMAGE for the passing - * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] - * + The 2-D grid array z (single precision) - * + An x-array of coordinates - * + An y-array of coordinates - * + Various Proj4 strings - * GMT_DATASET: Handled with an array of MATLAB data structures and we use GMT's GMT_DATASET for passing in and out of GMT. - * Each MEX structure contain data for one segment: - * + A text string for the segment header - * + A 2-D matrix with rows and columns (double precision) - * + An optional cell array with strings from trailing columns that could not be deciphered as data. - * + First segment may also have dataset comment and proj4/wtk strings - * GMT_PALETTE: Handled with a MATLAB structure and we use GMT's native GMT_PALETTE for the passing. - * + colormap is the N*3 matrix for MATLAB colormaps - * + range is a N-element array with z-values at color changes - * + alpha is a N-element array with transparencies - * + minmax is a 2-element array with zmin and zmax - * + bnf is a 3x3-element matrix with back,fore,NaN colors - * + depth is a 1-element matrix with color depth (1, 8, 24 bits) - * + hinge is a 1-element matrix with hinge z-value (or NaN) - * + cpt is a N*6-element matrix with original CPT slice values - * + comment holds any Palette comments - * GMT_POSTSCRIPT: Handled with a MATLAB structure and we use GMT's native GMT_POSTSCRIPT for the passing. - * + postscript is the single string with all the PostScript code - * + length is the number of bytes in the string - * + mode is the overlay/trailer indicator - * + comment holds any PostScript comments - */ - -static int gmtmex_print_func (FILE *fp, const char *message) { - /* Replacement for GMT's gmt_print_func. It is being used indirectly via - * API->print_func. Purpose of this is to allow MATLAB (which cannot use - * printf) to reset API->print_func to this function via GMT_Create_Session. - * This allows GMT's errors and warnings to appear in MATLAB console. */ - - mexPrintf (message); - return 0; -} - -static uint64_t gmtmex_getMNK (const mxArray *p, int which) { - /* Get number of columns or number of bands of a mxArray. - which = 0 to inquire n_rows - = 1 to inquire n_columns - = 2 to inquire n_bands - = ? ERROR - */ - uint64_t nx, ny, nBands, nDims; - const mwSize *dim_array = NULL; - - nDims = mxGetNumberOfDimensions(p); - dim_array = mxGetDimensions(p); - ny = dim_array[0]; - nx = dim_array[1]; - nBands = dim_array[2]; - if (nDims == 2) /* Otherwise it would stay undefined */ - nBands = 1; - - if (which == 0) - return ny; - else if (which == 1) - return nx; - else if (which == 2) - return nBands; - else - mexErrMsgTxt("gmtmex_getMNK: Bad dimension number!"); - return 0; -} - -static void gmtmex_quit_if_missing (const char *function, const char *field) { - char buffer[128] = {""}; - sprintf (buffer , "%s: Could not find structure field %s\n", function, field); - mexErrMsgTxt (buffer); -} - -static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { - /* Given an incoming GMT grid G, build a MATLAB structure and assign the output components. - * Note: Incoming GMT grid has standard padding while MATLAB grid has none. */ - - unsigned int k; - uint64_t row, col, gmt_ij; - float *f = NULL; - double *d = NULL, *G_x = NULL, *G_y = NULL, *x = NULL, *y = NULL; - mxArray *G_struct = NULL, *mxptr[N_MEX_FIELDNAMES_GRID]; - - if (!G->data) /* Safety valve */ - mexErrMsgTxt ("gmtmex_get_grid: programming error, output matrix G is empty\n"); - - /* Create a MATLAB struct to hold this grid [matrix will be a float (mxSINGLE_CLASS)]. */ - G_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); - - /* Get pointers and populate structure from the information in G */ - mxptr[0] = mxCreateNumericMatrix (G->header->n_rows, G->header->n_columns, mxSINGLE_CLASS, mxREAL); - mxptr[1] = mxCreateNumericMatrix (1, G->header->n_columns, mxDOUBLE_CLASS, mxREAL); - mxptr[2] = mxCreateNumericMatrix (1, G->header->n_rows, mxDOUBLE_CLASS, mxREAL); - mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); - mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); - mxptr[5] = mxCreateDoubleScalar ((double)G->header->registration); - mxptr[6] = mxCreateDoubleScalar ((double)G->header->nan_value); - mxptr[7] = mxCreateString (G->header->title); - mxptr[8] = mxCreateString (G->header->remark); - mxptr[9] = mxCreateString (G->header->command); - mxptr[10] = mxCreateString ("float32"); - mxptr[11] = mxCreateString (G->header->x_units); - mxptr[12] = mxCreateString (G->header->y_units); - mxptr[13] = mxCreateString (G->header->z_units); - mxptr[14] = (G->header->mem_layout[0]) ? mxCreateString(G->header->mem_layout) : mxCreateString ("TCF"); - mxptr[15] = mxCreateString (G->header->ProjRefPROJ4); - mxptr[16] = mxCreateString (G->header->ProjRefWKT); - - d = mxGetPr (mxptr[3]); /* Range */ - for (k = 0; k < 4; k++) d[k] = G->header->wesn[k]; - d[4] = G->header->z_min; d[5] = G->header->z_max; - - d = mxGetPr (mxptr[4]); /* Increments */ - for (k = 0; k < 2; k++) d[k] = G->header->inc[k]; - - /* Load the real grd array into a float MATLAB array by transposing - from padded GMT grd format to unpadded MATLAB format */ - f = mxGetData (mxptr[0]); - for (row = 0; row < G->header->n_rows; row++) { - for (col = 0; col < G->header->n_columns; col++) { - gmt_ij = GMT_IJP (G->header, row, col); - f[MEXG_IJ(G,row,col)] = G->data[gmt_ij]; - } - } - - /* Also return the convenient x and y arrays */ - G_x = GMT_Get_Coord (API, GMT_IS_GRID, GMT_X, G); /* Get array of x coordinates */ - G_y = GMT_Get_Coord (API, GMT_IS_GRID, GMT_Y, G); /* Get array of y coordinates */ - x = mxGetData (mxptr[1]); - y = mxGetData (mxptr[2]); - memcpy (x, G_x, G->header->n_columns * sizeof (double)); - for (k = 0; k < G->header->n_rows; k++) - y[G->header->n_rows-1-k] = G_y[k]; /* Must reverse the y-array */ - if (GMT_Destroy_Data (API, &G_x)) - mexPrintf("Warning: Failure to delete G_x (x coordinate vector)\n"); - if (GMT_Destroy_Data (API, &G_y)) - mexPrintf("Warning: Failure to delete G_y (y coordinate vector)\n"); - for (k = 0; k < N_MEX_FIELDNAMES_GRID; k++) - mxSetField (G_struct, 0, gmtmex_fieldname_grid[k], mxptr[k]); - return (G_struct); -} - -static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { - /* Given a GMT DATASET D, build a MATLAB array of segment structure and assign values. - * Each segment will have 6 items: - * header: Text string with the segment header (could be empty) - * data: Matrix with the data for this segment (n_rows by n_columns) - * text: Optional cell array for trailing text - * comment: Cell array with any comments - * proj4: String with any proj4 information - * wkt: String with any WKT information - */ - - int n_headers; - uint64_t tbl, seg, seg_out, col, row, start, k, n_items = 1; - double *data = NULL; - struct GMT_DATASEGMENT *S = NULL; - mxArray *D_struct = NULL, *mxheader = NULL, *mxdata = NULL, *mxtext = NULL, *mxstring = NULL; - - if (D == NULL) { /* No output produced (?) - return a null data set */ - D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); - return (D_struct); - } - - for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) /* Count non-zero segments */ - for (seg = 0; seg < D->table[tbl]->n_segments; seg++) - if (D->table[tbl]->segment[seg]->n_rows) - seg_out++; - if (seg_out == 0) n_items = 0; - D_struct = mxCreateStructMatrix ((mwSize)seg_out, (mwSize)n_items, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); - - n_headers = (D->n_tables) ? D->table[0]->n_headers : 0; /* Number of header records in first table */ - for (tbl = seg_out = 0; tbl < D->n_tables; tbl++) { - for (seg = 0; seg < D->table[tbl]->n_segments; seg++) { - S = D->table[tbl]->segment[seg]; /* Shorthand */ - if (S->n_rows == 0) continue; /* Skip empty segments */ - if (S->header) { /* Has segment header */ - mxheader = mxCreateString (S->header); - mxSetField (D_struct, (mwSize)seg_out, "header", mxheader); - } - if (S->text) { /* Has trailing text */ - mxtext = mxCreateCellMatrix (S->n_rows, 1); - for (row = 0; row < S->n_rows; row++) { - mxstring = mxCreateString (S->text[row]); - mxSetCell (mxtext, (int)row, mxstring); - } - mxSetField (D_struct, (mwSize)seg_out, "text", mxtext); - } - if (S->n_columns) { /* Has numerical data */ - mxdata = mxCreateNumericMatrix ((mwSize)S->n_rows, (mwSize)S->n_columns, mxDOUBLE_CLASS, mxREAL); - data = mxGetPr (mxdata); - for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ - memcpy (&data[start], S->data[col], S->n_rows * sizeof (double)); - mxSetField (D_struct, (mwSize)seg_out, "data", mxdata); - } - if (n_headers) { /* First segment will get any headers, the rest nothing */ - mxtext = mxCreateCellMatrix (n_headers, n_headers ? 1 : 0); - for (k = 0; k < n_headers; k++) { - mxstring = mxCreateString (D->table[0]->header[k]); - mxSetCell (mxtext, (int)k, mxstring); - } - mxSetField (D_struct, (mwSize)seg_out, "comment", mxtext); - n_headers = 0; /* No other segment will have a non-empty comment cell array */ - } - seg_out++; - } - } - return (D_struct); -} - -static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { - /* Given a GMT GMT_POSTSCRIPT P, build a MATLAB array of segment structure and assign values. - * Each segment will have 4 items: - * postscript: Text string with the entire PostScript plot - * length: Byte length of postscript - * mode: 1 has header, 2 has trailer, 3 is complete - * comment: Cell array with any comments - */ - uint64_t k, *length = NULL; - unsigned int *mode = NULL; - mxArray *P_struct = NULL, *mxptr[N_MEX_FIELDNAMES_PS], *mxstring = NULL; - - if (P == NULL) /* Safety valve */ - mexErrMsgTxt ("gmtmex_get_postscript: programming error, input POSTSCRIPT struct P is NULL or data string is empty\n"); - - if (!P->data) { - /* Return empty PS struct */ - P_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); - return P_struct; - } - - /* Return PS with postscript and length in a struct */ - P_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); - - mxptr[0] = mxCreateString (P->data); - mxptr[1] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); - mxptr[2] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); - mxptr[3] = mxCreateCellMatrix (P->n_headers, P->n_headers ? 1 : 0); - length = (uint64_t *)mxGetData(mxptr[1]); - mode = (uint32_t *)mxGetData(mxptr[2]); - - length[0] = (uint64_t)P->n_bytes; /* Set length of the PS string */ - mode[0] = (uint32_t)P->mode; /* Set mode of the PS string */ - - for (k = 0; k < P->n_headers; k++) { - mxstring = mxCreateString (P->header[k]); - mxSetCell (mxptr[3], (int)k, mxstring); - } - - for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) - mxSetField (P_struct, 0, gmtmex_fieldname_ps[k], mxptr[k]); - - return P_struct; -} - -static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { - /* Given a GMT GMT_PALETTE C, build a MATLAB structure and assign values. - * Each segment will have 10 items: - * colormap: Nx3 array of colors usable in Matlab' colormap - * alpha: Nx1 array with transparency values - * range: Nx1 arran with z-values at color changes - * minmax: 2x1 array with min/max zvalues - * bfn: 3x3 array with colors for background, forground, nan - * depth Color depth 24, 8, 1 - * hinge: Z-value at discontinuous color break, or NaN - * cpt: Nx6 full GMT CPT array - * model: String with color model rgb, hsv, or cmyk [rgb] - * comment: Cell array with any comments - * - * Limitation: MATLAB's colormap format can either hold discrete - * or continuous colormaps, but not a mixture of these, which GMT - * can do. Thus, mixed-mode GMT cpts being used in MATLAB or passed - * out from MATLAB cannot represent these changes accurately. */ - - unsigned int k, j, n_colors, *depth = NULL; - double *color = NULL, *cpt = NULL, *alpha = NULL, *minmax = NULL, *range = NULL, *hinge = NULL, *cyclic = NULL, *bfn = NULL; - mxArray *C_struct = NULL, *mxptr[N_MEX_FIELDNAMES_CPT], *mxstring = NULL; - - if (C == NULL) /* Safety valve */ - mexErrMsgTxt ("gmtmex_get_palette: programming error, output CPT C is empty\n"); - - if (!C->data) { /* Return empty struct */ - C_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); - return (C_struct); - } - - /* Return CPT via colormap, range, and alpha arrays in a struct */ - /* Create a MATLAB struct for this CPT */ - C_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); - - n_colors = (C->is_continuous) ? C->n_colors + 1 : C->n_colors; - mxptr[0] = mxCreateNumericMatrix (n_colors, 3, mxDOUBLE_CLASS, mxREAL); - mxptr[1] = mxCreateNumericMatrix (n_colors, 1, mxDOUBLE_CLASS, mxREAL); - mxptr[2] = mxCreateNumericMatrix (C->n_colors, 2, mxDOUBLE_CLASS, mxREAL); - mxptr[3] = mxCreateNumericMatrix (2, 1, mxDOUBLE_CLASS, mxREAL); - mxptr[4] = mxCreateNumericMatrix (3, 3, mxDOUBLE_CLASS, mxREAL); - mxptr[5] = mxCreateNumericMatrix (1, 1, mxUINT32_CLASS, mxREAL); - mxptr[6] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); - mxptr[7] = mxCreateNumericMatrix (C->n_colors, 6, mxDOUBLE_CLASS, mxREAL); - mxptr[8] = NULL; /* Set below */ - mxptr[9] = mxCreateNumericMatrix (1, 1, mxDOUBLE_CLASS, mxREAL); - mxptr[10] = mxCreateCellMatrix (C->n_headers, C->n_headers ? 1 : 0); - - color = mxGetPr (mxptr[0]); - alpha = mxGetPr (mxptr[1]); - range = mxGetPr (mxptr[2]); - minmax = mxGetPr (mxptr[3]); - bfn = mxGetPr (mxptr[4]); - depth = (uint32_t *)mxGetData (mxptr[5]); - hinge = mxGetPr (mxptr[6]); - cpt = mxGetPr (mxptr[7]); - cyclic = mxGetPr (mxptr[9]); - depth[0] = (C->is_bw) ? 1 : ((C->is_gray) ? 8 : 24); - hinge[0] = (C->has_hinge) ? C->hinge : mxGetNaN (); - cyclic[0] = (C->is_wrapping) ? 1.0 : 0.0; - for (j = 0; j < 3; j++) /* Copy r/g/b from palette bfn to MATLAB array */ - for (k = 0; k < 3; k++) bfn[j+3*k] = C->bfn[j].rgb[k]; - for (j = 0; j < C->n_colors; j++) { /* Copy r/g/b from palette to MATLAB colormap and cpt */ - for (k = 0; k < 3; k++) { - color[j+k*n_colors] = cpt[j+k*C->n_colors] = C->data[j].rgb_low[k]; - cpt[j+(k+3)*C->n_colors] = C->data[j].rgb_high[k]; - } - alpha[j] = C->data[j].rgb_low[3]; - range[j] = C->data[j].z_low; - range[j+C->n_colors] = C->data[j].z_high; - } - if (C->is_continuous) { /* Add last color/alpha to colormap */ - for (k = 0; k < 3; k++) color[j+k*n_colors] = C->data[C->n_colors-1].rgb_high[k]; - alpha[j] = C->data[j].rgb_low[3]; - } - minmax[0] = C->data[0].z_low; /* Set min/max limits */ - minmax[1] = C->data[C->n_colors-1].z_high; - if (C->n_headers) { - for (k = 0; k < C->n_headers; k++) { - mxstring = mxCreateString (C->header[k]); - mxSetCell (mxptr[10], (int)k, mxstring); - } - } - if (C->model & GMT_HSV) - mxptr[8] = mxCreateString ("hsv"); - else if (C->model & GMT_CMYK) - mxptr[8] = mxCreateString ("cmyk"); - else - mxptr[8] = mxCreateString ("rgb"); - - for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) /* Update all fields */ - mxSetField (C_struct, 0, gmtmex_fieldname_cpt[k], mxptr[k]); - return (C_struct); -} - -static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { - unsigned int k; - mwSize dim[3]; - uint8_t *u = NULL, *alpha = NULL; - double *d = NULL, *I_x = NULL, *I_y = NULL, *x = NULL, *y = NULL, *color = NULL; - mxArray *I_struct = NULL, *mxptr[N_MEX_FIELDNAMES_IMAGE]; - - if (I == NULL || !I->data) /* Safety valve */ - mexErrMsgTxt ("gmtmex_get_image: programming error, output image I is empty\n"); - - /* Return image via a uint8_t (mxUINT8_CLASS) matrix in a struct */ - /* Create a MATLAB struct for this image */ - I_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); - /* Create the various fields with information from I */ - mxptr[0] = NULL; /* Set below */ - mxptr[1] = mxCreateNumericMatrix (1, I->header->n_columns, mxDOUBLE_CLASS, mxREAL); - mxptr[2] = mxCreateNumericMatrix (1, I->header->n_rows, mxDOUBLE_CLASS, mxREAL); - mxptr[3] = mxCreateNumericMatrix (1, 6, mxDOUBLE_CLASS, mxREAL); - mxptr[4] = mxCreateNumericMatrix (1, 2, mxDOUBLE_CLASS, mxREAL); - mxptr[5] = mxCreateDoubleScalar ((double)I->header->registration); - mxptr[6] = mxCreateDoubleScalar ((double)I->header->nan_value); - mxptr[7] = mxCreateString (I->header->title); - mxptr[8] = mxCreateString (I->header->remark); - mxptr[9] = mxCreateString (I->header->command); - mxptr[10] = mxCreateString ("uint8"); - mxptr[11] = mxCreateString (I->header->x_units); - mxptr[12] = mxCreateString (I->header->y_units); - mxptr[13] = mxCreateString (I->header->z_units); - mxptr[14] = mxptr[15] = NULL; /* Set below */ - mxptr[16] = (I->header->mem_layout[0]) ? mxCreateString(I->header->mem_layout) : mxCreateString ("TCBa"); - mxptr[17] = mxCreateString (I->header->ProjRefPROJ4); - mxptr[18] = mxCreateString (I->header->ProjRefWKT); - - /* Fill in values */ - d = mxGetPr (mxptr[3]); /* Range */ - for (k = 0; k < 4; k++) d[k] = I->header->wesn[k]; - d[4] = I->header->z_min; d[5] = I->header->z_max; - - d = mxGetPr(mxptr[4]); /* Increments */ - for (k = 0; k < 2; k++) d[k] = I->header->inc[k]; - - if (I->colormap != NULL) { /* Indexed image has a color map */ - mxptr[14] = mxCreateNumericMatrix (I->n_indexed_colors, 3, mxDOUBLE_CLASS, mxREAL); - mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); - u = mxGetData (mxptr[0]); - color = mxGetPr (mxptr[14]); - for (k = 0; k < 4 * (unsigned int)I->n_indexed_colors && I->colormap[k] >= 0; k++) - color[k] = (uint8_t)I->colormap[k]; - k /= 4; - memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); - } - else if (I->header->n_bands == 1) { /* gray image */ - mxptr[0] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); - u = mxGetData (mxptr[0]); - memcpy (u, I->data, I->header->nm * sizeof (uint8_t)); - } - else if (I->header->n_bands == 3) { /* RGB image */ - dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; - mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); - u = mxGetData (mxptr[0]); - if (!strncmp(I->header->mem_layout, "TCBa", 4)) - memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); - else if (!strncmp(I->header->mem_layout, "TRPa", 4)) { - GMT_Change_Layout (API, GMT_IS_IMAGE, "TCB", 0, I, u, alpha); /* Convert from TRP to TCB */ - mxptr[16] = mxCreateString ("TCBa"); /* Because we just converted to it above */ - } - else { - mexPrintf("WarnError: this image's' memory layout, %s, is not implemented. Expect random art.\n"); - } - if (I->alpha) { - mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); - alpha = mxGetData (mxptr[15]); - memcpy (alpha, I->alpha, I->header->nm * sizeof (uint8_t)); - } - } - else if (I->header->n_bands == 4) { /* RGBA image, with a color map */ - dim[0] = I->header->n_rows; dim[1] = I->header->n_columns; dim[2] = 3; - mxptr[0] = mxCreateNumericArray (3, dim, mxUINT8_CLASS, mxREAL); - u = mxGetData (mxptr[0]); - mxptr[15] = mxCreateNumericMatrix (I->header->n_rows, I->header->n_columns, mxUINT8_CLASS, mxREAL); - alpha = mxGetData (mxptr[15]); - memcpy (u, I->data, 3 * I->header->nm * sizeof (uint8_t)); - memcpy (alpha, &(I->data)[3 * I->header->nm], I->header->nm * sizeof (uint8_t)); - /* - for (k = 0; k < I->header->nm; k++) { - for (m = 0; m < 3; m++) - u[k+m*I->header->nm] = (uint8_t)I->data[4*k+m]; - alpha[k] = (uint8_t)I->data[4*k+3]; - } - */ - } - - /* Also return the convenient x and y arrays */ - I_x = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_X, I); /* Get array of x coordinates */ - I_y = GMT_Get_Coord (API, GMT_IS_IMAGE, GMT_Y, I); /* Get array of y coordinates */ - x = mxGetData (mxptr[1]); - y = mxGetData (mxptr[2]); - memcpy (x, I_x, I->header->n_columns * sizeof (double)); - for (k = 0; k < I->header->n_rows; k++) /* Must reverse the y-array */ - y[I->header->n_rows-1-k] = I_y[k]; - if (GMT_Destroy_Data (API, &I_x)) - mexPrintf("Warning: Failure to delete I_x (x coordinate vector)\n"); - if (GMT_Destroy_Data (API, &I_y)) - mexPrintf("Warning: Failure to delete I_y (y coordinate vector)\n"); - for (k = 0; k < N_MEX_FIELDNAMES_IMAGE; k++) { /* Update fields */ - if (mxptr[k]) mxSetField (I_struct, 0, gmtmex_fieldname_image[k], mxptr[k]); - } - return (I_struct); -} - -static struct GMT_GRID *gmtmex_grid_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { - /* Used to Create an empty Grid container to hold a GMT grid. - * If direction is GMT_IN then we are given a MATLAB grid and can determine its size, etc. - * If direction is GMT_OUT then we allocate an empty GMT grid as a destination. */ - unsigned int row, col; - uint64_t gmt_ij; - struct GMT_GRID *G = NULL; - - if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ - unsigned int registration, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; - mxArray *mx_ptr = NULL, *mxGrid = NULL, *mxHdr = NULL; - - if (mxIsEmpty (ptr)) - mexErrMsgTxt ("gmtmex_grid_init: The input that was supposed to contain the Grid, is empty\n"); - if (!mxIsStruct (ptr)) { - if (!mxIsCell (ptr)) - mexErrMsgTxt ("gmtmex_grid_init: Expected a Grid structure or Cell array for input\n"); - else { /* Test that we have a {MxN,1x9} cell array */ - if (mxGetM(ptr) != 2 && mxGetN(ptr) != 2) - mexErrMsgTxt ("gmtmex_grid_init: Cell array must contain two elements\n"); - else { - mxGrid = mxGetCell(ptr, 0); - mxHdr = mxGetCell(ptr, 1); - if (mxGetM(mxGrid) < 2 || mxGetN(mxGrid) < 2) - mexErrMsgTxt ("gmtmex_grid_init: First element of grid's cell array must contain a decent matrix\n"); - if (mxGetM(mxHdr) != 1 || mxGetN(mxHdr) != 9) - mexErrMsgTxt ("gmtmex_grid_init: grid's cell array second element must contain a 1x9 vector\n"); - if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) - mexErrMsgTxt ("gmtmex_grid_init: grid's cell matrix must be either single or double.\n"); - } - } - } - - if (mxIsStruct(ptr)) { /* Passed a regular MEX Grid structure */ - double *inc = NULL, *range = NULL, *reg = NULL; - unsigned int pad = (unsigned int)GMT_NOTSET; - char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, - z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[3]; - mx_ptr = mxGetField (ptr, 0, "inc"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Could not find inc array with Grid increments\n"); - inc = mxGetData (mx_ptr); - - mx_ptr = mxGetField (ptr, 0, "range"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Could not find range array for Grid range\n"); - range = mxGetData (mx_ptr); - - mxGrid = mxGetField(ptr, 0, "z"); - if (mxGrid == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Could not find data array for Grid\n"); - if (!mxIsSingle(mxGrid) && !mxIsDouble(mxGrid)) - mexErrMsgTxt ("gmtmex_grid_init: data array must be either single or double.\n"); - - mx_ptr = mxGetField (ptr, 0, "registration"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Could not find registration array for Grid registration\n"); - reg = mxGetData (mx_ptr); - registration = (unsigned int)lrint(reg[0]); - - - mx_ptr = mxGetField(ptr, 0, "pad"); - if (mx_ptr != NULL) { - double *dpad = mxGetData(mx_ptr); - pad = (unsigned int)dpad[0]; - if (pad > 2) - mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); - } - - if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, - NULL, range, inc, registration, pad, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); - - G->header->z_min = range[4]; - G->header->z_max = range[5]; - - G->header->registration = registration; - - mx_ptr = mxGetField (ptr, 0, "nodata"); - if (mx_ptr != NULL) - G->header->nan_value = *(float *)mxGetData (mx_ptr); - - mx_ptr = mxGetField (ptr, 0, "proj4"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this lenght */ - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - G->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "wkt"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more thna this lenght */ - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - G->header->ProjRefWKT = GMT_Duplicate_String (API, str); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "title"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "command"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "comment"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr)+2); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "x_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "y_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "z_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "layout"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(G->header->mem_layout, layout, 3); - } - else - strncpy(G->header->mem_layout, "TRS", 3); - } - else { /* Passed header and grid separately */ - double *h = mxGetData(mxHdr); - registration = (unsigned int)lrint(h[6]); - if ((G = GMT_Create_Data (API, GMT_IS_GRID|flag, GMT_IS_SURFACE, GMT_GRID_ALL, - NULL, h, &h[7], registration, GMT_NOTSET, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT source matrix for input\n"); - G->header->z_min = h[4]; - G->header->z_max = h[5]; - } - - if (mxIsSingle(mxGrid)) { - float *f4 = mxGetData(mxGrid); - if (f4 == NULL) - mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); - for (row = 0; row < G->header->n_rows; row++) { - for (col = 0; col < G->header->n_columns; col++) { - gmt_ij = GMT_IJP (G->header, row, col); - G->data[gmt_ij] = f4[MEXG_IJ(G,row,col)]; - } - } - } - else { - double *f8 = mxGetData(mxGrid); - if (f8 == NULL) - mexErrMsgTxt("gmtmex_grid_init: Grid pointer is NULL where it absolutely could not be."); - for (row = 0; row < G->header->n_rows; row++) { - for (col = 0; col < G->header->n_columns; col++) { - gmt_ij = GMT_IJP (G->header, row, col); - G->data[gmt_ij] = (float)f8[MEXG_IJ(G,row,col)]; - } - } - } - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_grid_init: Allocated GMT Grid %lx\n", (long)G); - GMT_Report (API, GMT_MSG_DEBUG, - "gmtmex_grid_init: Registered GMT Grid array %lx via memory reference from MATLAB\n", - (long)G->data); - } - else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ - if ((G = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_IS_OUTPUT, - NULL, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_grid_init: Failure to alloc GMT blank grid container for holding output grid\n"); - } - return (G); -} - -static struct GMT_IMAGE *gmtmex_image_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { - /* Used to Create an empty Image container to hold a GMT image. - * If direction is GMT_IN then we are given a MATLAB image and can determine its size, etc. - * If direction is GMT_OUT then we allocate an empty GMT image as a destination. */ - struct GMT_IMAGE *I = NULL; - if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ - uint64_t dim[3]; - unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0, pad = 0; - char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, - z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[4]; - double *reg = NULL, *inc = NULL, *range = NULL; - mxArray *mx_ptr = NULL; - - if (mxIsEmpty (ptr)) - mexErrMsgTxt ("gmtmex_image_init: The input that was supposed to contain the Image, is empty\n"); - - if (!mxIsStruct (ptr)) - mexErrMsgTxt ("gmtmex_image_init: Expected a Image structure for input\n"); - - mx_ptr = mxGetField (ptr, 0, "range"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_image_init: Could not find range array for Image range\n"); - range = mxGetData (mx_ptr); - - mx_ptr = mxGetField (ptr, 0, "inc"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_image_init: Could not find inc array with Image increments\n"); - inc = mxGetData (mx_ptr); - - mx_ptr = mxGetField(ptr, 0, "registration"); - if (mx_ptr == NULL) - mexErrMsgTxt("gmtmex_image_init: Could not find registration info in Image struct\n"); - reg = mxGetData(mx_ptr); - - mx_ptr = mxGetField(ptr, 0, "pad"); - if (mx_ptr != NULL) { - double *dpad = mxGetData(mx_ptr); - pad = (unsigned int)dpad[0]; - if (pad > 2) - mexPrintf("gmtmex_grid_init: This pad value (%d) is very probably wrong.\n"); - } - - mx_ptr = mxGetField (ptr, 0, "image"); - if (mx_ptr == NULL) - mexErrMsgTxt ("gmtmex_image_init: Could not find data array for Image\n"); - - if (!mxIsUint8(mx_ptr)) - mexErrMsgTxt("gmtmex_image_init: The only data type supported by now is UInt8, and this image is not.\n"); - - dim[0] = gmtmex_getMNK (mx_ptr, 1); dim[1] = gmtmex_getMNK (mx_ptr, 0); dim[2] = gmtmex_getMNK (mx_ptr, 2); - if ((I = GMT_Create_Data (API, GMT_IS_IMAGE|flag, GMT_IS_SURFACE, GMT_GRID_HEADER_ONLY, dim, - range, inc, (unsigned int)reg[0], pad, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT source image for input\n"); - - I->data = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ - GMT_Set_AllocMode (API, GMT_IS_IMAGE, I); - //I->alloc_mode = GMT_ALLOC_EXTERNALLY; - -/* - memcpy (I->data, (unsigned char *)mxGetData (mx_ptr), I->header->nm * I->header->n_bands * sizeof (char)); - for (row = 0; row < I->header->n_rows; row++) { - for (col = 0; col < I->header->n_columns; col++) { - gmt_ij = GMT_IJP (I->header, row, col); - I->data [gmt_ij] = f[MEXG_IJ(I,row,col)]; - } - } -*/ - mx_ptr = mxGetField (ptr, 0, "alpha"); - I->alpha = NULL; - if (mx_ptr != NULL) { - if (mxGetNumberOfDimensions(mx_ptr) == 2) - I->alpha = (unsigned char *)mxGetData (mx_ptr); /* Send in the Matlab owned memory. */ - } - - I->header->z_min = range[4]; - I->header->z_max = range[5]; - - mx_ptr = mxGetField(ptr, 0, "x"); - if (mx_ptr == NULL) - mexErrMsgTxt("gmtmex_image_init: Could not find x-coords vector for Image\n"); - I->x = mxGetData(mx_ptr); - - mx_ptr = mxGetField(ptr, 0, "y"); - if (mx_ptr == NULL) - mexErrMsgTxt("gmtmex_image_init: Could not find y-coords vector for Image\n"); - I->y = mxGetData(mx_ptr); - - mx_ptr = mxGetField (ptr, 0, "nodata"); - if (mx_ptr != NULL) - I->header->nan_value = *(float *)mxGetData (mx_ptr); - - mx_ptr = mxGetField (ptr, 0, "proj4"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this length */ - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - I->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "wkt"); - if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WTT string will have more than this length */ - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - I->header->ProjRefWKT = GMT_Duplicate_String (API, str); - free (str); - } - - mx_ptr = mxGetField (ptr, 0, "title"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "command"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "comment"); - if (mx_ptr != NULL) { - char *str = malloc(mxGetN(mx_ptr) + 1); - mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); - free (str); - } - mx_ptr = mxGetField (ptr, 0, "x_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "y_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "z_unit"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); - } - mx_ptr = mxGetField (ptr, 0, "layout"); - if (mx_ptr != NULL) { - mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); - strncpy(I->header->mem_layout, layout, 4); - } - else - strncpy(I->header->mem_layout, "TCBa", 4); - - I->colormap = NULL; /* BUT IT SHOULD BE PROPERLY ASSIGNED IF IMAGE IS INDEXED */ - if (dim[2] == 1) - I->color_interp = "Gray"; - else - I->color_interp = "Unknown"; /* BUT WE CAN */ - - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_image_init: Allocated GMT Image %lx\n", (long)I); - GMT_Report (API, GMT_MSG_DEBUG, - "gmtmex_image_init: Registered GMT Image array %lx via memory reference from MATLAB\n", - (long)I->data); - } - else { /* Just allocate an empty container to hold an output image (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ - if ((I = GMT_Create_Data (API, GMT_IS_IMAGE, GMT_IS_SURFACE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_image_init: Failure to alloc GMT blank image container for holding output image\n"); - - GMT_Set_Default (API, "API_IMAGE_LAYOUT", "TCBa"); /* State how we wish to receive images from GDAL */ - } - return (I); -} - -static void *gmtmex_dataset_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr, unsigned int *actual_family) { - /* Create containers to hold or receive data tables: - * direction == GMT_IN: Create empty GMT_DATASET container, fill from Mex, and use as GMT input. - * Input from MATLAB may be a MEX data structure, a plain matrix, a cell array of strings or a single string. - * direction == GMT_OUT: Create empty GMT_DATASET container, let GMT fill it out, and use for Mex output. - * If direction is GMT_IN then we are given a MATLAB struct and can determine dimension. - * If output then we don't know size so we set dimensions to zero. */ - struct GMT_DATASET *D = NULL; - - *actual_family = GMT_IS_DATASET; /* Default but may change to matrix below */ - if (direction == GMT_IN) { /* Data given, dimensions are know, create container for GMT */ - uint64_t seg, col, row, start, k, n_headers, dim[4] = {1, 0, 0, 0}, n, m, n_rows; /* We only return a single table */ - size_t length = 0; - bool got_single_record = false; - unsigned int mode; - char buffer[BUFSIZ] = {""}, *txt = NULL; - mxArray *mx_ptr = NULL, *mx_ptr_d = NULL, *mx_ptr_t = NULL; - double *data = NULL; - struct GMT_DATASEGMENT *S = NULL; - - if (!ptr) mexErrMsgTxt ("gmtmex_dataset_init: Input is empty where it can't be.\n"); - if (mxIsNumeric (ptr)) { /* Got a MATLAB matrix as input - pass data pointers via MATRIX to save memory */ - struct GMT_MATRIX *M = NULL; - unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; - flag |= GMT_VIA_MATRIX; - *actual_family |= GMT_VIA_MATRIX; - mxClassID type = mxGetClassID (ptr); /* Storage type for this matrix */ - dim[DIM_ROW] = mxGetM (ptr); /* Number of rows */ - dim[DIM_COL] = mxGetN (ptr); /* Number of columns */ - /* Create matrix container but do not allocate any matrix memory */ - if ((M = GMT_Create_Data (API, GMT_IS_DATASET|flag, GMT_IS_PLP, GMT_CONTAINER_ONLY, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source matrix\n"); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Matrix %lx\n", (long)M); - switch (type) { /* Assign ML type pointer to the corresponding GMT matrix union pointer */ - case mxDOUBLE_CLASS: M->type = GMT_DOUBLE; M->data.f8 = mxGetData (ptr); break; - case mxSINGLE_CLASS: M->type = GMT_FLOAT; M->data.f4 = (float *)mxGetData (ptr); break; - case mxUINT64_CLASS: M->type = GMT_ULONG; M->data.ui8 = (uint64_t *)mxGetData (ptr); break; - case mxINT64_CLASS: M->type = GMT_LONG; M->data.si8 = (int64_t *)mxGetData (ptr); break; - case mxUINT32_CLASS: M->type = GMT_UINT; M->data.ui4 = (uint32_t *)mxGetData (ptr); break; - case mxINT32_CLASS: M->type = GMT_INT; M->data.si4 = (int32_t *)mxGetData (ptr); break; - case mxUINT16_CLASS: M->type = GMT_USHORT; M->data.ui2 = (uint16_t *)mxGetData (ptr); break; - case mxINT16_CLASS: M->type = GMT_SHORT; M->data.si2 = (int16_t *)mxGetData (ptr); break; - case mxUINT8_CLASS: M->type = GMT_UCHAR; M->data.uc1 = (uint8_t *)mxGetData (ptr); break; - case mxINT8_CLASS: M->type = GMT_CHAR; M->data.sc1 = (int8_t *)mxGetData (ptr); break; - default: - mexErrMsgTxt ("gmtmex_dataset_init: Unsupported MATLAB data type in GMT matrix input."); - break; - } - /* Data from MATLAB and Octave(mex) is in col format and data from Octave(oct) is in row format */ -#ifdef GMT_OCTOCT - M->dim = M->n_columns; -#else - M->dim = M->n_rows; -#endif - //M->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Since matrix was allocated by MATLAB/Octave we cannot free it in GMT */ - GMT_Set_AllocMode (API, GMT_IS_MATRIX, M); - M->shape = MEX_COL_ORDER; /* Either col or row order, depending on MATLAB/Octave setting in gmtmex.h */ - return (M); - } - /* We come here if we did not receive a matrix, There are three options: */ - /* 1. A dataset MATLAB structure or array of structures. - * 2. A Cell array of plain text strings for a text-only file. - * 3. A single text string instead of a one-item cell array of strings. */ - - if (mxIsStruct (ptr)) { /* Got the dataset structure */ - dim[GMT_SEG] = mxGetM (ptr); /* Number of segments */ - if (dim[GMT_SEG] == 0) mexErrMsgTxt ("gmtmex_dataset_init: Input has zero segments where it can't be.\n"); - mx_ptr_d = mxGetField (ptr, 0, "data"); /* Get first segment's data matrix [if available] */ - if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ - mx_ptr_t = mxGetField (ptr, 0, "text"); /* Get first segment's text matrix [if available] */ - if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ - - if (mx_ptr_d == NULL && mx_ptr_t == NULL) - mexErrMsgTxt("gmtmex_dataset_init: Both 'data' array and 'text' array are NULL!\n"); - if (mx_ptr_d) - dim[GMT_COL] = mxGetN (mx_ptr_d); /* Number of columns */ - if (dim[GMT_COL] == 0 && mx_ptr_t == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Input has zero columns where it can't be.\n"); - - if (mx_ptr_t) /* This segment also has a cell array of strings */ - mode = GMT_WITH_STRINGS; - else - mode = GMT_NO_STRINGS; - - if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); - - for (seg = 0; seg < dim[GMT_SEG]; seg++) { /* Each incoming structure is a new data segment */ - mx_ptr = mxGetField (ptr, (mwSize)seg, "header"); /* Get pointer to MEX segment header */ - buffer[0] = 0; /* Reset our temporary text buffer */ - if (mx_ptr && (length = mxGetN (mx_ptr)) != 0) /* These is a non-empty segment header to keep */ - mxGetString (mx_ptr, buffer, (mwSize)(length+1)); - mx_ptr_d = mxGetField (ptr, (mwSize)seg, "data"); /* Data matrix for this segment */ - if (mx_ptr_d && mxIsEmpty(mx_ptr_d)) mx_ptr_d = NULL; /* Got one but was empty */ - mx_ptr_t = mxGetField (ptr, (mwSize)seg, "text"); /* text cell array for this segment */ - if (mx_ptr_t && mxIsEmpty(mx_ptr_t)) mx_ptr_t = NULL; /* Got one but was empty */ - - if (mx_ptr_t) { /* This segment also has a cell array of strings or possibly a single string (if n_rows == 1) */ - got_single_record = false; - m = mxGetM (mx_ptr_t); n = mxGetN (mx_ptr_t); - if (!mxIsCell (mx_ptr_t) && (m == 1 || n == 1)) { - got_single_record = true; - m = n = 1; - } - } - else /* No trailing text */ - m = n = 0; - dim[GMT_ROW] = (mx_ptr_d == NULL) ? m : mxGetM (mx_ptr_d); /* Number of rows in matrix (or strings if no data) */ - if ((m == dim[GMT_ROW] && n == 1) || (n == dim[GMT_ROW] && m == 1)) - mode = GMT_WITH_STRINGS; - else - mode = GMT_NO_STRINGS; - /* Allocate a new data segment and hook up to to our single table */ - S = GMT_Alloc_Segment (API, mode, dim[GMT_ROW], dim[GMT_COL], buffer, D->table[0]->segment[seg]); - if (mx_ptr_d != NULL) data = mxGetData (mx_ptr_d); - for (col = start = 0; col < S->n_columns; col++, start += S->n_rows) /* Copy the data columns */ - memcpy (S->data[col], &data[start], S->n_rows * sizeof (double)); - if (mode == GMT_WITH_STRINGS) { /* Add in the trailing strings */ - if (got_single_record) { /* Only true when we got a single row with a single string instead of a cell array */ - txt = mxArrayToString (mx_ptr_t); - S->text[0] = GMT_Duplicate_String (API, txt); - } - else { /* Must extract text from the cell array */ - for (row = 0; row < S->n_rows; row++) { - mx_ptr = mxGetCell (mx_ptr_t, (mwSize)row); - txt = mxArrayToString (mx_ptr); - S->text[row] = GMT_Duplicate_String (API, txt); - } - } - } - D->table[0]->n_records += S->n_rows; /* Must manually keep track of totals */ - if (seg == 0) { /* First segment may have table information */ - mx_ptr_t = mxGetField (ptr, (mwSize)seg, "comment"); /* Table headers */ - if (mx_ptr_t && (n_headers = mxGetM (mx_ptr_t)) != 0) { /* Number of headers found */ - for (k = 0; k < n_headers; k++) { /* Extract the headers and insert into dataset */ - mx_ptr = mxGetCell (mx_ptr_t, (mwSize)k); - txt = mxArrayToString (mx_ptr); - if (GMT_Set_Comment (API, GMT_IS_DATASET, GMT_COMMENT_IS_TEXT, txt, D)) - mexErrMsgTxt("gmtmex_dataset_init: Failed to set a dataset header\n"); - } - } - } - if (mode == GMT_WITH_STRINGS) D->type = (D->n_columns) ? GMT_READ_MIXED : GMT_READ_TEXT; - else D->type = GMT_READ_DATA; - } - } - else if (mxIsCell (ptr)) { /* Got a cell array of strings and no numerical data */ - uint64_t k2 = 0; - m = mxGetM (ptr); n = mxGetN (ptr); - n_rows = (m > n) ? m : n; /* Number of items in cell array */ - mode = GMT_WITH_STRINGS; /* Since that is all we have */ - /* Determine number of segments up front since user may use '>' to indicate segment header */ - for (k = 0; k < n_rows; k++) { - mx_ptr = mxGetCell (ptr, (mwSize)k); - txt = mxArrayToString (mx_ptr); - if (txt[0] == '>') dim[GMT_SEG]++; /* Found start of a new segment */ - } - if (dim[GMT_SEG] == 0) dim[GMT_SEG] = 1; /* No segment headers given a single segment */ - if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, GMT_WITH_STRINGS, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); - k = seg = 0; - while (k < n_rows) { /* Examine the input records and look for segment breaks */ - mx_ptr = mxGetCell (ptr, (mwSize)k); - txt = mxArrayToString (mx_ptr); - buffer[0] = '\0'; - if (txt[0] == '>' || (k == 0 && txt[0] != '>')) { /* Found start of a new (or first and only) segment */ - if (txt[0] == '>') strcpy (buffer, txt), k++; /* Segment header */ - k2 = k; /* k and k2 initially point to the first row of the current segment */ - dim[GMT_ROW] = 0; /* Have no rows so far */ - while (k2 < n_rows && dim[GMT_ROW] == 0) { /* While not reached end of current segment */ - mx_ptr = mxGetCell (ptr, (mwSize)k2); - txt = mxArrayToString (mx_ptr); - if (txt[0] == '>') /* Got next segment header, must end current segment */ - dim[GMT_ROW] = k2 - k; - else /* Keep going */ - k2++; - } - if (dim[GMT_ROW] == 0) dim[GMT_ROW] = k2 - k; /* Happens for last segment when there is no "next" segment header */ - } - /* Now we have the length of this segment */ - S = GMT_Alloc_Segment (API, GMT_WITH_STRINGS, dim[GMT_ROW], 0, buffer, D->table[0]->segment[seg]); - for (row = 0; row < S->n_rows; row++) { /* Hook up the string records */ - mx_ptr = mxGetCell (ptr, (mwSize)(k+row)); /* k is the offset to 1st record of current segment in input cell array */ - txt = mxArrayToString (mx_ptr); - S->text[row] = GMT_Duplicate_String (API, txt); - } - D->table[0]->n_records += S->n_rows; /* Must manually keep track of total records */ - seg++; /* Got ourselves a new segment, increment counter */ - /* Move to next unused record or we have reached the end */ - k = k2; - } - D->type = GMT_READ_TEXT; - } - else if (mxIsChar (ptr)) { /* Got a single string only */ - dim[GMT_ROW] = dim[GMT_SEG] = 1; /* Put string into a single segment */ - mode = GMT_WITH_STRINGS; /* Since that is all we have */ - if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_TEXT, mode, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT destination dataset\n"); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT dataset %lx\n", (long)D); - S = D->table[0]->segment[0]; /* The lone segment */ - txt = mxArrayToString (ptr); /* The lone string */ - S->text[0] = GMT_Duplicate_String (API, txt); - D->type = GMT_READ_TEXT; - } - else - mexErrMsgTxt ("gmtmex_dataset_init: Expected a data structure, cell array with strings, or a single string for input\n"); - D->n_records = D->table[0]->n_records; - } - else { /* Here we set up an empty container to receive data from GMT (signal this by passing 0s and NULLs) */ - if ((D = GMT_Create_Data (API, GMT_IS_DATASET, GMT_IS_PLP, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_dataset_init: Failure to alloc GMT source dataset\n"); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_dataset_init: Allocated GMT Dataset %lx\n", (long)D); - } - return (D); -} - -static struct GMT_PALETTE *gmtmex_palette_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { - /* Used to create an empty CPT container to hold a GMT Color Palette. - * If direction is GMT_IN then we are given a MATLAB CPT struct and can determine its size, etc. - * If direction is GMT_OUT then we allocate an empty GMT CPT as a destination. */ - struct GMT_PALETTE *P = NULL; - if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ - unsigned int k, j, one = 1, n_headers, *depth = NULL; - uint64_t dim[2] = {0, 0}; - unsigned int flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; - char model[8] = {""}; - mxArray *mx_ptr[N_MEX_FIELDNAMES_CPT]; - double *colormap = NULL, *range = NULL, *minmax = NULL, *alpha = NULL, *bfn = NULL, *hinge = NULL, *cpt = NULL, *cyclic = NULL; - - if (mxIsEmpty (ptr)) - mexErrMsgTxt ("gmtmex_palette_init: The input that was supposed to contain the CPT, is empty\n"); - if (!mxIsStruct (ptr)) - mexErrMsgTxt ("gmtmex_palette_init: Expected a CPT structure for input\n"); - for (k = 0; k < N_MEX_FIELDNAMES_CPT; k++) { - if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_cpt[k])) == NULL) - gmtmex_quit_if_missing ("gmtmex_palette_init", gmtmex_fieldname_cpt[k]); - } - - dim[0] = mxGetM (mx_ptr[0]); /* Number of rows in colormap */ - if (dim[0] < 1) - mexErrMsgTxt ("gmtmex_palette_init: Colormap array has no CPT values\n"); - colormap = mxGetData (mx_ptr[0]); - alpha = mxGetData (mx_ptr[1]); - range = mxGetData (mx_ptr[2]); - minmax = mxGetData (mx_ptr[3]); - bfn = mxGetData (mx_ptr[4]); - depth = mxGetData (mx_ptr[5]); - hinge = mxGetData (mx_ptr[6]); - cpt = mxGetData (mx_ptr[7]); - cyclic = mxGetData (mx_ptr[9]); - - /* Disable unused-but-set-variable warnings */ - (void)(minmax); - (void)(colormap); - - dim[1] = mxGetM (mx_ptr[2]); /* Length of range array */ - if (dim[0] > dim[1]) { /* This only happens when we have a continuous color table */ - dim[1] = dim[0]; /* Actual length of colormap array */ - dim[0]--; /* Number of CPT slices */ - } - else /* Discrete, so the one offset needs to be zero */ - one = 0; - - if ((P = GMT_Create_Data (API, GMT_IS_PALETTE|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT source CPT for input\n"); - - if ((n_headers = (unsigned int)mxGetM (mx_ptr[10])) != 0) { /* Number of headers found */ - char *txt = NULL; - mxArray *ptr = NULL; - for (k = 0; k < n_headers; k++) { - ptr = mxGetCell (mx_ptr[10], (mwSize)k); - txt = mxArrayToString (ptr); - if (GMT_Set_Comment (API, GMT_IS_PALETTE, GMT_COMMENT_IS_TEXT, txt, P)) - mexErrMsgTxt("gmtmex_palette_init: Failed to set a CPT header\n"); - } - } - for (j = 0; j < 3; j++) { /* Do the bfn first */ - for (k = 0; k < 3; k++) - P->bfn[j].rgb[k] = bfn[j+k*3]; - } - for (j = 0; j < P->n_colors; j++) { /* OK to access j+1'th element since length of colormap is P->n_colors+1 */ - for (k = 0; k < 3; k++) { - P->data[j].rgb_low[k] = cpt[j+k*dim[0]]; - P->data[j].rgb_high[k] = cpt[j+(k+3)*dim[0]]; - } - P->data[j].rgb_low[3] = alpha[j]; - P->data[j].rgb_high[3] = alpha[j+one]; - P->data[j].z_low = range[j]; - P->data[j].z_high = range[j+P->n_colors]; - P->data[j].annot = 3; /* Enforce annotations for now */ - } - P->is_continuous = one; - P->is_bw = P->is_gray = 0; - P->is_wrapping = (unsigned int)rint (cyclic[0]); - if (depth[0] == 1) - P->is_bw = 1; - else if (depth[0] == 8) - P->is_gray = 1; - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_palette_init: Allocated GMT CPT %lx\n", (long)P); - if (!mxIsNaN (hinge[0])) { - P->has_hinge = 1; - P->mode |= GMT_CPT_HINGED; - P->hinge = hinge[0]; - } - mxGetString (mx_ptr[8], model, (mwSize)mxGetN(mx_ptr[8])+1); - if (!strncmp (model, "hsv", 3U)) - P->model = GMT_HSV; - else if (!strncmp (model, "cmyk", 4U)) - P->model = GMT_CMYK; - else - P->model = GMT_RGB; - } - else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ - if ((P = GMT_Create_Data (API, GMT_IS_PALETTE, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_palette_init: Failure to alloc GMT blank CPT container for holding output CPT\n"); - } - return (P); -} - -static struct GMT_POSTSCRIPT *gmtmex_ps_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { - /* Used to Create an empty POSTSCRIPT container to hold a GMT POSTSCRIPT object. - * If direction is GMT_IN then we are given a MATLAB structure with known sizes. - * If direction is GMT_OUT then we allocate an empty GMT POSTSCRIPT as a destination. */ - struct GMT_POSTSCRIPT *P = NULL; - if (direction == GMT_IN) { /* Dimensions are known from the MATLAB input pointer */ - uint64_t dim[1] = {0}, *length = NULL; - unsigned int k, n_headers, *mode = NULL, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; - mxArray *mx_ptr[N_MEX_FIELDNAMES_PS]; - char *PS = NULL; - - if (mxIsEmpty (ptr)) - mexErrMsgTxt ("gmtmex_ps_init: The input that was supposed to contain the PostScript structure is empty\n"); - if (!mxIsStruct (ptr)) - mexErrMsgTxt ("gmtmex_ps_init: Expected a MATLAB PostScript structure for input\n"); - for (k = 0; k < N_MEX_FIELDNAMES_PS; k++) { - if ((mx_ptr[k] = mxGetField (ptr, 0, gmtmex_fieldname_ps[k])) == NULL) - gmtmex_quit_if_missing ("gmtmex_ps_init", gmtmex_fieldname_ps[k]); - } - - length = mxGetData (mx_ptr[1]); - if (length[0] == 0) - mexErrMsgTxt ("gmtmex_ps_init: Dimension of PostScript given as zero\n"); - PS = malloc (mxGetN(mx_ptr[0])+1); - mxGetString (mx_ptr[0], PS, (mwSize)mxGetN(mx_ptr[0])); - mode = mxGetData (mx_ptr[2]); - /* Passing dim[0] = 0 since we dont want any allocation of a PS string */ - if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT|flag, GMT_IS_NONE, 0, dim, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT source for input\n"); - P->data = PS; /* PostScript string instead is coming from MATLAB */ - GMT_Set_AllocMode (API, GMT_IS_POSTSCRIPT, P); - //P->alloc_mode = GMT_ALLOC_EXTERNALLY; /* Hence we are not allowed to free it */ - P->n_bytes = length[0]; /* Length of the actual PS string */ - //P->n_alloc = 0; /* But nothing was actually allocated here - just passing pointer from MATLAB */ - P->mode = mode[0]; /* Inherit the mode */ - if ((n_headers = (unsigned int)mxGetM (mx_ptr[3])) != 0) { /* Number of headers found */ - char *txt = NULL; - mxArray *ptr = NULL; - for (k = 0; k < n_headers; k++) { - ptr = mxGetCell (mx_ptr[3], (mwSize)k); - txt = mxArrayToString (ptr); - if (GMT_Set_Comment (API, GMT_IS_POSTSCRIPT, GMT_COMMENT_IS_TEXT, txt, P)) - mexErrMsgTxt("gmtmex_ps_init: Failed to set a PostScript header\n"); - } - } - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_ps_init: Allocated GMT POSTSCRIPT %lx\n", (long)P); - } - else { /* Just allocate an empty container to hold an output PS object (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ - if ((P = GMT_Create_Data (API, GMT_IS_POSTSCRIPT, GMT_IS_NONE, GMT_IS_OUTPUT, NULL, NULL, NULL, 0, 0, NULL)) == NULL) - mexErrMsgTxt ("gmtmex_ps_init: Failure to alloc GMT POSTSCRIPT container for holding output PostScript\n"); - } - return (P); -} - -static char gmtmex_objecttype (const mxArray *ptr) { - /* Determine what we are returning so gmt write can pass the correct -T? flag */ - mxArray *mx_ptr = NULL; - if (mxIsEmpty (ptr)) - mexErrMsgTxt ("gmtmex_objecttype: Pointer is empty\n"); - if (mxIsStruct (ptr)) { /* This means either a dataset, grid, image, cpt, or PS, so must check for fields */ - mx_ptr = mxGetField (ptr, 0, "data"); - if (mx_ptr) return 'd'; - mx_ptr = mxGetField (ptr, 0, "postscript"); - if (mx_ptr) return 'p'; - mx_ptr = mxGetField (ptr, 0, "hinge"); - if (mx_ptr) return 'c'; - mx_ptr = mxGetField (ptr, 0, "image"); - if (mx_ptr) return 'i'; - mx_ptr = mxGetField (ptr, 0, "z"); - if (mx_ptr) return 'g'; - mexErrMsgTxt ("gmtmex_objecttype: Could not recognize the structure\n"); - } - else if (mxIsCell (ptr)) /* This is a dataset with text only */ - return 'd'; - else /* We have to assume it is a numerical matrix */ - return 'd'; - return '-'; /* Can never get here you would think */ -} - -static void gmtmex_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr) { - /* Create the GMT container and hook onto resource array as X->object */ - unsigned int module_input = (X->option->option == GMT_OPT_INFILE), actual_family = X->family; - - switch (X->family) { - case GMT_IS_GRID: /* Get a grid from Matlab or a dummy one to hold GMT output */ - X->object = gmtmex_grid_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Grid\n"); - break; - case GMT_IS_IMAGE: /* Get an image from Matlab or a dummy one to hold GMT output */ - X->object = gmtmex_image_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Image\n"); - break; - case GMT_IS_DATASET: /* Get a dataset from Matlab or a dummy one to hold GMT output */ - /* Because a GMT_DATASET may appears as a GMT_MATRIX or GMT_VECTOR we need the actual_family to open the virtual file later */ - X->object = gmtmex_dataset_init (API, X->direction, module_input, ptr, &actual_family); - break; - case GMT_IS_PALETTE: /* Get a palette from Matlab or a dummy one to hold GMT output */ - X->object = gmtmex_palette_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got CPT\n"); - break; - case GMT_IS_POSTSCRIPT: /* Get a PostScript struct from Matlab or a dummy one to hold GMT output */ - X->object = gmtmex_ps_init (API, X->direction, module_input, ptr); - GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got POSTSCRIPT\n"); - break; - default: - GMT_Report (API, GMT_MSG_NORMAL, "gmtmex_Set_Object: Bad data type (%d)\n", X->family); - break; - } - if (X->object == NULL) - mexErrMsgTxt("GMT: Failure to register the resource\n"); - if (GMT_Open_VirtualFile (API, actual_family, X->geometry, X->direction|GMT_IS_REFERENCE, X->object, X->name) != GMT_NOERROR) /* Make filename with embedded object ID */ - mexErrMsgTxt ("GMT: Failure to open virtual file\n"); - if (GMT_Expand_Option (API, X->option, X->name) != GMT_NOERROR) /* Replace ? in argument with name */ - mexErrMsgTxt ("GMT: Failure to expand filename marker (?)\n"); -} - -static void *gmtmex_Get_Object (void *API, struct GMT_RESOURCE *X) { - mxArray *ptr = NULL; - /* In line-by-line modules it is possible no output is produced, hence we make an exception for DATASET: */ - if ((X->object = GMT_Read_VirtualFile (API, X->name)) == NULL && X->family != GMT_IS_DATASET) - mexErrMsgTxt ("GMT: Error reading virtual file from GMT\n"); - switch (X->family) { /* Determine what container we got */ - case GMT_IS_GRID: /* A GMT grid; make it the pos'th output item */ - ptr = gmtmex_get_grid (API, X->object); - break; - case GMT_IS_DATASET: /* A GMT table; make it a data structure and the pos'th output item */ - ptr = gmtmex_get_dataset (API, X->object); - break; - case GMT_IS_PALETTE: /* A GMT CPT; make it a colormap and the pos'th output item */ - ptr = gmtmex_get_palette (API, X->object); - break; - case GMT_IS_IMAGE: /* A GMT Image; make it the pos'th output item */ - ptr = gmtmex_get_image (API, X->object); - break; - case GMT_IS_POSTSCRIPT: /* A GMT PostScript string; make it the pos'th output item */ - ptr = gmtmex_get_postscript (API, X->object); -#if 0 - { - char cmd[32] = {""}; - strcpy(cmd, name); strcat(cmd, " -A -Tf"); - GMT_Call_Module(API, "psconvert", GMT_MODULE_CMD, cmd); - } -#endif - break; - default: - mexErrMsgTxt ("GMT: Internal Error - unsupported data type\n"); - break; - } - return ptr; -} - -#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 -extern int gmt_get_V (char arg); /* Temporary here to allow full debug messaging */ -#else -extern int GMT_get_V (char arg); /* Temporary here to allow full debug messaging */ -#define gmt_get_V GMT_get_V /* Old name */ -#endif - -#ifndef SINGLE_SESSION -/* Being declared external we can access it between MEX calls */ -static uintptr_t *pPersistent; /* To store API address back and forth within a single MATLAB session */ - -/* Here is the exit function, which gets run when the MEX-file is - cleared and when the user exits MATLAB. The mexAtExit function - should always be declared as static. */ -static void force_Destroy_Session (void) { - void *API = (void *)pPersistent[0]; /* Get the GMT API pointer */ - if (API != NULL) { /* Otherwise just silently ignore this call */ - if (GMT_Destroy_Session (API)) mexErrMsgTxt ("Failure to destroy GMT session\n"); - *pPersistent = 0; /* Wipe the persistent memory */ - } -} -#endif - -static void usage (int nlhs, int nrhs) { - /* Basic usage message */ - if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ - mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", - MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); - mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); - mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); - mexPrintf("You may redistribute copies of this program under the terms of the\n"); - mexPrintf("GNU Lesser General Public License.\n"); - mexPrintf("For more information about these matters, see the file named LICENSE.TXT.\n"); - mexPrintf("For a brief description of GMT modules, type gmt ('help')\n\n"); - } - else { - mexPrintf("Usage is:\n\tgmt ('module_name', 'options'[, ]); %% Run a GMT module\n"); - if (nlhs != 0) - mexErrMsgTxt ("But meanwhile you already made an error by asking help and an output.\n"); - } -} - -static void *Initiate_Session (unsigned int verbose) { - /* Initialize the GMT Session and store the API pointer in a persistent variable */ - void *API = NULL; - /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ - /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ - if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + - GMT_SESSION_COLMAJOR, gmtmex_print_func)) == NULL) - mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); - -#ifndef SINGLE_SESSION - if (!pPersistent) pPersistent = mxMalloc(sizeof(uintptr_t)); - pPersistent[0] = (uintptr_t)(API); - mexMakeMemoryPersistent (pPersistent); -#endif - return (API); -} - -static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { - /* Allocate a default plhs when it was not stated in command line. That is, mimic the Matlab behavior - when we do for example (i.e. no lhs): sqrt([4 9]) - */ - void *ptr = NULL; - switch (X->family) { - case GMT_IS_GRID: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); - break; - case GMT_IS_IMAGE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); - break; - case GMT_IS_DATASET: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); - break; - case GMT_IS_PALETTE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); - break; - case GMT_IS_POSTSCRIPT: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); - break; - default: - break; - } - return ptr; -} - -/* This is the function that is called when we type gmt in MATLAB/Octave. - * It is the only function experted by the GMT API library. - */ - -void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { - int status = 0; /* Status code from GMT API */ - int n_in_objects = 0; /* Number of input objects passed to module */ - unsigned int first = 0; /* Array ID of first command argument (not 0 when API-ID is first) */ - unsigned int verbose = 0; /* Default verbose setting */ - unsigned int n_items = 0, pos = 0; /* Number of MATLAB arguments (left and right) */ - size_t str_length = 0, k = 0; /* Misc. counters */ - void *API = NULL; /* GMT API control structure */ - struct GMT_OPTION *options = NULL; /* Linked list of module options */ - struct GMT_RESOURCE *X = NULL; /* Array of information about MATLAB args */ - char *cmd = NULL; /* Pointer used to get the user's MATLAB command */ - char *gtxt = NULL; /* For debug printing of revised command */ - char *opt_args = NULL; /* Pointer to the user's module options */ - char module[MODULE_LEN] = {""}; /* Name of GMT module to call */ - char opt_buffer[BUFSIZ] = {""}; /* Local copy of command line options */ - void *ptr = NULL; -#ifndef SINGLE_SESSION - uintptr_t *pti = NULL; /* To locally store the API address */ -#endif - - /* -1. Check that the GMT library is of a suitable version for this GMTMEX version */ - - if (GMT_MAJOR_VERSION > GMTMEX_GMT_MAJOR_VERSION) { /* This may not work if, for instance, we want >= 6.1.x but is using GMT 7.0.0 */ - char message[128] = {""}; - sprintf (message, "Warning: Your GMT version (%d.%d.%d) may be too new to work with GMTMEX %d.%d.%d.\n", - GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION, GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION); - mexPrintf (message); - } - else if (GMT_MAJOR_VERSION < GMTMEX_GMT_MAJOR_VERSION || GMT_MINOR_VERSION < GMTMEX_GMT_MINOR_VERSION || (GMT_MINOR_VERSION == GMTMEX_GMT_MINOR_VERSION && GMT_RELEASE_VERSION < GMTMEX_GMT_PATCH_VERSION)) { - char message[128] = {""}; - sprintf (message, "Error: The GMT shared library must be at least version %d.%d.%d but you have %d.%d.%d.\n", - GMTMEX_GMT_MAJOR_VERSION, GMTMEX_GMT_MINOR_VERSION, GMTMEX_GMT_PATCH_VERSION, - GMT_MAJOR_VERSION, GMT_MINOR_VERSION, GMT_RELEASE_VERSION); - mexErrMsgTxt (message); - } - - /* 0. No arguments at all results in the GMT banner message */ - if (nrhs == 0) { - usage (nlhs, nrhs); - return; - } - - /* 1. Check for the special commands create and help */ - - if (nrhs == 1) { /* This may be create or help */ - cmd = mxArrayToString (prhs[0]); - if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string. Maybe a composition of a string and a cell array?\n"); - if (!strncmp (cmd, "help", 4U) || !strncmp (cmd, "--help", 6U)) { - usage (nlhs, 1); - return; - } -#ifndef SINGLE_SESSION - if (!strncmp (cmd, "create", 6U)) { /* Asked to create a new GMT session */ - if (nlhs > 1) /* Asked for too much output, only 1 or 0 is allowed */ - mexErrMsgTxt ("GMT: Usage: gmt ('create') or API = gmt ('create');\n"); - if (pPersistent) /* See if have a GMT API pointer */ - API = (void *)pPersistent[0]; - if (API != NULL) { /* If another session still exists */ - GMT_Report (API, GMT_MSG_VERBOSE, - "GMT: A previous GMT session is still active. Ignoring your 'create' request.\n"); - if (nlhs) /* Return nothing */ - plhs[0] = mxCreateNumericMatrix (1, 0, mxUINT64_CLASS, mxREAL); - return; - } - if ((gtxt = strstr (cmd, "-V")) != NULL) verbose = gmt_get_V (gtxt[2]); - API = Initiate_Session (verbose); /* Initializing a new GMT session */ - - if (nlhs) { /* Return the API address as an integer (nlhs == 1 here) )*/ - plhs[0] = mxCreateNumericMatrix (1, 1, mxUINT64_CLASS, mxREAL); - pti = mxGetData(plhs[0]); - *pti = *pPersistent; - } - - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - return; - } - - /* OK, neither create nor help, must be a single command with no arguments nor the API. So get it: */ - if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { /* No session yet, create one under the hood */ - API = Initiate_Session(verbose); /* Initializing a new GMT session */ - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - } - else - API = (void *)pPersistent[0]; /* Get the GMT API pointer */ - if (API == NULL) mexErrMsgTxt ("GMT: This GMT5 session has is corrupted. Better to start from scratch.\n"); - } - else if (mxIsScalar_(prhs[0]) && mxIsUint64(prhs[0])) { - /* Here, nrhs > 1 . If first arg is a scalar int, we assume it is the API memory address */ - pti = (uintptr_t *)mxGetData(prhs[0]); - API = (void *)pti[0]; /* Get the GMT API pointer */ - first = 1; /* Commandline args start at prhs[1] since prhs[0] had the API id argument */ - } - else { /* We still don't have the API, so we must get it from the past or initiate a new session */ - if (!pPersistent || (API = (void *)pPersistent[0]) == NULL) { - API = Initiate_Session (verbose); /* Initializing new GMT session */ - mexAtExit(force_Destroy_Session); /* Register an exit function. */ - } -#endif - } - -#ifdef SINGLE_SESSION - /* Initiate a new session */ - API = Initiate_Session (verbose); /* Initializing new GMT session */ -#endif - - if (!cmd) { /* First argument is the command string, e.g., 'blockmean -R0/5/0/5 -I1' or just 'destroy' */ - cmd = mxArrayToString(prhs[first]); - if (!cmd) mexErrMsgTxt("GMT: First input argument must be a string but is probably a cell array of strings.\n"); - } - - if (!strncmp (cmd, "destroy", 7U)) { /* Destroy the session */ -#ifndef SINGLE_SESSION - if (nlhs != 0) - mexErrMsgTxt ("GMT: Usage is gmt ('destroy');\n"); - - if (GMT_Destroy_Options (API, &options)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); - if (GMT_Destroy_Session (API)) mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); - *pPersistent = 0; /* Wipe the persistent memory */ -#endif - return; - } - - /* 2. Get module name and separate out args */ - - /* Here we have a GMT module call. The documented use is to give the module name separately from - * the module options, but users may forget and combine the two. So we check both cases. */ - - n_in_objects = nrhs - 1; - str_length = strlen (cmd); /* Length of module (or command) argument */ - for (k = 0; k < str_length && cmd[k] != ' '; k++); /* Determine first space in command */ - - if (k == str_length) { /* Case 2a): No spaces found: User gave 'module' separately from 'options' */ - strcpy (module, cmd); /* Isolate the module name in this string */ - if (nrhs > 1 && mxIsChar (prhs[first+1])) { /* Got option string */ - first++; /* Since we have a 2nd string to skip now */ - opt_args = mxArrayToString (prhs[first]); - n_in_objects--; - } - /* Else we got no options, just input objects */ - } - else { /* Case b2. Get mex arguments, if any, and extract the GMT module name */ - if (k >= MODULE_LEN) - mexErrMsgTxt ("GMT: Module name in command is too long\n"); - strncpy (module, cmd, k); /* Isolate the module name in this string */ - - while (cmd[k] == ' ') k++; /* Skip any spaces between module name and start of options */ - if (cmd[k]) opt_args = &cmd[k]; - } - - /* See if info about installation is required */ - if (!strcmp(module, "gmt")) { - char t[256] = {""}; - if (!opt_args) { - mexPrintf("Warning: calling the 'gmt' program by itself does nothing here.\n"); - return; - } - if (!strcmp(opt_args, "--show-bindir")) /* Show the directory that contains the 'gmt' executable */ - GMT_Get_Default (API, "BINDIR", t); - else if (!strcmp(opt_args, "--show-sharedir")) /* Show share directory */ - GMT_Get_Default (API, "SHAREDIR", t); - else if (!strcmp(opt_args, "--show-datadir")) /* Show the data directory */ - GMT_Get_Default (API, "DATADIR", t); - else if (!strcmp(opt_args, "--show-plugindir")) /* Show the plugin directory */ - GMT_Get_Default (API, "PLUGINDIR", t); - else if (!strcmp(opt_args, "--show-cores")) /* Show number of cores */ - GMT_Get_Default (API, "CORES", t); - - if (t[0] != '\0') { - if (nlhs) - plhs[0] = mxCreateString (t); - else - mexPrintf ("%s\n", t); - } - else - mexPrintf ("Warning: called the 'gmt' program with unknown option.\n"); - return; - } - - /* Make sure this is a valid module */ - if ((status = GMT_Call_Module (API, module, GMT_MODULE_EXIST, NULL)) != GMT_NOERROR) /* No, not found */ - mexErrMsgTxt ("GMT: No module by that name was found.\n"); - - /* Below here we may actually wish to add options to the opt_args, but it is a pointer. So we duplicate to - * another string with enough space. */ - - if (opt_args) strcpy (opt_buffer, opt_args); /* opt_buffer has lots of space for additions */ - /* 2+ Add -F to psconvert if user requested a return image but did not explicitly give -F */ - if (!strncmp (module, "psconvert", 9U) && nlhs == 1 && (!opt_args || !strstr ("-F", opt_args))) { /* OK, add -F */ - if (opt_args) - strcat (opt_buffer, " -F"); - else - strcpy (opt_buffer, "-F"); - } - - /* 2++ If gmtwrite then add -T? with correct object type */ - if (strstr(module, "write") && opt_args && !strstr(opt_args, "-T") && n_in_objects == 1) { /* Add type for writing to disk */ - char targ[5] = {" -T?"}; - targ[3] = gmtmex_objecttype (prhs[nrhs-1]); - strcat (opt_buffer, targ); - } - /* 2+++ If gmtread -Ti then temporarily set pad to 0 since we don't want padding in image arrays */ - else if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) - GMT_Set_Default(API, "API_PAD", "0"); - - /* 3. Convert mex command line arguments to a linked GMT option list */ - if (opt_buffer[0] && (options = GMT_Create_Options (API, 0, opt_buffer)) == NULL) - mexErrMsgTxt ("GMT: Failure to parse GMT5 command options\n"); - - if (!options && nlhs == 0 && nrhs == 1 && strcmp (module, "end")) /* Just requesting usage message, so add -? to options */ - options = GMT_Create_Options (API, 0, "-?"); - - /* 4. Preprocess to update GMT option lists and return info array X */ - if ((X = GMT_Encode_Options (API, module, n_in_objects, &options, &n_items)) == NULL) { - if (n_items == UINT_MAX) /* Just got usage/synopsis option */ - n_items = 0; - else - mexErrMsgTxt ("GMT: Failure to encode mex command options\n"); - } - - if (options) { /* Only for debugging - remove this section when stable */ - gtxt = GMT_Create_Cmd (API, options); - GMT_Report (API, GMT_MSG_DEBUG, "GMT_Encode_Options: Revised command after memory-substitution: %s\n", gtxt); - GMT_Destroy_Cmd (API, >xt); /* Only needed it for the above verbose */ - } - - /* 5. Assign input sources (from MATLAB to GMT) and output destinations (from GMT to MATLAB) */ - - for (k = 0; k < n_items; k++) { /* Number of GMT containers involved in this module call */ - if (X[k].direction == GMT_IN) { - if ((X[k].pos+first+1) < (unsigned int)nrhs) - ptr = (void *)prhs[X[k].pos+first+1]; - else - mexErrMsgTxt ("GMT: Attempting to address a prhs entry that does not exist\n"); - } - else { - if ((X[k].pos) < nlhs) - ptr = (void *)plhs[X[k].pos]; - else { - //mexErrMsgTxt ("GMT: Attempting to address a plhs entry that does not exist\n"); - ptr = alloc_default_plhs (API, &X[k]); - } - } - gmtmex_Set_Object (API, &X[k], ptr); /* Set object pointer */ - } - - /* 6. Run GMT module; give usage message if errors arise during parsing */ - status = GMT_Call_Module (API, module, GMT_MODULE_OPT, options); - if (status != GMT_NOERROR) { - if (status <= GMT_MODULE_PURPOSE) - return; - else { - mexPrintf("GMT: Module return with failure while executing the command\n%s\n", cmd); - mexErrMsgTxt("GMT: exiting\n"); - } - } - - /* 7. Hook up any GMT outputs to MATLAB plhs array */ - - for (k = 0; k < n_items; k++) { /* Get results from GMT into MATLAB arrays */ - if (X[k].direction == GMT_IN) continue; /* Only looking for stuff coming OUT of GMT here */ - pos = X[k].pos; /* Short-hand for index into the plhs[] array being returned to MATLAB */ - plhs[pos] = gmtmex_Get_Object (API, &X[k]); /* Hook mex object onto rhs list */ - } - - /* 2++- If gmtread -Ti then reset the sessions pad value that was temporarily changed above (2+++) */ - if (strstr(module, "read") && opt_args && strstr(opt_args, "-Ti")) - GMT_Set_Default (API, "API_PAD", "2"); - - /* 8. Free all GMT containers involved in this module call */ - - for (k = 0; k < n_items; k++) { - void *ppp = X[k].object; - if (GMT_Close_VirtualFile (API, X[k].name) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failed to close virtual file\n"); - if (GMT_Destroy_Data (API, &X[k].object) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failed to destroy object used in the interface between GMT and MATLAB\n"); - else { /* Success, now make sure we don't destroy the same pointer more than once */ - for (size_t kk = k+1; kk < n_items; kk++) - if (X[kk].object == ppp) X[kk].object = NULL; - } - } - - /* 9. Destroy linked option list */ - - if (GMT_Destroy_Options (API, &options) != GMT_NOERROR) - mexErrMsgTxt ("GMT: Failure to destroy GMT5 options\n"); -#ifdef SINGLE_SESSION - if (GMT_Destroy_Session (API)) - mexErrMsgTxt ("GMT: Failure to destroy GMT5 session\n"); -#endif - return; -} From 93ed7703e1548ea96a47f5d6be827197cccdeb73 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 3 Apr 2021 14:13:10 -1000 Subject: [PATCH 14/45] Finalize --- admin/ConfigReleaseBuild.cmake | 5 +---- cmake/ConfigUserAdvancedTemplate.cmake | 21 --------------------- src/gmtmex/CMakeLists.txt | 9 +++++---- 3 files changed, 6 insertions(+), 29 deletions(-) diff --git a/admin/ConfigReleaseBuild.cmake b/admin/ConfigReleaseBuild.cmake index b9674e3d435..d4343700bae 100644 --- a/admin/ConfigReleaseBuild.cmake +++ b/admin/ConfigReleaseBuild.cmake @@ -13,10 +13,7 @@ set (DCW_ROOT "$ENV{GMT_DCW_SOURCE}") set (GMT_ENABLE_OPENMP TRUE) set (GMT_PUBLIC_RELEASE TRUE) -# Include special gmtmex supplement for the GMT/MEX toolbox [which requires MATLAB] -set (GMT_BUILD_GMTMEX TRUE) - -# recommended even for release build +# Recommended even for release build set (CMAKE_C_FLAGS "-Wall -Wdeclaration-after-statement ${CMAKE_C_FLAGS}") # extra warnings set (CMAKE_C_FLAGS "-Wextra ${CMAKE_C_FLAGS}") diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index e2bc3c08691..a0407934525 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -251,27 +251,6 @@ # set (CMAKE_C_FLAGS_RELEASE "-ggdb3 -O2 -Wuninitialized") # check uninitialized variables #endif (HAVE_OPENMP) -# -# Building the GMT/MEX Toolbox -# -# Please export an environmental variable MATLAB that points to your Matlab application - -if (GMT_BUILD_GMTMEX) - if (APPLE) - set (MATLAB "$ENV{MATLAB}") - set (MEX_EXT "mexmaci64") - set (MATLAB_MEX "maci64") - set (MATLAB_FLAGS="-g") - add_definitions(-DGMT_MATLAB) - set (MEX_INC "${MATLAB}/extern/include") - set (MEX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmex.dylib") - set (MX_LIB "${MATLAB}/bin/${MATLAB_MEX}/libmx.dylib") - include_directories (${MEX_INC}) - list (APPEND GMT_OPTIONAL_LIBRARIES ${MEX_LIB}) - list (APPEND GMT_OPTIONAL_LIBRARIES ${MX_LIB}) - endif (APPLE) -endif (GMT_BUILD_GMTMEX) - # # System specific tweaks # diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index aad5207e014..e44f359253e 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -24,7 +24,7 @@ if (GMT_BUILD_GMTMEX) # Add the mexfunction as a shared library add_library (GMTmexfunction SHARED gmtmex.c) # Set the Matlab include file directory - target_include_directories (GMTmexfunction PUBLIC ${GMT_SOURCE_DIR}/src /Users/pwessel/GMTdev/gmt-dev/rbuild/src ${Matlab_INCLUDE_DIRS}) + target_include_directories (GMTmexfunction PUBLIC ${GMT_SOURCE_DIR}/src ${GMT_BINARY_DIR}/src ${Matlab_INCLUDE_DIRS}) # Link with the Matlab and mex libraries and gmt library target_link_libraries (GMTmexfunction gmtlib ${Matlab_MX_LIBRARY} ${Matlab_MEX_LIBRARY}) # Set output properties of shared library @@ -34,11 +34,12 @@ if (GMT_BUILD_GMTMEX) RUNTIME_OUTPUT_NAME gmtmex SUFFIX ".${Matlab_MEX_EXTENSION}" PREFIX "") - # Add the install target + # Install gmt.m in the bin dir install (PROGRAMS gmt.m DESTINATION ${GMT_BINDIR} COMPONENT Runtime) - install (TARGETS GMTmexfunction - RUNTIME DESTINATION ${GMT_BINDIR} + # Install the gmtmex function in the bin dir instead of lib dir + install (PROGRAMS "${GMT_BINARY_DIR}/src/gmtmex/gmtmex.${Matlab_MEX_EXTENSION}" + DESTINATION ${GMT_BINDIR} COMPONENT Runtime) endif (GMT_BUILD_GMTMEX) From 99f238c9842ef731213c63e5e656f021f948183e Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Wed, 7 Apr 2021 15:21:08 -1000 Subject: [PATCH 15/45] Update gmtmex.c --- src/gmtmex/gmtmex.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index ddddb4f0793..9a8767efbc7 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -1885,6 +1885,7 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] if (X[kk].object == ppp) X[kk].object = NULL; } } + free (X); /* 9. Destroy linked option list */ From 49a83d451bb436fd76ac9ec517ba5a424325150f Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Thu, 8 Apr 2021 07:39:45 -1000 Subject: [PATCH 16/45] Update gmt_api.c --- src/gmt_api.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/gmt_api.c b/src/gmt_api.c index 199572d7d9a..e26768bcb17 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -12328,7 +12328,7 @@ GMT_LOCAL const char *gmtapi_retrieve_module_keys (void *V_API, char *module) { return_null (V_API, GMT_NOT_A_VALID_MODULE); } -GMT_LOCAL int gmtapi_extract_argument (char *optarg, char *argument, char **key, int k, bool colon, int *n_pre) { +GMT_LOCAL int gmtapi_extract_argument (struct GMTAPI_CTRL *API, char option, char *optarg, char *argument, char **key, int k, bool colon, int *n_pre) { /* Two separate actions: * 1) If key ends with "=" then we pull out the option argument after stripping off +. * 2) If key ends with "=q" then we see if +q is given and return pos to this modifiers argument. @@ -12344,23 +12344,27 @@ GMT_LOCAL int gmtapi_extract_argument (char *optarg, char *argument, char **key, *n_pre = 0; if (k >= 0 && key[k][K_EQUAL] == '=') { /* Special handling */ *n_pre = (key[k][K_MODIFIER] && isdigit (key[k][K_MODIFIER])) ? (int)(key[k][K_MODIFIER]-'0') : 0; - if ((*n_pre || key[k][K_MODIFIER] == 0) && (c = strchr (optarg, '+'))) { /* Strip off trailing + */ + if ((*n_pre || key[k][K_MODIFIER] == 0) && optarg[0] && (c = strchr (optarg, '+'))) { /* Strip off trailing + */ c[0] = 0; strcpy (argument, optarg); c[0] = '+'; + GMT_Report (API, GMT_MSG_NOTICE, "Option %c: Strip %s: \n", option, c); } else if (key[k][K_MODIFIER]) { /* Look for + */ char code[3] = {"+?"}; - code[1] = key[k][K_MODIFIER]; - if ((c = strstr (optarg, code))) { /* Found + */ + code[1] = key[k][K_MODIFIER]; /* Place the modifier letter code after the + */ + if (optarg[0] && (c = strstr (optarg, code))) { /* Found + */ strcpy (argument, optarg); pos = (unsigned int) (c - optarg + 2); /* Position of this modifiers argument */ + GMT_Report (API, GMT_MSG_NOTICE, "Option %c: Detected %s: \n", option, code); } else strcpy (argument, optarg); } - else + else { strcpy (argument, optarg); + GMT_Report (API, GMT_MSG_NOTICE, "Option %c: Nothing\n", option); + } } else strcpy (argument, optarg); @@ -12806,7 +12810,7 @@ struct GMT_RESOURCE * GMT_Encode_Options (void *V_API, const char *module_name, } direction = (unsigned int) sdir; } - mod_pos = gmtapi_extract_argument (opt->arg, argument, key, k, strip, &n_pre_arg); /* Pull out the option argument, possibly modified by the key */ + mod_pos = gmtapi_extract_argument (API, opt->option, opt->arg, argument, key, k, strip, &n_pre_arg); /* Pull out the option argument, possibly modified by the key */ if (gmtapi_B_custom_annotations (opt)) { /* Special processing for -B[p|s][x|y|z]c] */ /* Add this item to our list */ direction = GMT_IN; From 8165620b0832188679e6ea518b855b0589d2ec19 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 9 Apr 2021 22:14:04 -1000 Subject: [PATCH 17/45] Add build script --- admin/build-macos-external-list.sh | 6 ++++++ share/tools/CMakeLists.txt | 1 + share/tools/gmt_mexbuild.sh | 22 ++++++++++++++++++++++ src/gmt_api.c | 4 ++-- 4 files changed, 31 insertions(+), 2 deletions(-) create mode 100755 share/tools/gmt_mexbuild.sh diff --git a/admin/build-macos-external-list.sh b/admin/build-macos-external-list.sh index ce3ae78a325..0b8c9f3b3e3 100755 --- a/admin/build-macos-external-list.sh +++ b/admin/build-macos-external-list.sh @@ -114,4 +114,10 @@ install (FILES /opt/local/share/GraphicsMagick-\${GMT_CONFIG_GM_VERSION}/config/log.mgk DESTINATION \${GMT_LIBDIR}/GraphicsMagick/config COMPONENT Runtime) + +# Place the GMT/MEX Toolbox files +install (FILES + ../../src/gmtmex/gmtmex.c ../../src/gmtmex/gmt.m + DESTINATION \${GMT_LIBDIR}/../share/tools + COMPONENT Runtime) EOF diff --git a/share/tools/CMakeLists.txt b/share/tools/CMakeLists.txt index 14ae5b2b684..ff80f12a631 100644 --- a/share/tools/CMakeLists.txt +++ b/share/tools/CMakeLists.txt @@ -26,6 +26,7 @@ install (PROGRAMS gmt_links.sh gmt_prepmex.sh gmt_uninstall.sh + gmt_mexbuild.sh ncdeflate ${CMAKE_CURRENT_BINARY_DIR}/gmt5syntax DESTINATION ${GMT_DATADIR}/tools diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh new file mode 100755 index 00000000000..2c79c2a6f77 --- /dev/null +++ b/share/tools/gmt_mexbuild.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# +# Compile and link the gmtmex shared library for the user's (most recent) MATLAB version +MATLAB=$(ls /Applications | grep MATLAB | sort -r | head -1) +if [ "X${MATLAB}" = "X" ]; then + printf "MATLAB not found in /Applications - exiting.\n" >&2 + exit -1 +fi + +printf "\ngmt_mexbuild.sh found most recent MATLAB applicationL %s\n" ${MATLAB} >&2 +printf "\ngmt_mexbuild.sh will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 +printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 +read answer +if [ "X$answer" = "Xn" ]; then + exit 0 +fi + +type=$(uname -m) +xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o +xcrun clang -undefined error -arch ${type} -bundle -DGMT_MATLAB /tmp/gmtmex.o -${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin +printf "You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 diff --git a/src/gmt_api.c b/src/gmt_api.c index 1722892e04c..f8a35e29504 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -12328,7 +12328,7 @@ GMT_LOCAL const char *gmtapi_retrieve_module_keys (void *V_API, char *module) { return_null (V_API, GMT_NOT_A_VALID_MODULE); } -GMT_LOCAL int gmtapi_extract_argument (char *optarg, char *argument, char **key, int k, bool colon, int *n_pre, unsigned int *takes_mod) { +GMT_LOCAL int gmtapi_extract_argument (struct GMTAPI_CTRL *API, char option, char *optarg, char *argument, char **key, int k, bool colon, int *n_pre, unsigned int *takes_mod) { /* Two separate actions: * 1) If key ends with "=" then we pull out the option argument after stripping off +. * 2) If key ends with "=q" then we see if +q is given and return pos to this modifiers argument. @@ -12812,7 +12812,7 @@ struct GMT_RESOURCE * GMT_Encode_Options (void *V_API, const char *module_name, } direction = (unsigned int) sdir; } - mod_pos = gmtapi_extract_argument (API, opt->option, opt->arg, argument, key, k, strip, &n_pre_arg); /* Pull out the option argument, possibly modified by the key */ + mod_pos = gmtapi_extract_argument (API, opt->option, opt->arg, argument, key, k, strip, &n_pre_arg, &takes_mod); /* Pull out the option argument, possibly modified by the key */ if (gmtapi_B_custom_annotations (opt)) { /* Special processing for -B[p|s][x|y|z]c] */ /* Add this item to our list */ direction = GMT_IN; From c28bc9c5e1e36c4e682851e3946a073c3d70fd01 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 9 Apr 2021 22:30:26 -1000 Subject: [PATCH 18/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index 2c79c2a6f77..ffe21603833 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -7,7 +7,7 @@ if [ "X${MATLAB}" = "X" ]; then exit -1 fi -printf "\ngmt_mexbuild.sh found most recent MATLAB applicationL %s\n" ${MATLAB} >&2 +printf "\ngmt_mexbuild.sh found most recent MATLAB application: %s\n" ${MATLAB} >&2 printf "\ngmt_mexbuild.sh will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 read answer From f4a0f2e1681820acca206d887f70334a3451ce12 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 10 Apr 2021 10:15:33 -1000 Subject: [PATCH 19/45] Update build-macos-external-list.sh --- admin/build-macos-external-list.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/admin/build-macos-external-list.sh b/admin/build-macos-external-list.sh index 0b8c9f3b3e3..ba02f0617a0 100755 --- a/admin/build-macos-external-list.sh +++ b/admin/build-macos-external-list.sh @@ -120,4 +120,10 @@ install (FILES ../../src/gmtmex/gmtmex.c ../../src/gmtmex/gmt.m DESTINATION \${GMT_LIBDIR}/../share/tools COMPONENT Runtime) + +# Place the script that builds the GMT/MEX toolbox in bin +install (FILES + ../../share/tools/gmt_mexbuild.sh + DESTINATION \${GMT_BINDIR} + COMPONENT Runtime) EOF From ff80e536f3f0bbf6d94f0d765522841e24899bda Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 10 Apr 2021 10:34:45 -1000 Subject: [PATCH 20/45] Install gmt_mexbuild.sh --- admin/build-macos-external-list.sh | 6 ------ share/tools/CMakeLists.txt | 5 +++++ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/admin/build-macos-external-list.sh b/admin/build-macos-external-list.sh index ba02f0617a0..0b8c9f3b3e3 100755 --- a/admin/build-macos-external-list.sh +++ b/admin/build-macos-external-list.sh @@ -120,10 +120,4 @@ install (FILES ../../src/gmtmex/gmtmex.c ../../src/gmtmex/gmt.m DESTINATION \${GMT_LIBDIR}/../share/tools COMPONENT Runtime) - -# Place the script that builds the GMT/MEX toolbox in bin -install (FILES - ../../share/tools/gmt_mexbuild.sh - DESTINATION \${GMT_BINDIR} - COMPONENT Runtime) EOF diff --git a/share/tools/CMakeLists.txt b/share/tools/CMakeLists.txt index ff80f12a631..d6d8e6fcf93 100644 --- a/share/tools/CMakeLists.txt +++ b/share/tools/CMakeLists.txt @@ -32,6 +32,11 @@ install (PROGRAMS DESTINATION ${GMT_DATADIR}/tools COMPONENT Runtime) +install (PROGRAMS + gmt_mexbuild.sh + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) + if (NOT DEFINED BASH_COMPLETION_DIR) set (BASH_COMPLETION_DIR etc/bash_completion.d) endif (NOT DEFINED BASH_COMPLETION_DIR) From cca393980b44f76405dbfaeaea92ee8e870c40e1 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 10 Apr 2021 11:01:19 -1000 Subject: [PATCH 21/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index ffe21603833..ff82d693638 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -1,13 +1,13 @@ #!/usr/bin/env bash # # Compile and link the gmtmex shared library for the user's (most recent) MATLAB version -MATLAB=$(ls /Applications | grep MATLAB | sort -r | head -1) -if [ "X${MATLAB}" = "X" ]; then +MATLAB_VERSION=$(ls /Applications | grep MATLAB | sort -r | head -1) +if [ "X${MATLAB_VERSION}" = "X" ]; then printf "MATLAB not found in /Applications - exiting.\n" >&2 exit -1 fi -printf "\ngmt_mexbuild.sh found most recent MATLAB application: %s\n" ${MATLAB} >&2 +printf "\ngmt_mexbuild.sh found most recent MATLAB application: %s\n" ${MATLAB_VERSION} >&2 printf "\ngmt_mexbuild.sh will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 read answer @@ -16,7 +16,7 @@ if [ "X$answer" = "Xn" ]; then fi type=$(uname -m) -xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o -xcrun clang -undefined error -arch ${type} -bundle -DGMT_MATLAB /tmp/gmtmex.o -${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o +xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin printf "You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 From fbe05ecb15418e9127b414bcdc5157b7cfc4aee1 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 16 Apr 2021 15:04:46 -1000 Subject: [PATCH 22/45] Update gmtmex.c --- src/gmtmex/gmtmex.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index 9a8767efbc7..fab91c4f04d 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -1885,7 +1885,7 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] if (X[kk].object == ppp) X[kk].object = NULL; } } - free (X); + GMT_Free (API, &X); /* 9. Destroy linked option list */ From de3699a39b3240c9e27d6736e54504aee0478cb5 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Sat, 8 May 2021 10:00:48 -1000 Subject: [PATCH 23/45] Fix grdinterpolate KEYS for external use The -G option sets the output file name for grids, cubes, or datasets. --- src/grdinterpolate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/grdinterpolate.c b/src/grdinterpolate.c index c37a46bcddf..d89a3d5ef29 100644 --- a/src/grdinterpolate.c +++ b/src/grdinterpolate.c @@ -35,7 +35,7 @@ #define THIS_MODULE_MODERN_NAME "grdinterpolate" #define THIS_MODULE_LIB "core" #define THIS_MODULE_PURPOSE "Interpolate a 3-D cube, 2-D grids or 1-D series from a 3-D data cube or stack of 2-D grids" -#define THIS_MODULE_KEYS "?}" +#define THIS_MODULE_KEYS "RVfn" From 7f3968fe16e56dbdc36d4af38ecd02677117f17c Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 12:58:06 -1000 Subject: [PATCH 24/45] Moving along --- CMakeLists.txt | 19 -- cmake/ConfigUserAdvancedTemplate.cmake | 3 - {src/gmtmex => share/tools}/gmt.m | 0 {src/gmtmex => share/tools}/gmtmex.c | 347 +++++++++++++++++++++++-- src/CMakeLists.txt | 5 - src/gmtmex/CMakeLists.txt | 45 ---- src/gmtmex/README.gmtmex | 9 - 7 files changed, 328 insertions(+), 100 deletions(-) rename {src/gmtmex => share/tools}/gmt.m (100%) rename {src/gmtmex => share/tools}/gmtmex.c (85%) delete mode 100644 src/gmtmex/CMakeLists.txt delete mode 100644 src/gmtmex/README.gmtmex diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b280e922f8..64b85189a83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,20 +186,6 @@ else (SUPPL_EXTRA_DIRS) set (PROTO "none") endif (SUPPL_EXTRA_DIRS) -if (GMT_BUILD_GMTMEX) - set (MATLAB_APP "$ENV{MATLAB}") - set (MATLAB_INC "${Matlab_INCLUDE_DIRS}") - set (MATLAB_LIB1 "${Matlab_MEX_LIBRARY}") - set (MATLAB_LIB2 "${Matlab_MX_LIBRARY}") - set (MATLAB_EXT "${Matlab_MEX_EXTENSION}") -else (GMT_INSTALL_MODULE_LINKS) - set (MATLAB_APP "not used") - set (MATLAB_INC "not used") - set (MATLAB_LIB1 "not used") - set (MATLAB_LIB2 "not used") - set (MATLAB_EXT "not used") -endif (GMT_BUILD_GMTMEX) - # Configure header file to pass some of the CMake settings to the source code configure_file (src/config.h.in src/config.h) @@ -251,11 +237,6 @@ message( "* Build GMT for developers : ${DEVEL}\n" "* Build proto supplements : ${PROTO}\n" "* Build module links : ${LINKS}\n" - "* MATLAB Application : ${MATLAB_APP}\n" - "* MATLAB include dir : ${MATLAB_INC}\n" - "* MATLAB MEX library : ${MATLAB_LIB1}\n" - "* MATLAB MX library : ${MATLAB_LIB2}\n" - "* MATLAB mex extension : ${MATLAB_EXT}\n" "* Found Ghostscript (gs) : ${GMT_CONFIG_GS_MESSAGE}\n" "* Found GraphicsMagick (gm) : ${GMT_CONFIG_GM_MESSAGE}\n" "* Found ffmpeg : ${GMT_CONFIG_FFMPEG_MESSAGE}\n" diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index a0407934525..d74f3f42a7f 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -57,9 +57,6 @@ #set (GMT_EXCLUDE_BLAS TRUE) #set (GMT_EXCLUDE_ZLIB TRUE) -# Include special gmtmex supplement for the GMT/MEX toolbox [which requires MATLAB] -#set (GMT_BUILD_GMTMEX TRUE) - # ============================================================================ # Advanced configuration begins here. Usually it is not necessary to edit any # settings below. You should know what you are doing if you do though. Note: diff --git a/src/gmtmex/gmt.m b/share/tools/gmt.m similarity index 100% rename from src/gmtmex/gmt.m rename to share/tools/gmt.m diff --git a/src/gmtmex/gmtmex.c b/share/tools/gmtmex.c similarity index 85% rename from src/gmtmex/gmtmex.c rename to share/tools/gmtmex.c index fab91c4f04d..ffe3d0d8628 100644 --- a/src/gmtmex/gmtmex.c +++ b/share/tools/gmtmex.c @@ -37,7 +37,7 @@ */ #define GMTMEX_MAJOR_VERSION 2 -#define GMTMEX_MINOR_VERSION 0 +#define GMTMEX_MINOR_VERSION 1 #define GMTMEX_PATCH_VERSION 0 /* Define the minimum GMT version suitable for this GMTMEX version */ @@ -132,6 +132,8 @@ typedef int mwSize; # define MEXM_IJ(M,row,col) ((row)*M->n_columns + (col)) /* And this on GMT_GRID */ # define MEXG_IJ(M,row,col) ((row)*M->header->n_columns + (col)) + /* And this on GMT_CUBE */ +# define MEXU_IJK(M,layer,row,col) ((layer)*M->header->nm + (row)*M->header->n_columns + (col)) #else /* Here we go for Matlab or Octave(mex) */ # ifdef GMT_MATLAB # define MEX_PROG "Matlab" @@ -144,6 +146,8 @@ typedef int mwSize; # define MEXM_IJ(M,row,col) ((col)*M->n_rows + (row)) /* And this on GMT_GRID */ # define MEXG_IJ(M,row,col) ((col)*M->header->n_rows + M->header->n_rows - (row) - 1) + /* And this on GMT_CUBE */ +# define MEXU_IJK(M,layer,row,col) ((layer)*M->header->nm + (col)*M->header->n_rows + M->header->n_rows - (row) - 1) #endif /* Definitions of MEX structures used to hold GMT objects. @@ -152,14 +156,23 @@ typedef int mwSize; /* GMT_IS_DATASET: * Returned by GMT via the parser as a MEX structure with the * fields listed below. Pure datasets will only set the data - * matrix and leave the text cell array empty, while textsets - * will fill out both. Only the first segment will have any - * information in the info and projection_ref_* items. */ + * matrix and leave the text cell array empty, while datasets + * with trailing text will fill out both. Only the first segment + * will have any information in the info and projection_ref_* items. */ #define N_MEX_FIELDNAMES_DATASET 6 static const char *gmtmex_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = {"data", "text", "header", "comment", "proj4", "wkt"}; +/* GMT_IS_CUBE: + * Returned by GMT via the parser as a MEX structure with the + * fields listed below. */ + +#define N_MEX_FIELDNAMES_CUBE 19 +static const char *GMTMEX_fieldname_cube[N_MEX_FIELDNAMES_CUBE] = + {"v", "x", "y", "z", "range", "inc", "registration", "nodata", "title", "comment", + "command", "datatype", "x_unit", "y_unit", "z_unit", "v_unit", "layout", "proj4", "wkt"}; + /* GMT_IS_GRID: * Returned by GMT via the parser as a MEX structure with the * fields listed below. */ @@ -219,7 +232,14 @@ enum MEX_dim { * The packing up of GMT structures into MATLAB structures and vice versa happens after getting the * results out of the GMT API and before passing them back to MATLAB. * - * All 5 GMT Resources are supported in this API, according to these rules: + * All 6 GMT Resources are supported in this API, according to these rules: + * GMT_CUBE: Handled with a MATLAB cube structure and we use GMT's GMT_CUBE for the passing + * + Basic header array of length 12 [xmin, xmax, ymin, ymax, zmin, zmax, vmin, vmax, reg, xinc, yinc, zinc] + * + The 3-D grid array w (single precision) + * + An x-array of coordinates + * + An y-array of coordinates + * + An z-array of coordinates + * + Various Proj4 strings * GMT_GRID: Handled with a MATLAB grid structure and we use GMT's GMT_GRID for the passing * + Basic header array of length 9 [xmin, xmax, ymin, ymax, zmin, zmax, reg, xinc, yinc] * + The 2-D grid array z (single precision) @@ -331,7 +351,7 @@ static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { mxptr[11] = mxCreateString (G->header->x_units); mxptr[12] = mxCreateString (G->header->y_units); mxptr[13] = mxCreateString (G->header->z_units); - mxptr[14] = (G->header->mem_layout[0]) ? mxCreateString(G->header->mem_layout) : mxCreateString ("TCF"); + mxptr[14] = (G->header->mem_layout[0]) ? mxCreateString(G->header->mem_layout) : mxCreateString ("TCB"); mxptr[15] = mxCreateString (G->header->ProjRefPROJ4); mxptr[16] = mxCreateString (G->header->ProjRefWKT); @@ -369,6 +389,97 @@ static void *gmtmex_get_grid (void *API, struct GMT_GRID *G) { return (G_struct); } +static void *gmtmex_get_cube (void *API, struct GMT_CUBE *U) { + /* Given an incoming GMT cube U, build a MATLAB structure and assign the output components. + * Note: Incoming GMT cube has standard padding while MATLAB cube has none. */ + + unsigned int k; + uint64_t row, col, layer, gmt_ij, offset = 0; + mwSize ndim = 3, dim[3]; + float *f = NULL; + double *d = NULL, *U_x = NULL, *U_y = NULL, *x = NULL, *y = NULL, *z = NULL; + mxArray *U_struct = NULL, *mxptr[N_MEX_FIELDNAMES_CUBE]; + + if (!U->data) /* Safety valve */ + mexErrMsgTxt ("gmtmex_get_cube: programming error, output cube U is empty\n"); + + if (U->header->n_bands == 1) { /* One layer cube is just a grid */ + struct GMT_GRID *G = NULL; + static void *ptr = NULL; + if ((G = GMT_Create_Data (API, GMT_IS_GRID, GMT_IS_SURFACE, GMT_CONTAINER_ONLY, + NULL, U->header->wesn, U->header->inc, U->header->registration, 2, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_get_cube: Failure to alloc GMT source grid for input\n"); + G->data = U->data; /* Temporarily read from the single cube layer */ + ptr = gmtmex_get_grid (API, G); + G->data = NULL; /* Undo this since nothing was allocated for G above */ + if (GMT_Destroy_Data (API, &G)) + mexPrintf("Warning: Failure to delete G (grid layer in gmtmex_get_cube)\n"); + + return (ptr); + } + + /* Create a MATLAB struct to hold this cube [matrix will be a float (mxSINGLE_CLASS)]. */ + U_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CUBE, GMTMEX_fieldname_cube); + dim[0] = U->header->n_rows; dim[1] = U->header->n_columns; dim[2] = U->header->n_bands; + + /* Get pointers and populate structure from the information in U */ + mxptr[0] = mxCreateNumericArray (ndim, dim, mxSINGLE_CLASS, mxREAL); + mxptr[1] = mxCreateNumericMatrix (1, U->header->n_columns, mxDOUBLE_CLASS, mxREAL); + mxptr[2] = mxCreateNumericMatrix (1, U->header->n_rows, mxDOUBLE_CLASS, mxREAL); + mxptr[3] = mxCreateNumericMatrix (1, U->header->n_bands, mxDOUBLE_CLASS, mxREAL); + mxptr[4] = mxCreateNumericMatrix (1, 8, mxDOUBLE_CLASS, mxREAL); + mxptr[5] = mxCreateNumericMatrix (1, 3, mxDOUBLE_CLASS, mxREAL); + mxptr[6] = mxCreateDoubleScalar ((double)U->header->registration); + mxptr[7] = mxCreateDoubleScalar ((double)U->header->nan_value); + mxptr[8] = mxCreateString (U->header->title); + mxptr[9] = mxCreateString (U->header->remark); + mxptr[10] = mxCreateString (U->header->command); + mxptr[11] = mxCreateString ("float32"); + mxptr[12] = mxCreateString (U->header->x_units); + mxptr[13] = mxCreateString (U->header->y_units); + mxptr[14] = mxCreateString (U->units); + mxptr[15] = mxCreateString (U->header->z_units); + mxptr[16] = (U->header->mem_layout[0]) ? mxCreateString(U->header->mem_layout) : mxCreateString ("TCB"); + mxptr[17] = mxCreateString (U->header->ProjRefPROJ4); + mxptr[18] = mxCreateString (U->header->ProjRefWKT); + + d = mxGetPr (mxptr[4]); /* Range */ + for (k = 0; k < 4; k++) d[k] = U->header->wesn[k]; + d[4] = U->z_range[0]; d[5] = U->z_range[1]; + d[6] = U->header->z_min; d[7] = U->header->z_max; + + d = mxGetPr (mxptr[5]); /* Increments */ + for (k = 0; k < 2; k++) d[k] = U->header->inc[k]; + d[2] = U->z_inc; + + /* Load the real grd array into a float MATLAB array by transposing + from padded GMT grd format to unpadded MATLAB format */ + f = mxGetData (mxptr[0]); + for (layer = 0; layer < U->header->n_bands; layer++) { + for (row = 0; row < U->header->n_rows; row++) { + for (col = 0; col < U->header->n_columns; col++) { + gmt_ij = GMT_IJP (U->header, row, col) + offset; + f[MEXU_IJK(U,layer,row,col)] = U->data[gmt_ij]; + } + } + offset += U->header->size; + } + + /* Also return the convenient x and y arrays */ + U_x = GMT_Get_Coord (API, GMT_IS_GRID, GMT_X, U); /* Get array of x coordinates */ + U_y = GMT_Get_Coord (API, GMT_IS_GRID, GMT_Y, U); /* Get array of y coordinates */ + x = mxGetData (mxptr[1]); + y = mxGetData (mxptr[2]); + z = mxGetData (mxptr[3]); + memcpy (z, U->z, U->header->n_bands * sizeof (double)); + memcpy (x, U->x, U->header->n_columns * sizeof (double)); + for (k = 0; k < U->header->n_rows; k++) + y[U->header->n_rows-1-k] = U->y[k]; /* Must reverse the y-array */ + for (k = 0; k < N_MEX_FIELDNAMES_CUBE; k++) + mxSetField (U_struct, 0, GMTMEX_fieldname_cube[k], mxptr[k]); + return (U_struct); +} + static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { /* Given a GMT DATASET D, build a MATLAB array of segment structure and assign values. * Each segment will have 6 items: @@ -687,6 +798,191 @@ static void *gmtmex_get_image (void *API, struct GMT_IMAGE *I) { return (I_struct); } +static struct GMT_CUBE *gmtmex_cube_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { + /* Used to Create an empty Cube container to hold a GMT cube. + * If direction is GMT_IN then we are given a MATLAB cube and can determine its size, etc. + * If direction is GMT_OUT then we allocate an empty GMT cube as a destination. */ + unsigned int row, col, layer; + uint64_t gmt_ij, offset = 0; + struct GMT_CUBE *U = NULL; + + if (direction == GMT_IN) { /* Dimensions are known from the input pointer */ + unsigned int registration, flag = (module_input) ? GMT_VIA_MODULE_INPUT : 0; + mxArray *mx_ptr = NULL, *mxCube = NULL; + + if (mxIsEmpty (ptr)) + mexErrMsgTxt ("gmtmex_cube_init: The input that was supposed to contain the Cube, is empty\n"); + + if (mxIsStruct(ptr)) { /* Passed a regular MEX Cube structure */ + double *inc = NULL, *range = NULL, *z = NULL, *reg = NULL; + unsigned int pad = (unsigned int)GMT_NOTSET; + uint64_t *this_dim = NULL, dims[3] = {0, 0, 0}; + char x_unit[GMT_GRID_VARNAME_LEN80] = { "" }, y_unit[GMT_GRID_VARNAME_LEN80] = { "" }, + z_unit[GMT_GRID_VARNAME_LEN80] = { "" }, v_unit[GMT_GRID_VARNAME_LEN80] = { "" }, layout[3]; + mx_ptr = mxGetField (ptr, 0, "inc"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Could not find inc array with Cube increments\n"); + inc = mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "range"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Could not find range array for Cube range\n"); + range = mxGetData (mx_ptr); + + mxCube = mxGetField(ptr, 0, "v"); + if (mxCube == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Could not find data array for Cube\n"); + if (!mxIsSingle(mxCube) && !mxIsDouble(mxCube)) + mexErrMsgTxt ("gmtmex_cube_init: data array must be either single or double.\n"); + + mx_ptr = mxGetField (ptr, 0, "registration"); + if (mx_ptr == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Could not find registration array for Cube registration\n"); + reg = mxGetData (mx_ptr); + registration = (unsigned int)lrint(reg[0]); + + + mx_ptr = mxGetField(ptr, 0, "pad"); + if (mx_ptr != NULL) { + double *dpad = mxGetData(mx_ptr); + pad = (unsigned int)dpad[0]; + if (pad > 2) + mexPrintf("gmtmex_cube_init: This pad value (%d) is very probably wrong.\n"); + } + + if (inc[2] == 0.0) { /* Non-equidistant cube layering, must allocate based on dimensions */ + mexPrintf("gmtmex_cube_init: Detected non-equidistant z-spacing.\n"); + if ((mx_ptr = mxGetField (ptr, 0, "z")) == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Could not find z array for Cube z-nodes\n"); + dims[2] = mxGetN(mx_ptr); /* Number of output levels */ + this_dim = dims; /* Pointer to the dims instead of NULL */ + z = mxGetData (mx_ptr); /* Get the non-equidistant z nodes */ + } + if ((U = GMT_Create_Data (API, GMT_IS_CUBE|flag, GMT_IS_VOLUME, GMT_CONTAINER_AND_DATA, + this_dim, range, inc, registration, pad, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Failure to alloc GMT source Cube for input\n"); + + if (this_dim) { /* If not equidistant we must duplicate the level array into the Cube manually */ + if (U->z == NULL && GMT_Put_Levels (API, U, z, dims[2])) + mexErrMsgTxt ("gmtmex_cube_init: Failure to put non-equidistant z-nodes into the cube structure\n"); + } + + U->z_range[0] = range[4]; + U->z_range[1] = range[5]; + U->header->z_min = range[6]; + U->header->z_max = range[7]; + + U->header->registration = registration; + + mx_ptr = mxGetField (ptr, 0, "nodata"); + if (mx_ptr != NULL) + U->header->nan_value = *(float *)mxGetData (mx_ptr); + + mx_ptr = mxGetField (ptr, 0, "proj4"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 6) { /* A true proj4 string will have at least this length */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + U->header->ProjRefPROJ4 = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "wkt"); + if (mx_ptr != NULL && mxGetN(mx_ptr) > 20) { /* A true WKT string will have more than this length */ + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + U->header->ProjRefWKT = GMT_Duplicate_String (API, str); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "title"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->title, str, GMT_GRID_VARNAME_LEN80 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "command"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr) + 1); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->command, str, GMT_GRID_COMMAND_LEN320 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "comment"); + if (mx_ptr != NULL) { + char *str = malloc(mxGetN(mx_ptr)+2); + mxGetString(mx_ptr, str, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->remark, str, GMT_GRID_REMARK_LEN160 - 1); + free (str); + } + mx_ptr = mxGetField (ptr, 0, "x_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, x_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->x_units, x_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "y_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, y_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->y_units, y_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "z_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, z_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "v_unit"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, v_unit, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->z_units, z_unit, GMT_GRID_VARNAME_LEN80 - 1); + } + mx_ptr = mxGetField (ptr, 0, "layout"); + if (mx_ptr != NULL) { + mxGetString(mx_ptr, layout, (mwSize)mxGetN(mx_ptr) + 1); + strncpy(U->header->mem_layout, layout, 3); + } + else + strncpy(U->header->mem_layout, "TRS", 3); + } + + if (mxIsSingle(mxCube)) { + float *f4 = mxGetData(mxCube); + if (f4 == NULL) + mexErrMsgTxt("gmtmex_cube_init: Cube pointer is NULL where it absolutely could not be."); + for (layer = 0; layer < U->header->n_bands; layer++) { + for (row = 0; row < U->header->n_rows; row++) { + for (col = 0; col < U->header->n_columns; col++) { + gmt_ij = GMT_IJP (U->header, row, col) + offset; + U->data[gmt_ij] = f4[MEXU_IJK(U,layer,row,col)]; + } + } + offset += U->header->size; + } + } + else { + double *f8 = mxGetData(mxCube); + if (f8 == NULL) + mexErrMsgTxt("gmtmex_cube_init: Cube pointer is NULL where it absolutely could not be."); + for (layer = 0; layer < U->header->n_bands; layer++) { + for (row = 0; row < U->header->n_rows; row++) { + for (col = 0; col < U->header->n_columns; col++) { + gmt_ij = GMT_IJP (U->header, row, col) + offset; + U->data[gmt_ij] = (float)f8[MEXU_IJK(U,layer,row,col)]; + } + } + offset += U->header->size; + } + } + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_cube_init: Allocated GMT Cube %lx\n", (long)U); + GMT_Report (API, GMT_MSG_DEBUG, + "gmtmex_cube_init: Registered GMT Grid array %lx via memory reference from MATLAB\n", + (long)U->data); + } + else { /* Just allocate an empty container to hold an output grid (signal this by passing 0s and NULLs [mode == GMT_IS_OUTPUT from 5.4]) */ + if ((U = GMT_Create_Data (API, GMT_IS_CUBE, GMT_IS_VOLUME, GMT_IS_OUTPUT, + NULL, NULL, NULL, 0, 0, NULL)) == NULL) + mexErrMsgTxt ("gmtmex_cube_init: Failure to alloc GMT blank Cube container for holding output cube\n"); + } + return (U); +} + static struct GMT_GRID *gmtmex_grid_init (void *API, unsigned int direction, unsigned int module_input, const mxArray *ptr) { /* Used to Create an empty Grid container to hold a GMT grid. * If direction is GMT_IN then we are given a MATLAB grid and can determine its size, etc. @@ -1422,7 +1718,9 @@ static char gmtmex_objecttype (const mxArray *ptr) { mxArray *mx_ptr = NULL; if (mxIsEmpty (ptr)) mexErrMsgTxt ("gmtmex_objecttype: Pointer is empty\n"); - if (mxIsStruct (ptr)) { /* This means either a dataset, grid, image, cpt, or PS, so must check for fields */ + if (mxIsStruct (ptr)) { /* This means either a cube, dataset, grid, image, cpt, or PS, so must check for fields */ + mx_ptr = mxGetField (ptr, 0, "v_unit"); + if (mx_ptr) return 'u'; mx_ptr = mxGetField (ptr, 0, "data"); if (mx_ptr) return 'd'; mx_ptr = mxGetField (ptr, 0, "postscript"); @@ -1447,6 +1745,10 @@ static void gmtmex_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray unsigned int module_input = (X->option->option == GMT_OPT_INFILE), actual_family = X->family; switch (X->family) { + case GMT_IS_CUBE: /* Get a cube from Matlab or a dummy one to hold GMT output */ + X->object = gmtmex_cube_init (API, X->direction, module_input, ptr); + GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Cube\n"); + break; case GMT_IS_GRID: /* Get a grid from Matlab or a dummy one to hold GMT output */ X->object = gmtmex_grid_init (API, X->direction, module_input, ptr); GMT_Report (API, GMT_MSG_DEBUG, "gmtmex_Set_Object: Got Grid\n"); @@ -1485,6 +1787,9 @@ static void *gmtmex_Get_Object (void *API, struct GMT_RESOURCE *X) { if ((X->object = GMT_Read_VirtualFile (API, X->name)) == NULL && X->family != GMT_IS_DATASET) mexErrMsgTxt ("GMT: Error reading virtual file from GMT\n"); switch (X->family) { /* Determine what container we got */ + case GMT_IS_CUBE: /* A GMT cube; make it the pos'th output item */ + ptr = gmtmex_get_cube (API, X->object); + break; case GMT_IS_GRID: /* A GMT grid; make it the pos'th output item */ ptr = gmtmex_get_grid (API, X->object); break; @@ -1542,7 +1847,7 @@ static void usage (int nlhs, int nrhs) { if (nrhs == 0) { /* No arguments at all results in the GMT banner message */ mexPrintf("\nGMT - The Generic Mapping Tools, %s API, Version %d.%d.%d\n", MEX_PROG, GMTMEX_MAJOR_VERSION, GMTMEX_MINOR_VERSION, GMTMEX_PATCH_VERSION); - mexPrintf("Copyright 1991-2018 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); + mexPrintf("Copyright 1991-2021 Paul Wessel, Walter H. F. Smith, R. Scharroo, J. Luis, and F. Wobbe\n\n"); mexPrintf("This program comes with NO WARRANTY, to the extent permitted by law.\n"); mexPrintf("You may redistribute copies of this program under the terms of the\n"); mexPrintf("GNU Lesser General Public License.\n"); @@ -1562,7 +1867,7 @@ static void *Initiate_Session (unsigned int verbose) { /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + - GMT_SESSION_COLMAJOR, gmtmex_print_func)) == NULL) + GMT_SESSION_COLMAJOR, GMTMEX_print_func)) == NULL) mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); #ifndef SINGLE_SESSION @@ -1579,20 +1884,23 @@ static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { */ void *ptr = NULL; switch (X->family) { + case GMT_IS_CUBE: + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CUBE, GMTMEX_fieldname_cube); + break; case GMT_IS_GRID: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); break; case GMT_IS_IMAGE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); break; case GMT_IS_DATASET: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); break; case GMT_IS_PALETTE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); break; case GMT_IS_POSTSCRIPT: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); break; default: break; @@ -1600,10 +1908,7 @@ static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { return ptr; } -/* This is the function that is called when we type gmt in MATLAB/Octave. - * It is the only function experted by the GMT API library. - */ - +/* This is the function that is called when we type gmt in MATLAB/Octave */ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { int status = 0; /* Status code from GMT API */ int n_in_objects = 0; /* Number of input objects passed to module */ @@ -1753,6 +2058,7 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] if (cmd[k]) opt_args = &cmd[k]; } + /* See if info about installation is required */ if (!strcmp(module, "gmt")) { char t[256] = {""}; @@ -1885,7 +2191,10 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] if (X[kk].object == ppp) X[kk].object = NULL; } } +#if GMT_MAJOR_VERSION == 6 && GMT_MINOR_VERSION > 1 + /* Before we just let the memory leak... */ GMT_Free (API, &X); +#endif /* 9. Destroy linked option list */ @@ -1900,4 +2209,4 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { GMT_mexFunction (nlhs, plhs, nrhs, prhs); -} +} \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 32e8531ad67..fb6eeb579cc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,11 +569,6 @@ endforeach (_gmt_prog) # Rename gmt target to prevent version clash set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) -if (GMT_BUILD_GMTMEX) - find_package (Matlab) - add_subdirectory (gmtmex) -endif (GMT_BUILD_GMTMEX) - # psldemo add_executable (psldemo psldemo.h psldemo.c) target_link_libraries (psldemo pslib) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt deleted file mode 100644 index e44f359253e..00000000000 --- a/src/gmtmex/CMakeLists.txt +++ /dev/null @@ -1,45 +0,0 @@ -# -# Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html) -# See LICENSE.TXT file for copying and redistribution conditions. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU Lesser General Public License as published by -# the Free Software Foundation; version 3 or any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Lesser General Public License for more details. -# -# Contact info: www.generic-mapping-tools.org -#------------------------------------------------------------------------------- -## -## Settings for GMT MexFunction shared library -## -# Note: Only activated if GMT_BUILD_GMTMEX is set in ConfigUserAdvanced.cmake - -if (GMT_BUILD_GMTMEX) - # Add the flag to select Matlab vs Octave - add_definitions(-DGMT_MATLAB) - # Add the mexfunction as a shared library - add_library (GMTmexfunction SHARED gmtmex.c) - # Set the Matlab include file directory - target_include_directories (GMTmexfunction PUBLIC ${GMT_SOURCE_DIR}/src ${GMT_BINARY_DIR}/src ${Matlab_INCLUDE_DIRS}) - # Link with the Matlab and mex libraries and gmt library - target_link_libraries (GMTmexfunction gmtlib ${Matlab_MX_LIBRARY} ${Matlab_MEX_LIBRARY}) - # Set output properties of shared library - set_target_properties (GMTmexfunction - PROPERTIES - OUTPUT_NAME gmtmex - RUNTIME_OUTPUT_NAME gmtmex - SUFFIX ".${Matlab_MEX_EXTENSION}" - PREFIX "") - # Install gmt.m in the bin dir - install (PROGRAMS gmt.m - DESTINATION ${GMT_BINDIR} - COMPONENT Runtime) - # Install the gmtmex function in the bin dir instead of lib dir - install (PROGRAMS "${GMT_BINARY_DIR}/src/gmtmex/gmtmex.${Matlab_MEX_EXTENSION}" - DESTINATION ${GMT_BINDIR} - COMPONENT Runtime) -endif (GMT_BUILD_GMTMEX) diff --git a/src/gmtmex/README.gmtmex b/src/gmtmex/README.gmtmex deleted file mode 100644 index 839932f09ed..00000000000 --- a/src/gmtmex/README.gmtmex +++ /dev/null @@ -1,9 +0,0 @@ -# -Distributed under the GNU Lesser Public License; see file -LICENSE.TXT in main GMT directory. - -GMT 5 Release introduced a full set of Matlab interface functions -for all GMT modules. GMT 6 continued this work, and GMT 6.1 made -some internal changes, necessitating GMTMEX 2.0.0 - -See documentation for usage. From 14efb57d68f065a77b3c6558482415d904a2c5cd Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 13:00:37 -1000 Subject: [PATCH 25/45] Update CMakeLists.txt --- share/tools/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/share/tools/CMakeLists.txt b/share/tools/CMakeLists.txt index d6d8e6fcf93..d25de9fa991 100644 --- a/share/tools/CMakeLists.txt +++ b/share/tools/CMakeLists.txt @@ -27,6 +27,8 @@ install (PROGRAMS gmt_prepmex.sh gmt_uninstall.sh gmt_mexbuild.sh + gmtmex.c + gmt.m ncdeflate ${CMAKE_CURRENT_BINARY_DIR}/gmt5syntax DESTINATION ${GMT_DATADIR}/tools From 7504cd85f6ba314fa101eee2e4acdbf70c92a508 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 13:27:15 -1000 Subject: [PATCH 26/45] Update gmtmex.c --- share/tools/gmtmex.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/share/tools/gmtmex.c b/share/tools/gmtmex.c index ffe3d0d8628..50e1c2dd421 100644 --- a/share/tools/gmtmex.c +++ b/share/tools/gmtmex.c @@ -169,7 +169,7 @@ static const char *gmtmex_fieldname_dataset[N_MEX_FIELDNAMES_DATASET] = * fields listed below. */ #define N_MEX_FIELDNAMES_CUBE 19 -static const char *GMTMEX_fieldname_cube[N_MEX_FIELDNAMES_CUBE] = +static const char *gmtmex_fieldname_cube[N_MEX_FIELDNAMES_CUBE] = {"v", "x", "y", "z", "range", "inc", "registration", "nodata", "title", "comment", "command", "datatype", "x_unit", "y_unit", "z_unit", "v_unit", "layout", "proj4", "wkt"}; @@ -419,7 +419,7 @@ static void *gmtmex_get_cube (void *API, struct GMT_CUBE *U) { } /* Create a MATLAB struct to hold this cube [matrix will be a float (mxSINGLE_CLASS)]. */ - U_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CUBE, GMTMEX_fieldname_cube); + U_struct = mxCreateStructMatrix (1, 1, N_MEX_FIELDNAMES_CUBE, gmtmex_fieldname_cube); dim[0] = U->header->n_rows; dim[1] = U->header->n_columns; dim[2] = U->header->n_bands; /* Get pointers and populate structure from the information in U */ @@ -476,7 +476,7 @@ static void *gmtmex_get_cube (void *API, struct GMT_CUBE *U) { for (k = 0; k < U->header->n_rows; k++) y[U->header->n_rows-1-k] = U->y[k]; /* Must reverse the y-array */ for (k = 0; k < N_MEX_FIELDNAMES_CUBE; k++) - mxSetField (U_struct, 0, GMTMEX_fieldname_cube[k], mxptr[k]); + mxSetField (U_struct, 0, gmtmex_fieldname_cube[k], mxptr[k]); return (U_struct); } @@ -1867,7 +1867,7 @@ static void *Initiate_Session (unsigned int verbose) { /* Initializing new GMT session with a MATLAB-acceptable replacement for the printf function */ /* For debugging with verbose we pass the specified verbose shifted by 10 bits - this is decoded in API */ if ((API = GMT_Create_Session (MEX_PROG, 2U, (verbose << 10) + GMT_SESSION_NOEXIT + GMT_SESSION_EXTERNAL + - GMT_SESSION_COLMAJOR, GMTMEX_print_func)) == NULL) + GMT_SESSION_COLMAJOR, gmtmex_print_func)) == NULL) mexErrMsgTxt ("GMT: Failure to create new GMT session\n"); #ifndef SINGLE_SESSION @@ -1885,22 +1885,22 @@ static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { void *ptr = NULL; switch (X->family) { case GMT_IS_CUBE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CUBE, GMTMEX_fieldname_cube); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CUBE, gmtmex_fieldname_cube); break; case GMT_IS_GRID: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, GMTMEX_fieldname_grid); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_GRID, gmtmex_fieldname_grid); break; case GMT_IS_IMAGE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, GMTMEX_fieldname_image); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_IMAGE, gmtmex_fieldname_image); break; case GMT_IS_DATASET: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, GMTMEX_fieldname_dataset); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); break; case GMT_IS_PALETTE: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, GMTMEX_fieldname_cpt); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CPT, gmtmex_fieldname_cpt); break; case GMT_IS_POSTSCRIPT: - ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, GMTMEX_fieldname_ps); + ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_PS, gmtmex_fieldname_ps); break; default: break; From e6647a36ab3dded3b02172ee93a869ac57d15491 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 15:54:11 -1000 Subject: [PATCH 27/45] Update build-macos-external-list.sh --- admin/build-macos-external-list.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/admin/build-macos-external-list.sh b/admin/build-macos-external-list.sh index 0b8c9f3b3e3..ce3ae78a325 100755 --- a/admin/build-macos-external-list.sh +++ b/admin/build-macos-external-list.sh @@ -114,10 +114,4 @@ install (FILES /opt/local/share/GraphicsMagick-\${GMT_CONFIG_GM_VERSION}/config/log.mgk DESTINATION \${GMT_LIBDIR}/GraphicsMagick/config COMPONENT Runtime) - -# Place the GMT/MEX Toolbox files -install (FILES - ../../src/gmtmex/gmtmex.c ../../src/gmtmex/gmt.m - DESTINATION \${GMT_LIBDIR}/../share/tools - COMPONENT Runtime) EOF From 411aae3b2932c567aa860440e48ae8817f00b374 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 16:14:05 -1000 Subject: [PATCH 28/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index ff82d693638..6d0d460d461 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -15,8 +15,23 @@ if [ "X$answer" = "Xn" ]; then exit 0 fi +# Make all the shared libraries use rpath instead of executable path +find . -name '*.dylib' > /tmp/raw.lis +rm -f /tmp/lib.lis +while read file; do + if [ ! -L $file ]; then + echo $file >> /tmp/lib.lis + fi +done < /tmp/raw.lis +# For each library, replace @executable_path/../lib/*.dylib with @rpath/lib*.dylib +while read file; do + echo $file + otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' | sh -s +done < /tmp/lib.lis + +# Build the gmtmex executable type=$(uname -m) xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o -xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -rpath ${BUNDLE_RESOURCES}/lib -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin printf "You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 From 98732f2ab7a1f4a623b56e6be90d3c7b1537cace Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 16:53:03 -1000 Subject: [PATCH 29/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 40 ++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index 6d0d460d461..3f418b526b3 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -1,35 +1,57 @@ #!/usr/bin/env bash # # Compile and link the gmtmex shared library for the user's (most recent) MATLAB version + +# 0. Check if already installed +if [ -f ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 ]; then + printf "GMT/MEX toolbox already installed - exiting.\n" >&2 + exit 1 +fi + +# 1. Determine a single, most recent Matlab version MATLAB_VERSION=$(ls /Applications | grep MATLAB | sort -r | head -1) if [ "X${MATLAB_VERSION}" = "X" ]; then printf "MATLAB not found in /Applications - exiting.\n" >&2 exit -1 fi +# 2. Determine that Xcode has been installed +if ! [ -x "$(command -v xcrun)" ]; then + echo 'build-release.sh: Error: xcrun is not found in your search PATH.' >&2 + exit 1 +fi + printf "\ngmt_mexbuild.sh found most recent MATLAB application: %s\n" ${MATLAB_VERSION} >&2 printf "\ngmt_mexbuild.sh will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 -printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 -read answer -if [ "X$answer" = "Xn" ]; then - exit 0 +# 3. If use does not have write permission, explain they must use sudo +if [ ! -w ${BUNDLE_RESOURCES} ]; then + printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 + read answer + if [ "X$answer" = "Xn" ]; then + exit 0 + fi fi -# Make all the shared libraries use rpath instead of executable path -find . -name '*.dylib' > /tmp/raw.lis +printf "\ngmt_mexbuild.sh: Working...\n" >&2 +# 4. Make a listing of all shared libraries but skip symbolic links +find ${BUNDLE_RESOURCES}/lib -name '*.dylib' > /tmp/raw.lis rm -f /tmp/lib.lis while read file; do - if [ ! -L $file ]; then + if [ ! -L $file ]; then # Skipping symbolic links echo $file >> /tmp/lib.lis fi done < /tmp/raw.lis -# For each library, replace @executable_path/../lib/*.dylib with @rpath/lib*.dylib +# 5. Make all shared libraries use rpath instead of executable path +# For each library, replace @executable_path/../lib/*.dylib with @rpath/lib*.dylib while read file; do echo $file otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' | sh -s done < /tmp/lib.lis -# Build the gmtmex executable +# 6. Update the gmt executable to use rpath also +install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib gmt + +# 7. Build the gmtmex executable type=$(uname -m) xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -rpath ${BUNDLE_RESOURCES}/lib -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 From fad2ccbf04b3d9015ebfc79375f427ed789f5c0a Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 17:16:06 -1000 Subject: [PATCH 30/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index 3f418b526b3..ff75f753335 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -4,7 +4,8 @@ # 0. Check if already installed if [ -f ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 ]; then - printf "GMT/MEX toolbox already installed - exiting.\n" >&2 + printf "GMT/MEX toolbox already installed on this computer.\n" >&2 + printf "To reinstall you must first remove ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64.\n" >&2 exit 1 fi @@ -17,7 +18,7 @@ fi # 2. Determine that Xcode has been installed if ! [ -x "$(command -v xcrun)" ]; then - echo 'build-release.sh: Error: xcrun is not found in your search PATH.' >&2 + echo 'build-release.sh: Error: Xcode CLI tools (xcrun) not found in your search PATH.' >&2 exit 1 fi @@ -32,7 +33,7 @@ if [ ! -w ${BUNDLE_RESOURCES} ]; then fi fi -printf "\ngmt_mexbuild.sh: Working...\n" >&2 +printf "\ngmt_mexbuild.sh: Working..." >&2 # 4. Make a listing of all shared libraries but skip symbolic links find ${BUNDLE_RESOURCES}/lib -name '*.dylib' > /tmp/raw.lis rm -f /tmp/lib.lis @@ -43,17 +44,21 @@ while read file; do done < /tmp/raw.lis # 5. Make all shared libraries use rpath instead of executable path # For each library, replace @executable_path/../lib/*.dylib with @rpath/lib*.dylib +rm -f /tmp/mexjob.sh while read file; do - echo $file - otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' | sh -s + otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' >> /tmp/mexjob.sh done < /tmp/lib.lis +sh /tmp/mexjob.sh # 6. Update the gmt executable to use rpath also install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib gmt # 7. Build the gmtmex executable type=$(uname -m) +version=$(gmt --version | awk -Fr '{print $1}') # Skip trailing stuff like rc1 xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -rpath ${BUNDLE_RESOURCES}/lib -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin +install_name_tool -change @executable_path/../lib/libgmt.${version}.dylib @rpath/libgmt.${version}.dylib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +printf "done\n" >&2 printf "You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 From a4e33dca90f429e41c71909ff5a9c9a57a79d595 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 17:25:27 -1000 Subject: [PATCH 31/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 48 +++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index ff75f753335..f3e27ac372b 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -1,29 +1,35 @@ #!/usr/bin/env bash # -# Compile and link the gmtmex shared library for the user's (most recent) MATLAB version +# Compile and link the GMT/MEX toolbox with the user's (most recent) MATLAB version. +# This step must be done by the user since we cannot distribute MATLAB, and the user +# needs to have a MATLAB license. The requirements to build the GMT/MEX toolbox are: +# +# 1. A recent MATLAB installation in /Applications +# 2. A recent installation of Xcode command line tools (CLI) +#------------------------------------------------------------------------------------ # 0. Check if already installed if [ -f ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 ]; then - printf "GMT/MEX toolbox already installed on this computer.\n" >&2 - printf "To reinstall you must first remove ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64.\n" >&2 + printf "gmt_mexbuild.sh: GMT/MEX toolbox already installed on this computer.\n" >&2 + printf "gmt_mexbuild.sh: To reinstall you must first remove ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64.\n" >&2 exit 1 fi # 1. Determine a single, most recent Matlab version MATLAB_VERSION=$(ls /Applications | grep MATLAB | sort -r | head -1) if [ "X${MATLAB_VERSION}" = "X" ]; then - printf "MATLAB not found in /Applications - exiting.\n" >&2 + printf "gmt_mexbuild.sh: MATLAB not found in /Applications - exiting.\n" >&2 exit -1 fi -# 2. Determine that Xcode has been installed +# 2. Determine that Xcode CLI has been installed if ! [ -x "$(command -v xcrun)" ]; then - echo 'build-release.sh: Error: Xcode CLI tools (xcrun) not found in your search PATH.' >&2 + echo 'gmt_mexbuild.sh: Error: Xcode CLI tools (xcrun) not found in your search PATH.' >&2 exit 1 fi -printf "\ngmt_mexbuild.sh found most recent MATLAB application: %s\n" ${MATLAB_VERSION} >&2 -printf "\ngmt_mexbuild.sh will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 +printf "\ngmt_mexbuild.sh: Found most recent MATLAB application: %s\n" ${MATLAB_VERSION} >&2 +printf "\ngmt_mexbuild.sh: Will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 # 3. If use does not have write permission, explain they must use sudo if [ ! -w ${BUNDLE_RESOURCES} ]; then printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 @@ -35,30 +41,32 @@ fi printf "\ngmt_mexbuild.sh: Working..." >&2 # 4. Make a listing of all shared libraries but skip symbolic links -find ${BUNDLE_RESOURCES}/lib -name '*.dylib' > /tmp/raw.lis -rm -f /tmp/lib.lis +mkdir -p /tmp/gmtmexinstall +find ${BUNDLE_RESOURCES}/lib -name '*.dylib' > /tmp/gmtmexinstall/raw.lis while read file; do if [ ! -L $file ]; then # Skipping symbolic links - echo $file >> /tmp/lib.lis + echo $file >> /tmp/gmtmexinstall/lib.lis fi -done < /tmp/raw.lis +done < /tmp/gmtmexinstall/raw.lis # 5. Make all shared libraries use rpath instead of executable path # For each library, replace @executable_path/../lib/*.dylib with @rpath/lib*.dylib -rm -f /tmp/mexjob.sh while read file; do - otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' >> /tmp/mexjob.sh -done < /tmp/lib.lis -sh /tmp/mexjob.sh + otool -L $file | grep '@executable_path' | tr '/' ' ' | awk '{printf "install_name_tool -change @executable_path/../lib/%s @rpath/%s %s\n", $4, $4, "'$file'"}' >> /tmp/gmtmexinstall/mexjob.sh +done < /tmp/gmtmexinstall/lib.lis +sh /tmp/gmtmexinstall/mexjob.sh # 6. Update the gmt executable to use rpath also install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib gmt # 7. Build the gmtmex executable type=$(uname -m) -version=$(gmt --version | awk -Fr '{print $1}') # Skip trailing stuff like rc1 -xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmex.o -xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -rpath ${BUNDLE_RESOURCES}/lib -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmexinstall/gmtmex.o +xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmexinstall/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin +# 8. Update the gmtmex plugin to use rpath also +version=$(gmt --version | awk -Fr '{print $1}') # Skip trailing stuff like rc1 install_name_tool -change @executable_path/../lib/libgmt.${version}.dylib @rpath/libgmt.${version}.dylib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +# 9. Clean up and we are done +rm -rf /tmp/gmtmexinstall printf "done\n" >&2 -printf "You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 +printf "gmt_mexbuild.sh: You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 From c81b42d55d1e6936dd4c2cc7a580c73ca8dd4d17 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 19:37:38 -1000 Subject: [PATCH 32/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index f3e27ac372b..2f8ec7b67c9 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -66,6 +66,7 @@ cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin # 8. Update the gmtmex plugin to use rpath also version=$(gmt --version | awk -Fr '{print $1}') # Skip trailing stuff like rc1 install_name_tool -change @executable_path/../lib/libgmt.${version}.dylib @rpath/libgmt.${version}.dylib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 +install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 # 9. Clean up and we are done rm -rf /tmp/gmtmexinstall printf "done\n" >&2 From 63f618719bbe2beb31a7ddcdb94c5c8083c319cd Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 19:57:13 -1000 Subject: [PATCH 33/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index 2f8ec7b67c9..dd413ef813a 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -43,6 +43,7 @@ printf "\ngmt_mexbuild.sh: Working..." >&2 # 4. Make a listing of all shared libraries but skip symbolic links mkdir -p /tmp/gmtmexinstall find ${BUNDLE_RESOURCES}/lib -name '*.dylib' > /tmp/gmtmexinstall/raw.lis +find ${BUNDLE_RESOURCES}/lib -name '*.so' >> /tmp/gmtmexinstall/raw.lis while read file; do if [ ! -L $file ]; then # Skipping symbolic links echo $file >> /tmp/gmtmexinstall/lib.lis From d5d800a5b0fdf4b3f9ac7eedb2b17645142591b2 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 17 May 2021 19:58:46 -1000 Subject: [PATCH 34/45] Update ConfigDefault.cmake --- cmake/ConfigDefault.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/ConfigDefault.cmake b/cmake/ConfigDefault.cmake index f62ec29b7fe..d5a44560794 100644 --- a/cmake/ConfigDefault.cmake +++ b/cmake/ConfigDefault.cmake @@ -52,7 +52,7 @@ set (GMT_PACKAGE_VERSION_MAJOR 6) set (GMT_PACKAGE_VERSION_MINOR 2) set (GMT_PACKAGE_VERSION_PATCH 0) # If this is a beta version or similar, add a string suffix -#set (GMT_PACKAGE_VERSION_SUFFIX "rc1") +#set (GMT_PACKAGE_VERSION_SUFFIX "rc2") # Whether to make a public release. # When making internal releases or just an ordinary developer build, it is set to FALSE. From cb2dcb931ab3f716af7dc64fdb7039423f165d02 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 07:54:11 -1000 Subject: [PATCH 35/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index dd413ef813a..fd207a03c6c 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -57,7 +57,7 @@ done < /tmp/gmtmexinstall/lib.lis sh /tmp/gmtmexinstall/mexjob.sh # 6. Update the gmt executable to use rpath also -install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib gmt +install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/bin/gmt # 7. Build the gmtmex executable type=$(uname -m) From 79a6b61400a372c9198b074e1987c797829fe02a Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 10:41:55 -1000 Subject: [PATCH 36/45] Try to do both a mex build and just distribute files WIth the gmt_mexbuild.sh script working, perhaps also offer the option of building and linking with mex as part of the cmake setup. --- CMakeLists.txt | 19 +++++++++++ cmake/ConfigUserAdvancedTemplate.cmake | 3 ++ src/CMakeLists.txt | 11 +++++++ src/gmtmex/CMakeLists.txt | 45 ++++++++++++++++++++++++++ src/gmtmex/README.gmtmex | 9 ++++++ {share/tools => src/gmtmex}/gmt.m | 0 {share/tools => src/gmtmex}/gmtmex.c | 0 7 files changed, 87 insertions(+) create mode 100644 src/gmtmex/CMakeLists.txt create mode 100644 src/gmtmex/README.gmtmex rename {share/tools => src/gmtmex}/gmt.m (100%) rename {share/tools => src/gmtmex}/gmtmex.c (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64b85189a83..8b280e922f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,6 +186,20 @@ else (SUPPL_EXTRA_DIRS) set (PROTO "none") endif (SUPPL_EXTRA_DIRS) +if (GMT_BUILD_GMTMEX) + set (MATLAB_APP "$ENV{MATLAB}") + set (MATLAB_INC "${Matlab_INCLUDE_DIRS}") + set (MATLAB_LIB1 "${Matlab_MEX_LIBRARY}") + set (MATLAB_LIB2 "${Matlab_MX_LIBRARY}") + set (MATLAB_EXT "${Matlab_MEX_EXTENSION}") +else (GMT_INSTALL_MODULE_LINKS) + set (MATLAB_APP "not used") + set (MATLAB_INC "not used") + set (MATLAB_LIB1 "not used") + set (MATLAB_LIB2 "not used") + set (MATLAB_EXT "not used") +endif (GMT_BUILD_GMTMEX) + # Configure header file to pass some of the CMake settings to the source code configure_file (src/config.h.in src/config.h) @@ -237,6 +251,11 @@ message( "* Build GMT for developers : ${DEVEL}\n" "* Build proto supplements : ${PROTO}\n" "* Build module links : ${LINKS}\n" + "* MATLAB Application : ${MATLAB_APP}\n" + "* MATLAB include dir : ${MATLAB_INC}\n" + "* MATLAB MEX library : ${MATLAB_LIB1}\n" + "* MATLAB MX library : ${MATLAB_LIB2}\n" + "* MATLAB mex extension : ${MATLAB_EXT}\n" "* Found Ghostscript (gs) : ${GMT_CONFIG_GS_MESSAGE}\n" "* Found GraphicsMagick (gm) : ${GMT_CONFIG_GM_MESSAGE}\n" "* Found ffmpeg : ${GMT_CONFIG_FFMPEG_MESSAGE}\n" diff --git a/cmake/ConfigUserAdvancedTemplate.cmake b/cmake/ConfigUserAdvancedTemplate.cmake index d74f3f42a7f..a0407934525 100644 --- a/cmake/ConfigUserAdvancedTemplate.cmake +++ b/cmake/ConfigUserAdvancedTemplate.cmake @@ -57,6 +57,9 @@ #set (GMT_EXCLUDE_BLAS TRUE) #set (GMT_EXCLUDE_ZLIB TRUE) +# Include special gmtmex supplement for the GMT/MEX toolbox [which requires MATLAB] +#set (GMT_BUILD_GMTMEX TRUE) + # ============================================================================ # Advanced configuration begins here. Usually it is not necessary to edit any # settings below. You should know what you are doing if you do though. Note: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fb6eeb579cc..0996e7b7e2f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -569,6 +569,11 @@ endforeach (_gmt_prog) # Rename gmt target to prevent version clash set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) +if (GMT_BUILD_GMTMEX) + find_package (Matlab) + add_subdirectory (gmtmex) +endif (GMT_BUILD_GMTMEX) + # psldemo add_executable (psldemo psldemo.h psldemo.c) target_link_libraries (psldemo pslib) @@ -643,6 +648,12 @@ install (PROGRAMS gmt_shell_functions.sh DESTINATION ${GMT_BINDIR} COMPONENT Runtime) +install (PROGRAMS + gmtmex/gmt.m + gmtmex/gmtmex.c + DESTINATION ${GMT_DATADIR}/tools + COMPONENT Runtime) + if (WIN32) install (PROGRAMS gmtswitch.bat DESTINATION ${GMT_BINDIR} diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt new file mode 100644 index 00000000000..e44f359253e --- /dev/null +++ b/src/gmtmex/CMakeLists.txt @@ -0,0 +1,45 @@ +# +# Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html) +# See LICENSE.TXT file for copying and redistribution conditions. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as published by +# the Free Software Foundation; version 3 or any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# Contact info: www.generic-mapping-tools.org +#------------------------------------------------------------------------------- +## +## Settings for GMT MexFunction shared library +## +# Note: Only activated if GMT_BUILD_GMTMEX is set in ConfigUserAdvanced.cmake + +if (GMT_BUILD_GMTMEX) + # Add the flag to select Matlab vs Octave + add_definitions(-DGMT_MATLAB) + # Add the mexfunction as a shared library + add_library (GMTmexfunction SHARED gmtmex.c) + # Set the Matlab include file directory + target_include_directories (GMTmexfunction PUBLIC ${GMT_SOURCE_DIR}/src ${GMT_BINARY_DIR}/src ${Matlab_INCLUDE_DIRS}) + # Link with the Matlab and mex libraries and gmt library + target_link_libraries (GMTmexfunction gmtlib ${Matlab_MX_LIBRARY} ${Matlab_MEX_LIBRARY}) + # Set output properties of shared library + set_target_properties (GMTmexfunction + PROPERTIES + OUTPUT_NAME gmtmex + RUNTIME_OUTPUT_NAME gmtmex + SUFFIX ".${Matlab_MEX_EXTENSION}" + PREFIX "") + # Install gmt.m in the bin dir + install (PROGRAMS gmt.m + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) + # Install the gmtmex function in the bin dir instead of lib dir + install (PROGRAMS "${GMT_BINARY_DIR}/src/gmtmex/gmtmex.${Matlab_MEX_EXTENSION}" + DESTINATION ${GMT_BINDIR} + COMPONENT Runtime) +endif (GMT_BUILD_GMTMEX) diff --git a/src/gmtmex/README.gmtmex b/src/gmtmex/README.gmtmex new file mode 100644 index 00000000000..839932f09ed --- /dev/null +++ b/src/gmtmex/README.gmtmex @@ -0,0 +1,9 @@ +# +Distributed under the GNU Lesser Public License; see file +LICENSE.TXT in main GMT directory. + +GMT 5 Release introduced a full set of Matlab interface functions +for all GMT modules. GMT 6 continued this work, and GMT 6.1 made +some internal changes, necessitating GMTMEX 2.0.0 + +See documentation for usage. diff --git a/share/tools/gmt.m b/src/gmtmex/gmt.m similarity index 100% rename from share/tools/gmt.m rename to src/gmtmex/gmt.m diff --git a/share/tools/gmtmex.c b/src/gmtmex/gmtmex.c similarity index 100% rename from share/tools/gmtmex.c rename to src/gmtmex/gmtmex.c From 168b4231017a8af32d8e12f9deb433729aec0e57 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 10:45:32 -1000 Subject: [PATCH 37/45] Update CMakeLists.txt --- share/tools/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/share/tools/CMakeLists.txt b/share/tools/CMakeLists.txt index d25de9fa991..d6d8e6fcf93 100644 --- a/share/tools/CMakeLists.txt +++ b/share/tools/CMakeLists.txt @@ -27,8 +27,6 @@ install (PROGRAMS gmt_prepmex.sh gmt_uninstall.sh gmt_mexbuild.sh - gmtmex.c - gmt.m ncdeflate ${CMAKE_CURRENT_BINARY_DIR}/gmt5syntax DESTINATION ${GMT_DATADIR}/tools From e2a7345e76ba3db3eb2d00e273142c9959e1d5d6 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 11:20:24 -1000 Subject: [PATCH 38/45] Update CMakeLists.txt --- src/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0996e7b7e2f..c7d6d0e66d8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -570,6 +570,9 @@ endforeach (_gmt_prog) set_target_properties (gmt PROPERTIES OUTPUT_NAME gmt${GMT_INSTALL_NAME_SUFFIX}) if (GMT_BUILD_GMTMEX) + # If your Matlab version is younger than Cmake then you must add that version here + set (MATLAB_ADDITIONAL_VERSIONS + "R2021a=9.10") find_package (Matlab) add_subdirectory (gmtmex) endif (GMT_BUILD_GMTMEX) From ba293befc55dddf9e8c349be38a989e46b00e78b Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 12:05:34 -1000 Subject: [PATCH 39/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index fd207a03c6c..08fbbff2236 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -11,7 +11,7 @@ # 0. Check if already installed if [ -f ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 ]; then printf "gmt_mexbuild.sh: GMT/MEX toolbox already installed on this computer.\n" >&2 - printf "gmt_mexbuild.sh: To reinstall you must first remove ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64.\n" >&2 + printf "gmt_mexbuild.sh: To reinstall you must first remove this file:\n${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64.\n" >&2 exit 1 fi @@ -29,10 +29,10 @@ if ! [ -x "$(command -v xcrun)" ]; then fi printf "\ngmt_mexbuild.sh: Found most recent MATLAB application: %s\n" ${MATLAB_VERSION} >&2 -printf "\ngmt_mexbuild.sh: Will build and place the GMT/MEX toolbox in %s.\n" ${BUNDLE_RESOURCES}/bin >&2 +printf "\ngmt_mexbuild.sh: Will build and place the GMT/MEX toolbox in:\n%s\n" ${BUNDLE_RESOURCES}/bin >&2 # 3. If use does not have write permission, explain they must use sudo if [ ! -w ${BUNDLE_RESOURCES} ]; then - printf "You must have sudo privileges on this computer.\n\nContinue? (y/n) [y]:" >&2 + printf "You must have sudo privileges on this computer to complete this installation.\n\nContinue? (y/n) [y]:" >&2 read answer if [ "X$answer" = "Xn" ]; then exit 0 @@ -56,19 +56,22 @@ while read file; do done < /tmp/gmtmexinstall/lib.lis sh /tmp/gmtmexinstall/mexjob.sh -# 6. Update the gmt executable to use rpath also +# 6. Update the plugin to use rpath +install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/lib/gmt/plugins/supplements.so + +# 7. Update the gmt executable to use rpath also install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/bin/gmt -# 7. Build the gmtmex executable +# 8. Build the gmtmex executable type=$(uname -m) xcrun clang -I${BUNDLE_RESOURCES}/include/gmt -I/Applications/${MATLAB_VERSION}/extern/include -m64 -fPIC -fno-strict-aliasing -std=c99 -DGMT_MATLAB -c ${BUNDLE_RESOURCES}/share/tools/gmtmex.c -o /tmp/gmtmexinstall/gmtmex.o xcrun clang -undefined error -arch ${type} -bundle /tmp/gmtmexinstall/gmtmex.o -L${BUNDLE_RESOURCES}/lib -lgmt -L/Applications/${MATLAB_VERSION}/bin/maci64 -lmx -lmex -o ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 cp -f ${BUNDLE_RESOURCES}/share/tools/gmt.m ${BUNDLE_RESOURCES}/bin -# 8. Update the gmtmex plugin to use rpath also +# 9. Update the gmtmex plugin to use rpath also version=$(gmt --version | awk -Fr '{print $1}') # Skip trailing stuff like rc1 install_name_tool -change @executable_path/../lib/libgmt.${version}.dylib @rpath/libgmt.${version}.dylib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/bin/gmtmex.mexmaci64 -# 9. Clean up and we are done +# 10. Clean up and we are done rm -rf /tmp/gmtmexinstall printf "done\n" >&2 printf "gmt_mexbuild.sh: You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 From 5634b5c87f6f08889120337e9475ffab12e1288a Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 18 May 2021 12:23:24 -1000 Subject: [PATCH 40/45] Update gmt_mexbuild.sh --- share/tools/gmt_mexbuild.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/tools/gmt_mexbuild.sh b/share/tools/gmt_mexbuild.sh index 08fbbff2236..57f7319cb24 100755 --- a/share/tools/gmt_mexbuild.sh +++ b/share/tools/gmt_mexbuild.sh @@ -74,4 +74,4 @@ install_name_tool -add_rpath ${BUNDLE_RESOURCES}/lib ${BUNDLE_RESOURCES}/bin/gmt # 10. Clean up and we are done rm -rf /tmp/gmtmexinstall printf "done\n" >&2 -printf "gmt_mexbuild.sh: You must add this path to your MATLAB path: %s\n" ${BUNDLE_RESOURCES}/bin >&2 +printf "gmt_mexbuild.sh: You must add this path to your MATLAB path:\n%s\n" ${BUNDLE_RESOURCES}/bin >&2 From 48004d99c23028e17dfdb3f4bad75494287fc895 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 15 Jun 2021 12:36:49 -1000 Subject: [PATCH 41/45] Update psternary.c --- src/psternary.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/psternary.c b/src/psternary.c index 3468a8cc30e..ac78762aa41 100644 --- a/src/psternary.c +++ b/src/psternary.c @@ -35,6 +35,10 @@ #define THIS_MODULE_OPTIONS "-:>BJKOPRUVXYbdefghipqstxy" struct PSTERNARY_CTRL { + struct PSTERNARY_Out { /* -> */ + bool active; + char *file; + } Out; struct PSTERNARY_A { /* -A[-][labelinfo] NOT IMPLEMENTED YET */ bool active; char *string; /* Since we will simply pass this on to pscontour */ @@ -85,6 +89,7 @@ static void *New_Ctrl (struct GMT_CTRL *GMT) { /* Allocate and initialize a new static void Free_Ctrl (struct GMT_CTRL *GMT, struct PSTERNARY_CTRL *C) { /* Deallocate control structure */ if (!C) return; + gmt_M_str_free (C->Out.file); gmt_M_str_free (C->A.string); gmt_M_str_free (C->C.string); gmt_M_str_free (C->G.string); @@ -142,7 +147,7 @@ static int parse (struct GMT_CTRL *GMT, struct PSTERNARY_CTRL *Ctrl, struct GMT_ * returned when registering these sources/destinations with the API. */ - unsigned int n_errors = 0, n_files = 0; + unsigned int n_errors = 0, ni_files = 0, no_files = 0; struct GMT_OPTION *opt = NULL; for (opt = options; opt; opt = opt->next) { /* Process all the options given */ @@ -151,7 +156,13 @@ static int parse (struct GMT_CTRL *GMT, struct PSTERNARY_CTRL *Ctrl, struct GMT_ case '<': /* Input files */ if (GMT_Get_FilePath (GMT->parent, GMT_IS_DATASET, GMT_IN, GMT_FILE_REMOTE, &(opt->arg))) n_errors++;; - n_files++; + ni_files++; + break; + case '>': /* Got named output file */ + if (no_files++ > 0) { n_errors++; continue; } + Ctrl->Out.active = true; + if (opt->arg[0]) Ctrl->Out.file = strdup (opt->arg); + if (GMT_Get_FilePath (GMT->parent, GMT_IS_DATASET, GMT_OUT, GMT_FILE_LOCAL, &(Ctrl->Out.file))) n_errors++; break; /* Processes program-specific parameters */ @@ -410,7 +421,7 @@ EXTERN_MSC int GMT_psternary (void *V_API, int mode, void *args) { gmt_set_dataset_minmax (GMT, D); /* Update column stats */ if (Ctrl->M.active) { /* Just print the converted data and exit */ - if (GMT_Write_Data (API, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_WRITE_NORMAL, NULL, NULL, D) != GMT_NOERROR) { + if (GMT_Write_Data (API, GMT_IS_DATASET, GMT_IS_FILE, GMT_IS_POINT, GMT_WRITE_NORMAL, NULL, Ctrl->Out.file, D) != GMT_NOERROR) { GMT_Report (API, GMT_MSG_ERROR, "Unable to write x,y file to stdout\n"); Return (API->error); } From d70a376ff09560fbcaac7e2d9a44600266dc454f Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 15 Jun 2021 17:10:06 -1000 Subject: [PATCH 42/45] let GMT_Encode_Option do its job for psternary No special code needed in gmt_api.c - the KEYS work. The initial problem was lack of output file name. --- src/gmt_api.c | 5 ----- src/psternary.c | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/gmt_api.c b/src/gmt_api.c index ed71f455baa..8b317b3e391 100644 --- a/src/gmt_api.c +++ b/src/gmt_api.c @@ -12812,11 +12812,6 @@ struct GMT_RESOURCE * GMT_Encode_Options (void *V_API, const char *module_name, else if (!strncmp (module, "grdinfo", 7U)) { type = ((opt = GMT_Find_Option (API, 'Q', *head))) ? 'U' : 'G'; /* Giving -Q means we are reading 3-D cubes */ } - /* 1u. Check if psternary is plotting or dumping */ - else if (!strncmp (module, "psternary", 9U)) { - /* PostScript output unless -M */ - type = (GMT_Find_Option (API, 'M', *head)) ? 'D' : 'X'; /* -M means dump (x,b,c) data */ - } /* 2a. Get the option key array for this module */ key = gmtapi_process_keys (API, keys, type, *head, n_per_family, &n_keys); /* This is the array of keys for this module, e.g., "?},>DM,C-(" +#define THIS_MODULE_KEYS "X},>DM,C-(" #define THIS_MODULE_NEEDS "Jd" #define THIS_MODULE_OPTIONS "-:>BJKOPRUVXYbdefghipqstxy" From 15aec0689202c6e2c40464b9966e3c3ea24d6938 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Tue, 5 Jul 2022 13:59:45 +0100 Subject: [PATCH 43/45] Update gmtmex.c --- src/gmtmex/gmtmex.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index 50e1c2dd421..30678d21901 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -63,6 +63,8 @@ #include #include +#define gmt_M_unused(x) (void)(x) + #ifdef GMT_OCTOCT # include #else @@ -150,6 +152,12 @@ typedef int mwSize; # define MEXU_IJK(M,layer,row,col) ((layer)*M->header->nm + (col)*M->header->n_rows + M->header->n_rows - (row) - 1) #endif +/* These 4 functions are used by gmtmex.c: */ +EXTERN_MSC char GMTMEX_objecttype (const mxArray *ptr); +EXTERN_MSC int GMTMEX_print_func (FILE *fp, const char *message); +EXTERN_MSC void GMTMEX_Set_Object (void *API, struct GMT_RESOURCE *X, const mxArray *ptr); +EXTERN_MSC void * GMTMEX_Get_Object (void *API, struct GMT_RESOURCE *X); + /* Definitions of MEX structures used to hold GMT objects. * DO NOT MODIFY THE ORDER OF THE FIELDNAMES */ @@ -280,7 +288,7 @@ static int gmtmex_print_func (FILE *fp, const char *message) { * API->print_func. Purpose of this is to allow MATLAB (which cannot use * printf) to reset API->print_func to this function via GMT_Create_Session. * This allows GMT's errors and warnings to appear in MATLAB console. */ - + gmt_M_unused (fp); mexPrintf (message); return 0; } @@ -472,9 +480,9 @@ static void *gmtmex_get_cube (void *API, struct GMT_CUBE *U) { y = mxGetData (mxptr[2]); z = mxGetData (mxptr[3]); memcpy (z, U->z, U->header->n_bands * sizeof (double)); - memcpy (x, U->x, U->header->n_columns * sizeof (double)); + memcpy (x, U_x, U->header->n_columns * sizeof (double)); for (k = 0; k < U->header->n_rows; k++) - y[U->header->n_rows-1-k] = U->y[k]; /* Must reverse the y-array */ + y[U->header->n_rows-1-k] = U_y[k]; /* Must reverse the y-array */ for (k = 0; k < N_MEX_FIELDNAMES_CUBE; k++) mxSetField (U_struct, 0, gmtmex_fieldname_cube[k], mxptr[k]); return (U_struct); @@ -496,6 +504,7 @@ static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { double *data = NULL; struct GMT_DATASEGMENT *S = NULL; mxArray *D_struct = NULL, *mxheader = NULL, *mxdata = NULL, *mxtext = NULL, *mxstring = NULL; + gmt_M_unused (API); if (D == NULL) { /* No output produced (?) - return a null data set */ D_struct = mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_DATASET, gmtmex_fieldname_dataset); @@ -535,7 +544,7 @@ static void *gmtmex_get_dataset (void *API, struct GMT_DATASET *D) { } if (n_headers) { /* First segment will get any headers, the rest nothing */ mxtext = mxCreateCellMatrix (n_headers, n_headers ? 1 : 0); - for (k = 0; k < n_headers; k++) { + for (k = 0; k < (uint64_t)n_headers; k++) { mxstring = mxCreateString (D->table[0]->header[k]); mxSetCell (mxtext, (int)k, mxstring); } @@ -559,6 +568,7 @@ static void *gmtmex_get_postscript (void *API, struct GMT_POSTSCRIPT *P) { uint64_t k, *length = NULL; unsigned int *mode = NULL; mxArray *P_struct = NULL, *mxptr[N_MEX_FIELDNAMES_PS], *mxstring = NULL; + gmt_M_unused (API); if (P == NULL) /* Safety valve */ mexErrMsgTxt ("gmtmex_get_postscript: programming error, input POSTSCRIPT struct P is NULL or data string is empty\n"); @@ -615,6 +625,7 @@ static void *gmtmex_get_palette (void *API, struct GMT_PALETTE *C) { unsigned int k, j, n_colors, *depth = NULL; double *color = NULL, *cpt = NULL, *alpha = NULL, *minmax = NULL, *range = NULL, *hinge = NULL, *cyclic = NULL, *bfn = NULL; mxArray *C_struct = NULL, *mxptr[N_MEX_FIELDNAMES_CPT], *mxstring = NULL; + gmt_M_unused (API); if (C == NULL) /* Safety valve */ mexErrMsgTxt ("gmtmex_get_palette: programming error, output CPT C is empty\n"); @@ -1883,6 +1894,8 @@ static void *alloc_default_plhs (void *API, struct GMT_RESOURCE *X) { when we do for example (i.e. no lhs): sqrt([4 9]) */ void *ptr = NULL; + gmt_M_unused (API); + switch (X->family) { case GMT_IS_CUBE: ptr = (void *)mxCreateStructMatrix (0, 0, N_MEX_FIELDNAMES_CUBE, gmtmex_fieldname_cube); @@ -2207,6 +2220,6 @@ void GMT_mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] return; } -void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { +EXTERN_MSC void mexFunction (int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { GMT_mexFunction (nlhs, plhs, nrhs, prhs); } \ No newline at end of file From 59c37f9e31c4eea040514e1e5ffd326ef1fa40a4 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Fri, 1 Dec 2023 10:48:09 +0100 Subject: [PATCH 44/45] Update dates and fix typo --- CMakeLists.txt | 2 +- src/gmtmex/CMakeLists.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cf9cf5032de..49ab5af9289 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ if (GMT_BUILD_GMTMEX) set (MATLAB_LIB1 "${Matlab_MEX_LIBRARY}") set (MATLAB_LIB2 "${Matlab_MX_LIBRARY}") set (MATLAB_EXT "${Matlab_MEX_EXTENSION}") -else (GMT_INSTALL_MODULE_LINKS) +else (GMT_BUILD_GMTMEX) set (MATLAB_APP "not used") set (MATLAB_INC "not used") set (MATLAB_LIB1 "not used") diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index e44f359253e..7c68dfb4dbb 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 1991-2021 by the GMT Team (https://www.generic-mapping-tools.org/team.html) +# Copyright (c) 1991-2023 by the GMT Team (https://www.generic-mapping-tools.org/team.html) # See LICENSE.TXT file for copying and redistribution conditions. # # This program is free software; you can redistribute it and/or modify From fb42dc7fa4b249004f2ec76e999b7204584de1d0 Mon Sep 17 00:00:00 2001 From: Paul Wessel Date: Mon, 1 Jan 2024 16:17:32 +0100 Subject: [PATCH 45/45] copyright year update --- src/gmtmex/CMakeLists.txt | 2 +- src/gmtmex/README.gmtmex | 2 +- src/gmtmex/gmt.m | 4 ++-- src/gmtmex/gmtmex.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/gmtmex/CMakeLists.txt b/src/gmtmex/CMakeLists.txt index 7c68dfb4dbb..47704989eea 100644 --- a/src/gmtmex/CMakeLists.txt +++ b/src/gmtmex/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright (c) 1991-2023 by the GMT Team (https://www.generic-mapping-tools.org/team.html) +# Copyright (c) 1991-2024 by the GMT Team (https://www.generic-mapping-tools.org/team.html) # See LICENSE.TXT file for copying and redistribution conditions. # # This program is free software; you can redistribute it and/or modify diff --git a/src/gmtmex/README.gmtmex b/src/gmtmex/README.gmtmex index 839932f09ed..bcaa4d76ed3 100644 --- a/src/gmtmex/README.gmtmex +++ b/src/gmtmex/README.gmtmex @@ -3,7 +3,7 @@ Distributed under the GNU Lesser Public License; see file LICENSE.TXT in main GMT directory. GMT 5 Release introduced a full set of Matlab interface functions -for all GMT modules. GMT 6 continued this work, and GMT 6.1 made +for all GMT modules. GMT 6 continued this work, and GMT 6.5 made some internal changes, necessitating GMTMEX 2.0.0 See documentation for usage. diff --git a/src/gmtmex/gmt.m b/src/gmtmex/gmt.m index bf0bcec436a..1ab138b74a4 100644 --- a/src/gmtmex/gmt.m +++ b/src/gmtmex/gmt.m @@ -2,8 +2,8 @@ % Helper function to call the gmtmex MEX function if (nargin == 0) - fprintf(sprintf('\n\t\tGMT - The Generic Mapping Tools, Version 6.2 API\n')) - fprintf(sprintf('Copyright 1991-2020 The GMT Team (https://www.generic-mapping-tools.org/team.html\n\n')) + fprintf(sprintf('\n\t\tGMT - The Generic Mapping Tools, Version 6.5 API\n')) + fprintf(sprintf('Copyright 1991-2024 The GMT Team (https://www.generic-mapping-tools.org/team.html\n\n')) fprintf(sprintf('Usage:\tTo call a GMT module:\n\t output = gmt (''module_name'', ''options'', numeric_input)\n\n')) fprintf(sprintf(['\tTo create a Grid structure from a 2-D Z array and a 1x9 header vector:\n\t' ... diff --git a/src/gmtmex/gmtmex.c b/src/gmtmex/gmtmex.c index 30678d21901..a1e96a346cc 100644 --- a/src/gmtmex/gmtmex.c +++ b/src/gmtmex/gmtmex.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2021 by P. Wessel and J. Luis + * Copyright (c) 2015-2024 by P. Wessel and J. Luis * See LICENSE.TXT file for copying and redistribution conditions. * * This program is free software; you can redistribute it and/or modify