sortshp.c 8.27 KB
Newer Older
1
/******************************************************************************
2
 * $Id$
3 4
 *
 * Project:  MapServer
5 6
 * Purpose:  Command line utility to sort a shapefile based on a single
 *           attribute in ascending or decending order. Useful for
7 8 9 10 11 12 13 14 15 16 17 18 19
 *           prioritizing drawing or labeling of shapes.
 * Author:   Steve Lime and the MapServer team.
 *
 ******************************************************************************
 * Copyright (c) 1996-2005 Regents of the University of Minnesota.
 *
 * 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:
 *
20
 * The above copyright notice and this permission notice shall be included in
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
 * all copies of this Software or works derived from this 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.
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>

#include "mapserver.h"

39

40 41 42 43 44 45 46 47 48 49 50 51 52

typedef struct {
  double number;
  char string[255];
  int index;
} sortStruct;

static int compare_string_descending(const void *a, const void *b)
{
  const sortStruct *i = a, *j = b;
  return(strcmp(j->string, i->string));
}

53
static int compare_string_ascending(const void *a, const void *b)
54 55 56 57 58 59 60 61 62
{
  const sortStruct *i = a, *j = b;
  return(strcmp(i->string, j->string));
}

static int compare_number_descending(const void *a, const void *b)
{
  const sortStruct *i = a, *j = b;
  if(i->number > j->number)
63 64
    return(-1);
  if(i->number < j->number)
65 66 67 68
    return(1);
  return(0);
}

69
static int compare_number_ascending(const void *a, const void *b)
70 71 72
{
  const sortStruct *i = a, *j = b;
  if(i->number > j->number)
73 74
    return(1);
  if(i->number < j->number)
75 76 77 78 79 80 81
    return(-1);
  return(0);
}

int main(int argc, char *argv[])
{
  SHPHandle    inSHP,outSHP; /* ---- Shapefile file pointers ---- */
82
  DBFHandle    inDBF,outDBF; /* ---- DBF file pointers ---- */
83 84 85 86 87 88
  sortStruct   *array;
  shapeObj     shape;
  int          shpType, nShapes;
  int          fieldNumber=-1; /* ---- Field number of item to be sorted on ---- */
  DBFFieldType dbfField;
  char         fName[20];
89
  int          fWidth,fnDecimals;
90 91 92
  char         buffer[1024];
  int i,j;
  int num_fields, num_records;
93

94 95 96 97 98 99 100 101 102
  if(argc > 1 && strcmp(argv[1], "-v") == 0) {
    printf("%s\n", msGetVersion());
    exit(0);
  }

  /* ------------------------------------------------------------------------------- */
  /*       Check the number of arguments, return syntax if not correct               */
  /* ------------------------------------------------------------------------------- */
  if( argc != 5 ) {
103 104
    fprintf(stderr,"Syntax: sortshp [infile] [outfile] [item] [ascending|descending]\n" );
    exit(1);
105 106
  }

107 108
  msSetErrorFile("stderr", NULL);

109 110 111 112 113 114
  /* ------------------------------------------------------------------------------- */
  /*       Open the shapefile                                                        */
  /* ------------------------------------------------------------------------------- */
  inSHP = msSHPOpen(argv[1], "rb" );
  if( !inSHP ) {
    fprintf(stderr,"Unable to open %s shapefile.\n",argv[1]);
115
    exit(1);
116 117 118 119 120 121
  }
  msSHPGetInfo(inSHP, &nShapes, &shpType);

  /* ------------------------------------------------------------------------------- */
  /*       Open the dbf file                                                         */
  /* ------------------------------------------------------------------------------- */
122
  snprintf(buffer, sizeof(buffer), "%s.dbf",argv[1]);
123 124 125
  inDBF = msDBFOpen(buffer,"rb");
  if( inDBF == NULL ) {
    fprintf(stderr,"Unable to open %s XBASE file.\n",buffer);
126
    exit(1);
127 128 129 130 131
  }

  num_fields = msDBFGetFieldCount(inDBF);
  num_records = msDBFGetRecordCount(inDBF);

132
  for(i=0; i<num_fields; i++) {
133 134 135 136 137 138 139 140 141
    msDBFGetFieldInfo(inDBF,i,fName,NULL,NULL);
    if(strncasecmp(argv[3],fName,strlen(argv[3])) == 0) { /* ---- Found it ---- */
      fieldNumber = i;
      break;
    }
  }

  if(fieldNumber < 0) {
    fprintf(stderr,"Item %s doesn't exist in %s\n",argv[3],buffer);
142
    exit(1);
143
  }
