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