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 unit tests for the location utility functions.
27 
28 #include "kml/engine/location_util.h"
29 #include "kml/base/file.h"
30 #include "kml/dom.h"
31 #include "kml/engine/bbox.h"
32 #include "kml/engine/kml_file.h"
33 #include "gtest/gtest.h"
34 
35 using kmlbase::File;
36 using kmldom::CoordinatesPtr;
37 using kmldom::KmlFactory;
38 using kmldom::LatLonBoxPtr;
39 using kmldom::LatLonAltBoxPtr;
40 using kmldom::LinearRingPtr;
41 using kmldom::LineStringPtr;
42 using kmldom::LocationPtr;
43 using kmldom::ModelPtr;
44 using kmldom::MultiGeometryPtr;
45 using kmldom::PhotoOverlayPtr;
46 using kmldom::PlacemarkPtr;
47 using kmldom::PointPtr;
48 using kmldom::PolygonPtr;
49 
50 // The following define is a convenience for testing inside Google.
51 #ifdef GOOGLE_INTERNAL
52 #include "kml/base/google_internal_test.h"
53 #endif
54 
55 #ifndef DATADIR
56 #error *** DATADIR must be defined! ***
57 #endif
58 
59 namespace kmlengine {
60 
61 // Avoid linking in kmlconvenience...
CreatePointCoordinates(double lat,double lon)62 static PointPtr CreatePointCoordinates(double lat, double lon) {
63   KmlFactory* kml_factory = KmlFactory::GetFactory();
64   PointPtr point = kml_factory->CreatePoint();
65   CoordinatesPtr coordinates = KmlFactory::GetFactory()->CreateCoordinates();
66   coordinates->add_latlng(lat, lon);
67   point->set_coordinates(coordinates);
68   return point;
69 }
70 
71 // This tests the GetCenter() function.
TEST(LocationUtilTest,TestGetCenter)72 TEST(LocationUtilTest, TestGetCenter) {
73   KmlFactory* factory = KmlFactory::GetFactory();
74   // NULL output pointer(s) should not crash.
75   LatLonBoxPtr llb = factory->CreateLatLonBox();
76   GetCenter(llb, NULL, NULL);
77   double lat, lon;
78   GetCenter(llb, &lat, NULL);
79   // Missing lon pointer still saves a result for lat.
80   ASSERT_EQ(0.0, lat);
81   GetCenter(llb, NULL, &lon);
82   // Missing lat pointer still saves a result for lon.
83   ASSERT_EQ(0.0, lat);
84   // A default LatLonBox is well defined thus so is its center.
85   GetCenter(llb, &lat, &lon);
86   ASSERT_EQ(0.0, lat);
87   ASSERT_EQ(0.0, lon);
88   // A default LatLonAltBox is well defined thus so is its center.
89   LatLonAltBoxPtr llab = factory->CreateLatLonAltBox();
90   GetCenter(llab, &lat, &lon);
91   ASSERT_EQ(0.0, lat);
92   ASSERT_EQ(0.0, lon);
93 }
94 
TEST(LocationUtilTest,TestGetFeatureLatLon)95 TEST(LocationUtilTest, TestGetFeatureLatLon) {
96   KmlFactory* factory = KmlFactory::GetFactory();
97   const double kLat(-22.22);
98   const double kLon(42.123);
99   PlacemarkPtr placemark = factory->CreatePlacemark();
100   placemark->set_geometry(CreatePointCoordinates(kLat, kLon));
101   double lat, lon;
102   ASSERT_TRUE(GetFeatureLatLon(placemark, &lat, &lon));
103   ASSERT_EQ(kLat, lat);
104   ASSERT_EQ(kLon, lon);
105 }
106 
TEST(LocationUtilTest,TestGetModelLatLon)107 TEST(LocationUtilTest, TestGetModelLatLon) {
108   KmlFactory* factory = KmlFactory::GetFactory();
109   const double kLat(-22.22);
110   const double kLon(42.123);
111   ModelPtr model = factory->CreateModel();
112   LocationPtr location = factory->CreateLocation();
113   location->set_latitude(kLat);
114   location->set_longitude(kLon);
115   model->set_location(location);
116   double lat, lon;
117   ASSERT_TRUE(GetModelLatLon(model, &lat, &lon));
118   ASSERT_EQ(kLat, lat);
119   ASSERT_EQ(kLon, lon);
120 }
121 
TEST(LocationUtilTest,TestGetPlacemarkLatLon)122 TEST(LocationUtilTest, TestGetPlacemarkLatLon) {
123   KmlFactory* factory = KmlFactory::GetFactory();
124   const double kLat(-22.22);
125   const double kLon(42.123);
126   PlacemarkPtr placemark = factory->CreatePlacemark();
127   placemark->set_geometry(CreatePointCoordinates(kLat, kLon));
128   double lat, lon;
129   ASSERT_TRUE(GetPlacemarkLatLon(placemark, &lat, &lon));
130   ASSERT_EQ(kLat, lat);
131   ASSERT_EQ(kLon, lon);
132 }
133 
TEST(LocationUtilTest,TestGetPointLatLon)134 TEST(LocationUtilTest, TestGetPointLatLon) {
135   const double kLat(-22.22);
136   const double kLon(42.123);
137   double lat, lon;
138   ASSERT_TRUE(GetPointLatLon(CreatePointCoordinates(kLat, kLon), &lat, &lon));
139   ASSERT_EQ(kLat, lat);
140   ASSERT_EQ(kLon, lon);
141 }
142 
143 // This internal utility function parses the testcase file to a KmlFile.
ParseFromDataDirFile(const string & subdir,const string & filename)144 static KmlFilePtr ParseFromDataDirFile(const string& subdir,
145                                        const string& filename) {
146   string kml_data;
147   const string kml_file =
148     File::JoinPaths(File::JoinPaths(string(DATADIR), subdir), filename);
149   return File::ReadFileToString(kml_file, &kml_data) ?
150       KmlFile::CreateFromParse(kml_data, NULL) : NULL;
151 }
152 
153 // This is a table of test cases.
154 static const struct {
155   const char* subdir;  // Subdirectory of testdata.
156   const char* kml_filename;  // Path relative to subdir.
157   const char* feature_id;  // id= of Feature within file.
158   bool has_bounds;  // Expected return value of GetFeatureBounds.
159   double north;  // Expected values of Bbox fields iff has_bounds == true.
160   double south;
161   double east;
162   double west;
163   bool has_loc;  // Expected return value of GetFeatureLatLon.
164   double lat;  // Expected values of GetFeatureLatLon iff has_loc == true.
165   double lon;
166 } kTestCases[] = {
167   {  // A 2d Placemark.
168     "kml", "kmlsamples.kml", "simple-placemark",
169     true, 37.4222899014025, 37.4222899014025, -122.082203542568,
170     -122.082203542568,
171     true, 37.4222899014025, -122.082203542568 },
172   {  // A 3d Placemark.
173     "kml", "kmlsamples.kml", "floating-placemark",
174     true, 37.4220033612141, 37.4220033612141, -122.084075, -122.084075,
175     true, 37.4220033612141, -122.084075 },
176   {  // A Placemark with no Geometry.
177     "kml", "kmlsamples.kml", "descriptive-html-placemark",
178     false, 0, 0, 0, 0,
179     false, 0, 0 },
180   {  // A 2d LineString Placemark.
181     "kml", "kmlsamples.kml", "tessellated-linestring-placemark",
182     true, 36.1067787047714, 36.0905099328766, -112.081423783034,
183     -112.087026775269,
184     true, 36.098644318824, -112.0842252791515 },
185   {  // A 3d LineString Placemark (altitudeMode=absolute, and alt != 0).
186     "kml", "kmlsamples.kml", "purple-line-placemark", true, 36.0944767260255,
187     36.086463123013, -112.265654928602, -112.269526855561,
188     true, 36.09046992451925, -112.2675908920815 },
189   {  // A Polygon with only an outerBoundaryIs.
190     "kml", "kmlsamples.kml", "b41", true, 37.4228181532365, 37.4220817196725,
191     -122.08509907149, -122.086016227378,
192     true, 37.4224499364545, -122.085557649434 },
193   {  // A Polygon with holes.
194     "kml", "kmlsamples.kml", "pentagon", true, 38.872910162817, 38.868757801256,
195     -77.0531553685479, -77.0584405629039,
196     true, 38.8708339820365, -77.0557979657259 },
197   {  // A Folder with multiple Features, but none with any location info.
198     "kml", "kmlsamples.kml", "screen-overlays-folder",
199     false, 0, 0, 0, 0,
200     false, 0, 0 },
201   {  // Model
202     "kml", "model-macky.kml", "model-macky",
203     true, 40.009993372683, 40.009993372683, -105.272774533734,
204     -105.272774533734,
205     true, 40.009993372683, -105.272774533734
206   },
207   {  // PhotoOverlay
208     "kml", "photooverlay-zermatt.kml", "photooverlay-zermatt",
209     true, 45.968226693, 45.968226693, 7.71792711000002, 7.71792711000002,
210     true, 45.968226693, 7.71792711000002
211   },
212 #if 0
213   {  // TODO: GroundOverlay
214     "kml", "kmlsamples.kml", "large-groundoverlay",
215     true, 45.968226693, 45.968226693, 7.71792711000002, 7.71792711000002,
216     true, 45.968226693, 7.71792711000002
217   },
218 #endif
219   {  // A Folder with multiple Point Placemarks.
220     "kml", "kmlsamples.kml", "placemarks-folder",
221     true, 37.4222899014025, 37.4215692786755, -122.082203542568,
222     -122.085766700618,
223     true, 37.421929590039, -122.083985121593
224   },
225   {  // A Folder with multiple LineString Placemarks.
226     "kml", "kmlsamples.kml", "paths-folder",
227     true, 36.1067787047714, 36.0795495214565, -112.080622229595,
228     -112.269526855561,
229     true, 36.09316411311395, -112.175074542578
230   },
231   {  // A Folder with multiple Polygon Placemarks.
232     "kml", "kmlsamples.kml", "google-campus-folder",
233     true, 37.4228181532365, 37.4212932884059, -122.082850226966,
234     -122.086016227378,
235     true, 37.4220557208212, -122.084433227172
236   },
237   {  // A Document with multiple Folders each with multiple Features.
238     "kml", "kmlsamples.kml", "root-document",
239     true, 38.872910162817, 36.0795495214565, -77.0531553685479,
240     -122.086016227378,
241     true, 37.476229842136746, -99.569585797962958
242   }
243 };
244 
TEST(LocationUtilTest,RunTestCases)245 TEST(LocationUtilTest, RunTestCases) {
246   size_t size = sizeof(kTestCases)/sizeof(kTestCases[0]);
247   for (size_t i = 0; i < size; ++i) {
248     KmlFilePtr kml_file =
249         ParseFromDataDirFile(kTestCases[i].subdir, kTestCases[i].kml_filename);
250     // Assert basic sanity of KmlFile.
251     ASSERT_TRUE(kml_file) << kTestCases[i].kml_filename;
252     ASSERT_TRUE(kml_file->get_root());
253     kmldom::FeaturePtr feature = kmldom::AsFeature(
254         kml_file->GetObjectById(kTestCases[i].feature_id));
255     // Asserts both that this id is found and is a Feature.
256     ASSERT_TRUE(feature);
257     Bbox bbox;
258     ASSERT_EQ(kTestCases[i].has_bounds, GetFeatureBounds(feature, &bbox))
259         << kTestCases[i].kml_filename << " " << kTestCases[i].feature_id;
260     // GetFeatureBounds returns the same no matter the state of the bbox arg.
261     ASSERT_EQ(kTestCases[i].has_bounds, GetFeatureBounds(feature, NULL));
262     if (kTestCases[i].has_bounds) {
263       // If has_bounds then the test case n,s,e,w are valid to test.
264       ASSERT_EQ(kTestCases[i].north, bbox.get_north());
265       ASSERT_EQ(kTestCases[i].south, bbox.get_south());
266       ASSERT_EQ(kTestCases[i].east, bbox.get_east());
267       ASSERT_EQ(kTestCases[i].west, bbox.get_west());
268     }
269     double lat, lon;
270     ASSERT_EQ(kTestCases[i].has_loc, GetFeatureLatLon(feature, &lat, &lon));
271     // GetFeatureBounds returns same no matter the state of the lat/lon args.
272     ASSERT_EQ(kTestCases[i].has_loc, GetFeatureLatLon(feature, &lat, NULL));
273     ASSERT_EQ(kTestCases[i].has_loc, GetFeatureLatLon(feature, NULL, &lon));
274     ASSERT_EQ(kTestCases[i].has_loc, GetFeatureLatLon(feature, NULL, NULL));
275     if (kTestCases[i].has_loc) {
276       // If has_loc then the test case lat,lon are valid to test.
277       ASSERT_DOUBLE_EQ(kTestCases[i].lat, lat);
278       ASSERT_DOUBLE_EQ(kTestCases[i].lon, lon);
279     }
280   }
281 }
282 
283 // Test GetGeometryBounds on null/empty args.
TEST(LocationUtilTest,TestGetGeometryBoundsNullEmpty)284 TEST(LocationUtilTest, TestGetGeometryBoundsNullEmpty) {
285   ASSERT_FALSE(GetGeometryBounds(NULL, NULL));
286   KmlFactory* kml_factory = KmlFactory::GetFactory();
287   Bbox bbox;
288   PointPtr point = kml_factory->CreatePoint();
289   ASSERT_FALSE(GetGeometryBounds(point, NULL));
290   ASSERT_FALSE(GetGeometryBounds(point, &bbox));
291   LineStringPtr linestring = kml_factory->CreateLineString();
292   ASSERT_FALSE(GetGeometryBounds(linestring, NULL));
293   ASSERT_FALSE(GetGeometryBounds(linestring, &bbox));
294   LinearRingPtr linearring = kml_factory->CreateLinearRing();
295   ASSERT_FALSE(GetGeometryBounds(linearring, NULL));
296   ASSERT_FALSE(GetGeometryBounds(linearring, &bbox));
297   PolygonPtr poly = kml_factory->CreatePolygon();
298   ASSERT_FALSE(GetGeometryBounds(poly, &bbox));  // Issue 148
299   poly->set_outerboundaryis(kml_factory->CreateOuterBoundaryIs());
300   ASSERT_FALSE(GetGeometryBounds(poly, &bbox));
301   ModelPtr model = kml_factory->CreateModel();
302   ASSERT_FALSE(GetGeometryBounds(model, NULL));
303   ASSERT_FALSE(GetGeometryBounds(model, &bbox));
304   MultiGeometryPtr multigeometry = kml_factory->CreateMultiGeometry();
305   ASSERT_FALSE(GetGeometryBounds(multigeometry, NULL));
306   ASSERT_FALSE(GetGeometryBounds(multigeometry, &bbox));
307 }
308 
309 }  // end namespace kmlengine
310