1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkGeoJSONReader.cxx
5
6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
7 All rights reserved.
8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
9
10 This software is distributed WITHOUT ANY WARRANTY; without even
11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
12 PURPOSE. See the above copyright notice for more information.
13
14 =========================================================================*/
15
16 #include "vtkGeoJSONReader.h"
17
18 // VTK Includes
19 #include "vtkAbstractArray.h"
20 #include "vtkBitArray.h"
21 #include "vtkCellArray.h"
22 #include "vtkCellData.h"
23 #include "vtkDoubleArray.h"
24 #include "vtkGeoJSONFeature.h"
25 #include "vtkInformation.h"
26 #include "vtkInformationVector.h"
27 #include "vtkIntArray.h"
28 #include "vtkNew.h"
29 #include "vtkObjectFactory.h"
30 #include "vtkPolyData.h"
31 #include "vtkStringArray.h"
32 #include "vtkTriangleFilter.h"
33 #include "vtk_jsoncpp.h"
34 #include "vtksys/FStream.hxx"
35
36 // C++ includes
37 #include <fstream>
38 #include <iostream>
39 #include <sstream>
40
41 vtkStandardNewMacro(vtkGeoJSONReader);
42
43 //------------------------------------------------------------------------------
44 class vtkGeoJSONReader::GeoJSONReaderInternal
45 {
46 public:
47 struct GeoJSONProperty_t
48 {
49 std::string Name;
50 vtkVariant Value;
51 };
52 using GeoJSONProperty = struct GeoJSONProperty_t;
53
54 // List of property names to read. Property value is used the default
55 std::vector<GeoJSONProperty> PropertySpecs;
56
57 // Parse the Json Value corresponding to the root of the geoJSON data from the file
58 void ParseRoot(const Json::Value& root, vtkPolyData* output, bool outlinePolygons,
59 const char* serializedPropertiesArrayName);
60
61 // Verify if file exists and can be read by the parser
62 // If exists, parse into Jsoncpp data structure
63 int CanParseFile(const char* filename, Json::Value& root);
64
65 // Verify if string can be read by the parser
66 // If exists, parse into Jsoncpp data structure
67 int CanParseString(char* input, Json::Value& root);
68
69 // Extract property values from json node
70 void ParseFeatureProperties(const Json::Value& propertiesNode,
71 std::vector<GeoJSONProperty>& properties, const char* serializedPropertiesArrayName);
72
73 void InsertFeatureProperties(
74 vtkPolyData* polyData, const std::vector<GeoJSONProperty>& featureProperties);
75 };
76
77 //------------------------------------------------------------------------------
ParseRoot(const Json::Value & root,vtkPolyData * output,bool outlinePolygons,const char * serializedPropertiesArrayName)78 void vtkGeoJSONReader::GeoJSONReaderInternal::ParseRoot(const Json::Value& root,
79 vtkPolyData* output, bool outlinePolygons, const char* serializedPropertiesArrayName)
80 {
81 // Initialize geometry containers
82 vtkNew<vtkPoints> points;
83 points->SetDataTypeToDouble();
84 output->SetPoints(points);
85 vtkNew<vtkCellArray> verts;
86 output->SetVerts(verts);
87 vtkNew<vtkCellArray> lines;
88 output->SetLines(lines);
89 vtkNew<vtkCellArray> polys;
90 output->SetPolys(polys);
91
92 // Initialize feature-id array
93 vtkStringArray* featureIdArray = vtkStringArray::New();
94 featureIdArray->SetName("feature-id");
95 output->GetCellData()->AddArray(featureIdArray);
96 featureIdArray->Delete();
97
98 // Initialize properties arrays
99 if (serializedPropertiesArrayName)
100 {
101 vtkStringArray* propertiesArray = vtkStringArray::New();
102 propertiesArray->SetName(serializedPropertiesArrayName);
103 output->GetCellData()->AddArray(propertiesArray);
104 propertiesArray->Delete();
105 }
106
107 vtkAbstractArray* array;
108 std::vector<GeoJSONProperty>::iterator iter = this->PropertySpecs.begin();
109 for (; iter != this->PropertySpecs.end(); ++iter)
110 {
111 array = nullptr;
112 switch (iter->Value.GetType())
113 {
114 case VTK_BIT:
115 array = vtkBitArray::New();
116 break;
117
118 case VTK_INT:
119 array = vtkIntArray::New();
120 break;
121
122 case VTK_DOUBLE:
123 array = vtkDoubleArray::New();
124 break;
125
126 case VTK_STRING:
127 array = vtkStringArray::New();
128 break;
129
130 default:
131 vtkGenericWarningMacro("unexpected data type " << iter->Value.GetType());
132 break;
133 }
134
135 // Skip if array not created for some reason
136 if (!array)
137 {
138 continue;
139 }
140
141 array->SetName(iter->Name.c_str());
142 output->GetCellData()->AddArray(array);
143 array->Delete();
144 }
145
146 // Check type
147 Json::Value rootType = root["type"];
148 if (rootType.isNull())
149 {
150 vtkGenericWarningMacro(<< "ParseRoot: Missing type node");
151 return;
152 }
153
154 // Parse features
155 Json::Value rootFeatures;
156 std::string strRootType = rootType.asString();
157 std::vector<GeoJSONProperty> properties;
158 if ("FeatureCollection" == strRootType)
159 {
160 rootFeatures = root["features"];
161 if (rootFeatures.isNull())
162 {
163 vtkGenericWarningMacro(<< "ParseRoot: Missing \"features\" node");
164 return;
165 }
166
167 if (!rootFeatures.isArray())
168 {
169 vtkGenericWarningMacro(<< "ParseRoot: features node is not an array");
170 return;
171 }
172
173 GeoJSONProperty property;
174 for (Json::Value::ArrayIndex i = 0; i < rootFeatures.size(); i++)
175 {
176 // Append extracted geometry to existing outputData
177 Json::Value featureNode = rootFeatures[i];
178 Json::Value propertiesNode = featureNode["properties"];
179 this->ParseFeatureProperties(propertiesNode, properties, serializedPropertiesArrayName);
180 vtkNew<vtkGeoJSONFeature> feature;
181 feature->SetOutlinePolygons(outlinePolygons);
182 feature->ExtractGeoJSONFeature(featureNode, output);
183 this->InsertFeatureProperties(output, properties);
184 }
185 }
186 else if ("Feature" == strRootType)
187 {
188 // Process single feature
189 this->ParseFeatureProperties(root, properties, serializedPropertiesArrayName);
190 vtkNew<vtkGeoJSONFeature> feature;
191 feature->SetOutlinePolygons(outlinePolygons);
192
193 // Next call adds (exactly) one cell to the polydata
194 feature->ExtractGeoJSONFeature(root, output);
195 // Next call adds (exactly) one tuple to the polydata's cell data
196 this->InsertFeatureProperties(output, properties);
197 }
198 else
199 {
200 vtkGenericWarningMacro(<< "ParseRoot: do not support root type \"" << strRootType << "\"");
201 }
202 }
203
204 //------------------------------------------------------------------------------
CanParseFile(const char * filename,Json::Value & root)205 int vtkGeoJSONReader::GeoJSONReaderInternal::CanParseFile(const char* filename, Json::Value& root)
206 {
207 if (!filename)
208 {
209 vtkGenericWarningMacro(<< "Input filename not specified");
210 return VTK_ERROR;
211 }
212
213 vtksys::ifstream file;
214 file.open(filename);
215
216 if (!file.is_open())
217 {
218 vtkGenericWarningMacro(<< "Unable to Open File " << filename);
219 return VTK_ERROR;
220 }
221
222 Json::CharReaderBuilder builder;
223 builder["collectComments"] = false;
224
225 std::string formattedErrors;
226
227 // parse the entire geoJSON data into the Json::Value root
228 bool parsedSuccess = parseFromStream(builder, file, &root, &formattedErrors);
229
230 if (!parsedSuccess)
231 {
232 // Report failures and their locations in the document
233 vtkGenericWarningMacro(<< "Failed to parse JSON" << endl << formattedErrors);
234 return VTK_ERROR;
235 }
236
237 return VTK_OK;
238 }
239
240 //------------------------------------------------------------------------------
CanParseString(char * input,Json::Value & root)241 int vtkGeoJSONReader::GeoJSONReaderInternal::CanParseString(char* input, Json::Value& root)
242 {
243 if (!input)
244 {
245 vtkGenericWarningMacro(<< "Input string is empty");
246 return VTK_ERROR;
247 }
248
249 Json::CharReaderBuilder builder;
250 builder["collectComments"] = false;
251
252 std::unique_ptr<Json::CharReader> reader(builder.newCharReader());
253
254 std::string formattedErrors;
255
256 // parse the entire geoJSON data into the Json::Value root
257 bool parsedSuccess = reader->parse(input, input + strlen(input), &root, &formattedErrors);
258
259 if (!parsedSuccess)
260 {
261 // Report failures and their locations in the document
262 vtkGenericWarningMacro(<< "Failed to parse JSON" << endl << formattedErrors);
263 return VTK_ERROR;
264 }
265
266 return VTK_OK;
267 }
268
269 //------------------------------------------------------------------------------
ParseFeatureProperties(const Json::Value & propertiesNode,std::vector<GeoJSONProperty> & featureProperties,const char * serializedPropertiesArrayName)270 void vtkGeoJSONReader::GeoJSONReaderInternal::ParseFeatureProperties(
271 const Json::Value& propertiesNode, std::vector<GeoJSONProperty>& featureProperties,
272 const char* serializedPropertiesArrayName)
273 {
274 featureProperties.clear();
275
276 GeoJSONProperty spec;
277 GeoJSONProperty property;
278 std::vector<GeoJSONProperty>::iterator iter = this->PropertySpecs.begin();
279 for (; iter != this->PropertySpecs.end(); ++iter)
280 {
281 spec = *iter;
282 property.Name = spec.Name;
283
284 Json::Value propertyNode = propertiesNode[spec.Name];
285 if (propertyNode.isNull())
286 {
287 property.Value = spec.Value;
288 featureProperties.push_back(property);
289 continue;
290 }
291
292 // (else)
293 switch (spec.Value.GetType())
294 {
295 case VTK_BIT:
296 property.Value = vtkVariant(propertyNode.asBool());
297 break;
298
299 case VTK_DOUBLE:
300 property.Value = vtkVariant(propertyNode.asDouble());
301 break;
302
303 case VTK_INT:
304 property.Value = vtkVariant(propertyNode.asInt());
305 break;
306
307 case VTK_STRING:
308 property.Value = vtkVariant(propertyNode.asString());
309 break;
310 }
311
312 featureProperties.push_back(property);
313 }
314
315 // Add GeoJSON string if enabled
316 if (serializedPropertiesArrayName)
317 {
318 property.Name = serializedPropertiesArrayName;
319
320 Json::StreamWriterBuilder builder;
321 builder["commentStyle"] = "None";
322 builder["indentation"] = "";
323 std::unique_ptr<Json::StreamWriter> writer(builder.newStreamWriter());
324
325 std::stringstream stream;
326 writer->write(propertiesNode, &stream);
327 std::string propString = stream.str();
328
329 if (!propString.empty() && *propString.rbegin() == '\n')
330 {
331 propString.resize(propString.size() - 1);
332 }
333 property.Value = vtkVariant(propString);
334 featureProperties.push_back(property);
335 }
336 }
337
338 //------------------------------------------------------------------------------
InsertFeatureProperties(vtkPolyData * polyData,const std::vector<GeoJSONProperty> & featureProperties)339 void vtkGeoJSONReader::GeoJSONReaderInternal::InsertFeatureProperties(
340 vtkPolyData* polyData, const std::vector<GeoJSONProperty>& featureProperties)
341 {
342 std::vector<GeoJSONProperty>::const_iterator iter = featureProperties.begin();
343 for (; iter != featureProperties.end(); ++iter)
344 {
345 std::string name = iter->Name;
346 vtkVariant value = iter->Value;
347
348 vtkAbstractArray* array = polyData->GetCellData()->GetAbstractArray(name.c_str());
349 switch (array->GetDataType())
350 {
351 case VTK_BIT:
352 vtkArrayDownCast<vtkBitArray>(array)->InsertNextValue(value.ToChar());
353 break;
354
355 case VTK_DOUBLE:
356 vtkArrayDownCast<vtkDoubleArray>(array)->InsertNextValue(value.ToDouble());
357 break;
358
359 case VTK_INT:
360 vtkArrayDownCast<vtkIntArray>(array)->InsertNextValue(value.ToInt());
361 break;
362
363 case VTK_STRING:
364 vtkArrayDownCast<vtkStringArray>(array)->InsertNextValue(value.ToString());
365 break;
366 }
367 }
368 }
369
370 //------------------------------------------------------------------------------
vtkGeoJSONReader()371 vtkGeoJSONReader::vtkGeoJSONReader()
372 {
373 this->FileName = nullptr;
374 this->StringInput = nullptr;
375 this->StringInputMode = false;
376 this->TriangulatePolygons = false;
377 this->OutlinePolygons = false;
378 this->SerializedPropertiesArrayName = nullptr;
379 this->SetNumberOfInputPorts(0);
380 this->SetNumberOfOutputPorts(1);
381 this->Internal = new GeoJSONReaderInternal;
382 }
383
384 //------------------------------------------------------------------------------
~vtkGeoJSONReader()385 vtkGeoJSONReader::~vtkGeoJSONReader()
386 {
387 delete[] FileName;
388 delete[] StringInput;
389 delete Internal;
390 }
391
392 //------------------------------------------------------------------------------
AddFeatureProperty(const char * name,vtkVariant & typeAndDefaultValue)393 void vtkGeoJSONReader::AddFeatureProperty(const char* name, vtkVariant& typeAndDefaultValue)
394 {
395 GeoJSONReaderInternal::GeoJSONProperty property;
396
397 // Traverse internal list checking if name already used
398 std::vector<GeoJSONReaderInternal::GeoJSONProperty>::iterator iter =
399 this->Internal->PropertySpecs.begin();
400 for (; iter != this->Internal->PropertySpecs.end(); ++iter)
401 {
402 if (iter->Name == name)
403 {
404 vtkGenericWarningMacro(<< "Overwriting property spec for name " << name);
405 property.Name = name;
406 property.Value = typeAndDefaultValue;
407 *iter = property;
408 break;
409 }
410 }
411
412 // If not found, add to list
413 if (iter == this->Internal->PropertySpecs.end())
414 {
415 property.Name = name;
416 property.Value = typeAndDefaultValue;
417 this->Internal->PropertySpecs.push_back(property);
418 vtkDebugMacro(<< "Added feature property " << property.Name);
419 }
420 }
421
422 //------------------------------------------------------------------------------
RequestData(vtkInformation * vtkNotUsed (request),vtkInformationVector ** vtkNotUsed (request),vtkInformationVector * outputVector)423 int vtkGeoJSONReader::RequestData(vtkInformation* vtkNotUsed(request),
424 vtkInformationVector** vtkNotUsed(request), vtkInformationVector* outputVector)
425 {
426 // Get the info object
427 vtkInformation* outInfo = outputVector->GetInformationObject(0);
428
429 // Get the output
430 vtkPolyData* output = vtkPolyData::SafeDownCast(outInfo->Get(vtkDataObject::DATA_OBJECT()));
431
432 // Parse either string input of file, depending on mode
433 Json::Value root;
434 int parseResult = 0;
435 if (this->StringInputMode)
436 {
437 parseResult = this->Internal->CanParseString(this->StringInput, root);
438 }
439 else
440 {
441 parseResult = this->Internal->CanParseFile(this->FileName, root);
442 }
443
444 if (parseResult != VTK_OK)
445 {
446 return VTK_ERROR;
447 }
448
449 // If parsed successfully into Json, then convert it
450 // into appropriate vtkPolyData
451 if (root.isObject())
452 {
453 this->Internal->ParseRoot(
454 root, output, this->OutlinePolygons, this->SerializedPropertiesArrayName);
455
456 // Convert Concave Polygons to convex polygons using triangulation
457 if (output->GetNumberOfPolys() && this->TriangulatePolygons)
458 {
459 vtkNew<vtkTriangleFilter> filter;
460 filter->SetInputData(output);
461 filter->Update();
462
463 output->ShallowCopy(filter->GetOutput());
464 }
465 }
466 return VTK_OK;
467 }
468
469 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)470 void vtkGeoJSONReader::PrintSelf(ostream& os, vtkIndent indent)
471 {
472 Superclass::PrintSelf(os, indent);
473 os << "vtkGeoJSONReader" << std::endl;
474 os << "Filename: " << this->FileName << std::endl;
475 }
476