You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
606 lines
15 KiB
606 lines
15 KiB
7 years ago
|
/******************************************************************************
|
||
|
* Copyright (c) 2004, Eric G. Miller
|
||
|
*
|
||
|
* This code is based in part on the earlier work of Frank Warmerdam
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
||
|
* copy of this software and associated documentation files (the "Software"),
|
||
|
* to deal in the Software without restriction, including without limitation
|
||
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||
|
* and/or sell copies of the Software, and to permit persons to whom the
|
||
|
* Software is furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included
|
||
|
* in all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||
|
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||
|
* DEALINGS IN THE SOFTWARE.
|
||
|
******************************************************************************
|
||
|
* shpsort
|
||
|
*
|
||
|
* Rewrite a shapefile sorted by a field or by the geometry. For polygons,
|
||
|
* sort by area, for lines sort by length and do nothing for all others.
|
||
|
*
|
||
|
* $Log: shpsort.c,v $
|
||
|
* Revision 1.3 2004-07-06 21:23:17 fwarmerdam
|
||
|
* minor const warning fix
|
||
|
*
|
||
|
* Revision 1.2 2004/07/06 21:20:49 fwarmerdam
|
||
|
* major upgrade .. sort on multiple fields
|
||
|
*
|
||
|
* Revision 1.4 2004/06/30 18:19:53 emiller
|
||
|
* handle POINTZ, POINTM
|
||
|
*
|
||
|
* Revision 1.3 2004/06/30 17:40:32 emiller
|
||
|
* major rewrite allows sorting on multiple fields.
|
||
|
*
|
||
|
* Revision 1.2 2004/06/23 23:19:58 emiller
|
||
|
* use tuple copy, misc changes
|
||
|
*
|
||
|
* Revision 1.1 2004/06/23 21:38:17 emiller
|
||
|
* Initial revision
|
||
|
*
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <ctype.h>
|
||
|
#include <string.h>
|
||
|
#include <math.h>
|
||
|
#include "shapefil.h"
|
||
|
|
||
|
enum FieldOrderEnum {DESCENDING, ASCENDING};
|
||
|
enum FieldTypeEnum {
|
||
|
FIDType = -2,
|
||
|
SHPType = -1,
|
||
|
StringType = FTString,
|
||
|
LogicalType = FTLogical,
|
||
|
IntegerType = FTInteger,
|
||
|
DoubleType = FTDouble
|
||
|
};
|
||
|
|
||
|
struct DataUnion {
|
||
|
int null;
|
||
|
union {
|
||
|
int i;
|
||
|
double d;
|
||
|
char *s;
|
||
|
} u;
|
||
|
};
|
||
|
|
||
|
struct DataStruct {
|
||
|
int record;
|
||
|
struct DataUnion *value;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
globals used in sorting, each element could have a pointer to
|
||
|
a single data struct, but that's still nShapes pointers more
|
||
|
memory. Alternatively, write a custom sort rather than using
|
||
|
library qsort.
|
||
|
*/
|
||
|
int nFields;
|
||
|
int *fldIdx;
|
||
|
int *fldOrder;
|
||
|
int *fldType;
|
||
|
int shpType;
|
||
|
int nShapes;
|
||
|
|
||
|
static struct DataStruct * build_index (SHPHandle shp, DBFHandle dbf);
|
||
|
static char * dupstr (const char *);
|
||
|
static void copy_related (const char *inName, const char *outName,
|
||
|
const char *old_ext, const char *new_ext);
|
||
|
static char ** split(const char *arg, const char *delim);
|
||
|
static int compare(const void *, const void *);
|
||
|
static double area2d_polygon (int n, double *x, double *y);
|
||
|
static double shp_area (SHPObject *feat);
|
||
|
static double length2d_polyline (int n, double *x, double *y);
|
||
|
static double shp_length (SHPObject *feat);
|
||
|
|
||
|
int main (int argc, char *argv[]) {
|
||
|
|
||
|
SHPHandle inSHP, outSHP;
|
||
|
DBFHandle inDBF, outDBF;
|
||
|
int len;
|
||
|
int i;
|
||
|
char **fieldNames;
|
||
|
char **strOrder = 0;
|
||
|
struct DataStruct *index;
|
||
|
int width;
|
||
|
int decimals;
|
||
|
SHPObject *feat;
|
||
|
void *tuple;
|
||
|
|
||
|
if (argc < 4) {
|
||
|
printf("USAGE: shpsort <infile> <outfile> <field[;...]> [<(ASCENDING|DESCENDING)[;...]>]\n");
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
inSHP = SHPOpen (argv[1], "rb");
|
||
|
if (!inSHP) {
|
||
|
fputs("Couldn't open shapefile for reading!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
SHPGetInfo(inSHP, &nShapes, &shpType, NULL, NULL);
|
||
|
|
||
|
/* If we can open the inSHP, open its DBF */
|
||
|
inDBF = DBFOpen (argv[1], "rb");
|
||
|
if (!inDBF) {
|
||
|
fputs("Couldn't open dbf file for reading!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
/* Parse fields and validate existence */
|
||
|
fieldNames = split(argv[3], ";");
|
||
|
if (!fieldNames) {
|
||
|
fputs("ERROR: parsing field names!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
for (nFields = 0; fieldNames[nFields] ; nFields++) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
fldIdx = malloc(sizeof *fldIdx * nFields);
|
||
|
if (!fldIdx) {
|
||
|
fputs("malloc failed!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
for (i = 0; i < nFields; i++) {
|
||
|
len = (int)strlen(fieldNames[i]);
|
||
|
while(len > 0) {
|
||
|
--len;
|
||
|
fieldNames[i][len] = (char)toupper((unsigned char)fieldNames[i][len]);
|
||
|
}
|
||
|
fldIdx[i] = DBFGetFieldIndex(inDBF, fieldNames[i]);
|
||
|
if (fldIdx[i] < 0) {
|
||
|
/* try "SHAPE" */
|
||
|
if (strcmp(fieldNames[i], "SHAPE") == 0) {
|
||
|
fldIdx[i] = -1;
|
||
|
}
|
||
|
else if (strcmp(fieldNames[i], "FID") == 0) {
|
||
|
fldIdx[i] = -2;
|
||
|
}
|
||
|
else {
|
||
|
fprintf(stderr, "ERROR: field '%s' not found!\n", fieldNames[i]);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* set up field type array */
|
||
|
fldType = malloc(sizeof *fldType * nFields);
|
||
|
if (!fldType) {
|
||
|
fputs("malloc failed!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
for (i = 0; i < nFields; i++) {
|
||
|
if (fldIdx[i] < 0) {
|
||
|
fldType[i] = fldIdx[i];
|
||
|
}
|
||
|
else {
|
||
|
fldType[i] = DBFGetFieldInfo(inDBF, fldIdx[i], NULL, &width, &decimals);
|
||
|
if (fldType[i] == FTInvalid) {
|
||
|
fputs("Unrecognized field type in dBASE file!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/* set up field order array */
|
||
|
fldOrder = malloc(sizeof *fldOrder * nFields);
|
||
|
if (!fldOrder) {
|
||
|
fputs("malloc failed!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
for (i = 0; i < nFields; i++) {
|
||
|
/* default to ascending order */
|
||
|
fldOrder[i] = ASCENDING;
|
||
|
}
|
||
|
if (argc > 4) {
|
||
|
strOrder = split(argv[4], ";");
|
||
|
if (!strOrder) {
|
||
|
fputs("ERROR: parsing fields ordering!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
for (i = 0; i < nFields && strOrder[i]; i++) {
|
||
|
if (strcmp(strOrder[i], "DESCENDING") == 0) {
|
||
|
fldOrder[i] = DESCENDING;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* build the index */
|
||
|
index = build_index (inSHP, inDBF);
|
||
|
|
||
|
/* Create output shapefile */
|
||
|
outSHP = SHPCreate(argv[2], shpType);
|
||
|
if (!outSHP) {
|
||
|
fprintf(stderr, "%s:%d: couldn't create output shapefile!\n",
|
||
|
__FILE__, __LINE__);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
/* Create output dbf */
|
||
|
outDBF = DBFCloneEmpty(inDBF, argv[2]);
|
||
|
if (!outDBF) {
|
||
|
fprintf(stderr, "%s:%d: couldn't create output dBASE file!\n",
|
||
|
__FILE__, __LINE__);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
/* Copy projection file, if any */
|
||
|
copy_related(argv[1], argv[2], ".shp", ".prj");
|
||
|
|
||
|
/* Copy metadata file, if any */
|
||
|
copy_related(argv[1], argv[2], ".shp", ".shp.xml");
|
||
|
|
||
|
/* Write out sorted results */
|
||
|
for (i = 0; i < nShapes; i++) {
|
||
|
feat = SHPReadObject(inSHP, index[i].record);
|
||
|
if (SHPWriteObject(outSHP, -1, feat) < 0) {
|
||
|
fprintf(stderr, "%s:%d: error writing shapefile!\n", __FILE__, __LINE__);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
tuple = (void *) DBFReadTuple(inDBF, index[i].record);
|
||
|
if (DBFWriteTuple(outDBF, i, tuple) < 0) {
|
||
|
fprintf(stderr, "%s:%d: error writing dBASE file!\n", __FILE__, __LINE__);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
}
|
||
|
SHPClose(inSHP);
|
||
|
SHPClose(outSHP);
|
||
|
DBFClose(inDBF);
|
||
|
DBFClose(outDBF);
|
||
|
|
||
|
return EXIT_SUCCESS;
|
||
|
|
||
|
}
|
||
|
|
||
|
static char ** split(const char *arg, const char *delim)
|
||
|
{
|
||
|
char *copy = dupstr(arg);
|
||
|
char *cptr = copy;
|
||
|
char **result = NULL;
|
||
|
char **tmp;
|
||
|
int i = 0;
|
||
|
|
||
|
for (cptr = strtok(copy, delim); cptr; cptr = strtok(NULL, delim)) {
|
||
|
tmp = realloc (result, sizeof *result * (i + 1));
|
||
|
if (!tmp && result) {
|
||
|
while (i > 0) {
|
||
|
free(result[--i]);
|
||
|
}
|
||
|
free(result);
|
||
|
free(copy);
|
||
|
return NULL;
|
||
|
}
|
||
|
result = tmp;
|
||
|
result[i++] = dupstr(cptr);
|
||
|
}
|
||
|
|
||
|
free(copy);
|
||
|
|
||
|
if (i) {
|
||
|
tmp = realloc(result, sizeof *result * (i + 1));
|
||
|
if (!tmp) {
|
||
|
while (i > 0) {
|
||
|
free(result[--i]);
|
||
|
}
|
||
|
free(result);
|
||
|
free(copy);
|
||
|
return NULL;
|
||
|
}
|
||
|
result = tmp;
|
||
|
result[i++] = NULL;
|
||
|
}
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
static void copy_related (const char *inName, const char *outName,
|
||
|
const char *old_ext, const char *new_ext)
|
||
|
{
|
||
|
char *in;
|
||
|
char *out;
|
||
|
FILE *inFile;
|
||
|
FILE *outFile;
|
||
|
int c;
|
||
|
size_t name_len = strlen(inName);
|
||
|
size_t old_len = strlen(old_ext);
|
||
|
size_t new_len = strlen(new_ext);
|
||
|
|
||
|
in = malloc(name_len - old_len + new_len + 1);
|
||
|
strncpy(in, inName, (name_len - old_len));
|
||
|
strcpy(&in[(name_len - old_len)], new_ext);
|
||
|
inFile = fopen(in, "rb");
|
||
|
if (!inFile) {
|
||
|
free(in);
|
||
|
return;
|
||
|
}
|
||
|
name_len = strlen(outName);
|
||
|
out = malloc(name_len - old_len + new_len + 1);
|
||
|
strncpy(out, outName, (name_len - old_len));
|
||
|
strcpy(&out[(name_len - old_len)], new_ext);
|
||
|
outFile = fopen(out, "wb");
|
||
|
if (!out) {
|
||
|
fprintf(stderr, "%s:%d: couldn't copy related file!\n",
|
||
|
__FILE__, __LINE__);
|
||
|
free(in);
|
||
|
free(out);
|
||
|
return;
|
||
|
}
|
||
|
while ((c = fgetc(inFile)) != EOF) {
|
||
|
fputc(c, outFile);
|
||
|
}
|
||
|
fclose(inFile);
|
||
|
fclose(outFile);
|
||
|
free(in);
|
||
|
free(out);
|
||
|
}
|
||
|
|
||
|
static char * dupstr (const char *src)
|
||
|
{
|
||
|
char *dst = malloc(strlen(src) + 1);
|
||
|
char *cptr;
|
||
|
if (!dst) {
|
||
|
fprintf(stderr, "%s:%d: malloc failed!\n", __FILE__, __LINE__);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
cptr = dst;
|
||
|
while ((*cptr++ = *src++))
|
||
|
;
|
||
|
return dst;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
static void PrintDataStruct (struct DataStruct *data) {
|
||
|
int i, j;
|
||
|
for (i = 0; i < nShapes; i++) {
|
||
|
printf("data[%d] {\n", i);
|
||
|
printf("\t.record = %d\n", data[i].record);
|
||
|
for (j = 0; j < nFields; j++) {
|
||
|
printf("\t.value[%d].null = %d\n", j, data[i].value[j].null);
|
||
|
if (!data[i].value[j].null) {
|
||
|
switch(fldType[j]) {
|
||
|
case FIDType:
|
||
|
case IntegerType:
|
||
|
case LogicalType:
|
||
|
printf("\t.value[%d].u.i = %d\n", j, data[i].value[j].u.i);
|
||
|
break;
|
||
|
case DoubleType:
|
||
|
case SHPType:
|
||
|
printf("\t.value[%d].u.d = %f\n", j, data[i].value[j].u.d);
|
||
|
break;
|
||
|
case StringType:
|
||
|
printf("\t.value[%d].u.s = %s\n", j, data[i].value[j].u.s);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
puts("}");
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static struct DataStruct * build_index (SHPHandle shp, DBFHandle dbf) {
|
||
|
struct DataStruct *data;
|
||
|
SHPObject *feat;
|
||
|
int i;
|
||
|
int j;
|
||
|
|
||
|
/* make array */
|
||
|
data = malloc (sizeof *data * nShapes);
|
||
|
if (!data) {
|
||
|
fputs("malloc failed!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
|
||
|
/* populate array */
|
||
|
for (i = 0; i < nShapes; i++) {
|
||
|
data[i].value = malloc(sizeof data[0].value[0] * nFields);
|
||
|
if (0 == data[i].value) {
|
||
|
fputs("malloc failed!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
data[i].record = i;
|
||
|
for (j = 0; j < nFields; j++) {
|
||
|
data[i].value[j].null = 0;
|
||
|
switch (fldType[j]) {
|
||
|
case FIDType:
|
||
|
data[i].value[j].u.i = i;
|
||
|
break;
|
||
|
case SHPType:
|
||
|
feat = SHPReadObject(shp, i);
|
||
|
switch (feat->nSHPType) {
|
||
|
case SHPT_NULL:
|
||
|
fprintf(stderr, "Shape %d is a null feature!\n", i);
|
||
|
data[i].value[j].null = 1;
|
||
|
break;
|
||
|
case SHPT_POINT:
|
||
|
case SHPT_POINTZ:
|
||
|
case SHPT_POINTM:
|
||
|
case SHPT_MULTIPOINT:
|
||
|
case SHPT_MULTIPOINTZ:
|
||
|
case SHPT_MULTIPOINTM:
|
||
|
case SHPT_MULTIPATCH:
|
||
|
/* Y-sort bounds */
|
||
|
data[i].value[j].u.d = feat->dfYMax;
|
||
|
break;
|
||
|
case SHPT_ARC:
|
||
|
case SHPT_ARCZ:
|
||
|
case SHPT_ARCM:
|
||
|
data[i].value[j].u.d = shp_length(feat);
|
||
|
break;
|
||
|
case SHPT_POLYGON:
|
||
|
case SHPT_POLYGONZ:
|
||
|
case SHPT_POLYGONM:
|
||
|
data[i].value[j].u.d = shp_area(feat);
|
||
|
break;
|
||
|
default:
|
||
|
fputs("Can't sort on Shapefile feature type!\n", stderr);
|
||
|
exit(EXIT_FAILURE);
|
||
|
}
|
||
|
SHPDestroyObject(feat);
|
||
|
break;
|
||
|
case FTString:
|
||
|
data[i].value[j].null = DBFIsAttributeNULL(dbf, i, fldIdx[j]);
|
||
|
if (!data[i].value[j].null) {
|
||
|
data[i].value[j].u.s = dupstr(DBFReadStringAttribute(dbf, i, fldIdx[j]));
|
||
|
}
|
||
|
break;
|
||
|
case FTInteger:
|
||
|
case FTLogical:
|
||
|
data[i].value[j].null = DBFIsAttributeNULL(dbf, i, fldIdx[j]);
|
||
|
if (!data[i].value[j].null) {
|
||
|
data[i].value[j].u.i = DBFReadIntegerAttribute(dbf, i, fldIdx[j]);
|
||
|
}
|
||
|
break;
|
||
|
case FTDouble:
|
||
|
data[i].value[j].null = DBFIsAttributeNULL(dbf, i, fldIdx[j]);
|
||
|
if (!data[i].value[j].null) {
|
||
|
data[i].value[j].u.d = DBFReadDoubleAttribute(dbf, i, fldIdx[j]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
PrintDataStruct(data);
|
||
|
fputs("build_index: sorting array\n", stdout);
|
||
|
#endif
|
||
|
|
||
|
qsort (data, nShapes, sizeof data[0], compare);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
PrintDataStruct(data);
|
||
|
fputs("build_index: returning array\n", stdout);
|
||
|
#endif
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
static int compare(const void *A, const void *B) {
|
||
|
const struct DataStruct *a = A;
|
||
|
const struct DataStruct *b = B;
|
||
|
int i;
|
||
|
int result = 0;
|
||
|
|
||
|
for (i = 0; i < nFields; i++) {
|
||
|
if (a->value[i].null && b->value[i].null) {
|
||
|
continue;
|
||
|
}
|
||
|
if (a->value[i].null && !b->value[i].null) {
|
||
|
return (fldOrder[i]) ? 1 : -1;
|
||
|
}
|
||
|
if (!a->value[i].null && b->value[i].null) {
|
||
|
return (fldOrder[i]) ? -1 : 1;
|
||
|
}
|
||
|
switch (fldType[i]) {
|
||
|
case FIDType:
|
||
|
case IntegerType:
|
||
|
case LogicalType:
|
||
|
if (a->value[i].u.i < b->value[i].u.i) {
|
||
|
return (fldOrder[i]) ? -1 : 1;
|
||
|
}
|
||
|
if (a->value[i].u.i > b->value[i].u.i) {
|
||
|
return (fldOrder[i]) ? 1 : -1;
|
||
|
}
|
||
|
break;
|
||
|
case DoubleType:
|
||
|
case SHPType:
|
||
|
if (a->value[i].u.d < b->value[i].u.d) {
|
||
|
return (fldOrder[i]) ? -1 : 1;
|
||
|
}
|
||
|
if (a->value[i].u.d > b->value[i].u.d) {
|
||
|
return (fldOrder[i]) ? 1 : -1;
|
||
|
}
|
||
|
break;
|
||
|
case StringType:
|
||
|
result = strcmp(a->value[i].u.s, b->value[i].u.s);
|
||
|
if (result) {
|
||
|
return (fldOrder[i]) ? result : -result;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
fprintf(stderr, "compare: Program Error! Unhandled field type! fldType[%d] = %d\n", i, fldType[i]);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static double area2d_polygon (int n, double *x, double *y) {
|
||
|
double area = 0;
|
||
|
int i;
|
||
|
for (i = 1; i < n; i++) {
|
||
|
area += (x[i-1] + x[i]) * (y[i] - y[i-1]);
|
||
|
}
|
||
|
return area / 2.0;
|
||
|
}
|
||
|
|
||
|
static double shp_area (SHPObject *feat) {
|
||
|
double area = 0.0;
|
||
|
if (feat->nParts == 0) {
|
||
|
area = area2d_polygon (feat->nVertices, feat->padfX, feat->padfY);
|
||
|
}
|
||
|
else {
|
||
|
int part, n;
|
||
|
for (part = 0; part < feat->nParts; part++) {
|
||
|
if (part < feat->nParts - 1) {
|
||
|
n = feat->panPartStart[part+1] - feat->panPartStart[part];
|
||
|
}
|
||
|
else {
|
||
|
n = feat->nVertices - feat->panPartStart[part];
|
||
|
}
|
||
|
area += area2d_polygon (n, &(feat->padfX[feat->panPartStart[part]]),
|
||
|
&(feat->padfY[feat->panPartStart[part]]));
|
||
|
}
|
||
|
}
|
||
|
/* our area function computes in opposite direction */
|
||
|
return -area;
|
||
|
}
|
||
|
|
||
|
static double length2d_polyline (int n, double *x, double *y) {
|
||
|
double length = 0.0;
|
||
|
int i;
|
||
|
for (i = 1; i < n; i++) {
|
||
|
length += sqrt((x[i] - x[i-1])*(x[i] - x[i-1])
|
||
|
+
|
||
|
(y[i] - y[i-1])*(y[i] - y[i-1]));
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|
||
|
static double shp_length (SHPObject *feat) {
|
||
|
double length = 0.0;
|
||
|
if (feat->nParts == 0) {
|
||
|
length = length2d_polyline(feat->nVertices, feat->padfX, feat->padfY);
|
||
|
}
|
||
|
else {
|
||
|
int part, n;
|
||
|
for (part = 0; part < feat->nParts; part++) {
|
||
|
if (part < feat->nParts - 1) {
|
||
|
n = feat->panPartStart[part+1] - feat->panPartStart[part];
|
||
|
}
|
||
|
else {
|
||
|
n = feat->nVertices - feat->panPartStart[part];
|
||
|
}
|
||
|
length += length2d_polyline (n,
|
||
|
&(feat->padfX[feat->panPartStart[part]]),
|
||
|
&(feat->padfY[feat->panPartStart[part]]));
|
||
|
}
|
||
|
}
|
||
|
return length;
|
||
|
}
|
||
|
|