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