144 145 146 147

  array = (sortStruct *)malloc(sizeof(sortStruct)*num_records); /* ---- Allocate the array ---- */
  if(!array) {
    fprintf(stderr, "Unable to allocate sort array.\n");
148
    exit(1);
149
  }
150

151 152 153 154 155
  /* ------------------------------------------------------------------------------- */
  /*       Load the array to be sorted                                               */
  /* ------------------------------------------------------------------------------- */
  dbfField = msDBFGetFieldInfo(inDBF,fieldNumber,NULL,NULL,NULL);
  switch (dbfField) {
156 157 158 159 160
    case FTString:
      for(i=0; i<num_records; i++) {
        strlcpy(array[i].string, msDBFReadStringAttribute( inDBF, i, fieldNumber), sizeof(array[i].string));
        array[i].index = i;
      }
161

162 163 164 165 166 167 168 169 170 171 172
      if(*argv[4] == 'd')
        qsort(array, num_records, sizeof(sortStruct), compare_string_descending);
      else
        qsort(array, num_records, sizeof(sortStruct), compare_string_ascending);
      break;
    case FTInteger:
    case FTDouble:
      for(i=0; i<num_records; i++) {
        array[i].number = msDBFReadDoubleAttribute( inDBF, i, fieldNumber);
        array[i].index = i;
      }
173

174 175 176 177
      if(*argv[4] == 'd')
        qsort(array, num_records, sizeof(sortStruct), compare_number_descending);
      else
        qsort(array, num_records, sizeof(sortStruct), compare_number_ascending);
178

179 180
      break;
    default:
181
      fprintf(stderr,"Data type for item %s not supported.\n",argv[3]);
182
      exit(1);
183 184
  }

185 186 187 188
  /* ------------------------------------------------------------------------------- */
  /*       Setup the output .shp/.shx and .dbf files                                 */
  /* ------------------------------------------------------------------------------- */
  outSHP = msSHPCreate(argv[2],shpType);
189 190 191
  if( outSHP == NULL ) {
    fprintf( stderr, "Failed to create file '%s'.\n", argv[2] );
    exit( 1 );
192 193
  }

194 195
  sprintf(buffer,"%s.dbf",argv[2]);
  outDBF = msDBFCreate(buffer);
196 197 198
  if( outDBF == NULL ) {
    fprintf( stderr, "Failed to create dbf file '%s'.\n", buffer );
    exit( 1 );
199
  }
200

201
  for(i=0; i<num_fields; i++) {
202 203 204 205 206 207 208
    dbfField = msDBFGetFieldInfo(inDBF,i,fName,&fWidth,&fnDecimals); /* ---- Get field info from in file ---- */
    msDBFAddField(outDBF,fName,dbfField,fWidth,fnDecimals);
  }

  /* ------------------------------------------------------------------------------- */
  /*       Write the sorted .shp/.shx and .dbf files                                 */
  /* ------------------------------------------------------------------------------- */
209
  for(i=0; i<num_records; i++) { /* ---- For each shape/record ---- */
210

211
    for(j=0; j<num_fields; j++) { /* ---- For each .dbf field ---- */
212

213
      dbfField = msDBFGetFieldInfo(inDBF,j,fName,&fWidth,&fnDecimals);
214 215

      switch (dbfField) {
216 217 218 219 220 221 222 223 224 225 226 227
        case FTInteger:
          msDBFWriteIntegerAttribute(outDBF, i, j, msDBFReadIntegerAttribute( inDBF, array[i].index, j));
          break;
        case FTDouble:
          msDBFWriteDoubleAttribute(outDBF, i, j, msDBFReadDoubleAttribute( inDBF, array[i].index, j));
          break;
        case FTString:
          msDBFWriteStringAttribute(outDBF, i, j, msDBFReadStringAttribute( inDBF, array[i].index, j));
          break;
        default:
          fprintf(stderr,"Unsupported data type for field: %s, exiting.\n",fName);
          exit(0);
228 229
      }
    }
230

231 232 233 234 235 236 237 238 239 240 241 242 243 244
    msSHPReadShape( inSHP, array[i].index, &shape );
    msSHPWriteShape( outSHP, &shape );
    msFreeShape( &shape );
  }

  free(array);

  msSHPClose(inSHP);
  msDBFClose(inDBF);
  msSHPClose(outSHP);
  msDBFClose(outDBF);

  return(0);
}