PostGIS  2.3.7dev-r@@SVN_REVISION@@
shp2pgsql-core.c
Go to the documentation of this file.
1 /**********************************************************************
2  *
3  * PostGIS - Spatial Types for PostgreSQL
4  * http://postgis.net
5  *
6  * Copyright (C) 2008 OpenGeo.org
7  * Copyright (C) 2009 Mark Cave-Ayland <mark.cave-ayland@siriusit.co.uk>
8  *
9  * This is free software; you can redistribute and/or modify it under
10  * the terms of the GNU General Public Licence. See the COPYING file.
11  *
12  * Maintainer: Paul Ramsey <pramsey@opengeo.org>
13  *
14  **********************************************************************/
15 
16 #include "../postgis_config.h"
17 
18 #include "shp2pgsql-core.h"
19 #include "../liblwgeom/liblwgeom.h"
20 #include "../liblwgeom/lwgeom_log.h" /* for LWDEBUG macros */
21 
22 
23 
24 /* Internal ring/point structures */
25 typedef struct struct_point
26 {
27  double x, y, z, m;
28 } Point;
29 
30 typedef struct struct_ring
31 {
32  Point *list; /* list of points */
33  struct struct_ring *next;
34  int n; /* number of points in list */
35  unsigned int linked; /* number of "next" rings */
36 } Ring;
37 
38 
39 /*
40  * Internal functions
41  */
42 
43 #define UTF8_GOOD_RESULT 0
44 #define UTF8_BAD_RESULT 1
45 #define UTF8_NO_RESULT 2
46 
47 char *escape_copy_string(char *str);
48 char *escape_insert_string(char *str);
49 
50 int GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry, int force_multi);
51 int GenerateLineStringGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry);
52 int PIP(Point P, Point *V, int n);
53 int FindPolygons(SHPObject *obj, Ring ***Out);
54 void ReleasePolygons(Ring **polys, int npolys);
55 int GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry);
56 
57 
58 /* Return allocated string containing UTF8 string converted from encoding fromcode */
59 static int
60 utf8(const char *fromcode, char *inputbuf, char **outputbuf)
61 {
62  iconv_t cd;
63  char *outputptr;
64  size_t outbytesleft;
65  size_t inbytesleft;
66 
67  inbytesleft = strlen(inputbuf);
68 
69  cd = iconv_open("UTF-8", fromcode);
70  if ( cd == ((iconv_t)(-1)) )
71  return UTF8_NO_RESULT;
72 
73  outbytesleft = inbytesleft * 3 + 1; /* UTF8 string can be 3 times larger */
74  /* then local string */
75  *outputbuf = (char *)malloc(outbytesleft);
76  if (!*outputbuf)
77  return UTF8_NO_RESULT;
78 
79  memset(*outputbuf, 0, outbytesleft);
80  outputptr = *outputbuf;
81 
82  /* Does this string convert cleanly? */
83  if ( iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft) == -1 )
84  {
85 #ifdef HAVE_ICONVCTL
86  int on = 1;
87  /* No. Try to convert it while transliterating. */
88  iconvctl(cd, ICONV_SET_TRANSLITERATE, &on);
89  if ( iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft) == -1 )
90  {
91  /* No. Try to convert it while discarding errors. */
92  iconvctl(cd, ICONV_SET_DISCARD_ILSEQ, &on);
93  if ( iconv(cd, &inputbuf, &inbytesleft, &outputptr, &outbytesleft) == -1 )
94  {
95  /* Still no. Throw away the buffer and return. */
96  free(*outputbuf);
97  iconv_close(cd);
98  return UTF8_NO_RESULT;
99  }
100  }
101  iconv_close(cd);
102  return UTF8_BAD_RESULT;
103 #else
104  free(*outputbuf);
105  iconv_close(cd);
106  return UTF8_NO_RESULT;
107 #endif
108  }
109  /* Return a good result, converted string is in buffer. */
110  iconv_close(cd);
111  return UTF8_GOOD_RESULT;
112 }
113 
118 char *
120 {
121  /*
122  * Escape the following characters by adding a preceding backslash
123  * tab, backslash, cr, lf
124  *
125  * 1. find # of escaped characters
126  * 2. make new string
127  *
128  */
129 
130  char *result;
131  char *ptr, *optr;
132  int toescape = 0;
133  size_t size;
134 
135  ptr = str;
136 
137  /* Count how many characters we need to escape so we know the size of the string we need to return */
138  while (*ptr)
139  {
140  if (*ptr == '\t' || *ptr == '\\' || *ptr == '\n' || *ptr == '\r')
141  toescape++;
142 
143  ptr++;
144  }
145 
146  /* If we don't have to escape anything, simply return the input pointer */
147  if (toescape == 0)
148  return str;
149 
150  size = ptr - str + toescape + 1;
151  result = calloc(1, size);
152  optr = result;
153  ptr = str;
154 
155  while (*ptr)
156  {
157  if ( *ptr == '\t' || *ptr == '\\' || *ptr == '\n' || *ptr == '\r' )
158  *optr++ = '\\';
159 
160  *optr++ = *ptr++;
161  }
162 
163  *optr = '\0';
164 
165  return result;
166 }
167 
168 
173 char *
175 {
176  /*
177  * Escape single quotes by adding a preceding single quote
178  *
179  * 1. find # of characters
180  * 2. make new string
181  */
182 
183  char *result;
184  char *ptr, *optr;
185  int toescape = 0;
186  size_t size;
187 
188  ptr = str;
189 
190  /* Count how many characters we need to escape so we know the size of the string we need to return */
191  while (*ptr)
192  {
193  if (*ptr == '\'')
194  toescape++;
195 
196  ptr++;
197  }
198 
199  /* If we don't have to escape anything, simply return the input pointer */
200  if (toescape == 0)
201  return str;
202 
203  size = ptr - str + toescape + 1;
204  result = calloc(1, size);
205  optr = result;
206  ptr = str;
207 
208  while (*ptr)
209  {
210  if (*ptr == '\'')
211  *optr++='\'';
212 
213  *optr++ = *ptr++;
214  }
215 
216  *optr='\0';
217 
218  return result;
219 }
220 
221 
226 int
227 GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry, int force_multi)
228 {
229  LWGEOM **lwmultipoints;
230  LWGEOM *lwgeom = NULL;
231 
232  POINT4D point4d;
233 
234  int dims = 0;
235  int u;
236 
237  char *mem;
238  size_t mem_length;
239 
240  FLAGS_SET_Z(dims, state->has_z);
241  FLAGS_SET_M(dims, state->has_m);
242 
243  /* Allocate memory for our array of LWPOINTs and our dynptarrays */
244  lwmultipoints = malloc(sizeof(LWPOINT *) * obj->nVertices);
245 
246  /* We need an array of pointers to each of our sub-geometries */
247  for (u = 0; u < obj->nVertices; u++)
248  {
249  /* Create a ptarray containing a single point */
250  POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, 1);
251 
252  /* Generate the point */
253  point4d.x = obj->padfX[u];
254  point4d.y = obj->padfY[u];
255 
256  if (state->has_z)
257  point4d.z = obj->padfZ[u];
258  if (state->has_m)
259  point4d.m = obj->padfM[u];
260 
261  /* Add in the point! */
262  ptarray_append_point(pa, &point4d, LW_TRUE);
263 
264  /* Generate the LWPOINT */
265  lwmultipoints[u] = lwpoint_as_lwgeom(lwpoint_construct(state->from_srid, NULL, pa));
266  }
267 
268  /* If we have more than 1 vertex then we are working on a MULTIPOINT and so generate a MULTIPOINT
269  rather than a POINT */
270  if ((obj->nVertices > 1) || force_multi)
271  {
272  lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTIPOINTTYPE, state->from_srid, NULL, obj->nVertices, lwmultipoints));
273  }
274  else
275  {
276  lwgeom = lwmultipoints[0];
277  lwfree(lwmultipoints);
278  }
279 
280  if (state->config->use_wkt)
281  {
282  mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length);
283  }
284  else
285  {
286  mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length);
287  }
288 
289  if ( !mem )
290  {
291  snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry");
292  return SHPLOADERERR;
293  }
294 
295  /* Free all of the allocated items */
296  lwgeom_free(lwgeom);
297 
298  /* Return the string - everything ok */
299  *geometry = mem;
300 
301  return SHPLOADEROK;
302 }
303 
304 
308 int
310 {
311 
312  LWGEOM **lwmultilinestrings;
313  LWGEOM *lwgeom = NULL;
314  POINT4D point4d;
315  int dims = 0;
316  int u, v, start_vertex, end_vertex;
317  char *mem;
318  size_t mem_length;
319 
320 
321  FLAGS_SET_Z(dims, state->has_z);
322  FLAGS_SET_M(dims, state->has_m);
323 
324  if (state->config->simple_geometries == 1 && obj->nParts > 1)
325  {
326  snprintf(state->message, SHPLOADERMSGLEN, _("We have a Multilinestring with %d parts, can't use -S switch!"), obj->nParts);
327 
328  return SHPLOADERERR;
329  }
330 
331  /* Allocate memory for our array of LWLINEs and our dynptarrays */
332  lwmultilinestrings = malloc(sizeof(LWPOINT *) * obj->nParts);
333 
334  /* We need an array of pointers to each of our sub-geometries */
335  for (u = 0; u < obj->nParts; u++)
336  {
337  /* Create a ptarray containing the line points */
338  POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, obj->nParts);
339 
340  /* Set the start/end vertices depending upon whether this is
341  a MULTILINESTRING or not */
342  if ( u == obj->nParts-1 )
343  end_vertex = obj->nVertices;
344  else
345  end_vertex = obj->panPartStart[u + 1];
346 
347  start_vertex = obj->panPartStart[u];
348 
349  for (v = start_vertex; v < end_vertex; v++)
350  {
351  /* Generate the point */
352  point4d.x = obj->padfX[v];
353  point4d.y = obj->padfY[v];
354 
355  if (state->has_z)
356  point4d.z = obj->padfZ[v];
357  if (state->has_m)
358  point4d.m = obj->padfM[v];
359 
360  ptarray_append_point(pa, &point4d, LW_FALSE);
361  }
362 
363  /* Generate the LWLINE */
364  lwmultilinestrings[u] = lwline_as_lwgeom(lwline_construct(state->from_srid, NULL, pa));
365  }
366 
367  /* If using MULTILINESTRINGs then generate the serialized collection, otherwise just a single LINESTRING */
368  if (state->config->simple_geometries == 0)
369  {
370  lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTILINETYPE, state->from_srid, NULL, obj->nParts, lwmultilinestrings));
371  }
372  else
373  {
374  lwgeom = lwmultilinestrings[0];
375  lwfree(lwmultilinestrings);
376  }
377 
378  if (!state->config->use_wkt)
379  mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length);
380  else
381  mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length);
382 
383  if ( !mem )
384  {
385  snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry");
386  return SHPLOADERERR;
387  }
388 
389  /* Free all of the allocated items */
390  lwgeom_free(lwgeom);
391 
392  /* Return the string - everything ok */
393  *geometry = mem;
394 
395  return SHPLOADEROK;
396 }
397 
398 
405 int
406 PIP(Point P, Point *V, int n)
407 {
408  int cn = 0; /* the crossing number counter */
409  int i;
410 
411  /* loop through all edges of the polygon */
412  for (i = 0; i < n-1; i++) /* edge from V[i] to V[i+1] */
413  {
414  if (((V[i].y <= P.y) && (V[i + 1].y > P.y)) /* an upward crossing */
415  || ((V[i].y > P.y) && (V[i + 1].y <= P.y))) /* a downward crossing */
416  {
417  double vt = (float)(P.y - V[i].y) / (V[i + 1].y - V[i].y);
418  if (P.x < V[i].x + vt * (V[i + 1].x - V[i].x)) /* P.x < intersect */
419  ++cn; /* a valid crossing of y=P.y right of P.x */
420  }
421  }
422 
423  return (cn&1); /* 0 if even (out), and 1 if odd (in) */
424 }
425 
426 
427 int
429 {
430  Ring **Outer; /* Pointers to Outer rings */
431  int out_index=0; /* Count of Outer rings */
432  Ring **Inner; /* Pointers to Inner rings */
433  int in_index=0; /* Count of Inner rings */
434  int pi; /* part index */
435 
436 #if POSTGIS_DEBUG_LEVEL > 0
437  static int call = -1;
438  call++;
439 #endif
440 
441  LWDEBUGF(4, "FindPolygons[%d]: allocated space for %d rings\n", call, obj->nParts);
442 
443  /* Allocate initial memory */
444  Outer = (Ring **)malloc(sizeof(Ring *) * obj->nParts);
445  Inner = (Ring **)malloc(sizeof(Ring *) * obj->nParts);
446 
447  /* Iterate over rings dividing in Outers and Inners */
448  for (pi=0; pi < obj->nParts; pi++)
449  {
450  int vi; /* vertex index */
451  int vs; /* start index */
452  int ve; /* end index */
453  int nv; /* number of vertex */
454  double area = 0.0;
455  Ring *ring;
456 
457  /* Set start and end vertexes */
458  if (pi == obj->nParts - 1)
459  ve = obj->nVertices;
460  else
461  ve = obj->panPartStart[pi + 1];
462 
463  vs = obj->panPartStart[pi];
464 
465  /* Compute number of vertexes */
466  nv = ve - vs;
467 
468  /* Allocate memory for a ring */
469  ring = (Ring *)malloc(sizeof(Ring));
470  ring->list = (Point *)malloc(sizeof(Point) * nv);
471  ring->n = nv;
472  ring->next = NULL;
473  ring->linked = 0;
474 
475  /* Iterate over ring vertexes */
476  for (vi = vs; vi < ve; vi++)
477  {
478  int vn = vi+1; /* next vertex for area */
479  if (vn == ve)
480  vn = vs;
481 
482  ring->list[vi - vs].x = obj->padfX[vi];
483  ring->list[vi - vs].y = obj->padfY[vi];
484  ring->list[vi - vs].z = obj->padfZ[vi];
485  ring->list[vi - vs].m = obj->padfM[vi];
486 
487  area += (obj->padfX[vi] * obj->padfY[vn]) -
488  (obj->padfY[vi] * obj->padfX[vn]);
489  }
490 
491  /* Close the ring with first vertex */
492  /*ring->list[vi].x = obj->padfX[vs]; */
493  /*ring->list[vi].y = obj->padfY[vs]; */
494  /*ring->list[vi].z = obj->padfZ[vs]; */
495  /*ring->list[vi].m = obj->padfM[vs]; */
496 
497  /* Clockwise (or single-part). It's an Outer Ring ! */
498  if (area < 0.0 || obj->nParts == 1)
499  {
500  Outer[out_index] = ring;
501  out_index++;
502  }
503  else
504  {
505  /* Counterclockwise. It's an Inner Ring ! */
506  Inner[in_index] = ring;
507  in_index++;
508  }
509  }
510 
511  LWDEBUGF(4, "FindPolygons[%d]: found %d Outer, %d Inners\n", call, out_index, in_index);
512 
513  /* Put the inner rings into the list of the outer rings */
514  /* of which they are within */
515  for (pi = 0; pi < in_index; pi++)
516  {
517  Point pt, pt2;
518  int i;
519  Ring *inner = Inner[pi], *outer = NULL;
520 
521  pt.x = inner->list[0].x;
522  pt.y = inner->list[0].y;
523 
524  pt2.x = inner->list[1].x;
525  pt2.y = inner->list[1].y;
526 
527  /*
528  * If we assume that the case of the "big polygon w/o hole
529  * containing little polygon w/ hold" is ordered so that the
530  * big polygon comes first, then checking the list in reverse
531  * will assign the little polygon's hole to the little polygon
532  * w/o a lot of extra fancy containment logic here
533  */
534  for (i = out_index - 1; i >= 0; i--)
535  {
536  int in;
537 
538  in = PIP(pt, Outer[i]->list, Outer[i]->n);
539  if ( in || PIP(pt2, Outer[i]->list, Outer[i]->n) )
540  {
541  outer = Outer[i];
542  break;
543  }
544  }
545 
546  if (outer)
547  {
548  outer->linked++;
549  while (outer->next)
550  outer = outer->next;
551 
552  outer->next = inner;
553  }
554  else
555  {
556  /* The ring wasn't within any outer rings, */
557  /* assume it is a new outer ring. */
558  LWDEBUGF(4, "FindPolygons[%d]: hole %d is orphan\n", call, pi);
559 
560  Outer[out_index] = inner;
561  out_index++;
562  }
563  }
564 
565  *Out = Outer;
566  /*
567  * Only free the containing Inner array, not the ring elements, because
568  * the rings are now owned by the linked lists in the Outer array elements.
569  */
570  free(Inner);
571 
572  return out_index;
573 }
574 
575 
576 void
577 ReleasePolygons(Ring **polys, int npolys)
578 {
579  int pi;
580 
581  /* Release all memory */
582  for (pi = 0; pi < npolys; pi++)
583  {
584  Ring *Poly, *temp;
585  Poly = polys[pi];
586  while (Poly != NULL)
587  {
588  temp = Poly;
589  Poly = Poly->next;
590  free(temp->list);
591  free(temp);
592  }
593  }
594 
595  free(polys);
596 }
597 
598 
606 int
607 GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
608 {
609  Ring **Outer;
610  int polygon_total, ring_total;
611  int pi, vi; /* part index and vertex index */
612 
613  LWGEOM **lwpolygons;
614  LWGEOM *lwgeom;
615 
616  POINT4D point4d;
617 
618  int dims = 0;
619 
620  char *mem;
621  size_t mem_length;
622 
623  FLAGS_SET_Z(dims, state->has_z);
624  FLAGS_SET_M(dims, state->has_m);
625 
626  polygon_total = FindPolygons(obj, &Outer);
627 
628  if (state->config->simple_geometries == 1 && polygon_total != 1) /* We write Non-MULTI geometries, but have several parts: */
629  {
630  snprintf(state->message, SHPLOADERMSGLEN, _("We have a Multipolygon with %d parts, can't use -S switch!"), polygon_total);
631 
632  return SHPLOADERERR;
633  }
634 
635  /* Allocate memory for our array of LWPOLYs */
636  lwpolygons = malloc(sizeof(LWPOLY *) * polygon_total);
637 
638  /* Cycle through each individual polygon */
639  for (pi = 0; pi < polygon_total; pi++)
640  {
641  LWPOLY *lwpoly = lwpoly_construct_empty(state->from_srid, state->has_z, state->has_m);
642 
643  Ring *polyring;
644  int ring_index = 0;
645 
646  /* Firstly count through the total number of rings in this polygon */
647  ring_total = 0;
648  polyring = Outer[pi];
649  while (polyring)
650  {
651  ring_total++;
652  polyring = polyring->next;
653  }
654 
655  /* Cycle through each ring within the polygon, starting with the outer */
656  polyring = Outer[pi];
657 
658  while (polyring)
659  {
660  /* Create a POINTARRAY containing the points making up the ring */
661  POINTARRAY *pa = ptarray_construct_empty(state->has_z, state->has_m, polyring->n);
662 
663  for (vi = 0; vi < polyring->n; vi++)
664  {
665  /* Build up a point array of all the points in this ring */
666  point4d.x = polyring->list[vi].x;
667  point4d.y = polyring->list[vi].y;
668 
669  if (state->has_z)
670  point4d.z = polyring->list[vi].z;
671  if (state->has_m)
672  point4d.m = polyring->list[vi].m;
673 
674  ptarray_append_point(pa, &point4d, LW_TRUE);
675  }
676 
677  /* Copy the POINTARRAY pointer so we can use the LWPOLY constructor */
678  lwpoly_add_ring(lwpoly, pa);
679 
680  polyring = polyring->next;
681  ring_index++;
682  }
683 
684  /* Generate the LWGEOM */
685  lwpolygons[pi] = lwpoly_as_lwgeom(lwpoly);
686  }
687 
688  /* If using MULTIPOLYGONS then generate the serialized collection, otherwise just a single POLYGON */
689  if (state->config->simple_geometries == 0)
690  {
691  lwgeom = lwcollection_as_lwgeom(lwcollection_construct(MULTIPOLYGONTYPE, state->from_srid, NULL, polygon_total, lwpolygons));
692  }
693  else
694  {
695  lwgeom = lwpolygons[0];
696  lwfree(lwpolygons);
697  }
698 
699  if (!state->config->use_wkt)
700  mem = lwgeom_to_hexwkb(lwgeom, WKB_EXTENDED, &mem_length);
701  else
702  mem = lwgeom_to_wkt(lwgeom, WKT_EXTENDED, WKT_PRECISION, &mem_length);
703 
704  if ( !mem )
705  {
706  /* Free the linked list of rings */
707  ReleasePolygons(Outer, polygon_total);
708 
709  snprintf(state->message, SHPLOADERMSGLEN, "unable to write geometry");
710  return SHPLOADERERR;
711  }
712 
713  /* Free all of the allocated items */
714  lwgeom_free(lwgeom);
715 
716  /* Free the linked list of rings */
717  ReleasePolygons(Outer, polygon_total);
718 
719  /* Return the string - everything ok */
720  *geometry = mem;
721 
722  return SHPLOADEROK;
723 }
724 
725 
726 /*
727  * External functions (defined in shp2pgsql-core.h)
728  */
729 
730 
731 /* Convert the string to lower case */
732 void
733 strtolower(char *s)
734 {
735  int j;
736 
737  for (j = 0; j < strlen(s); j++)
738  s[j] = tolower(s[j]);
739 }
740 
741 
742 /* Default configuration settings */
743 void
745 {
746  config->opt = 'c';
747  config->table = NULL;
748  config->schema = NULL;
749  config->geo_col = NULL;
750  config->shp_file = NULL;
751  config->dump_format = 0;
752  config->simple_geometries = 0;
753  config->geography = 0;
754  config->quoteidentifiers = 0;
755  config->forceint4 = 0;
756  config->createindex = 0;
757  config->readshape = 1;
759  config->encoding = strdup(ENCODING_DEFAULT);
761  config->sr_id = SRID_UNKNOWN;
762  config->shp_sr_id = SRID_UNKNOWN;
763  config->use_wkt = 0;
764  config->tablespace = NULL;
765  config->idxtablespace = NULL;
766  config->usetransaction = 1;
767  config->column_map_filename = NULL;
768 }
769 
770 /* Create a new shapefile state object */
773 {
774  SHPLOADERSTATE *state;
775 
776  /* Create a new state object and assign the config to it */
777  state = malloc(sizeof(SHPLOADERSTATE));
778  state->config = config;
779 
780  /* Set any state defaults */
781  state->hSHPHandle = NULL;
782  state->hDBFHandle = NULL;
783  state->has_z = 0;
784  state->has_m = 0;
785  state->num_fields = 0;
786  state->types = NULL;
787  state->widths = NULL;
788  state->precisions = NULL;
789  state->col_names = NULL;
790  state->field_names = NULL;
791  state->pgfieldtypes = NULL;
792 
793  state->from_srid = config->shp_sr_id;
794  state->to_srid = config->sr_id;
795 
796  /* If only one has a valid SRID, use it for both. */
797  if (state->to_srid == SRID_UNKNOWN)
798  {
799  if (config->geography)
800  {
801  state->to_srid = 4326;
802  }
803  else
804  {
805  state->to_srid = state->from_srid;
806  }
807  }
808 
809  if (state->from_srid == SRID_UNKNOWN)
810  {
811  state->from_srid = state->to_srid;
812  }
813 
814  /* If the geo col name is not set, use one of the defaults. */
815  state->geo_col = config->geo_col;
816 
817  if (!state->geo_col)
818  {
819  state->geo_col = strdup(config->geography ? GEOGRAPHY_DEFAULT : GEOMETRY_DEFAULT);
820  }
821 
822  colmap_init(&state->column_map);
823 
824  return state;
825 }
826 
827 
828 /* Open the shapefile and extract the relevant field information */
829 int
831 {
832  SHPObject *obj = NULL;
833  int j, z;
834  int ret = SHPLOADEROK;
835 
836  int field_precision, field_width;
837  char name[MAXFIELDNAMELEN];
838  char name2[MAXFIELDNAMELEN];
839  DBFFieldType type = -1;
840  char *utf8str;
841 
842  /* If we are reading the entire shapefile, open it */
843  if (state->config->readshape == 1)
844  {
845  state->hSHPHandle = SHPOpen(state->config->shp_file, "rb");
846 
847  if (state->hSHPHandle == NULL)
848  {
849  snprintf(state->message, SHPLOADERMSGLEN, _("%s: shape (.shp) or index files (.shx) can not be opened, will just import attribute data."), state->config->shp_file);
850  state->config->readshape = 0;
851 
852  ret = SHPLOADERWARN;
853  }
854  }
855 
856  /* Open the DBF (attributes) file */
857  state->hDBFHandle = DBFOpen(state->config->shp_file, "rb");
858  if ((state->hSHPHandle == NULL && state->config->readshape == 1) || state->hDBFHandle == NULL)
859  {
860  snprintf(state->message, SHPLOADERMSGLEN, _("%s: dbf file (.dbf) can not be opened."), state->config->shp_file);
861 
862  return SHPLOADERERR;
863  }
864 
865 
866  /* Open the column map if one was specified */
867  if (state->config->column_map_filename)
868  {
869  ret = colmap_read(state->config->column_map_filename,
870  &state->column_map, state->message, SHPLOADERMSGLEN);
871  if (!ret) return SHPLOADERERR;
872  }
873 
874  /* User hasn't altered the default encoding preference... */
875  if ( strcmp(state->config->encoding, ENCODING_DEFAULT) == 0 )
876  {
877  /* But the file has a code page entry... */
878  if ( state->hDBFHandle->pszCodePage )
879  {
880  /* And we figured out what iconv encoding it maps to, so use it! */
881  char *newencoding = NULL;
882  if ( (newencoding = codepage2encoding(state->hDBFHandle->pszCodePage)) )
883  {
884  lwfree(state->config->encoding);
885  state->config->encoding = newencoding;
886  }
887  }
888  }
889 
890  /* If reading the whole shapefile (not just attributes)... */
891  if (state->config->readshape == 1)
892  {
893  SHPGetInfo(state->hSHPHandle, &state->num_entities, &state->shpfiletype, NULL, NULL);
894 
895  /* If null_policy is set to abort, check for NULLs */
896  if (state->config->null_policy == POLICY_NULL_ABORT)
897  {
898  /* If we abort on null items, scan the entire file for NULLs */
899  for (j = 0; j < state->num_entities; j++)
900  {
901  obj = SHPReadObject(state->hSHPHandle, j);
902 
903  if (!obj)
904  {
905  snprintf(state->message, SHPLOADERMSGLEN, _("Error reading shape object %d"), j);
906  return SHPLOADERERR;
907  }
908 
909  if (obj->nVertices == 0)
910  {
911  snprintf(state->message, SHPLOADERMSGLEN, _("Empty geometries found, aborted.)"));
912  return SHPLOADERERR;
913  }
914 
915  SHPDestroyObject(obj);
916  }
917  }
918 
919  /* Check the shapefile type */
920  int geomtype = 0;
921  switch (state->shpfiletype)
922  {
923  case SHPT_POINT:
924  /* Point */
925  state->pgtype = "POINT";
926  geomtype = POINTTYPE;
927  state->pgdims = 2;
928  break;
929 
930  case SHPT_ARC:
931  /* PolyLine */
932  state->pgtype = "MULTILINESTRING";
933  geomtype = MULTILINETYPE ;
934  state->pgdims = 2;
935  break;
936 
937  case SHPT_POLYGON:
938  /* Polygon */
939  state->pgtype = "MULTIPOLYGON";
940  geomtype = MULTIPOLYGONTYPE;
941  state->pgdims = 2;
942  break;
943 
944  case SHPT_MULTIPOINT:
945  /* MultiPoint */
946  state->pgtype = "MULTIPOINT";
947  geomtype = MULTIPOINTTYPE;
948  state->pgdims = 2;
949  break;
950 
951  case SHPT_POINTM:
952  /* PointM */
953  geomtype = POINTTYPE;
954  state->has_m = 1;
955  state->pgtype = "POINTM";
956  state->pgdims = 3;
957  break;
958 
959  case SHPT_ARCM:
960  /* PolyLineM */
961  geomtype = MULTILINETYPE;
962  state->has_m = 1;
963  state->pgtype = "MULTILINESTRINGM";
964  state->pgdims = 3;
965  break;
966 
967  case SHPT_POLYGONM:
968  /* PolygonM */
969  geomtype = MULTIPOLYGONTYPE;
970  state->has_m = 1;
971  state->pgtype = "MULTIPOLYGONM";
972  state->pgdims = 3;
973  break;
974 
975  case SHPT_MULTIPOINTM:
976  /* MultiPointM */
977  geomtype = MULTIPOINTTYPE;
978  state->has_m = 1;
979  state->pgtype = "MULTIPOINTM";
980  state->pgdims = 3;
981  break;
982 
983  case SHPT_POINTZ:
984  /* PointZ */
985  geomtype = POINTTYPE;
986  state->has_m = 1;
987  state->has_z = 1;
988  state->pgtype = "POINT";
989  state->pgdims = 4;
990  break;
991 
992  case SHPT_ARCZ:
993  /* PolyLineZ */
994  state->pgtype = "MULTILINESTRING";
995  geomtype = MULTILINETYPE;
996  state->has_z = 1;
997  state->has_m = 1;
998  state->pgdims = 4;
999  break;
1000 
1001  case SHPT_POLYGONZ:
1002  /* MultiPolygonZ */
1003  state->pgtype = "MULTIPOLYGON";
1004  geomtype = MULTIPOLYGONTYPE;
1005  state->has_z = 1;
1006  state->has_m = 1;
1007  state->pgdims = 4;
1008  break;
1009 
1010  case SHPT_MULTIPOINTZ:
1011  /* MultiPointZ */
1012  state->pgtype = "MULTIPOINT";
1013  geomtype = MULTIPOINTTYPE;
1014  state->has_z = 1;
1015  state->has_m = 1;
1016  state->pgdims = 4;
1017  break;
1018 
1019  default:
1020  state->pgtype = "GEOMETRY";
1021  geomtype = COLLECTIONTYPE;
1022  state->has_z = 1;
1023  state->has_m = 1;
1024  state->pgdims = 4;
1025 
1026  snprintf(state->message, SHPLOADERMSGLEN, _("Unknown geometry type: %d\n"), state->shpfiletype);
1027  return SHPLOADERERR;
1028 
1029  break;
1030  }
1031 
1032  /* Force Z/M-handling if configured to do so */
1033  switch(state->config->force_output)
1034  {
1035  case FORCE_OUTPUT_2D:
1036  state->has_z = 0;
1037  state->has_m = 0;
1038  state->pgdims = 2;
1039  break;
1040 
1041  case FORCE_OUTPUT_3DZ:
1042  state->has_z = 1;
1043  state->has_m = 0;
1044  state->pgdims = 3;
1045  break;
1046 
1047  case FORCE_OUTPUT_3DM:
1048  state->has_z = 0;
1049  state->has_m = 1;
1050  state->pgdims = 3;
1051  break;
1052 
1053  case FORCE_OUTPUT_4D:
1054  state->has_z = 1;
1055  state->has_m = 1;
1056  state->pgdims = 4;
1057  break;
1058  default:
1059  /* Simply use the auto-detected values above */
1060  break;
1061  }
1062 
1063  /* If in simple geometry mode, alter names for CREATE TABLE by skipping MULTI */
1064  if (state->config->simple_geometries)
1065  {
1066  if ((geomtype == MULTIPOLYGONTYPE) || (geomtype == MULTILINETYPE) || (geomtype == MULTIPOINTTYPE))
1067  {
1068  /* Chop off the "MULTI" from the string. */
1069  state->pgtype += 5;
1070  }
1071  }
1072 
1073  }
1074  else
1075  {
1076  /* Otherwise just count the number of records in the DBF */
1077  state->num_entities = DBFGetRecordCount(state->hDBFHandle);
1078  }
1079 
1080 
1081  /* Get the field information from the DBF */
1082  state->num_fields = DBFGetFieldCount(state->hDBFHandle);
1083  state->num_records = DBFGetRecordCount(state->hDBFHandle);
1084 
1085  /* Allocate storage for field information */
1086  state->field_names = malloc(state->num_fields * sizeof(char*));
1087  state->types = (DBFFieldType *)malloc(state->num_fields * sizeof(int));
1088  state->widths = malloc(state->num_fields * sizeof(int));
1089  state->precisions = malloc(state->num_fields * sizeof(int));
1090  state->pgfieldtypes = malloc(state->num_fields * sizeof(char *));
1091  state->col_names = malloc((state->num_fields + 2) * sizeof(char) * MAXFIELDNAMELEN);
1092 
1093  /* Generate a string of comma separated column names of the form "(col1, col2 ... colN)" for the SQL
1094  insertion string */
1095  strcpy(state->col_names, "(" );
1096 
1097  for (j = 0; j < state->num_fields; j++)
1098  {
1099  type = DBFGetFieldInfo(state->hDBFHandle, j, name, &field_width, &field_precision);
1100 
1101  state->types[j] = type;
1102  state->widths[j] = field_width;
1103  state->precisions[j] = field_precision;
1104 
1105  if (state->config->encoding)
1106  {
1107  char *encoding_msg = _("Try \"LATIN1\" (Western European), or one of the values described at http://www.gnu.org/software/libiconv/.");
1108 
1109  int rv = utf8(state->config->encoding, name, &utf8str);
1110 
1111  if (rv != UTF8_GOOD_RESULT)
1112  {
1113  if ( rv == UTF8_BAD_RESULT )
1114  snprintf(state->message, SHPLOADERMSGLEN, _("Unable to convert field name \"%s\" to UTF-8 (iconv reports \"%s\"). Current encoding is \"%s\". %s"), utf8str, strerror(errno), state->config->encoding, encoding_msg);
1115  else if ( rv == UTF8_NO_RESULT )
1116  snprintf(state->message, SHPLOADERMSGLEN, _("Unable to convert field name to UTF-8 (iconv reports \"%s\"). Current encoding is \"%s\". %s"), strerror(errno), state->config->encoding, encoding_msg);
1117  else
1118  snprintf(state->message, SHPLOADERMSGLEN, _("Unexpected return value from utf8()"));
1119 
1120  if ( rv == UTF8_BAD_RESULT )
1121  free(utf8str);
1122 
1123  return SHPLOADERERR;
1124  }
1125 
1126  strncpy(name, utf8str, MAXFIELDNAMELEN);
1127  free(utf8str);
1128  }
1129 
1130  /* If a column map file has been passed in, use this to create the postgresql field name from
1131  the dbf column name */
1132  {
1133  const char *mapped = colmap_pg_by_dbf(&state->column_map, name);
1134  if (mapped)
1135  {
1136  strncpy(name, mapped, MAXFIELDNAMELEN);
1137  name[MAXFIELDNAMELEN-1] = '\0';
1138  }
1139  }
1140 
1141  /*
1142  * Make field names lowercase unless asked to
1143  * keep identifiers case.
1144  */
1145  if (!state->config->quoteidentifiers)
1146  strtolower(name);
1147 
1148  /*
1149  * Escape names starting with the
1150  * escape char (_), those named 'gid'
1151  * or after pgsql reserved attribute names
1152  */
1153  if (name[0] == '_' ||
1154  ! strcmp(name, "gid") || ! strcmp(name, "tableoid") ||
1155  ! strcmp(name, "cmin") ||
1156  ! strcmp(name, "cmax") ||
1157  ! strcmp(name, "xmin") ||
1158  ! strcmp(name, "xmax") ||
1159  ! strcmp(name, "primary") ||
1160  ! strcmp(name, "oid") || ! strcmp(name, "ctid"))
1161  {
1162  strncpy(name2 + 2, name, MAXFIELDNAMELEN - 2);
1163  name2[0] = '_';
1164  name2[1] = '_';
1165  strcpy(name, name2);
1166  }
1167 
1168  /* Avoid duplicating field names */
1169  for (z = 0; z < j ; z++)
1170  {
1171  if (strcmp(state->field_names[z], name) == 0)
1172  {
1173  strncat(name, "__", MAXFIELDNAMELEN);
1174  snprintf(name + strlen(name), MAXFIELDNAMELEN, "%i", j);
1175  break;
1176  }
1177  }
1178 
1179  state->field_names[j] = malloc(strlen(name) + 1);
1180  strcpy(state->field_names[j], name);
1181 
1182  /* Now generate the PostgreSQL type name string and width based upon the shapefile type */
1183  switch (state->types[j])
1184  {
1185  case FTString:
1186  state->pgfieldtypes[j] = malloc(strlen("varchar") + 1);
1187  strcpy(state->pgfieldtypes[j], "varchar");
1188  break;
1189 
1190  case FTDate:
1191  state->pgfieldtypes[j] = malloc(strlen("date") + 1);
1192  strcpy(state->pgfieldtypes[j], "date");
1193  break;
1194 
1195  case FTInteger:
1196  /* Determine exact type based upon field width */
1197  if (state->config->forceint4 || (state->widths[j] >=5 && state->widths[j] < 10))
1198  {
1199  state->pgfieldtypes[j] = malloc(strlen("int4") + 1);
1200  strcpy(state->pgfieldtypes[j], "int4");
1201  }
1202  else if (state->widths[j] < 5)
1203  {
1204  state->pgfieldtypes[j] = malloc(strlen("int2") + 1);
1205  strcpy(state->pgfieldtypes[j], "int2");
1206  }
1207  else
1208  {
1209  state->pgfieldtypes[j] = malloc(strlen("numeric") + 1);
1210  strcpy(state->pgfieldtypes[j], "numeric");
1211  }
1212  break;
1213 
1214  case FTDouble:
1215  /* Determine exact type based upon field width */
1216  if (state->widths[j] > 18)
1217  {
1218  state->pgfieldtypes[j] = malloc(strlen("numeric") + 1);
1219  strcpy(state->pgfieldtypes[j], "numeric");
1220  }
1221  else
1222  {
1223  state->pgfieldtypes[j] = malloc(strlen("float8") + 1);
1224  strcpy(state->pgfieldtypes[j], "float8");
1225  }
1226  break;
1227 
1228  case FTLogical:
1229  state->pgfieldtypes[j] = malloc(strlen("boolean") + 1);
1230  strcpy(state->pgfieldtypes[j], "boolean");
1231  break;
1232 
1233  default:
1234  snprintf(state->message, SHPLOADERMSGLEN, _("Invalid type %x in DBF file"), state->types[j]);
1235  return SHPLOADERERR;
1236  }
1237 
1238  strcat(state->col_names, "\"");
1239  strcat(state->col_names, name);
1240 
1241  if (state->config->readshape == 1 || j < (state->num_fields - 1))
1242  {
1243  /* Don't include last comma if its the last field and no geometry field will follow */
1244  strcat(state->col_names, "\",");
1245  }
1246  else
1247  {
1248  strcat(state->col_names, "\"");
1249  }
1250  }
1251 
1252  /* Append the geometry column if required */
1253  if (state->config->readshape == 1)
1254  strcat(state->col_names, state->geo_col);
1255 
1256  strcat(state->col_names, ")");
1257 
1258 
1259  /* Return status */
1260  return ret;
1261 }
1262 
1263 /* Return a pointer to an allocated string containing the header for the specified loader state */
1264 int
1265 ShpLoaderGetSQLHeader(SHPLOADERSTATE *state, char **strheader)
1266 {
1267  stringbuffer_t *sb;
1268  char *ret;
1269  int j;
1270 
1271  /* Create the stringbuffer containing the header; we use this API as it's easier
1272  for handling string resizing during append */
1273  sb = stringbuffer_create();
1274  stringbuffer_clear(sb);
1275 
1276  /* Set the client encoding if required */
1277  if (state->config->encoding)
1278  {
1279  stringbuffer_aprintf(sb, "SET CLIENT_ENCODING TO UTF8;\n");
1280  }
1281 
1282  /* Use SQL-standard string escaping rather than PostgreSQL standard */
1283  stringbuffer_aprintf(sb, "SET STANDARD_CONFORMING_STRINGS TO ON;\n");
1284 
1285  /* Drop table if requested */
1286  if (state->config->opt == 'd')
1287  {
1299  if (state->config->schema)
1300  {
1301  if (state->config->readshape == 1 && (! state->config->geography) )
1302  {
1303  stringbuffer_aprintf(sb, "SELECT DropGeometryColumn('%s','%s','%s');\n",
1304  state->config->schema, state->config->table, state->geo_col);
1305  }
1306 
1307  stringbuffer_aprintf(sb, "DROP TABLE IF EXISTS \"%s\".\"%s\";\n", state->config->schema,
1308  state->config->table);
1309  }
1310  else
1311  {
1312  if (state->config->readshape == 1 && (! state->config->geography) )
1313  {
1314  stringbuffer_aprintf(sb, "SELECT DropGeometryColumn('','%s','%s');\n",
1315  state->config->table, state->geo_col);
1316  }
1317 
1318  stringbuffer_aprintf(sb, "DROP TABLE IF EXISTS \"%s\";\n", state->config->table);
1319  }
1320  }
1321 
1322  /* Start of transaction if we are using one */
1323  if (state->config->usetransaction)
1324  {
1325  stringbuffer_aprintf(sb, "BEGIN;\n");
1326  }
1327 
1328  /* If not in 'append' mode create the spatial table */
1329  if (state->config->opt != 'a')
1330  {
1331  /*
1332  * Create a table for inserting the shapes into with appropriate
1333  * columns and types
1334  */
1335  if (state->config->schema)
1336  {
1337  stringbuffer_aprintf(sb, "CREATE TABLE \"%s\".\"%s\" (gid serial",
1338  state->config->schema, state->config->table);
1339  }
1340  else
1341  {
1342  stringbuffer_aprintf(sb, "CREATE TABLE \"%s\" (gid serial", state->config->table);
1343  }
1344 
1345  /* Generate the field types based upon the shapefile information */
1346  for (j = 0; j < state->num_fields; j++)
1347  {
1348  stringbuffer_aprintf(sb, ",\n\"%s\" ", state->field_names[j]);
1349 
1350  /* First output the raw field type string */
1351  stringbuffer_aprintf(sb, "%s", state->pgfieldtypes[j]);
1352 
1353  /* Some types do have typmods though... */
1354  if (!strcmp("varchar", state->pgfieldtypes[j]))
1355  stringbuffer_aprintf(sb, "(%d)", state->widths[j]);
1356 
1357  if (!strcmp("numeric", state->pgfieldtypes[j]))
1358  {
1359  /* Doubles we just allow PostgreSQL to auto-detect the size */
1360  if (state->types[j] != FTDouble)
1361  stringbuffer_aprintf(sb, "(%d,0)", state->widths[j]);
1362  }
1363  }
1364 
1365  /* Add the geography column directly to the table definition, we don't
1366  need to do an AddGeometryColumn() call. */
1367  if (state->config->readshape == 1 && state->config->geography)
1368  {
1369  char *dimschar;
1370 
1371  if (state->pgdims == 4)
1372  dimschar = "ZM";
1373  else
1374  dimschar = "";
1375 
1376  if (state->to_srid != SRID_UNKNOWN && state->to_srid != 4326)
1377  {
1378  snprintf(state->message, SHPLOADERMSGLEN, _("Invalid SRID for geography type: %d"), state->to_srid);
1380  return SHPLOADERERR;
1381  }
1382  stringbuffer_aprintf(sb, ",\n\"%s\" geography(%s%s,%d)", state->geo_col, state->pgtype, dimschar, 4326);
1383  }
1384  stringbuffer_aprintf(sb, ")");
1385 
1386  /* Tablespace is optional. */
1387  if (state->config->tablespace != NULL)
1388  {
1389  stringbuffer_aprintf(sb, " TABLESPACE \"%s\"", state->config->tablespace);
1390  }
1391  stringbuffer_aprintf(sb, ";\n");
1392 
1393  /* Create the primary key. This is done separately because the index for the PK needs
1394  * to be in the correct tablespace. */
1395 
1396  /* TODO: Currently PostgreSQL does not allow specifying an index to use for a PK (so you get
1397  * a default one called table_pkey) and it does not provide a way to create a PK index
1398  * in a specific tablespace. So as a hacky solution we create the PK, then move the
1399  * index to the correct tablespace. Eventually this should be:
1400  * CREATE INDEX table_pkey on table(gid) TABLESPACE tblspc;
1401  * ALTER TABLE table ADD PRIMARY KEY (gid) USING INDEX table_pkey;
1402  * A patch has apparently been submitted to PostgreSQL to enable this syntax, see this thread:
1403  * http://archives.postgresql.org/pgsql-hackers/2011-01/msg01405.php */
1404  stringbuffer_aprintf(sb, "ALTER TABLE ");
1405 
1406  /* Schema is optional, include if present. */
1407  if (state->config->schema)
1408  {
1409  stringbuffer_aprintf(sb, "\"%s\".",state->config->schema);
1410  }
1411  stringbuffer_aprintf(sb, "\"%s\" ADD PRIMARY KEY (gid);\n", state->config->table);
1412 
1413  /* Tablespace is optional for the index. */
1414  if (state->config->idxtablespace != NULL)
1415  {
1416  stringbuffer_aprintf(sb, "ALTER INDEX ");
1417  if (state->config->schema)
1418  {
1419  stringbuffer_aprintf(sb, "\"%s\".",state->config->schema);
1420  }
1421 
1422  /* WARNING: We're assuming the default "table_pkey" name for the primary
1423  * key index. PostgreSQL may use "table_pkey1" or similar in the
1424  * case of a name conflict, so you may need to edit the produced
1425  * SQL in this rare case. */
1426  stringbuffer_aprintf(sb, "\"%s_pkey\" SET TABLESPACE \"%s\";\n",
1427  state->config->table, state->config->idxtablespace);
1428  }
1429 
1430  /* Create the geometry column with an addgeometry call */
1431  if (state->config->readshape == 1 && (!state->config->geography))
1432  {
1433  /* If they didn't specify a target SRID, see if they specified a source SRID. */
1434  int srid = state->to_srid;
1435  if (state->config->schema)
1436  {
1437  stringbuffer_aprintf(sb, "SELECT AddGeometryColumn('%s','%s','%s','%d',",
1438  state->config->schema, state->config->table, state->geo_col, srid);
1439  }
1440  else
1441  {
1442  stringbuffer_aprintf(sb, "SELECT AddGeometryColumn('','%s','%s','%d',",
1443  state->config->table, state->geo_col, srid);
1444  }
1445 
1446  stringbuffer_aprintf(sb, "'%s',%d);\n", state->pgtype, state->pgdims);
1447  }
1448  }
1449 
1450  /* Copy the string buffer into a new string, destroying the string buffer */
1451  ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
1452  strcpy(ret, (char *)stringbuffer_getstring(sb));
1454 
1455  *strheader = ret;
1456 
1457  return SHPLOADEROK;
1458 }
1459 
1460 
1461 /* Return an allocated string containing the copy statement for this state */
1462 int
1464 {
1465  char *copystr;
1466 
1467  /* Allocate the string for the COPY statement */
1468  if (state->config->dump_format)
1469  {
1470  if (state->config->schema)
1471  {
1472  copystr = malloc(strlen(state->config->schema) + strlen(state->config->table) +
1473  strlen(state->col_names) + 40);
1474 
1475  sprintf(copystr, "COPY \"%s\".\"%s\" %s FROM stdin;\n",
1476  state->config->schema, state->config->table, state->col_names);
1477  }
1478  else
1479  {
1480  copystr = malloc(strlen(state->config->table) + strlen(state->col_names) + 40);
1481 
1482  sprintf(copystr, "COPY \"%s\" %s FROM stdin;\n", state->config->table, state->col_names);
1483  }
1484 
1485  *strheader = copystr;
1486  return SHPLOADEROK;
1487  }
1488  else
1489  {
1490  /* Flag an error as something has gone horribly wrong */
1491  snprintf(state->message, SHPLOADERMSGLEN, _("Internal error: attempt to generate a COPY statement for data that hasn't been requested in COPY format"));
1492 
1493  return SHPLOADERERR;
1494  }
1495 }
1496 
1497 
1498 /* Return a count of the number of entities in this shapefile */
1499 int
1501 {
1502  return state->num_entities;
1503 }
1504 
1505 
1506 /* Return an allocated string representation of a specified record item */
1507 int
1508 ShpLoaderGenerateSQLRowStatement(SHPLOADERSTATE *state, int item, char **strrecord)
1509 {
1510  SHPObject *obj = NULL;
1511  stringbuffer_t *sb;
1512  stringbuffer_t *sbwarn;
1513  char val[MAXVALUELEN];
1514  char *escval;
1515  char *geometry=NULL, *ret;
1516  char *utf8str;
1517  int res, i;
1518  int rv;
1519 
1520  /* Clear the stringbuffers */
1521  sbwarn = stringbuffer_create();
1522  stringbuffer_clear(sbwarn);
1523  sb = stringbuffer_create();
1524  stringbuffer_clear(sb);
1525 
1526  /* If we are reading the DBF only and the record has been marked deleted, return deleted record status */
1527  if (state->config->readshape == 0 && DBFIsRecordDeleted(state->hDBFHandle, item))
1528  {
1529  *strrecord = NULL;
1530  return SHPLOADERRECDELETED;
1531  }
1532 
1533  /* If we are reading the shapefile, open the specified record */
1534  if (state->config->readshape == 1)
1535  {
1536  obj = SHPReadObject(state->hSHPHandle, item);
1537  if (!obj)
1538  {
1539  snprintf(state->message, SHPLOADERMSGLEN, _("Error reading shape object %d"), item);
1540  return SHPLOADERERR;
1541  }
1542 
1543  /* If we are set to skip NULLs, return a NULL record status */
1544  if (state->config->null_policy == POLICY_NULL_SKIP && obj->nVertices == 0 )
1545  {
1546  SHPDestroyObject(obj);
1547 
1548  *strrecord = NULL;
1549  return SHPLOADERRECISNULL;
1550  }
1551  }
1552 
1553  /* If not in dump format, generate the INSERT string */
1554  if (!state->config->dump_format)
1555  {
1556  if (state->config->schema)
1557  {
1558  stringbuffer_aprintf(sb, "INSERT INTO \"%s\".\"%s\" %s VALUES (", state->config->schema,
1559  state->config->table, state->col_names);
1560  }
1561  else
1562  {
1563  stringbuffer_aprintf(sb, "INSERT INTO \"%s\" %s VALUES (", state->config->table,
1564  state->col_names);
1565  }
1566  }
1567 
1568 
1569  /* Read all of the attributes from the DBF file for this item */
1570  for (i = 0; i < DBFGetFieldCount(state->hDBFHandle); i++)
1571  {
1572  /* Special case for NULL attributes */
1573  if (DBFIsAttributeNULL(state->hDBFHandle, item, i))
1574  {
1575  if (state->config->dump_format)
1576  stringbuffer_aprintf(sb, "\\N");
1577  else
1578  stringbuffer_aprintf(sb, "NULL");
1579  }
1580  else
1581  {
1582  /* Attribute NOT NULL */
1583  switch (state->types[i])
1584  {
1585  case FTInteger:
1586  case FTDouble:
1587  rv = snprintf(val, MAXVALUELEN, "%s", DBFReadStringAttribute(state->hDBFHandle, item, i));
1588  if (rv >= MAXVALUELEN || rv == -1)
1589  {
1590  stringbuffer_aprintf(sbwarn, "Warning: field %d name truncated\n", i);
1591  val[MAXVALUELEN - 1] = '\0';
1592  }
1593 
1594  /* If the value is an empty string, change to 0 */
1595  if (val[0] == '\0')
1596  {
1597  val[0] = '0';
1598  val[1] = '\0';
1599  }
1600 
1601  /* If the value ends with just ".", remove the dot */
1602  if (val[strlen(val) - 1] == '.')
1603  val[strlen(val) - 1] = '\0';
1604  break;
1605 
1606  case FTString:
1607  case FTLogical:
1608  case FTDate:
1609  rv = snprintf(val, MAXVALUELEN, "%s", DBFReadStringAttribute(state->hDBFHandle, item, i));
1610  if (rv >= MAXVALUELEN || rv == -1)
1611  {
1612  stringbuffer_aprintf(sbwarn, "Warning: field %d name truncated\n", i);
1613  val[MAXVALUELEN - 1] = '\0';
1614  }
1615  break;
1616 
1617  default:
1618  snprintf(state->message, SHPLOADERMSGLEN, _("Error: field %d has invalid or unknown field type (%d)"), i, state->types[i]);
1619 
1620  /* clean up and return err */
1621  SHPDestroyObject(obj);
1622  stringbuffer_destroy(sbwarn);
1624  return SHPLOADERERR;
1625  }
1626 
1627  if (state->config->encoding)
1628  {
1629  char *encoding_msg = _("Try \"LATIN1\" (Western European), or one of the values described at http://www.postgresql.org/docs/current/static/multibyte.html.");
1630 
1631  rv = utf8(state->config->encoding, val, &utf8str);
1632 
1633  if (rv != UTF8_GOOD_RESULT)
1634  {
1635  if ( rv == UTF8_BAD_RESULT )
1636  snprintf(state->message, SHPLOADERMSGLEN, _("Unable to convert data value \"%s\" to UTF-8 (iconv reports \"%s\"). Current encoding is \"%s\". %s"), utf8str, strerror(errno), state->config->encoding, encoding_msg);
1637  else if ( rv == UTF8_NO_RESULT )
1638  snprintf(state->message, SHPLOADERMSGLEN, _("Unable to convert data value to UTF-8 (iconv reports \"%s\"). Current encoding is \"%s\". %s"), strerror(errno), state->config->encoding, encoding_msg);
1639  else
1640  snprintf(state->message, SHPLOADERMSGLEN, _("Unexpected return value from utf8()"));
1641 
1642  if ( rv == UTF8_BAD_RESULT )
1643  free(utf8str);
1644 
1645  /* clean up and return err */
1646  SHPDestroyObject(obj);
1647  stringbuffer_destroy(sbwarn);
1649  return SHPLOADERERR;
1650  }
1651  strncpy(val, utf8str, MAXVALUELEN);
1652  free(utf8str);
1653 
1654  }
1655 
1656  /* Escape attribute correctly according to dump format */
1657  if (state->config->dump_format)
1658  {
1659  escval = escape_copy_string(val);
1660  stringbuffer_aprintf(sb, "%s", escval);
1661  }
1662  else
1663  {
1664  escval = escape_insert_string(val);
1665  stringbuffer_aprintf(sb, "'%s'", escval);
1666  }
1667 
1668  /* Free the escaped version if required */
1669  if (val != escval)
1670  free(escval);
1671  }
1672 
1673  /* Only put in delimeter if not last field or a shape will follow */
1674  if (state->config->readshape == 1 || i < DBFGetFieldCount(state->hDBFHandle) - 1)
1675  {
1676  if (state->config->dump_format)
1677  stringbuffer_aprintf(sb, "\t");
1678  else
1679  stringbuffer_aprintf(sb, ",");
1680  }
1681 
1682  /* End of DBF attribute loop */
1683  }
1684 
1685 
1686  /* Add the shape attribute if we are reading it */
1687  if (state->config->readshape == 1)
1688  {
1689  /* Force the locale to C */
1690  char *oldlocale = setlocale(LC_NUMERIC, "C");
1691 
1692  /* Handle the case of a NULL shape */
1693  if (obj->nVertices == 0)
1694  {
1695  if (state->config->dump_format)
1696  stringbuffer_aprintf(sb, "\\N");
1697  else
1698  stringbuffer_aprintf(sb, "NULL");
1699  }
1700  else
1701  {
1702  /* Handle all other shape attributes */
1703  switch (obj->nSHPType)
1704  {
1705  case SHPT_POLYGON:
1706  case SHPT_POLYGONM:
1707  case SHPT_POLYGONZ:
1708  res = GeneratePolygonGeometry(state, obj, &geometry);
1709  break;
1710 
1711  case SHPT_POINT:
1712  case SHPT_POINTM:
1713  case SHPT_POINTZ:
1714  res = GeneratePointGeometry(state, obj, &geometry, 0);
1715  break;
1716 
1717  case SHPT_MULTIPOINT:
1718  case SHPT_MULTIPOINTM:
1719  case SHPT_MULTIPOINTZ:
1720  /* Force it to multi unless using -S */
1721  res = GeneratePointGeometry(state, obj, &geometry,
1722  state->config->simple_geometries ? 0 : 1);
1723  break;
1724 
1725  case SHPT_ARC:
1726  case SHPT_ARCM:
1727  case SHPT_ARCZ:
1728  res = GenerateLineStringGeometry(state, obj, &geometry);
1729  break;
1730 
1731  default:
1732  snprintf(state->message, SHPLOADERMSGLEN, _("Shape type is not supported, type id = %d"), obj->nSHPType);
1733  SHPDestroyObject(obj);
1734  stringbuffer_destroy(sbwarn);
1736 
1737  return SHPLOADERERR;
1738  }
1739  /* The default returns out of the function, so res will always have been set. */
1740  if (res != SHPLOADEROK)
1741  {
1742  /* Error message has already been set */
1743  SHPDestroyObject(obj);
1744  stringbuffer_destroy(sbwarn);
1746 
1747  return SHPLOADERERR;
1748  }
1749 
1750  /* Now generate the geometry string according to the current configuration */
1751  if (!state->config->dump_format)
1752  {
1753  if (state->to_srid != state->from_srid)
1754  {
1755  stringbuffer_aprintf(sb, "ST_Transform(");
1756  }
1757  stringbuffer_aprintf(sb, "'");
1758  }
1759 
1760  stringbuffer_aprintf(sb, "%s", geometry);
1761 
1762  if (!state->config->dump_format)
1763  {
1764  stringbuffer_aprintf(sb, "'");
1765 
1766  /* Close the ST_Transform if reprojecting. */
1767  if (state->to_srid != state->from_srid)
1768  {
1769  /* We need to add an explicit cast to geography/geometry to ensure that
1770  PostgreSQL doesn't get confused with the ST_Transform() raster
1771  function. */
1772  if (state->config->geography)
1773  stringbuffer_aprintf(sb, "::geometry, %d)::geography", state->to_srid);
1774  else
1775  stringbuffer_aprintf(sb, "::geometry, %d)", state->to_srid);
1776  }
1777  }
1778 
1779  free(geometry);
1780  }
1781 
1782  /* Tidy up everything */
1783  SHPDestroyObject(obj);
1784 
1785  setlocale(LC_NUMERIC, oldlocale);
1786  }
1787 
1788  /* Close the line correctly for dump/insert format */
1789  if (!state->config->dump_format)
1790  stringbuffer_aprintf(sb, ");");
1791 
1792 
1793  /* Copy the string buffer into a new string, destroying the string buffer */
1794  ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
1795  strcpy(ret, (char *)stringbuffer_getstring(sb));
1797 
1798  *strrecord = ret;
1799 
1800  /* If any warnings occurred, set the returned message string and warning status */
1801  if (strlen((char *)stringbuffer_getstring(sbwarn)) > 0)
1802  {
1803  snprintf(state->message, SHPLOADERMSGLEN, "%s", stringbuffer_getstring(sbwarn));
1804  stringbuffer_destroy(sbwarn);
1805 
1806  return SHPLOADERWARN;
1807  }
1808  else
1809  {
1810  /* Everything went okay */
1811  stringbuffer_destroy(sbwarn);
1812 
1813  return SHPLOADEROK;
1814  }
1815 }
1816 
1817 
1818 /* Return a pointer to an allocated string containing the header for the specified loader state */
1819 int
1820 ShpLoaderGetSQLFooter(SHPLOADERSTATE *state, char **strfooter)
1821 {
1822  stringbuffer_t *sb;
1823  char *ret;
1824 
1825  /* Create the stringbuffer containing the header; we use this API as it's easier
1826  for handling string resizing during append */
1827  sb = stringbuffer_create();
1828  stringbuffer_clear(sb);
1829 
1830  /* Create gist index if specified and not in "prepare" mode */
1831  if (state->config->readshape && state->config->createindex)
1832  {
1833  stringbuffer_aprintf(sb, "CREATE INDEX ON ");
1834  /* Schema is optional, include if present. */
1835  if (state->config->schema)
1836  {
1837  stringbuffer_aprintf(sb, "\"%s\".",state->config->schema);
1838  }
1839  stringbuffer_aprintf(sb, "\"%s\" USING GIST (\"%s\")", state->config->table, state->geo_col);
1840  /* Tablespace is also optional. */
1841  if (state->config->idxtablespace != NULL)
1842  {
1843  stringbuffer_aprintf(sb, " TABLESPACE \"%s\"", state->config->idxtablespace);
1844  }
1845  stringbuffer_aprintf(sb, ";\n");
1846  }
1847 
1848  /* End the transaction if there is one. */
1849  if (state->config->usetransaction)
1850  {
1851  stringbuffer_aprintf(sb, "COMMIT;\n");
1852  }
1853 
1854  /* Always ANALYZE the resulting table, for better stats */
1855  stringbuffer_aprintf(sb, "ANALYZE ");
1856  if (state->config->schema)
1857  {
1858  stringbuffer_aprintf(sb, "\"%s\".", state->config->schema);
1859  }
1860  stringbuffer_aprintf(sb, "\"%s\";\n", state->config->table);
1861 
1862  /* Copy the string buffer into a new string, destroying the string buffer */
1863  ret = (char *)malloc(strlen((char *)stringbuffer_getstring(sb)) + 1);
1864  strcpy(ret, (char *)stringbuffer_getstring(sb));
1866 
1867  *strfooter = ret;
1868 
1869  return SHPLOADEROK;
1870 }
1871 
1872 
1873 void
1875 {
1876  /* Destroy a state object created with ShpLoaderOpenShape */
1877  int i;
1878 
1879  if (state != NULL)
1880  {
1881  if (state->hSHPHandle)
1882  SHPClose(state->hSHPHandle);
1883  if (state->hDBFHandle)
1884  DBFClose(state->hDBFHandle);
1885  if (state->field_names)
1886  {
1887  for (i = 0; i < state->num_fields; i++)
1888  free(state->field_names[i]);
1889 
1890  free(state->field_names);
1891  }
1892  if (state->pgfieldtypes)
1893  {
1894  for (i = 0; i < state->num_fields; i++)
1895  free(state->pgfieldtypes[i]);
1896 
1897  free(state->pgfieldtypes);
1898  }
1899  if (state->types)
1900  free(state->types);
1901  if (state->widths)
1902  free(state->widths);
1903  if (state->precisions)
1904  free(state->precisions);
1905  if (state->col_names)
1906  free(state->col_names);
1907 
1908  /* Free any column map fieldnames if specified */
1909  colmap_clean(&state->column_map);
1910 
1911  /* Free the state itself */
1912  free(state);
1913  }
1914 }
double x
Definition: liblwgeom.h:351
char * lwgeom_to_hexwkb(const LWGEOM *geom, uint8_t variant, size_t *size_out)
Definition: lwout_wkb.c:848
SHPObject SHPAPI_CALL1 * SHPReadObject(SHPHandle hSHP, int iShape);int SHPAPI_CALLSHPWriteObject(SHPHandle hSHP, int iShape, SHPObject *psObject
void colmap_clean(colmap *map)
Definition: shpcommon.c:167
SHPLOADERCONFIG * config
int ShpLoaderGetSQLFooter(SHPLOADERSTATE *state, char **strfooter)
#define SHPT_ARCM
Definition: shapefil.h:316
tuple res
Definition: window.py:78
DBFFieldType * types
int * panPartStart
Definition: shapefil.h:345
double m
Definition: liblwgeom.h:351
LWCOLLECTION * lwcollection_construct(uint8_t type, int srid, GBOX *bbox, uint32_t ngeoms, LWGEOM **geoms)
Definition: lwcollection.c:43
stringbuffer_t * stringbuffer_create(void)
Allocate a new stringbuffer_t.
Definition: stringbuffer.c:35
char * lwgeom_to_wkt(const LWGEOM *geom, uint8_t variant, int precision, size_t *size_out)
WKT emitter function.
Definition: lwout_wkt.c:669
void lwfree(void *mem)
Definition: lwutil.c:242
#define FORCE_OUTPUT_3DM
#define SHPT_POLYGONM
Definition: shapefil.h:317
int colmap_read(const char *filename, colmap *map, char *errbuf, size_t errbuflen)
Read the content of filename into a symbol map.
Definition: shpcommon.c:211
#define FORCE_OUTPUT_3DZ
int PIP(Point P, Point *V, int n)
PIP(): crossing number test for a point in a polygon input: P = a point, V[] = vertex points of a pol...
int ShpLoaderOpenShape(SHPLOADERSTATE *state)
int nVertices
Definition: shapefil.h:348
#define POLICY_NULL_INSERT
#define SHPLOADERRECISNULL
Datum area(PG_FUNCTION_ARGS)
#define _(String)
Definition: shpcommon.h:24
char * codepage2encoding(const char *cpg)
Definition: shpcommon.c:286
#define UTF8_BAD_RESULT
const char SHPAPI_CALL1 * DBFReadStringAttribute(DBFHandle psDBF, int iRecord, int iField){return((const char *) DBFReadAttribute(psDBF, iRecord, iField, 'C')
POINTARRAY * ptarray_construct_empty(char hasz, char hasm, uint32_t maxpoints)
Create a new POINTARRAY with no points.
Definition: ptarray.c:70
void lwgeom_free(LWGEOM *geom)
Definition: lwgeom.c:1063
#define MULTIPOINTTYPE
Definition: liblwgeom.h:87
#define SHPLOADERWARN
double * padfX
Definition: shapefil.h:349
#define SHPT_MULTIPOINT
Definition: shapefil.h:310
#define POLICY_NULL_SKIP
#define SHPLOADERRECDELETED
#define SHPT_POLYGON
Definition: shapefil.h:309
void SHPAPI_CALL SHPDestroyObject(SHPObject *psObject)
Definition: shpopen.c:2182
#define SHPT_MULTIPOINTZ
Definition: shapefil.h:314
int GeneratePointGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry, int force_multi)
Generate an allocated geometry string for shapefile object obj using the state parameters if "force_m...
int GeneratePolygonGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
Generate an allocated geometry string for shapefile object obj using the state parameters.
int GenerateLineStringGeometry(SHPLOADERSTATE *state, SHPObject *obj, char **geometry)
Generate an allocated geometry string for shapefile object obj using the state parameters.
int ShpLoaderGetSQLHeader(SHPLOADERSTATE *state, char **strheader)
struct struct_ring Ring
double * padfY
Definition: shapefil.h:350
DBFHandle hDBFHandle
#define UTF8_GOOD_RESULT
LWGEOM * lwpoly_as_lwgeom(const LWPOLY *obj)
Definition: lwgeom.c:252
int SHPAPI_CALL DBFGetRecordCount(DBFHandle psDBF)
Definition: dbfopen.c:1208
#define SHPT_ARCZ
Definition: shapefil.h:312
void ShpLoaderDestroy(SHPLOADERSTATE *state)
#define GEOGRAPHY_DEFAULT
void SHPAPI_CALL SHPGetInfo(SHPHandle hSHP, int *pnEntities, int *pnShapeType, double *padfMinBound, double *padfMaxBound)
Definition: shpopen.c:796
#define POLICY_NULL_ABORT
char * escape_insert_string(char *str)
Escape input string suitable for INSERT.
int stringbuffer_aprintf(stringbuffer_t *s, const char *fmt,...)
Appends a formatted string to the current string buffer, using the format and argument list provided...
Definition: stringbuffer.c:253
void colmap_init(colmap *map)
Definition: shpcommon.c:159
SHPHandle SHPAPI_CALL SHPOpen(const char *pszShapeFile, const char *pszAccess)
Definition: shpopen.c:464
DBFFieldType SHPAPI_CALL DBFGetFieldInfo(DBFHandle psDBF, int iField, char *pszFieldName, int *pnWidth, int *pnDecimals)
Definition: dbfopen.c:1221
#define MAXFIELDNAMELEN
#define FLAGS_SET_Z(flags, value)
Definition: liblwgeom.h:145
int SHPAPI_CALL DBFGetFieldCount(DBFHandle psDBF)
Definition: dbfopen.c:1195
DBFHandle SHPAPI_CALL DBFOpen(const char *pszFilename, const char *pszAccess)
Definition: dbfopen.c:365
void set_loader_config_defaults(SHPLOADERCONFIG *config)
#define GEOMETRY_DEFAULT
LWGEOM * lwline_as_lwgeom(const LWLINE *obj)
Definition: lwgeom.c:262
#define FORCE_OUTPUT_2D
int ShpLoaderGetRecordCount(SHPLOADERSTATE *state)
int ptarray_append_point(POINTARRAY *pa, const POINT4D *pt, int allow_duplicates)
Append a point to the end of an existing POINTARRAY If allow_duplicate is LW_FALSE, then a duplicate point will not be added.
Definition: ptarray.c:156
void stringbuffer_clear(stringbuffer_t *s)
Reset the stringbuffer_t.
Definition: stringbuffer.c:90
#define LW_FALSE
Definition: liblwgeom.h:76
int FindPolygons(SHPObject *obj, Ring ***Out)
struct struct_point Point
#define LW_TRUE
Return types for functions with status returns.
Definition: liblwgeom.h:75
LWLINE * lwline_construct(int srid, GBOX *bbox, POINTARRAY *points)
Definition: lwline.c:42
char * escape_copy_string(char *str)
Escape input string suitable for COPY.
#define SRID_UNKNOWN
Unknown SRID value.
Definition: liblwgeom.h:187
struct struct_ring * next
SHPLOADERSTATE * ShpLoaderCreate(SHPLOADERCONFIG *config)
int nParts
Definition: shapefil.h:344
char * s
Definition: cu_in_wkt.c:23
#define SHPLOADEROK
double z
Definition: liblwgeom.h:351
#define WKB_EXTENDED
Definition: liblwgeom.h:2048
#define UTF8_NO_RESULT
#define WKT_PRECISION
#define SHPT_MULTIPOINTM
Definition: shapefil.h:318
#define FORCE_OUTPUT_DISABLE
#define ENCODING_DEFAULT
#define FORCE_OUTPUT_4D
#define SHPT_POINTZ
Definition: shapefil.h:311
#define MULTIPOLYGONTYPE
Definition: liblwgeom.h:89
SHPHandle hSHPHandle
Point * list
#define SHPT_POINTM
Definition: shapefil.h:315
#define WKT_EXTENDED
Definition: liblwgeom.h:2057
#define SHPT_POLYGONZ
Definition: shapefil.h:313
char message[SHPLOADERMSGLEN]
void ReleasePolygons(Ring **polys, int npolys)
#define SHPT_POINT
Definition: shapefil.h:307
void stringbuffer_destroy(stringbuffer_t *s)
Free the stringbuffer_t and all memory managed within it.
Definition: stringbuffer.c:78
static int utf8(const char *fromcode, char *inputbuf, char **outputbuf)
#define POINTTYPE
LWTYPE numbers, used internally by PostGIS.
Definition: liblwgeom.h:84
LWPOLY * lwpoly_construct_empty(int srid, char hasz, char hasm)
Definition: lwpoly.c:137
void SHPAPI_CALL DBFClose(DBFHandle psDBF)
Definition: dbfopen.c:578
double * padfZ
Definition: shapefil.h:351
const char * colmap_pg_by_dbf(colmap *map, const char *dbfname)
Definition: shpcommon.c:197
const char * stringbuffer_getstring(stringbuffer_t *s)
Returns a reference to the internal string being managed by the stringbuffer.
Definition: stringbuffer.c:149
#define MAXVALUELEN
void free(void *)
LWGEOM * lwpoint_as_lwgeom(const LWPOINT *obj)
Definition: lwgeom.c:267
int ShpLoaderGetSQLCopyStatement(SHPLOADERSTATE *state, char **strheader)
void * malloc(YYSIZE_T)
int lwpoly_add_ring(LWPOLY *poly, POINTARRAY *pa)
Add a ring, allocating extra space if necessary.
Definition: lwpoly.c:225
int ShpLoaderGenerateSQLRowStatement(SHPLOADERSTATE *state, int item, char **strrecord)
int SHPAPI_CALL DBFIsAttributeNULL(DBFHandle psDBF, int iRecord, int iField)
Definition: dbfopen.c:1175
void strtolower(char *s)
void SHPAPI_CALL SHPClose(SHPHandle hSHP)
Definition: shpopen.c:759
LWPOINT * lwpoint_construct(int srid, GBOX *bbox, POINTARRAY *point)
Definition: lwpoint.c:111
#define SHPLOADERERR
int nSHPType
Definition: shapefil.h:340
double y
Definition: liblwgeom.h:351
unsigned int linked
#define MULTILINETYPE
Definition: liblwgeom.h:88
#define LWDEBUGF(level, msg,...)
Definition: lwgeom_log.h:88
#define SHPT_ARC
Definition: shapefil.h:308
double * padfM
Definition: shapefil.h:352
#define SHPLOADERMSGLEN
tuple y
Definition: pixval.py:54
#define COLLECTIONTYPE
Definition: liblwgeom.h:90
int SHPAPI_CALL DBFIsRecordDeleted(DBFHandle psDBF, int iShape)
Definition: dbfopen.c:1732
#define FLAGS_SET_M(flags, value)
Definition: liblwgeom.h:146
LWGEOM * lwcollection_as_lwgeom(const LWCOLLECTION *obj)
Definition: lwgeom.c:232