1 /*=========================================================================
2
3 Program: Visualization Toolkit
4 Module: vtkGeoJSONWriter.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 "vtkGeoJSONWriter.h"
17
18 #include "vtkCellArray.h"
19 #include "vtkInformation.h"
20 #include "vtkLookupTable.h"
21 #include "vtkMath.h"
22 #include "vtkObjectFactory.h"
23 #include "vtkPointData.h"
24 #include "vtkPolyData.h"
25
26 #include <sstream>
27
28 vtkStandardNewMacro(vtkGeoJSONWriter);
29
30 #define VTK_GJWRITER_MAXPOINTS 32000
31
32 class vtkGeoJSONWriter::Internals
33 {
34 public:
Internals()35 Internals()
36 {
37 this->MaxBufferSize = 128;
38 this->Buffer = new char[this->MaxBufferSize];
39 this->Top = this->Buffer;
40 };
~Internals()41 ~Internals()
42 {
43 delete[] this->Buffer;
44 }
GetSize()45 inline size_t GetSize()
46 {
47 return this->Top-this->Buffer;
48 }
Clear()49 void Clear()
50 {
51 this->Top = this->Buffer;
52 }
Grow()53 inline void Grow()
54 {
55 this->MaxBufferSize*=2;
56 //cerr << "GROW " << this->MaxBufferSize << endl;
57 char *biggerBuffer = new char[this->MaxBufferSize];
58 size_t curSize = this->Top-this->Buffer;
59 memcpy(biggerBuffer, this->Buffer, curSize);
60 delete[] this->Buffer;
61 this->Buffer = biggerBuffer;
62 this->Top = this->Buffer+curSize;
63 }
append(const char * newcontent)64 inline void append(const char *newcontent)
65 {
66 while (this->Top+strlen(newcontent)>=this->Buffer+this->MaxBufferSize)
67 {
68 this->Grow();
69 }
70 int nchars = snprintf(this->Top, this->MaxBufferSize, "%s", newcontent);
71 this->Top+=nchars;
72 }
append(const double newcontent)73 inline void append(const double newcontent)
74 {
75 snprintf(this->NumBuffer, 64, "%g", newcontent);
76 while (this->Top+strlen(NumBuffer)>=this->Buffer+this->MaxBufferSize)
77 {
78 this->Grow();
79 }
80 int nchars = snprintf(this->Top, this->MaxBufferSize, "%s", this->NumBuffer);
81 this->Top+=nchars;
82 }
83 char *Buffer;
84 char *Top;
85 size_t MaxBufferSize;
86 char NumBuffer[64];
87 };
88
89 //------------------------------------------------------------------------------
vtkGeoJSONWriter()90 vtkGeoJSONWriter::vtkGeoJSONWriter()
91 {
92 this->FileName = nullptr;
93 this->OutputString = nullptr;
94 this->SetNumberOfOutputPorts(0);
95 this->WriteToOutputString = false;
96 this->ScalarFormat = 2;
97 this->LookupTable = nullptr;
98 this->WriterHelper = new vtkGeoJSONWriter::Internals();
99 }
100
101 //------------------------------------------------------------------------------
~vtkGeoJSONWriter()102 vtkGeoJSONWriter::~vtkGeoJSONWriter()
103 {
104 this->SetFileName(nullptr);
105 delete[] this->OutputString;
106 this->SetLookupTable(nullptr);
107 delete this->WriterHelper;
108 }
109
110 //------------------------------------------------------------------------------
PrintSelf(ostream & os,vtkIndent indent)111 void vtkGeoJSONWriter::PrintSelf(ostream & os, vtkIndent indent)
112 {
113 this->Superclass::PrintSelf(os, indent);
114 os << indent << "FileName: "
115 << (this->FileName?this->FileName:"NONE") << endl;
116 os << indent << "WriteToOutputString: "
117 << (this->WriteToOutputString?"True":"False") << endl;
118 os << indent << "ScalarFormat: " << this->ScalarFormat << endl;
119 }
120
121 //------------------------------------------------------------------------------
FillInputPortInformation(int port,vtkInformation * info)122 int vtkGeoJSONWriter::FillInputPortInformation(int port, vtkInformation *info)
123 {
124 if (port == 0)
125 {
126 info->Set(vtkAlgorithm::INPUT_REQUIRED_DATA_TYPE(), "vtkPolyData");
127 }
128 return 1;
129 }
130
131 //------------------------------------------------------------------------------
OpenFile()132 ostream *vtkGeoJSONWriter::OpenFile()
133 {
134 vtkDebugMacro(<<"Opening file\n");
135
136 ostream *fptr;
137
138 if (!this->WriteToOutputString)
139 {
140 if (!this->FileName)
141 {
142 vtkErrorMacro(<< "No FileName specified! Can't write!");
143 return nullptr;
144 }
145
146 fptr = new ofstream(this->FileName, ios::out);
147 }
148 else
149 {
150 // Get rid of any old output string.
151 if (this->OutputString)
152 {
153 delete [] this->OutputString;
154 this->OutputString = nullptr;
155 this->OutputStringLength = 0;
156 }
157 fptr = new std::ostringstream;
158 }
159
160 if (fptr->fail())
161 {
162 vtkErrorMacro(<< "Unable to open file: "<< this->FileName);
163 delete fptr;
164 return nullptr;
165 }
166
167 return fptr;
168 }
169
170 //------------------------------------------------------------------------------
CloseFile(ostream * fp)171 void vtkGeoJSONWriter::CloseFile(ostream *fp)
172 {
173 vtkDebugMacro(<<"Closing file\n");
174
175 if ( fp != nullptr )
176 {
177 if (this->WriteToOutputString)
178 {
179 std::ostringstream *ostr =
180 static_cast<std::ostringstream*>(fp);
181
182 delete [] this->OutputString;
183 this->OutputStringLength = static_cast<int>(ostr->str().size());
184 //+1's account for null terminator
185 this->OutputString = new char[ostr->str().size()+1];
186 memcpy(this->OutputString, ostr->str().c_str(),
187 this->OutputStringLength+1);
188 }
189
190 delete fp;
191 }
192 }
193
194 //------------------------------------------------------------------------------
ConditionalComma(vtkIdType cnt,vtkIdType limit)195 void vtkGeoJSONWriter::ConditionalComma(
196 vtkIdType cnt, vtkIdType limit)
197 {
198 if (cnt+1 != limit)
199 {
200 this->WriterHelper->append(",");
201 }
202 }
203
204 //------------------------------------------------------------------------------
WriteScalar(vtkDataArray * da,vtkIdType ptId)205 void vtkGeoJSONWriter::WriteScalar(
206 vtkDataArray *da, vtkIdType ptId)
207 {
208 if (this->ScalarFormat == 0)
209 {
210 return;
211 }
212 if (da)
213 {
214 double b = da->GetTuple1(ptId);
215 if (this->ScalarFormat == 1)
216 {
217 vtkLookupTable *lut = this->GetLookupTable();
218 if (!lut)
219 {
220 lut = vtkLookupTable::New();
221 lut->SetNumberOfColors(256);
222 lut->SetHueRange(0.0,0.667);
223 lut->SetRange(da->GetRange());
224 lut->Build();
225 this->SetLookupTable(lut);
226 lut->Delete();
227 }
228 const unsigned char *color = lut->MapValue(b);
229 this->WriterHelper->append(",");
230 this->WriterHelper->append((double)color[0]/255.0);
231 this->WriterHelper->append(",");
232 this->WriterHelper->append((double)color[1]/255.0);
233 this->WriterHelper->append(",");
234 this->WriterHelper->append((double)color[2]/255.0);
235 }
236 else
237 {
238 if (vtkMath::IsNan(b))
239 {
240 this->WriterHelper->append(",null");
241 }
242 else
243 {
244 this->WriterHelper->append(",");
245 this->WriterHelper->append(b);
246 }
247 }
248 }
249 }
250
251 //------------------------------------------------------------------------------
WriteData()252 void vtkGeoJSONWriter::WriteData()
253 {
254 ostream *fp;
255 vtkPolyData *input = vtkPolyData::SafeDownCast(this->GetInput());
256
257 vtkDebugMacro(<<"Writing vtk polygonal data to geojson file...");
258 fp=this->OpenFile();
259 if ( !fp )
260 {
261 return;
262 }
263
264 this->WriterHelper->append("{\n");
265 this->WriterHelper->append("\"type\": \"Feature\",\n");
266 vtkDataArray *da = input->GetPointData()->GetScalars();
267 if (!da)
268 {
269 da = input->GetPointData()->GetArray(0);
270 }
271 if (da)
272 {
273 switch (this->ScalarFormat)
274 {
275 case 0:
276 this->WriterHelper->append("\"properties\": {\"ScalarFormat\": \"none\"},\n");
277 break;
278 case 1:
279 this->WriterHelper->append("\"properties\": {\"ScalarFormat\": \"rgb\"},\n");
280 break;
281 case 2:
282 double rng[2];
283 da->GetRange(rng);
284 this->WriterHelper->append("\"properties\": {\"ScalarFormat\": \"values\", \"ScalarRange\": [");
285 this->WriterHelper->append(rng[0]);
286 this->WriterHelper->append(",");
287 this->WriterHelper->append(rng[1]);
288 this->WriterHelper->append("] },\n");
289 break;
290 }
291 }
292 else
293 {
294 this->WriterHelper->append("\"properties\": {\"ScalarFormat\": \"none\"},\n");
295 }
296 this->WriterHelper->append("\"geometry\":\n");
297 this->WriterHelper->append("{\n");
298 this->WriterHelper->append("\"type\": \"GeometryCollection\",\n");
299 this->WriterHelper->append("\"geometries\":\n");
300 this->WriterHelper->append("[\n");
301
302 vtkIdType cellLoc = 0;
303 vtkIdType *cellPts = nullptr;
304 vtkIdType cellSize = 0;
305 vtkIdType numlines, numpolys;
306 numlines = input->GetLines()->GetNumberOfCells();
307 numpolys = input->GetPolys()->GetNumberOfCells();
308
309 //VERTS
310 vtkCellArray *ca;
311 ca = input->GetVerts();
312 if (ca && ca->GetNumberOfCells())
313 {
314 bool done = false;
315 vtkIdType inCell = 0;
316 vtkIdType ptCnt = 0;
317 do //loop to break into sections with < VTK_GJWRITER_MAXPOINTS points
318 {
319 this->WriterHelper->append("{\n");
320 this->WriterHelper->append("\"type\": \"MultiPoint\",\n");
321 this->WriterHelper->append("\"coordinates\":\n");
322 this->WriterHelper->append("[\n");
323 for (; inCell < ca->GetNumberOfCells() && ptCnt < VTK_GJWRITER_MAXPOINTS; inCell++)
324 {
325 ca->GetCell(cellLoc, cellSize, cellPts);
326 cellLoc += cellSize+1;
327 ptCnt += cellSize;
328 vtkIdType inPt;
329 for (inPt = 0; inPt < cellSize; inPt++)
330 {
331 double coords[3];
332 input->GetPoint(cellPts[inPt], coords);
333 this->WriterHelper->append("[");
334 for (int i=0; i<3; i++)
335 {
336 if (vtkMath::IsNan(coords[i]))
337 {
338 this->WriterHelper->append("null");
339 }
340 else
341 {
342 this->WriterHelper->append(coords[i]);
343 }
344 if (i!=2)
345 {
346 this->WriterHelper->append(",");
347 }
348 }
349 this->WriteScalar(da, cellPts[inPt]);
350 this->WriterHelper->append("]");
351 this->ConditionalComma(inPt, cellSize);
352 }
353 if (ptCnt<VTK_GJWRITER_MAXPOINTS)
354 {
355 this->ConditionalComma(inCell, ca->GetNumberOfCells());
356 }
357 this->WriterHelper->append("\n");
358 }
359 this->WriterHelper->append("]"); //coordinates for this cell array
360 if (inCell < ca->GetNumberOfCells())
361 {
362 ptCnt = 0;
363 this->WriterHelper->append(",\n");
364 }
365 else
366 {
367 if (numlines || numpolys)
368 {
369 this->WriterHelper->append(",");
370 }
371 done = true;
372 }
373 } while (!done);
374 }
375
376 //lines
377 ca = input->GetLines();
378 if (ca && ca->GetNumberOfCells())
379 {
380 bool done = false;
381 vtkIdType inCell = 0;
382 vtkIdType ptCnt = 0;
383 do //loop to break into sections with < VTK_GJWRITER_MAXPOINTS points
384 {
385 this->WriterHelper->append("{\n");
386 this->WriterHelper->append("\"type\": \"MultiLineString\",\n");
387 this->WriterHelper->append("\"coordinates\":\n");
388 this->WriterHelper->append("[\n");
389 for (; inCell < ca->GetNumberOfCells() && ptCnt < VTK_GJWRITER_MAXPOINTS; inCell++)
390 {
391 this->WriterHelper->append("[ "); //one cell
392 ca->GetCell(cellLoc, cellSize, cellPts);
393 cellLoc += cellSize+1;
394 ptCnt += cellSize;
395 vtkIdType inPt;
396 for (inPt = 0; inPt < cellSize; inPt++)
397 {
398 double coords[3];
399 input->GetPoint(cellPts[inPt], coords);
400 this->WriterHelper->append("[");
401 for (int i =0; i<3; i++)
402 {
403 if (vtkMath::IsNan(coords[i]))
404 {
405 this->WriterHelper->append("null");
406 }
407 else
408 {
409 this->WriterHelper->append(coords[i]);
410 }
411 if (i!=2)
412 {
413 this->WriterHelper->append(",");
414 }
415 }
416 this->WriteScalar(da, cellPts[inPt]);
417 this->WriterHelper->append("]");
418 this->ConditionalComma(inPt, cellSize);
419 }
420 this->WriterHelper->append("]");//one cell
421 if (ptCnt<VTK_GJWRITER_MAXPOINTS)
422 {
423 this->ConditionalComma(inCell, ca->GetNumberOfCells());
424 }
425 this->WriterHelper->append("\n");
426 }
427 this->WriterHelper->append("]"); //coordinates for this cell array
428 this->WriterHelper->append("\n");
429 this->WriterHelper->append("}\n"); //this cell array
430 if (inCell < ca->GetNumberOfCells())
431 {
432 ptCnt = 0;
433 this->WriterHelper->append(",\n");
434 }
435 else
436 {
437 if (numpolys)
438 {
439 this->WriterHelper->append(",");
440 }
441 done = true;
442 }
443 } while (!done);
444 }
445 //polygons
446 ca = input->GetPolys();
447 if (ca && ca->GetNumberOfCells())
448 {
449 bool done = false;
450 vtkIdType inCell = 0;
451 vtkIdType ptCnt = 0;
452 do //loop to break into sections with < VTK_GJWRITER_MAXPOINTS points
453 {
454 this->WriterHelper->append("{\n");
455 this->WriterHelper->append("\"type\": \"MultiPolygon\",\n");
456 this->WriterHelper->append("\"coordinates\":\n");
457 this->WriterHelper->append("[\n");
458 for (; inCell < ca->GetNumberOfCells() && ptCnt < VTK_GJWRITER_MAXPOINTS; inCell++)
459 {
460 this->WriterHelper->append("[[ "); //one cell
461 ca->GetCell(cellLoc, cellSize, cellPts);
462 cellLoc += cellSize+1;
463 ptCnt += cellSize;
464 vtkIdType inPt;
465 for (inPt = 0; inPt < cellSize; inPt++)
466 {
467 double coords[3];
468 input->GetPoint(cellPts[inPt], coords);
469 this->WriterHelper->append("[");
470 for (int i =0; i<3; i++)
471 {
472 if (vtkMath::IsNan(coords[i]))
473 {
474 this->WriterHelper->append("null");
475 }
476 else
477 {
478 this->WriterHelper->append(coords[i]);
479 }
480 if (i!=2)
481 {
482 this->WriterHelper->append(",");
483 }
484 }
485 this->WriteScalar(da, cellPts[inPt]);
486 this->WriterHelper->append("]");
487 this->ConditionalComma(inPt, cellSize);
488 }
489 this->WriterHelper->append(" ]]");//one cell
490 if (ptCnt<VTK_GJWRITER_MAXPOINTS)
491 {
492 this->ConditionalComma(inCell, ca->GetNumberOfCells());
493 }
494 this->WriterHelper->append("\n");
495 }
496 this->WriterHelper->append("]"); //coordinates for this cell array
497 this->WriterHelper->append("\n");
498 this->WriterHelper->append("}\n"); //this cell array
499 if (inCell < ca->GetNumberOfCells())
500 {
501 ptCnt = 0;
502 this->WriterHelper->append(",\n");
503 }
504 else
505 {
506 done = true;
507 }
508 } while (!done);
509 }
510
511 this->WriterHelper->append("]\n");//feature.geometry.GeometryCollection.geometries
512 this->WriterHelper->append("}\n");//feature.geometry
513 this->WriterHelper->append("}\n");//feature
514
515 fp->write(this->WriterHelper->Buffer, this->WriterHelper->GetSize());
516 this->WriterHelper->Clear();
517
518 fp->flush();
519 if (fp->fail())
520 {
521 vtkErrorMacro("Problem writing result check disk space.");
522 delete fp;
523 fp = nullptr;
524 }
525
526 this->CloseFile(fp);
527 }
528
529 //------------------------------------------------------------------------------
RegisterAndGetOutputString()530 char *vtkGeoJSONWriter::RegisterAndGetOutputString()
531 {
532 char *tmp = this->OutputString;
533
534 this->OutputString = nullptr;
535 this->OutputStringLength = 0;
536
537 return tmp;
538 }
539
540 //------------------------------------------------------------------------------
GetOutputStdString()541 vtkStdString vtkGeoJSONWriter::GetOutputStdString()
542 {
543 return vtkStdString(this->OutputString, this->OutputStringLength);
544 }
545
546 //------------------------------------------------------------------------------
547 vtkCxxSetObjectMacro(vtkGeoJSONWriter, LookupTable, vtkLookupTable)
548