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