FlightGear next
SHPParser.cxx
Go to the documentation of this file.
1
3
4// Written by James Turner, started 2013.
5//
6// Copyright (C) 2013 James Turner <zakalawe@mac.com>
7//
8// This program is free software; you can redistribute it and/or
9// modify it under the terms of the GNU General Public License as
10// published by the Free Software Foundation; either version 2 of the
11// License, or (at your option) any later version.
12//
13// This program is distributed in the hope that it will be useful, but
14// WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// General Public License for more details.
17//
18// You should have received a copy of the GNU General Public License
19// along with this program; if not, write to the Free Software
20// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
22#ifdef HAVE_CONFIG_H
23 #include "config.h"
24#endif
25
26#include "SHPParser.hxx"
27
28#include <simgear/debug/logstream.hxx>
29#include <simgear/structure/exception.hxx>
30#include <simgear/misc/sg_path.hxx>
31#include <simgear/io/lowlevel.hxx>
32
33// http://www.esri.com/library/whitepapers/pdfs/shapefile.pdf table 1
34const int SHP_FILE_MAGIC = 9994;
35const int SHP_FILE_VERSION = 1000;
36
37const int SHP_NULL_TYPE = 0;
38const int SHP_POLYLINE_TYPE = 3;
39const int SHP_POLYGON_TYPE = 5;
40
41namespace
42{
43
44void sgReadIntBE ( gzFile fd, int& var )
45{
46 if ( gzread ( fd, &var, sizeof(int) ) != sizeof(int) ) {
47 throw sg_io_exception("gzread failed");
48 }
49
50 if ( sgIsLittleEndian() ) {
51 sgEndianSwap( (uint32_t *) &var);
52 }
53}
54
55void sgReadIntLE ( gzFile fd, int& var )
56{
57 if ( gzread ( fd, &var, sizeof(int) ) != sizeof(int) ) {
58 throw sg_io_exception("gzread failed");
59 }
60
61 if ( sgIsBigEndian() ) {
62 sgEndianSwap( (uint32_t *) &var);
63 }
64}
65
66
67void readSHPRecordHeader(gzFile fd, int &recordNumber, int& contentLength)
68{
69 sgReadIntBE(fd, recordNumber);
70 sgReadIntBE(fd, contentLength);
71}
72
73void parseSHPPoints2D(gzFile fd, int numPoints, flightgear::SGGeodVec& aPoints)
74{
75 aPoints.reserve(numPoints);
76 std::vector<double> ds;
77 ds.resize(numPoints * 2);
78 sgReadDouble(fd, numPoints * 2, ds.data());
79
80 unsigned int index = 0;
81 for (int i=0; i<numPoints; ++i, index += 2) {
82 aPoints.push_back(SGGeod::fromDeg(ds[index], ds[index+1]));
83 }
84}
85
86} // anonymous namespace
87
88namespace flightgear
89{
90
91void SHPParser::parsePolyLines(const SGPath& aPath, PolyLine::Type aTy,
92 PolyLineList& aResult, bool aClosed)
93{
94#if defined(SG_WINDOWS)
95 const auto ws = aPath.wstr();
96 gzFile file = gzopen_w(ws.c_str(), "rb");
97#else
98 const auto s = aPath.utf8Str();
99 gzFile file = gzopen(s.c_str(), "rb");
100#endif
101 if (!file) {
102 throw sg_io_exception("couldn't open file:", aPath);
103 }
104
105 try {
106 int header, fileLength, fileVersion, shapeType;
107 sgReadIntBE(file, header);
108 if (header != SHP_FILE_MAGIC) {
109 throw sg_io_exception("bad SHP header value", aPath);
110 }
111
112 // skip 5 ints, then read the file length
113 for (int i=0; i<6; ++i) {
114 sgReadIntBE(file, fileLength);
115 }
116
117 sgReadIntLE(file, fileVersion);
118 sgReadIntLE(file, shapeType);
119
120 if (fileVersion != SHP_FILE_VERSION) {
121 throw sg_io_exception("bad SHP file version", aPath);
122 }
123
124 if (aClosed && (shapeType != SHP_POLYGON_TYPE)) {
125 throw sg_io_exception("SHP file does not contain Polygon data", aPath);
126 }
127
128 if (!aClosed && (shapeType != SHP_POLYLINE_TYPE)) {
129 throw sg_io_exception("SHP file does not contain PolyLine data", aPath);
130 }
131
132 // we don't care about range values
133 double range;
134 for (int i=0; i<8; ++i) {
135 sgReadDouble(file, &range);
136 }
137
138 int readLength = 100; // sizeof the header
139 while (readLength < fileLength) {
140 int recordNumber, contentLengthWords;
141 readSHPRecordHeader(file, recordNumber, contentLengthWords);
142
143 int recordShapeType;
144 sgReadIntLE(file, recordShapeType);
145 if (recordShapeType == SHP_NULL_TYPE) {
146 continue; // nothing else to do
147 }
148
149 if (recordShapeType != shapeType) {
150 // vesion 1000 requires files to have homogenous shape type
151 throw sg_io_exception("SHP file shape-type mismatch", aPath);
152 }
153 // read PolyLine record from now on
154 double box[4];
155 for (int i=0; i<4; ++i) {
156 sgReadDouble(file, &box[i]);
157 }
158
159 int numParts, numPoints;
160 sgReadInt(file, &numParts);
161 sgReadInt(file, &numPoints);
162
163 std::vector<int> parts;
164 parts.resize(numParts);
165 sgReadInt(file, numParts, parts.data());
166
167 SGGeodVec points;
168 parseSHPPoints2D(file, numPoints, points);
169
170 for (int part=0; part<numParts; ++part) {
171 SGGeodVec partPoints;
172 unsigned int startIndex = parts[part];
173 unsigned int endIndex = ((part + 1) == numParts) ? numPoints : parts[part + 1];
174 partPoints.insert(partPoints.begin(), points.begin() + startIndex, points.begin() + endIndex);
175
176 if (aClosed) {
177 aResult.push_back(PolyLine::create(aTy, partPoints));
178 } else {
179 PolyLineList lines = PolyLine::createChunked(aTy, partPoints);
180 aResult.insert(aResult.end(), lines.begin(), lines.end());
181 }
182 }
183
184 // total record size if contentLenght + 4 words for the two record fields
185 readLength += (contentLengthWords + 4);
186 // partition
187 } // of record parsing
188
189 } catch (sg_exception& e) {
190 aResult.clear();
191 gzclose(file);
192 throw e; // rethrow
193 }
194}
195
196} // of namespace flightgear
#define i(x)
const int SHP_POLYGON_TYPE
Definition SHPParser.cxx:39
const int SHP_FILE_VERSION
Definition SHPParser.cxx:35
const int SHP_FILE_MAGIC
SHPParser - parse ESRI ShapeFiles containing PolyLines.
Definition SHPParser.cxx:34
const int SHP_NULL_TYPE
Definition SHPParser.cxx:37
const int SHP_POLYLINE_TYPE
Definition SHPParser.cxx:38
static PolyLineList createChunked(Type aTy, const SGGeodVec &aRawPoints)
create poly line objects from raw input points and a type.
Definition PolyLine.cxx:60
static PolyLineRef create(Type aTy, const SGGeodVec &aRawPoints)
Definition PolyLine.cxx:98
static void parsePolyLines(const SGPath &, PolyLine::Type aTy, PolyLineList &aResult, bool aClosed)
Parse a shape file containing PolyLine data.
Definition SHPParser.cxx:91
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
std::vector< PolyLineRef > PolyLineList
Definition PolyLine.hxx:41
std::vector< SGGeod > SGGeodVec
Definition PolyLine.hxx:36