shapelib 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +36 -0
- data/LICENSE-CC-BY-SA-3.0 +375 -0
- data/LICENSE-LGPL +481 -0
- data/LICENSE-MIT +21 -0
- data/README.md +75 -24
- data/Rakefile +142 -32
- data/doc/Interface.html +53 -32
- data/doc/Interface.rd +51 -30
- data/ext/shapelib_ext/extconf.rb +5 -1
- data/ext/shapelib_ext/main.c +37 -0
- data/ext/shapelib_ext/sfcode.h +67 -39
- data/ext/shapelib_ext/sflist.h +18 -18
- data/ext/shapelib_ext/shpplus.c +245 -57
- data/ext/shapelib_ext/shpplus.h +1 -0
- data/ext/shapelib_ext/spcode.h +245 -126
- data/ext/shapelib_ext/splist.h +23 -41
- data/ext/shapelib_ext/spwkt.h +29 -24
- data/ext/shapelib_ext/valconv.h +33 -21
- data/lib/shapelib/shape.rb +8 -1
- data/lib/shapelib/version.rb +4 -2
- data/lib/shapelib.rb +2 -1
- data/shapelib.gemspec +58 -0
- data/test/data/ZillowNeighborhoods-CA/ZillowNeighborhoods-CA.dbf +0 -0
- data/test/data/ZillowNeighborhoods-CA/ZillowNeighborhoods-CA.prj +1 -0
- data/test/data/ZillowNeighborhoods-CA/ZillowNeighborhoods-CA.shp +0 -0
- data/test/data/ZillowNeighborhoods-CA/ZillowNeighborhoods-CA.shp.xml +3 -0
- data/test/data/ZillowNeighborhoods-CA/ZillowNeighborhoods-CA.shx +0 -0
- data/test/test_zillow_data.rb +2233 -0
- metadata +70 -65
- data/test/libtest.rb +0 -80
- data/test/zsample1.rb +0 -168
- data/test/ztest1.rb +0 -253
data/ext/shapelib_ext/shpplus.c
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
1
2
|
#include <stdlib.h>
|
|
2
3
|
#include <ctype.h>
|
|
3
4
|
#include <string.h>
|
|
@@ -10,6 +11,9 @@
|
|
|
10
11
|
#include <ruby.h>
|
|
11
12
|
#include "shpplus.h"
|
|
12
13
|
|
|
14
|
+
/* Match native shapelib's numeric formatting instead of Ruby's substitute. */
|
|
15
|
+
#undef snprintf
|
|
16
|
+
|
|
13
17
|
#define HAS_DBFMARKRECORDDELETED 0
|
|
14
18
|
#define ATTR_TABLE_DEFAULT 16
|
|
15
19
|
|
|
@@ -80,16 +84,26 @@ static shapefile_t *ShapefileInit(void)
|
|
|
80
84
|
sfile = xmalloc(sizeof(shapefile_t));
|
|
81
85
|
if (sfile == NULL)
|
|
82
86
|
return NULL;
|
|
87
|
+
sfile->hshp = NULL;
|
|
88
|
+
sfile->hdbf = NULL;
|
|
83
89
|
sfile->nrecs = sfile->nfields = -1;
|
|
84
90
|
sfile->ftab = NULL;
|
|
85
91
|
sfile->irec = 0;
|
|
86
92
|
return sfile;
|
|
87
93
|
}
|
|
88
94
|
|
|
95
|
+
static void ShapefileFail(shapefile_t *sfile, const char *operation,
|
|
96
|
+
const char *filename)
|
|
97
|
+
{
|
|
98
|
+
shapefile_close(sfile);
|
|
99
|
+
xfree(sfile);
|
|
100
|
+
rb_raise(rb_eIOError, "%s failed for %s", operation, filename);
|
|
101
|
+
}
|
|
102
|
+
|
|
89
103
|
static void ShapefileBuildFieldTable(shapefile_t *sfile)
|
|
90
104
|
{
|
|
91
105
|
int i;
|
|
92
|
-
if (sfile->ftab)
|
|
106
|
+
if (!sfile->hdbf || sfile->ftab)
|
|
93
107
|
return;
|
|
94
108
|
sfile->nfields = shapefile_field_count(sfile);
|
|
95
109
|
sfile->ftab = xmalloc(sizeof(field_t) * sfile->nfields);
|
|
@@ -107,12 +121,22 @@ static void ShapefileBuildFieldTable(shapefile_t *sfile)
|
|
|
107
121
|
}
|
|
108
122
|
}
|
|
109
123
|
|
|
124
|
+
static VALUE ShapefileBuildFieldTableProtected(VALUE ptr)
|
|
125
|
+
{
|
|
126
|
+
ShapefileBuildFieldTable((shapefile_t *)ptr);
|
|
127
|
+
return Qnil;
|
|
128
|
+
}
|
|
129
|
+
|
|
110
130
|
static void ShapefileWriteAttribute(shapefile_t *sfile,
|
|
111
131
|
int irec, int ifield, attrib_t *a)
|
|
112
132
|
{
|
|
113
133
|
int s;
|
|
114
134
|
if (a->type == -1) {
|
|
115
135
|
rb_bug("supposed to be rejected in shapefile_write");
|
|
136
|
+
#ifdef HAVE_CONST_FTDATE
|
|
137
|
+
} else if (sfile->ftab[ifield].type == FTDate) {
|
|
138
|
+
s = DBFWriteAttributeDirectly(sfile->hdbf, irec, ifield, a->val.sval);
|
|
139
|
+
#endif
|
|
116
140
|
} else if (a->type == FTInteger) {
|
|
117
141
|
s = DBFWriteIntegerAttribute(sfile->hdbf, irec, ifield,
|
|
118
142
|
a->val.ival);
|
|
@@ -141,12 +165,20 @@ static void ShapefileWriteAttribute(shapefile_t *sfile,
|
|
|
141
165
|
shapefile_t *shapefile_open(const char *filename, const char *mode)
|
|
142
166
|
{
|
|
143
167
|
shapefile_t *sfile;
|
|
168
|
+
int state;
|
|
144
169
|
sfile = ShapefileInit();
|
|
145
170
|
sfile->hshp = SHPOpen(filename, mode);
|
|
146
171
|
monitor(("SHPOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hshp));
|
|
172
|
+
if (!sfile->hshp)
|
|
173
|
+
ShapefileFail(sfile, "SHPOpen", filename);
|
|
147
174
|
sfile->hdbf = DBFOpen(filename, mode);
|
|
148
175
|
monitor(("DBFOpen(\"%s\", \"%s\") -> %p", filename, mode, sfile->hdbf));
|
|
149
|
-
|
|
176
|
+
rb_protect(ShapefileBuildFieldTableProtected, (VALUE)sfile, &state);
|
|
177
|
+
if (state) {
|
|
178
|
+
shapefile_close(sfile);
|
|
179
|
+
xfree(sfile);
|
|
180
|
+
rb_jump_tag(state);
|
|
181
|
+
}
|
|
150
182
|
return sfile;
|
|
151
183
|
}
|
|
152
184
|
|
|
@@ -156,8 +188,12 @@ shapefile_t *shapefile_new(const char *filename, int shptype)
|
|
|
156
188
|
sfile = ShapefileInit();
|
|
157
189
|
sfile->hshp = SHPCreate(filename, shptype);
|
|
158
190
|
monitor(("SHPCreate(\"%s\", %d) -> %p", filename, shptype, sfile->hshp));
|
|
191
|
+
if (!sfile->hshp)
|
|
192
|
+
ShapefileFail(sfile, "SHPCreate", filename);
|
|
159
193
|
sfile->hdbf = DBFCreate(filename);
|
|
160
194
|
monitor(("DBFCreate(\"%s\") -> %p", filename, sfile->hdbf));
|
|
195
|
+
if (!sfile->hdbf)
|
|
196
|
+
ShapefileFail(sfile, "DBFCreate", filename);
|
|
161
197
|
return sfile;
|
|
162
198
|
}
|
|
163
199
|
|
|
@@ -173,6 +209,16 @@ int shapefile_add_field(shapefile_t *sfile, const char *name,
|
|
|
173
209
|
rb_raise(rb_eRuntimeError, "add field before you write a record");
|
|
174
210
|
return -1;
|
|
175
211
|
}
|
|
212
|
+
if ((atype == FTInteger || atype == FTDouble) && (decimals < 0 || decimals > 255))
|
|
213
|
+
rb_raise(rb_eArgError, "numeric field precision must be between 0 and 255");
|
|
214
|
+
#ifdef HAVE_CONST_FTDATE
|
|
215
|
+
if (atype == FTDate && (width != 8 || decimals != 0))
|
|
216
|
+
rb_raise(rb_eArgError, "Date fields require width 8 and zero decimal places");
|
|
217
|
+
#endif
|
|
218
|
+
if (strlen(name) > XBASE_FLDNAME_LEN_WRITE)
|
|
219
|
+
rb_raise(rb_eArgError, "field name exceeds 10 bytes");
|
|
220
|
+
if (DBFGetFieldIndex(sfile->hdbf, name) >= 0)
|
|
221
|
+
rb_raise(rb_eArgError, "field name already exists: %s", name);
|
|
176
222
|
monitor(("DBFAddField(%p, %s, %d, %d, %d)", sfile->hdbf, name,
|
|
177
223
|
atype, width, decimals));
|
|
178
224
|
r = DBFAddField(sfile->hdbf, name, (DBFFieldType)atype, width, decimals);
|
|
@@ -206,7 +252,8 @@ int shapefile_field_decimals(shapefile_t *sfile, unsigned ifield)
|
|
|
206
252
|
int decimals;
|
|
207
253
|
if (NULL == sfile->hdbf)
|
|
208
254
|
return -1;
|
|
209
|
-
DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, &decimals)
|
|
255
|
+
if (DBFGetFieldInfo(sfile->hdbf, ifield, NULL, NULL, &decimals) == FTInvalid)
|
|
256
|
+
return -1;
|
|
210
257
|
return decimals;
|
|
211
258
|
}
|
|
212
259
|
|
|
@@ -259,7 +306,8 @@ const char *shapefile_field_name(shapefile_t *sfile, unsigned ifield)
|
|
|
259
306
|
static char buf[DBF_NAMELEN];
|
|
260
307
|
if (NULL == sfile->hdbf)
|
|
261
308
|
return NULL;
|
|
262
|
-
DBFGetFieldInfo(sfile->hdbf, ifield, buf, NULL, NULL)
|
|
309
|
+
if (DBFGetFieldInfo(sfile->hdbf, ifield, buf, NULL, NULL) == FTInvalid)
|
|
310
|
+
return NULL;
|
|
263
311
|
return buf;
|
|
264
312
|
}
|
|
265
313
|
|
|
@@ -282,7 +330,8 @@ int shapefile_field_width(shapefile_t *sfile, unsigned ifield)
|
|
|
282
330
|
int width;
|
|
283
331
|
if (NULL == sfile->hdbf)
|
|
284
332
|
return -1;
|
|
285
|
-
DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, NULL)
|
|
333
|
+
if (DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, NULL) == FTInvalid)
|
|
334
|
+
return -1;
|
|
286
335
|
return width;
|
|
287
336
|
}
|
|
288
337
|
|
|
@@ -319,29 +368,19 @@ void shapefile_bound(shapefile_t *sfile, double *minbound, double *maxbound)
|
|
|
319
368
|
SHPGetInfo(sfile->hshp, NULL, NULL, minbound, maxbound);
|
|
320
369
|
}
|
|
321
370
|
|
|
322
|
-
|
|
323
|
-
|
|
371
|
+
struct shapefile_read_attributes_args {
|
|
372
|
+
shapefile_t *sfile;
|
|
324
373
|
shape_t *shape;
|
|
374
|
+
int ishape;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
static VALUE ShapefileReadAttributesProtected(VALUE ptr)
|
|
378
|
+
{
|
|
379
|
+
struct shapefile_read_attributes_args *args = (struct shapefile_read_attributes_args *)ptr;
|
|
380
|
+
shapefile_t *sfile = args->sfile;
|
|
381
|
+
shape_t *shape = args->shape;
|
|
382
|
+
int ishape = args->ishape;
|
|
325
383
|
int i, iattr;
|
|
326
|
-
if (NULL == sfile->hshp)
|
|
327
|
-
return NULL;
|
|
328
|
-
if (ishape < 0) {
|
|
329
|
-
if (sfile->nrecs < 0) {
|
|
330
|
-
shapefile_size(sfile);
|
|
331
|
-
sfile->irec = 0;
|
|
332
|
-
}
|
|
333
|
-
ishape = (sfile->irec)++;
|
|
334
|
-
} else {
|
|
335
|
-
sfile->irec = ishape + 1;
|
|
336
|
-
}
|
|
337
|
-
shape = shape_init(SHPReadObject(sfile->hshp, ishape));
|
|
338
|
-
if (shape == NULL)
|
|
339
|
-
return NULL;
|
|
340
|
-
monitor(("SHPReadObject(%p) -> %p", sfile->hshp, shape));
|
|
341
|
-
if (shape->obj->nSHPType == SHPT_NULL) {
|
|
342
|
-
monitor(("shape type NULL"));
|
|
343
|
-
return NULL;
|
|
344
|
-
}
|
|
345
384
|
ShapefileBuildFieldTable(sfile);
|
|
346
385
|
if (NULL == sfile->hdbf)
|
|
347
386
|
goto no_dbf;
|
|
@@ -351,6 +390,12 @@ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
|
|
|
351
390
|
goto no_dbf;
|
|
352
391
|
}
|
|
353
392
|
#endif
|
|
393
|
+
if (sfile->nfields > 0 && DBFReadTuple(sfile->hdbf, ishape) == NULL) {
|
|
394
|
+
/* A partial read can overwrite the buffer without changing its record number. */
|
|
395
|
+
if (!sfile->hdbf->bCurrentRecordModified)
|
|
396
|
+
sfile->hdbf->nCurrentRecord = -1;
|
|
397
|
+
rb_raise(rb_eIOError, "cannot read DBF record %d", ishape);
|
|
398
|
+
}
|
|
354
399
|
ShapeSetAttrTable(shape, sfile->nfields);
|
|
355
400
|
for (i = iattr = 0; i < sfile->nfields; i++) {
|
|
356
401
|
attrib_t *a;
|
|
@@ -381,6 +426,47 @@ shape_t *shapefile_read(shapefile_t *sfile, int ishape)
|
|
|
381
426
|
;
|
|
382
427
|
}
|
|
383
428
|
no_dbf:
|
|
429
|
+
return Qnil;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
shape_t *shapefile_read(shapefile_t *sfile, int ishape)
|
|
433
|
+
{
|
|
434
|
+
shape_t *shape;
|
|
435
|
+
struct shapefile_read_attributes_args args;
|
|
436
|
+
int state;
|
|
437
|
+
int sequential = ishape < 0;
|
|
438
|
+
if (NULL == sfile->hshp)
|
|
439
|
+
return NULL;
|
|
440
|
+
if (ishape < 0) {
|
|
441
|
+
if (sfile->nrecs < 0) {
|
|
442
|
+
shapefile_size(sfile);
|
|
443
|
+
}
|
|
444
|
+
ishape = sfile->irec;
|
|
445
|
+
} else {
|
|
446
|
+
sfile->irec = ishape + 1;
|
|
447
|
+
}
|
|
448
|
+
while (1) {
|
|
449
|
+
shape = shape_init(SHPReadObject(sfile->hshp, ishape));
|
|
450
|
+
if (shape == NULL)
|
|
451
|
+
return NULL;
|
|
452
|
+
sfile->irec = ishape + 1;
|
|
453
|
+
monitor(("SHPReadObject(%p) -> %p", sfile->hshp, shape));
|
|
454
|
+
if (shape->obj->nSHPType != SHPT_NULL)
|
|
455
|
+
break;
|
|
456
|
+
monitor(("shape type NULL"));
|
|
457
|
+
shape_close(shape);
|
|
458
|
+
if (!sequential)
|
|
459
|
+
return NULL;
|
|
460
|
+
ishape = sfile->irec;
|
|
461
|
+
}
|
|
462
|
+
args.sfile = sfile;
|
|
463
|
+
args.shape = shape;
|
|
464
|
+
args.ishape = ishape;
|
|
465
|
+
rb_protect(ShapefileReadAttributesProtected, (VALUE)&args, &state);
|
|
466
|
+
if (state) {
|
|
467
|
+
shape_close(shape);
|
|
468
|
+
rb_jump_tag(state);
|
|
469
|
+
}
|
|
384
470
|
return shape;
|
|
385
471
|
}
|
|
386
472
|
|
|
@@ -417,23 +503,115 @@ int shapefile_size(shapefile_t *sfile)
|
|
|
417
503
|
int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
|
|
418
504
|
{
|
|
419
505
|
int r;
|
|
506
|
+
int shape_type, file_type, record_count;
|
|
507
|
+
int ipart;
|
|
420
508
|
int ifield;
|
|
421
509
|
int iattr;
|
|
422
510
|
int *usedfield;
|
|
511
|
+
VALUE usedfield_storage = 0;
|
|
423
512
|
/* precondition */
|
|
424
513
|
if (!sfile->hshp || !shape->obj)
|
|
425
514
|
return -1;
|
|
515
|
+
if (!sfile->hdbf)
|
|
516
|
+
rb_raise(rb_eIOError, "DBF file is not open for writing");
|
|
517
|
+
SHPGetInfo(sfile->hshp, &record_count, &file_type, NULL, NULL);
|
|
518
|
+
if (irec < -1 || (irec >= 0 && irec >= record_count))
|
|
519
|
+
rb_raise(rb_eArgError,
|
|
520
|
+
"write index %d must be -1 (append) or an existing record index (record count: %d)",
|
|
521
|
+
irec, record_count);
|
|
522
|
+
shape_type = shape->obj->nSHPType;
|
|
523
|
+
switch (shape_type) {
|
|
524
|
+
case SHPT_NULL:
|
|
525
|
+
case SHPT_POINT: case SHPT_POINTM: case SHPT_POINTZ:
|
|
526
|
+
case SHPT_ARC: case SHPT_ARCM: case SHPT_ARCZ:
|
|
527
|
+
case SHPT_POLYGON: case SHPT_POLYGONM: case SHPT_POLYGONZ:
|
|
528
|
+
case SHPT_MULTIPOINT: case SHPT_MULTIPOINTM: case SHPT_MULTIPOINTZ:
|
|
529
|
+
case SHPT_MULTIPATCH:
|
|
530
|
+
break;
|
|
531
|
+
default:
|
|
532
|
+
rb_raise(rb_eArgError, "unsupported shape type %d", shape_type);
|
|
533
|
+
}
|
|
534
|
+
if (shape_type != SHPT_NULL && shape_type != file_type)
|
|
535
|
+
rb_raise(rb_eArgError, "shape type %d does not match ShapeFile type %d", shape_type, file_type);
|
|
536
|
+
if (shape_type == SHPT_POINT || shape_type == SHPT_POINTM || shape_type == SHPT_POINTZ) {
|
|
537
|
+
if (shape->obj->nVertices < 1)
|
|
538
|
+
rb_raise(rb_eArgError, "point has no vertices");
|
|
539
|
+
if (shape->obj->nVertices > 1)
|
|
540
|
+
rb_raise(rb_eArgError, "point must have exactly one vertex (got %d)", shape->obj->nVertices);
|
|
541
|
+
}
|
|
542
|
+
if (shape_type == SHPT_NULL && shape->obj->nVertices != 0)
|
|
543
|
+
rb_raise(rb_eArgError, "null shape must have no vertices (got %d)", shape->obj->nVertices);
|
|
544
|
+
/* Match the part-layout checks in SHPReadObject before changing either file. */
|
|
545
|
+
for (ipart = 0; ipart < shape->obj->nParts; ipart++) {
|
|
546
|
+
int start = shape->obj->panPartStart[ipart];
|
|
547
|
+
int vertices = shape->obj->nVertices;
|
|
548
|
+
if (start < 0 || (vertices > 0 ? start >= vertices : start != 0))
|
|
549
|
+
rb_raise(rb_eArgError, "part_start[%d] is out of bounds for %d vertices", ipart, vertices);
|
|
550
|
+
if (ipart > 0 && start <= shape->obj->panPartStart[ipart - 1])
|
|
551
|
+
rb_raise(rb_eArgError, "part_start[%d] must be greater than part_start[%d]", ipart, ipart - 1);
|
|
552
|
+
if (shape_type == SHPT_MULTIPATCH &&
|
|
553
|
+
(shape->obj->panPartType[ipart] < 0 || shape->obj->panPartType[ipart] > 5))
|
|
554
|
+
rb_raise(rb_eArgError, "part_type[%d] must be between 0 and 5 (got %d)",
|
|
555
|
+
ipart, shape->obj->panPartType[ipart]);
|
|
556
|
+
}
|
|
426
557
|
ShapefileBuildFieldTable(sfile);
|
|
558
|
+
/* Reject invalid attributes before changing either file. */
|
|
559
|
+
for (iattr = 0; iattr < shape->nattr; iattr++) {
|
|
560
|
+
attrib_t *a = &(shape->attr[iattr]);
|
|
561
|
+
DBFFieldType type;
|
|
562
|
+
if (a->type == -1)
|
|
563
|
+
continue;
|
|
564
|
+
ifield = DBFGetFieldIndex(sfile->hdbf, a->name);
|
|
565
|
+
if (ifield == -1)
|
|
566
|
+
rb_raise(rb_eArgError, "attribute %s not defined in ShapeFile", a->name);
|
|
567
|
+
type = sfile->ftab[ifield].type;
|
|
568
|
+
#ifdef HAVE_CONST_FTDATE
|
|
569
|
+
if (type == FTDate) {
|
|
570
|
+
if (a->type != FTString && a->type != FTDate)
|
|
571
|
+
rb_raise(rb_eTypeError, "attribute %s has incompatible type for ShapeFile field", a->name);
|
|
572
|
+
if (shapefile_field_width(sfile, ifield) != 8 || shapefile_field_decimals(sfile, ifield) != 0)
|
|
573
|
+
rb_raise(rb_eArgError, "Date fields require width 8 and zero decimal places");
|
|
574
|
+
if (strlen(a->val.sval) != 8 || strspn(a->val.sval, "0123456789") != 8)
|
|
575
|
+
rb_raise(rb_eArgError, "attribute %s must be an 8-digit YYYYMMDD string", a->name);
|
|
576
|
+
continue;
|
|
577
|
+
}
|
|
578
|
+
#endif
|
|
579
|
+
if (!(((type == FTInteger || type == FTDouble) &&
|
|
580
|
+
(a->type == FTInteger || a->type == FTDouble)) ||
|
|
581
|
+
((type == FTString || type == FTLogical) &&
|
|
582
|
+
(a->type == FTString || a->type == FTLogical
|
|
583
|
+
#ifdef HAVE_CONST_FTDATE
|
|
584
|
+
|| a->type == FTDate
|
|
585
|
+
#endif
|
|
586
|
+
))))
|
|
587
|
+
rb_raise(rb_eTypeError, "attribute %s has incompatible type for ShapeFile field", a->name);
|
|
588
|
+
if (type == FTLogical && a->val.sval[0] != 'T' && a->val.sval[0] != 'F')
|
|
589
|
+
rb_raise(rb_eArgError, "attribute %s must start with T or F for a Logical field", a->name);
|
|
590
|
+
if (type == FTString && strlen(a->val.sval) > (size_t)shapefile_field_width(sfile, ifield))
|
|
591
|
+
rb_raise(rb_eRuntimeError, "attribute %s exceeds field width", a->name);
|
|
592
|
+
if (type == FTInteger || type == FTDouble) {
|
|
593
|
+
int width, decimals, length;
|
|
594
|
+
double value = a->type == FTInteger ? (double)a->val.ival : a->val.fval;
|
|
595
|
+
DBFGetFieldInfo(sfile->hdbf, ifield, NULL, &width, &decimals);
|
|
596
|
+
length = snprintf(NULL, 0, "%.*f", decimals, value);
|
|
597
|
+
if (length < 0)
|
|
598
|
+
rb_raise(rb_eRuntimeError, "cannot format numeric attribute %s", a->name);
|
|
599
|
+
if (length > width)
|
|
600
|
+
rb_raise(rb_eRuntimeError, "attribute %s exceeds field width", a->name);
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
/* Ruby can reclaim this temporary buffer if attribute writing raises. */
|
|
604
|
+
usedfield = ALLOCV_N(int, usedfield_storage, sfile->nfields);
|
|
605
|
+
for (ifield = 0; ifield < sfile->nfields; ifield++) {
|
|
606
|
+
usedfield[ifield] = 0;
|
|
607
|
+
}
|
|
427
608
|
/* write the object */
|
|
428
609
|
r = SHPWriteObject(sfile->hshp, irec, shape->obj);
|
|
429
610
|
monitor(("SHPWriteObject(%p, %d, %p) -> %d", sfile->hshp, irec,
|
|
430
611
|
shape->obj, r));
|
|
431
|
-
if (r < 0)
|
|
612
|
+
if (r < 0) {
|
|
613
|
+
ALLOCV_END(usedfield_storage);
|
|
432
614
|
return -1;
|
|
433
|
-
/* make field table */
|
|
434
|
-
usedfield = xmalloc(sizeof(int) * sfile->nfields);
|
|
435
|
-
for (ifield = 0; ifield < sfile->nfields; ifield++) {
|
|
436
|
-
usedfield[ifield] = 0;
|
|
437
615
|
}
|
|
438
616
|
for (iattr = 0; iattr < shape->nattr; iattr++) {
|
|
439
617
|
attrib_t *a;
|
|
@@ -442,10 +620,6 @@ int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
|
|
|
442
620
|
continue;
|
|
443
621
|
}
|
|
444
622
|
ifield = DBFGetFieldIndex(sfile->hdbf, a->name);
|
|
445
|
-
if (ifield == -1) {
|
|
446
|
-
rb_raise(rb_eArgError,
|
|
447
|
-
"attribute %s not defined in ShapeFile", a->name);
|
|
448
|
-
}
|
|
449
623
|
ShapefileWriteAttribute(sfile, r, ifield, a);
|
|
450
624
|
usedfield[ifield] = 1;
|
|
451
625
|
}
|
|
@@ -453,29 +627,35 @@ int shapefile_write(shapefile_t *sfile, int irec, shape_t *shape)
|
|
|
453
627
|
int r2;
|
|
454
628
|
if (usedfield[ifield] == 0) {
|
|
455
629
|
r2 = DBFWriteNULLAttribute(sfile->hdbf, r, ifield);
|
|
456
|
-
monitor(("DBFWriteNULLAttribute(%p %d %d -> %d",
|
|
457
|
-
sfile->hdbf, r, ifield, r2));
|
|
630
|
+
monitor(("DBFWriteNULLAttribute(%p %d %d -> %d", sfile->hdbf, r, ifield, r2));
|
|
458
631
|
if (r2 == 0)
|
|
459
632
|
rb_raise(rb_eRuntimeError, "write error");
|
|
460
633
|
}
|
|
461
634
|
}
|
|
462
|
-
|
|
635
|
+
ALLOCV_END(usedfield_storage);
|
|
463
636
|
return r;
|
|
464
637
|
}
|
|
465
638
|
|
|
639
|
+
static VALUE ShapeInitProtected(VALUE ptr)
|
|
640
|
+
{
|
|
641
|
+
shape_t *sh = xmalloc(sizeof(shape_t));
|
|
642
|
+
sh->obj = (SHPObject *)ptr;
|
|
643
|
+
sh->attr = NULL;
|
|
644
|
+
sh->nattr = 0;
|
|
645
|
+
return (VALUE)sh;
|
|
646
|
+
}
|
|
647
|
+
|
|
466
648
|
static shape_t *shape_init(SHPObject *objp)
|
|
467
649
|
{
|
|
468
650
|
shape_t *sh;
|
|
651
|
+
int state;
|
|
469
652
|
if (objp == NULL)
|
|
470
653
|
return NULL;
|
|
471
|
-
sh =
|
|
472
|
-
if (
|
|
654
|
+
sh = (shape_t *)rb_protect(ShapeInitProtected, (VALUE)objp, &state);
|
|
655
|
+
if (state) {
|
|
473
656
|
SHPDestroyObject(objp);
|
|
474
|
-
|
|
657
|
+
rb_jump_tag(state);
|
|
475
658
|
}
|
|
476
|
-
sh->obj = objp;
|
|
477
|
-
sh->attr = NULL;
|
|
478
|
-
sh->nattr = 0;
|
|
479
659
|
return sh;
|
|
480
660
|
}
|
|
481
661
|
|
|
@@ -545,20 +725,37 @@ int shape_attrib_index(shape_t *sh, const char *key, int extend)
|
|
|
545
725
|
}
|
|
546
726
|
for (i = 0; i < sh->nattr; i++) {
|
|
547
727
|
if (sh->attr[i].type == -1) {
|
|
548
|
-
|
|
728
|
+
strncpy(sh->attr[i].name, key, sizeof(sh->attr[i].name) - 1);
|
|
729
|
+
sh->attr[i].name[sizeof(sh->attr[i].name) - 1] = '\0';
|
|
549
730
|
return i;
|
|
550
731
|
}
|
|
551
732
|
}
|
|
552
733
|
if (extend) {
|
|
553
734
|
i = sh->nattr;
|
|
554
735
|
ShapeSetAttrTable(sh, sh->nattr * 2);
|
|
555
|
-
|
|
736
|
+
strncpy(sh->attr[i].name, key, sizeof(sh->attr[i].name) - 1);
|
|
737
|
+
sh->attr[i].name[sizeof(sh->attr[i].name) - 1] = '\0';
|
|
556
738
|
return i;
|
|
557
739
|
} else {
|
|
558
740
|
return -1;
|
|
559
741
|
}
|
|
560
742
|
}
|
|
561
743
|
|
|
744
|
+
void shape_attrib_clear_value(attrib_t *attr)
|
|
745
|
+
{
|
|
746
|
+
switch (attr->type) {
|
|
747
|
+
case -1:
|
|
748
|
+
case FTInteger:
|
|
749
|
+
case FTDouble:
|
|
750
|
+
break;
|
|
751
|
+
default:
|
|
752
|
+
xfree(attr->val.sval);
|
|
753
|
+
break;
|
|
754
|
+
}
|
|
755
|
+
attr->type = -1;
|
|
756
|
+
memset(&attr->val, 0, sizeof(attr->val));
|
|
757
|
+
}
|
|
758
|
+
|
|
562
759
|
int shape_close(shape_t *shape)
|
|
563
760
|
{
|
|
564
761
|
if (shape->obj) {
|
|
@@ -569,18 +766,9 @@ int shape_close(shape_t *shape)
|
|
|
569
766
|
if (shape->attr) {
|
|
570
767
|
int i;
|
|
571
768
|
for (i = 0; i < shape->nattr; i++) {
|
|
572
|
-
|
|
573
|
-
case FTInteger:
|
|
574
|
-
case FTDouble:
|
|
575
|
-
break;
|
|
576
|
-
case FTString:
|
|
577
|
-
default:
|
|
578
|
-
free(shape->attr[i].val.sval);
|
|
579
|
-
shape->attr[i].val.ival = 0;
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
769
|
+
shape_attrib_clear_value(&shape->attr[i]);
|
|
582
770
|
}
|
|
583
|
-
|
|
771
|
+
xfree(shape->attr);
|
|
584
772
|
shape->attr = NULL;
|
|
585
773
|
}
|
|
586
774
|
xfree(shape);
|
data/ext/shapelib_ext/shpplus.h
CHANGED
|
@@ -62,6 +62,7 @@ extern shape_t *shape_new_obj(int shapetype,
|
|
|
62
62
|
int n_parts, int *part_start, int *part_type, int n_vert,
|
|
63
63
|
double *x, double *y, double *m, double *z);
|
|
64
64
|
extern int shape_close(shape_t *shape);
|
|
65
|
+
extern void shape_attrib_clear_value(attrib_t *attr);
|
|
65
66
|
extern int shape_attrib_index(shape_t *sh, const char *key, int extend);
|
|
66
67
|
|
|
67
68
|
/*
|