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 Regionator and RegionHandler
27 // classes.
28 
29 #include "kml/regionator/regionator.h"
30 #include <map>
31 #include "kml/convenience/convenience.h"
32 #include "kml/dom.h"
33 #include "kml/engine/location_util.h"
34 #include "kml/regionator/region_handler.h"
35 #include "kml/regionator/regionator_qid.h"
36 #include "kml/regionator/regionator_util.h"
37 #include "gtest/gtest.h"
38 
39 namespace kmlregionator {
40 
41 using kmldom::AtomLinkPtr;
42 using kmldom::DocumentPtr;
43 using kmldom::FeaturePtr;
44 using kmldom::FolderPtr;
45 using kmldom::KmlPtr;
46 using kmldom::NetworkLinkPtr;
47 using kmldom::RegionPtr;
48 
49 // This map is basically used as a mini in-core filesystem.
50 typedef std::map<string, KmlPtr> kml_file_map_t;
51 
52 // This is a simple RegionHandler used in the unit tests in this file.
53 // Create the output map, create a PointRegionHandler pointing to the map,
54 // and create and run a Regionator with this handler.  After the Regionator
55 // has completed the client code can discard both the Regionator and the
56 // PointRegionHandler and use the output KML in the map as desired.
57 class PointRegionHandler : public RegionHandler {
58 public:
59   // The depth is how many levels of Region hierarchy to create.
60   // The map is where to save the generated KML.
PointRegionHandler(size_t depth,kml_file_map_t * kml_file_map)61   PointRegionHandler(size_t depth, kml_file_map_t* kml_file_map)
62     : depth_(depth), kml_file_map_(kml_file_map) {
63   }
64 
65   // The determination of whether this region has data is based solely
66   // on the depth of the region here in this test.
HasData(const RegionPtr & region)67   bool HasData(const RegionPtr& region) {
68     Qid qid(region->get_id());
69     if (qid.depth() > depth_) {
70       return false;
71     }
72     return true;
73   }
74 
75   // The feature for a given region is a LineString box around the edges
76   // and a Point Placemark in the center with the name of the qid.
GetFeature(const RegionPtr & region)77   FeaturePtr GetFeature(const RegionPtr& region) {
78     double lat, lon;
79     kmlengine::GetCenter(region->get_latlonaltbox(), &lat, &lon);
80     FolderPtr folder = kmldom::KmlFactory::GetFactory()->CreateFolder();
81     folder->add_feature(kmlconvenience::CreatePointPlacemark(region->get_id(),
82                                                              lat, lon));
83     folder->add_feature(CreateLineStringBox(region->get_id(), region));
84     return folder;
85   }
86 
87   // The generated KML is saved off to the map supplied in the constructor.
SaveKml(const KmlPtr & kml,const string & filename)88   void SaveKml(const KmlPtr& kml, const string& filename) {
89     (*kml_file_map_)[filename] = kml;
90   }
91 
92  private:
93   size_t depth_;
94   kml_file_map_t* kml_file_map_;
95 };
96 
97 
98 // This class is the unit test fixture for the KmlHandler class.
99 class RegionatorTest : public testing::Test {
100  protected:
101   kml_file_map_t kml_file_map_;
102   void TwoLevelPointRegionatorTest();
103   void FourLevelPointRegionatorTest();
104 };
105 
TEST_F(RegionatorTest,TwoLevelPointRegionatorTest)106 TEST_F(RegionatorTest, TwoLevelPointRegionatorTest) {
107   // This RegionHandler saves each KML file out to a map.
108   PointRegionHandler depth2(2, &kml_file_map_);
109   Regionator rtor(depth2, kmlconvenience::CreateRegion2d(10,0,10,0,128,-1));
110   // Run the regionator algorithm from the given region on our RegionHandler.
111   rtor.Regionate(NULL);
112 
113   // A 2 level RbNL hierarchy has one root and 4 children.
114   ASSERT_EQ(static_cast<size_t>(5), kml_file_map_.size());
115 
116   // Verify that the 5 files have the expected name and each has <kml> as root.
117   // This essentially a test of Regionator::RegionFilename().
118   ASSERT_EQ(kmldom::Type_kml, kml_file_map_["1.kml"]->Type());
119   ASSERT_EQ(kmldom::Type_kml, kml_file_map_["2.kml"]->Type());
120   ASSERT_EQ(kmldom::Type_kml, kml_file_map_["3.kml"]->Type());
121   ASSERT_EQ(kmldom::Type_kml, kml_file_map_["4.kml"]->Type());
122   ASSERT_EQ(kmldom::Type_kml, kml_file_map_["5.kml"]->Type());
123 
124   // Each KML file has a Document whose name is the region qid.
125   // This is essentially a test of Regionator::_Recurse().
126   ASSERT_EQ(string("q0"),
127                        kml_file_map_["1.kml"]->get_feature()->get_name());
128   ASSERT_EQ(string("q00"),
129                        kml_file_map_["2.kml"]->get_feature()->get_name());
130   ASSERT_EQ(string("q01"),
131                        kml_file_map_["3.kml"]->get_feature()->get_name());
132   ASSERT_EQ(string("q02"),
133                        kml_file_map_["4.kml"]->get_feature()->get_name());
134   ASSERT_EQ(string("q03"),
135                        kml_file_map_["5.kml"]->get_feature()->get_name());
136 
137   // The Document in the root KML has 5 features.
138   DocumentPtr document1 =
139       kmldom::AsDocument(kml_file_map_["1.kml"]->get_feature());
140   ASSERT_TRUE(document1);
141   ASSERT_EQ(static_cast<size_t>(5), document1->get_feature_array_size());
142   // The 5 features are 4 x NetworkLinks + the Folder returned by
143   // PointRegionHandler's GetFeature
144   NetworkLinkPtr nw = kmldom::AsNetworkLink(document1->get_feature_array_at(0));
145   NetworkLinkPtr ne = kmldom::AsNetworkLink(document1->get_feature_array_at(1));
146   NetworkLinkPtr sw = kmldom::AsNetworkLink(document1->get_feature_array_at(2));
147   NetworkLinkPtr se = kmldom::AsNetworkLink(document1->get_feature_array_at(3));
148   ASSERT_EQ(string("2.kml"), nw->get_link()->get_href());
149   ASSERT_EQ(string("3.kml"), ne->get_link()->get_href());
150   ASSERT_EQ(string("4.kml"), sw->get_link()->get_href());
151   //ASSERT_EQ(string("5.kml"), se->get_link()->get_href());
152   ASSERT_EQ(kmldom::Type_Folder,
153             document1->get_feature_array_at(4)->Type());
154 }
155 
TEST_F(RegionatorTest,FourLevelPointRegionatorTest)156 TEST_F(RegionatorTest, FourLevelPointRegionatorTest) {
157   // This RegionHandler saves each KML file out to a map.
158   PointRegionHandler depth4(4, &kml_file_map_);
159   Regionator rtor(depth4, kmlconvenience::CreateRegion2d(10,0,10,0,128,-1));
160   // Run the regionator algorithm from the given region on our RegionHandler.
161   rtor.Regionate(NULL);
162 
163   // A 4 level RbNL hierarchy has one root, 4 children, 16 grand-children,
164   // and 64 great-grand-children.  This is partly particular to
165   // PointRegionHandler as it always creates 4 children if it creates any.
166   ASSERT_EQ(static_cast<size_t>(85), kml_file_map_.size());
167 }
168 
169 class LoggingRegionHandler : public RegionHandler {
170  public:
LoggingRegionHandler(int max_regions,std::vector<kmldom::RegionPtr> * region_vector)171   LoggingRegionHandler(int max_regions,
172                        std::vector<kmldom::RegionPtr>* region_vector)
173     : max_regions_(max_regions),
174       region_count_(0),
175       region_vector_(region_vector) {
176   }
177 
178   // RegionHandler::HasData()
HasData(const RegionPtr & region)179   virtual bool HasData(const RegionPtr& region) {
180     if (++region_count_ > max_regions_) {
181       return false;
182     }
183     region_vector_->push_back(region);
184     return true;
185   }
186 
187   // RegionHandler::GetFeature()
GetFeature(const kmldom::RegionPtr & region)188   virtual kmldom::FeaturePtr GetFeature(const kmldom::RegionPtr& region) {
189     return NULL;
190   }
191 
192   // RegionHandler::SaveKml()
SaveKml(const kmldom::KmlPtr & kml,const string & filename)193   virtual void SaveKml(const kmldom::KmlPtr& kml, const string& filename) {
194   }
195 
196  private:
197   const int max_regions_;
198   int region_count_;
199   std::vector<kmldom::RegionPtr>* region_vector_;
200 };
201 
TEST_F(RegionatorTest,SimpleRegionateAligned)202 TEST_F(RegionatorTest, SimpleRegionateAligned) {
203   std::vector<kmldom::RegionPtr> region_vector;
204   LoggingRegionHandler rha(1, &region_vector);
205 
206   const kmldom::RegionPtr region = kmlconvenience::CreateRegion2d(1, -1, 1, -1,
207                                                                   128, 1024);
208   ASSERT_TRUE(Regionator::RegionateAligned(rha, region, NULL));
209   ASSERT_EQ(static_cast<size_t>(1), region_vector.size());
210   const kmldom::LatLonAltBoxPtr llab = region_vector[0]->get_latlonaltbox();
211   ASSERT_EQ(180, llab->get_north());
212   ASSERT_EQ(-180, llab->get_south());
213   ASSERT_EQ(180, llab->get_east());
214   ASSERT_EQ(-180, llab->get_west());
215 }
216 
TEST_F(RegionatorTest,SetRootFilenameTest)217 TEST_F(RegionatorTest, SetRootFilenameTest) {
218   PointRegionHandler depth2(2, &kml_file_map_);
219   Regionator rtor(depth2, kmlconvenience::CreateRegion2d(10,0,10,0,128,-1));
220   const string kPickleKml("pickle.kml");
221   rtor.SetRootFilename(kPickleKml.c_str());
222   rtor.Regionate(NULL);
223   ASSERT_EQ(kmldom::Type_kml, kml_file_map_[kPickleKml]->Type());
224   const string k2Kml("2.kml");
225   ASSERT_EQ(kmldom::Type_kml, kml_file_map_[k2Kml]->Type());
226   DocumentPtr d = kmldom::AsDocument(kml_file_map_[kPickleKml]->get_feature());
227   ASSERT_TRUE(d);
228   ASSERT_TRUE(d->has_atomlink());
229   AtomLinkPtr link = d->get_atomlink();
230   ASSERT_EQ(string(kPickleKml), link->get_href());
231   ASSERT_EQ(string("self"), link->get_rel());
232   d = kmldom::AsDocument(kml_file_map_[k2Kml]->get_feature());
233   ASSERT_TRUE(d);
234   ASSERT_TRUE(d->has_atomlink());
235   link = d->get_atomlink();
236   ASSERT_EQ(string(kPickleKml), link->get_href());
237   ASSERT_EQ(string("up"), link->get_rel());
238 }
239 
TEST_F(RegionatorTest,SetNaturalRegionTest)240 TEST_F(RegionatorTest, SetNaturalRegionTest) {
241   PointRegionHandler depth2(2, &kml_file_map_);
242   const double north(36.59062);
243   const double south(34.98788);
244   const double east(-82.00043);
245   const double west(-90.06512);
246   RegionPtr region =
247       kmlconvenience::CreateRegion2d(north,south,east,west,128,-1);
248   Regionator rtor(depth2, region);
249   rtor.SetNaturalRegion(region);
250   rtor.Regionate(NULL);
251   kmldom::KmlPtr kml = kml_file_map_["1.kml"];
252   ASSERT_TRUE(kml);
253   ASSERT_TRUE(kml->has_feature());
254   kmldom::LookAtPtr lookat =
255       kmldom::AsLookAt(kml->get_feature()->get_abstractview());
256   ASSERT_TRUE(lookat);
257   ASSERT_DOUBLE_EQ(35.78925, lookat->get_latitude());
258   ASSERT_DOUBLE_EQ(-86.032775, lookat->get_longitude());
259 }
260 
261 }  // end namespace kmlregionator
262