source: trunk/loader/shp2pgsql-core.c @ 5185

Last change on this file since 5185 was 5185, checked in by pramsey, 7 years ago

Fix mis-handling of the commandline operations modes (#401)

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