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, ®ion_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