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