1 // Copyright 2008, Google Inc. All rights reserved.
2 //
3 // Redistribution and use in source and binary forms, with or without
4 // modification, are permitted provided that the following conditions are met:
5 //
6 //  1. Redistributions of source code must retain the above copyright notice,
7 //     this list of conditions and the following disclaimer.
8 //  2. Redistributions in binary form must reproduce the above copyright notice,
9 //     this list of conditions and the following disclaimer in the documentation
10 //     and/or other materials provided with the distribution.
11 //  3. Neither the name of Google Inc. nor the names of its contributors may be
12 //     used to endorse or promote products derived from this software without
13 //     specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
16 // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17 // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
18 // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
19 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
21 // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
22 // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
23 // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
24 // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 
26 // This file contains the implementation of the abstract element Geometry
27 // and the concrete elements coordinates, Point, LineString, LinearRing,
28 // outerBoundaryIs, innerBoundaryIs and Polygon.
29 
30 #include "kml/dom/geometry.h"
31 #include <ctype.h>
32 #include <stdlib.h>
33 #include "kml/base/attributes.h"
34 #include "kml/base/xml_namespaces.h"
35 #include "kml/dom/element.h"
36 #include "kml/dom/kml22.h"
37 #include "kml/dom/kml_cast.h"
38 #include "kml/dom/kml_ptr.h"
39 #include "kml/dom/serializer.h"
40 
41 using kmlbase::Attributes;
42 using kmlbase::Vec3;
43 
44 namespace kmldom {
45 
Coordinates()46 Coordinates::Coordinates() {
47   set_xmlns(kmlbase::XMLNS_KML22);
48 }
49 
~Coordinates()50 Coordinates::~Coordinates() {}
51 
52 // This parses off a Vec3 from the given string and returns a pointer
53 // to the end of chars consumed.  The purpose is for use in the inner loop
54 // of the overall parse of a <coordinates> string.  This handles both 2d and
55 // 3d points and handles any form of whitespace around the commas between
56 // coordinates.  The general formula is commas between 2 or 3 coordinates
57 // with any form of whitespace permitted around the commas and whitespace
58 // between tuples including before the first and after the last.
59 // Here are some example 3d cstr's which all set the Vec3(1.1, 2.2, 3.3)
60 // Comma separated coords, space only between tuples:
61 // "1.1,2.2,3.3 4.4,5.5,6.6"
62 // Comma separated coords, leading whitespace:
63 // " 1.1,2.2,3.3 4.4,5.5,6.6"
64 // "\n 1.1,2.2,3.3 4.4,5.5,6.6"
65 // Whitespace between coordinates:
66 // "\t1.1 , 2.2 , 3.3  4.4 , 5.5,6.6"
67 // Here are some 2d cstr's which all parse to Vec3(1.1, 2.2, 0.0).
68 // Note that lack of altitude is represented as altitude = 0.0.
69 // (Actual use of the altitude typeically depends on altitudeMode).
70 // No spaces. Comma separated as might be common for Point:
71 // "1.1,2.2"
72 // A couple of tuples with comma-separated coordinates and single space
73 // separatting the tuples as might be used in LineString:
74 // "1.1,2.2 4.4,5.5"
75 // Leading newlines and tabs as might created by a pretty printer:
76 // "\n\t1.1, 2.2\t\t4.4, 5.5\n"
77 // Bad separators are simply discarded and we move to the next comma. A string
78 // like this: "1.1*2.2,3,3" will become "1.1,3.3,0.0". This precisely matches
79 // the precent for parsing of bad coordinate strings set by Google Earth.
ParseVec3(const char * cstr,char ** nextp,Vec3 * vec)80 bool Coordinates::ParseVec3(const char* cstr, char** nextp, Vec3* vec) {
81   if (!cstr || !vec) {  // Not much to do w/o input or output.
82     return false;
83   }
84   bool done = false;
85   char* endp = const_cast<char*>(cstr);
86 
87   // Ignore any commas at the start of our scan. This will cause this:
88   // <coordinates>1,2,3,4,5</coordinates> to be treated as:
89   // <coordinates>1,2,3 4,5</coordinates>, which is how Google Earth treats
90   // the misuse of commas as separators.
91   if (*endp == ',') {
92     ++endp;
93   }
94 
95   // Longitude first.  strtod() eats leading whitespace.
96   vec->set(0, strtod(endp, &endp));
97   if (endp) {
98 
99     // Latitude next.
100     while (isspace(*endp) || *endp != ',') {
101       // We check here to make sure the parse is sane. If we've been passed
102       // an invalid coordinate string, this loop will reach the null
103       // terminator. If we see it, we set the nextp pointer to the end and
104       // return which will let Coordinates::Parse know that it's finished.
105       if (*endp == '\0') {
106         *nextp = endp;
107         return done;
108       }
109       // Eat whitespace between double and comma.
110       ++endp;
111     }
112     vec->set(1, strtod(endp+1, &endp));
113     done = true;  // Need at least lon,lat to be valid.
114 
115     // If no altitude set to 0
116     while (isspace(*endp)) {  // Eat whitespace between double and comma.
117       ++endp;
118     }
119     if (*endp == ',') {
120       // Note that this sets altitude only if an altitude is supplied.
121       vec->set(2, strtod(endp+1, &endp));
122     }
123   }
124   if (nextp) {
125     while ((endp != NULL) && isspace(*endp)) {  // Eat the remaining whitespace before return.
126       ++endp;
127     }
128     *nextp = endp;
129   }
130   return done;
131 }
132 
133 // The char_data is everything between <coordinates> elements including
134 // leading and trailing whitespace.
Parse(const string & char_data)135 void Coordinates::Parse(const string& char_data) {
136   const char* cstr = char_data.c_str();
137   const char* endp = cstr + char_data.size();
138   char* next = const_cast<char*>(cstr);
139   while (next != endp) {
140     Vec3 vec;
141     if (ParseVec3(next, &next, &vec)) {
142       coordinates_array_.push_back(vec);
143     }
144   }
145 }
146 
147 // Coordinates essentially parses itself.
AddElement(const ElementPtr & element)148 void Coordinates::AddElement(const ElementPtr& element) {
149   Parse(get_char_data());
150 }
151 
Serialize(Serializer & serializer) const152 void Coordinates::Serialize(Serializer& serializer) const {
153   Attributes dummy;
154   serializer.BeginById(Type(), dummy);
155   serializer.BeginElementArray(Type(), coordinates_array_.size());
156   for (size_t i = 0; i < coordinates_array_.size(); ++i) {
157     serializer.SaveVec3(coordinates_array_[i]);
158   }
159   serializer.EndElementArray(Type_coordinates);
160   serializer.End();
161 }
162 
Accept(Visitor * visitor)163 void Coordinates::Accept(Visitor* visitor) {
164   visitor->VisitCoordinates(CoordinatesPtr(this));
165 }
166 
Geometry()167 Geometry::Geometry() {}
168 
~Geometry()169 Geometry::~Geometry() {}
170 
AltitudeGeometryCommon()171 AltitudeGeometryCommon::AltitudeGeometryCommon()
172   : altitudemode_(ALTITUDEMODE_CLAMPTOGROUND),
173     has_altitudemode_(false),
174     gx_altitudemode_(GX_ALTITUDEMODE_CLAMPTOSEAFLOOR),
175     has_gx_altitudemode_(false) {
176 }
177 
~AltitudeGeometryCommon()178 AltitudeGeometryCommon::~AltitudeGeometryCommon() {
179 }
180 
AddElement(const ElementPtr & element)181 void AltitudeGeometryCommon::AddElement(const ElementPtr& element) {
182   if (!element) {
183     return;
184   }
185   switch (element->Type()) {
186     case Type_altitudeMode:
187       has_altitudemode_ = element->SetEnum(&altitudemode_);
188       return;
189     case Type_GxAltitudeMode:
190       has_gx_altitudemode_ = element->SetEnum(&gx_altitudemode_);
191       return;
192     default:
193       Geometry::AddElement(element);
194   }
195 }
196 
ExtrudeGeometryCommon()197 ExtrudeGeometryCommon::ExtrudeGeometryCommon()
198   : extrude_(false),
199     has_extrude_(false) {
200 }
201 
~ExtrudeGeometryCommon()202 ExtrudeGeometryCommon::~ExtrudeGeometryCommon() {
203 }
204 
AddElement(const ElementPtr & element)205 void ExtrudeGeometryCommon::AddElement(const ElementPtr& element) {
206   if (!element) {
207     return;
208   }
209   if (element->Type() == Type_extrude) {
210     has_extrude_ = element->SetBool(&extrude_);
211     return;
212   }
213   AltitudeGeometryCommon::AddElement(element);
214 }
215 
CoordinatesGeometryCommon()216 CoordinatesGeometryCommon::CoordinatesGeometryCommon() {}
217 
~CoordinatesGeometryCommon()218 CoordinatesGeometryCommon::~CoordinatesGeometryCommon() {}
219 
AddElement(const ElementPtr & element)220 void CoordinatesGeometryCommon::AddElement(const ElementPtr& element) {
221   if (CoordinatesPtr coordinates = AsCoordinates(element)) {
222     set_coordinates(coordinates);
223   } else {
224     ExtrudeGeometryCommon::AddElement(element);
225   }
226 }
227 
228 
AcceptChildren(VisitorDriver * driver)229 void CoordinatesGeometryCommon::AcceptChildren(VisitorDriver* driver) {
230   ExtrudeGeometryCommon::AcceptChildren(driver);
231   if (has_coordinates()) {
232     driver->Visit(get_coordinates());
233   }
234 }
235 
Point()236 Point::Point() {}
237 
~Point()238 Point::~Point() {}
239 
Serialize(Serializer & serializer) const240 void Point::Serialize(Serializer& serializer) const {
241   ElementSerializer element_serializer(*this, serializer);
242   Geometry::Serialize(serializer);
243   if (has_extrude()) {
244     serializer.SaveFieldById(Type_extrude, get_extrude());
245   }
246   if (has_altitudemode()) {
247     serializer.SaveEnum(Type_altitudeMode, get_altitudemode());
248   }
249   if (has_gx_altitudemode()) {
250     serializer.SaveEnum(Type_GxAltitudeMode, get_gx_altitudemode());
251   }
252   if (has_coordinates()) {
253     serializer.SaveElement(get_coordinates());
254   }
255 }
256 
Accept(Visitor * visitor)257 void Point::Accept(Visitor* visitor) {
258   visitor->VisitPoint(PointPtr(this));
259 }
260 
LineCommon()261 LineCommon::LineCommon()
262   : tessellate_(false),
263     has_tessellate_(false) {
264 }
265 
~LineCommon()266 LineCommon::~LineCommon() {}
267 
AddElement(const ElementPtr & element)268 void LineCommon::AddElement(const ElementPtr& element) {
269   if (!element) {
270     return;
271   }
272   if (element->Type() == Type_tessellate) {
273     has_tessellate_ = element->SetBool(&tessellate_);
274     return;
275   }
276   CoordinatesGeometryCommon::AddElement(element);
277 }
278 
Serialize(Serializer & serializer) const279 void LineCommon::Serialize(Serializer& serializer) const {
280   ElementSerializer element_serializer(*this, serializer);
281   Geometry::Serialize(serializer);
282   if (has_extrude()) {
283     serializer.SaveFieldById(Type_extrude, get_extrude());
284   }
285   if (has_tessellate()) {
286     serializer.SaveFieldById(Type_tessellate, get_tessellate());
287   }
288   if (has_altitudemode()) {
289     serializer.SaveEnum(Type_altitudeMode, get_altitudemode());
290   }
291   if (has_gx_altitudemode()) {
292     serializer.SaveEnum(Type_GxAltitudeMode, get_gx_altitudemode());
293   }
294   if (has_coordinates()) {
295     serializer.SaveElement(get_coordinates());
296   }
297 }
298 
LineString()299 LineString::LineString() {}
300 
~LineString()301 LineString::~LineString() {}
302 
Accept(Visitor * visitor)303 void LineString::Accept(Visitor* visitor) {
304   visitor->VisitLineString(LineStringPtr(this));
305 }
306 
LinearRing()307 LinearRing::LinearRing() {}
308 
~LinearRing()309 LinearRing::~LinearRing() {}
310 
Accept(Visitor * visitor)311 void LinearRing::Accept(Visitor* visitor) {
312   visitor->VisitLinearRing(LinearRingPtr(this));
313 }
314 
BoundaryCommon()315 BoundaryCommon::BoundaryCommon() {}
316 
~BoundaryCommon()317 BoundaryCommon::~BoundaryCommon() {}
318 
AddElement(const ElementPtr & element)319 void BoundaryCommon::AddElement(const ElementPtr& element) {
320   if (LinearRingPtr linearring = AsLinearRing(element)) {
321     set_linearring(linearring);
322   } else {
323     Element::AddElement(element);
324   }
325 }
326 
Serialize(Serializer & serializer) const327 void BoundaryCommon::Serialize(Serializer& serializer) const {
328   ElementSerializer element_serializer(*this, serializer);
329   if (has_linearring()) {
330     serializer.SaveElement(get_linearring());
331   }
332 }
333 
334 
AcceptChildren(VisitorDriver * driver)335 void BoundaryCommon::AcceptChildren(VisitorDriver* driver) {
336   Element::AcceptChildren(driver);
337   if (has_linearring()) {
338     driver->Visit(get_linearring());
339   }
340 }
341 
OuterBoundaryIs()342 OuterBoundaryIs::OuterBoundaryIs() {}
343 
~OuterBoundaryIs()344 OuterBoundaryIs::~OuterBoundaryIs() {}
345 
Accept(Visitor * visitor)346 void OuterBoundaryIs::Accept(Visitor* visitor) {
347   visitor->VisitOuterBoundaryIs(OuterBoundaryIsPtr(this));
348 }
349 
InnerBoundaryIs()350 InnerBoundaryIs::InnerBoundaryIs() {}
351 
~InnerBoundaryIs()352 InnerBoundaryIs::~InnerBoundaryIs() {}
353 
Accept(Visitor * visitor)354 void InnerBoundaryIs::Accept(Visitor* visitor) {
355   visitor->VisitInnerBoundaryIs(InnerBoundaryIsPtr(this));
356 }
357 
Polygon()358 Polygon::Polygon()
359   : tessellate_(false),
360     has_tessellate_(false) {
361 }
362 
~Polygon()363 Polygon::~Polygon() {}
364 
AddElement(const ElementPtr & element)365 void Polygon::AddElement(const ElementPtr& element) {
366   if (!element) {
367     return;
368   }
369   switch (element->Type()) {
370     case Type_tessellate:
371       has_tessellate_ = element->SetBool(&tessellate_);
372       break;
373     case Type_outerBoundaryIs:
374       set_outerboundaryis(AsOuterBoundaryIs(element));
375       break;
376     case Type_innerBoundaryIs:
377       add_innerboundaryis(AsInnerBoundaryIs(element));
378       break;
379     default:
380       ExtrudeGeometryCommon::AddElement(element);
381   }
382 }
383 
Serialize(Serializer & serializer) const384 void Polygon::Serialize(Serializer& serializer) const {
385   ElementSerializer element_serializer(*this, serializer);
386   Geometry::Serialize(serializer);
387   if (has_extrude()) {
388     serializer.SaveFieldById(Type_extrude, get_extrude());
389   }
390   if (has_tessellate()) {
391     serializer.SaveFieldById(Type_tessellate, get_tessellate());
392   }
393   if (has_altitudemode()) {
394     serializer.SaveEnum(Type_altitudeMode, get_altitudemode());
395   }
396   if (has_gx_altitudemode()) {
397     serializer.SaveEnum(Type_GxAltitudeMode, get_gx_altitudemode());
398   }
399   if (has_outerboundaryis()) {
400     serializer.SaveElement(get_outerboundaryis());
401   }
402   serializer.SaveElementArray(innerboundaryis_array_);
403 }
404 
Accept(Visitor * visitor)405 void Polygon::Accept(Visitor* visitor) {
406   visitor->VisitPolygon(PolygonPtr(this));
407 }
408 
AcceptChildren(VisitorDriver * driver)409 void Polygon::AcceptChildren(VisitorDriver* driver) {
410   ExtrudeGeometryCommon::AcceptChildren(driver);
411   if (has_outerboundaryis()) {
412     driver->Visit(get_outerboundaryis());
413   }
414   Element::AcceptRepeated<InnerBoundaryIsPtr>(&innerboundaryis_array_, driver);
415 }
416 
MultiGeometry()417 MultiGeometry::MultiGeometry() {}
418 
~MultiGeometry()419 MultiGeometry::~MultiGeometry() {}
420 
add_geometry(const GeometryPtr & geometry)421 void MultiGeometry::add_geometry(const GeometryPtr& geometry) {
422   AddComplexChild(geometry, &geometry_array_);
423 }
424 
AddElement(const ElementPtr & element)425 void MultiGeometry::AddElement(const ElementPtr& element) {
426   if (!element) {
427     return;
428   }
429   if (element->IsA(Type_Geometry)) {
430     add_geometry(AsGeometry(element));
431     return;
432   }
433   Geometry::AddElement(element);
434 }
435 
Serialize(Serializer & serializer) const436 void MultiGeometry::Serialize(Serializer& serializer) const {
437   ElementSerializer element_serializer(*this, serializer);
438   Geometry::Serialize(serializer);
439   serializer.SaveElementGroupArray(geometry_array_, Type_Geometry);
440 }
441 
Accept(Visitor * visitor)442 void MultiGeometry::Accept(Visitor* visitor) {
443   visitor->VisitMultiGeometry(MultiGeometryPtr(this));
444 }
445 
AcceptChildren(VisitorDriver * driver)446 void MultiGeometry::AcceptChildren(VisitorDriver* driver) {
447   Geometry::AcceptChildren(driver);
448   Element::AcceptRepeated<GeometryPtr>(&geometry_array_, driver);
449 }
450 
GxTrack()451 GxTrack::GxTrack() {
452   set_xmlns(kmlbase::XMLNS_GX22);
453 }
454 
~GxTrack()455 GxTrack::~GxTrack() {}
456 
AddElement(const ElementPtr & element)457 void GxTrack::AddElement(const ElementPtr& element) {
458   if (!element) {
459     return;
460   }
461   switch (element->Type()) {
462     case Type_when:
463       add_when(element->get_char_data());
464       break;
465     case Type_GxAngles:
466       Parse(element->get_char_data(), &gx_angles_array_);
467       break;
468     case Type_GxCoord:
469       Parse(element->get_char_data(), &gx_coord_array_);
470       break;
471     case Type_Model:
472       set_model(AsModel(element));
473       break;
474     case Type_ExtendedData:
475       set_extendeddata(AsExtendedData(element));
476       break;
477     default:
478       AltitudeGeometryCommon::AddElement(element);
479   }
480 }
481 
Serialize(Serializer & serializer) const482 void GxTrack::Serialize(Serializer& serializer) const {
483   ElementSerializer element_serializer(*this, serializer);
484   Geometry::Serialize(serializer);
485   if (has_altitudemode()) {
486     serializer.SaveEnum(Type_altitudeMode, get_altitudemode());
487   }
488   if (has_gx_altitudemode()) {
489     serializer.SaveEnum(Type_GxAltitudeMode, get_gx_altitudemode());
490   }
491   for (size_t i = 0; i < when_array_.size(); i++) {
492     serializer.SaveStringFieldById(Type_when, when_array_[i]);
493   }
494   const Attributes dummy;
495   for (size_t i = 0; i < gx_coord_array_.size(); i++) {
496     serializer.SaveSimpleVec3(Type_GxCoord, gx_coord_array_.at(i), " ");
497   }
498   for (size_t i = 0; i < gx_angles_array_.size(); i++) {
499     serializer.SaveSimpleVec3(Type_GxAngles, gx_angles_array_.at(i), " ");
500   }
501   if (has_model()) {
502     serializer.SaveElement(get_model());
503   }
504   if (has_extendeddata()) {
505     serializer.SaveElement(get_extendeddata());
506   }
507 }
508 
Accept(Visitor * visitor)509 void GxTrack::Accept(Visitor* visitor) {
510   visitor->VisitGxTrack(GxTrackPtr(this));
511 }
512 
AcceptChildren(VisitorDriver * driver)513 void GxTrack::AcceptChildren(VisitorDriver* driver) {
514   AltitudeGeometryCommon::AcceptChildren(driver);
515   if (has_model()) {
516     driver->Visit(get_model());
517   }
518   if (has_extendeddata()) {
519     driver->Visit(get_extendeddata());
520   }
521 }
522 
Parse(const string & char_data,std::vector<Vec3> * out)523 void GxTrack::Parse(const string& char_data, std::vector<Vec3>* out) {
524   if (!out) {
525     return;
526   }
527   // TODO: this is a little heavy. Optimization along the lines of
528   // Coordinates::Parse may be required.
529   std::vector<string> s;
530   kmlbase::SplitStringUsing(char_data, " ", &s);
531   kmlbase::Vec3 vec;
532   for (size_t i = 0; i < s.size(); i++) {
533     vec.set(i, strtod(s[i].c_str(), NULL));
534     if (i > 2) break;
535   }
536   out->push_back(vec);
537 }
538 
GxMultiTrack()539 GxMultiTrack::GxMultiTrack()
540   : gx_interpolate_(false), has_gx_interpolate_(false) {
541   set_xmlns(kmlbase::XMLNS_GX22);
542 }
543 
~GxMultiTrack()544 GxMultiTrack::~GxMultiTrack() {}
545 
add_gx_track(const GxTrackPtr & gx_track)546 void GxMultiTrack::add_gx_track(const GxTrackPtr& gx_track) {
547   AddComplexChild(gx_track, &gx_track_array_);
548 }
549 
AddElement(const ElementPtr & element)550 void GxMultiTrack::AddElement(const ElementPtr& element) {
551   if (!element) {
552     return;
553   }
554   if (element->Type() == Type_GxInterpolate) {
555     has_gx_interpolate_ = element->SetBool(&gx_interpolate_);
556     return;
557   }
558   if (element->IsA(Type_GxTrack)) {
559     add_gx_track(AsGxTrack(element));
560     return;
561   }
562   Geometry::AddElement(element);
563 }
564 
Serialize(Serializer & serializer) const565 void GxMultiTrack::Serialize(Serializer& serializer) const {
566   ElementSerializer element_serializer(*this, serializer);
567   Geometry::Serialize(serializer);
568   if (has_gx_interpolate_) {
569     serializer.SaveFieldById(Type_GxInterpolate, gx_interpolate_);
570   }
571   serializer.SaveElementGroupArray(gx_track_array_, Type_GxTrack);
572 }
573 
Accept(Visitor * visitor)574 void GxMultiTrack::Accept(Visitor* visitor) {
575   visitor->VisitGxMultiTrack(GxMultiTrackPtr(this));
576 }
577 
AcceptChildren(VisitorDriver * driver)578 void GxMultiTrack::AcceptChildren(VisitorDriver* driver) {
579   Geometry::AcceptChildren(driver);
580   Element::AcceptRepeated<GxTrackPtr>(&gx_track_array_, driver);
581 }
582 
583 }  // end namespace kmldom
584