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 defines the Regionator class which implements the core
27 // "regionation" algorithm. See region_handler.h for how this operates.
28
29 #include "kml/regionator/regionator.h"
30 #include <sstream>
31 #include "kml/base/file.h"
32 #include "kml/base/mimetypes.h"
33 #include "kml/base/util.h"
34 #include "kml/convenience/atom_util.h"
35 #include "kml/dom.h"
36 #include "kml/engine/bbox.h"
37 #include "kml/engine/feature_view.h"
38 #include "kml/regionator/regionator_qid.h"
39 #include "kml/regionator/regionator_util.h"
40
41 using kmldom::DocumentPtr;
42 using kmldom::ElementPtr;
43 using kmldom::FeaturePtr;
44 using kmldom::KmlPtr;
45 using kmldom::RegionPtr;
46
47 namespace kmlregionator {
48
49 // A Regionator instance is created from a class derived from RegionHandler
50 // and descends over a Region hierarchy as specified.
Regionator(RegionHandler & rhandler,const RegionPtr & region)51 Regionator::Regionator(RegionHandler& rhandler, const RegionPtr& region)
52 : rhandler_(rhandler)
53 , region_count_(0)
54 , output_directory_(0)
55 , root_filename_(0)
56 {
57 root_region_ = CloneRegion(region);
58 root_region_->set_id(Qid::CreateRoot().str());
59 }
60
~Regionator()61 Regionator::~Regionator() {
62 }
63
64 // This provides the relative path name to the KML file for the given Region.
65 // This is the name baked into the NetworkLink/Link/href to the .kml for the
66 // give Region.
RegionFilename(const RegionPtr & region)67 string Regionator::RegionFilename(const RegionPtr& region) {
68 Qid qid(region->get_id());
69 if (root_filename_ && qid.IsRoot()) {
70 return root_filename_;
71 }
72 std::stringstream str;
73 str << qid_map_[qid.str()];
74 return str.str() + ".kml";
75 }
76
77 // This is an internal method to recurse down to a child of the given Region.
78 // This will save the child region to output vector if the child has data.
Recurse(const RegionPtr & parent,quadrant_t quadrant,region_vector_t * children)79 void Regionator::Recurse(const RegionPtr& parent, quadrant_t quadrant,
80 region_vector_t* children) {
81 kmldom::RegionPtr child = CreateChildRegion(parent, quadrant);
82 Qid qid(parent->get_id());
83 child->set_id(qid.CreateChild(quadrant).str());
84 if (_Regionate(child)) {
85 children->push_back(child);
86 }
87 }
88
89 // This is an internal method to visit a given Region. All calls to
90 // the RegionHandler are within this method. This method returns false
91 // if there is no data at this Region or below.
_Regionate(const RegionPtr & region)92 bool Regionator::_Regionate(const RegionPtr& region) {
93 // Ask the RegionHandler if this region has any data.
94 if (rhandler_.HasData(region) == false) {
95 return false; // No data here or below so no recursing on any children.
96 }
97
98 Qid qid(region->get_id());
99 qid_map_[qid.str()] = ++region_count_;
100
101 // Recurse on each child region saving each with data in the vector.
102 region_vector_t children;
103 Recurse(region, NW, &children);
104 Recurse(region, NE, &children);
105 Recurse(region, SW, &children);
106 Recurse(region, SE, &children);
107
108 // Create the Document that will be the root feature of the KML file for this
109 // Region.
110 DocumentPtr document = CreateRegionDocument(region);
111 document->set_name(region->get_id());
112
113 // Add an <atom:link> for each node. The root gets a "self" relation.
114 // All other nodes get an "up" relation whose href is the root KML file.
115 // up: "A URI that refers to a parent document in a hierarchy of documents."
116 // See: http://www.iana.org/assignments/link-relations/link-relations.xhtml
117 document->set_atomlink(kmlconvenience::AtomUtil::CreateBasicLink(
118 root_filename_ ? root_filename_ : "1.kml",
119 qid.IsRoot() ? "self" : "up",
120 kmlbase::kKmlMimeType));
121
122 // Create a NetworkLink to the KML file for each child region with data.
123 for (size_t i = 0; i < children.size(); ++i) {
124 string href = RegionFilename(children[i]);
125 document->add_feature(CreateRegionNetworkLink(children[i], href));
126 }
127
128 // Ask the RegionHandler for the Feature for this region.
129 FeaturePtr feature = rhandler_.GetFeature(region);
130 if (feature == NULL) {
131 return false; // This region has no data.
132 }
133 document->add_feature(feature);
134
135 // Supply the root node of the RbNL hierarchy with a <LookAt> to the natural
136 // bounds of the data if such bounds were supplied using SetNaturalRegion().
137 if (natural_region_ && qid.IsRoot()) {
138 if (kmldom::LatLonAltBoxPtr llab = natural_region_->get_latlonaltbox()) {
139 kmlengine::Bbox bbox(llab->get_north(), llab->get_south(),
140 llab->get_east(), llab->get_west());
141 document->set_abstractview(kmlengine::ComputeBboxLookAt(bbox));
142 }
143 }
144
145 // Create the root element for the KML file and set the Document as the root
146 // feature. Hand the completed KML file to the RegionHandler for it to save.
147 KmlPtr kml = kmldom::KmlFactory::GetFactory()->CreateKml();
148 kml->set_feature(document);
149 string filename(RegionFilename(region));
150 if (output_directory_) {
151 filename = kmlbase::File::JoinPaths(output_directory_, filename);
152 }
153 rhandler_.SaveKml(kml, filename);
154
155 return true; // This region has data.
156 }
157
158 // This is the public API to start the "regionation" at the Region supplied
159 // in the constructor.
Regionate(const char * output_directory)160 bool Regionator::Regionate(const char* output_directory) {
161 output_directory_ = const_cast<char*>(output_directory);
162 _Regionate(root_region_);
163 return true;
164 }
165
166 // static
RegionateAligned(RegionHandler & rhandler,const RegionPtr & region,const char * output_directory)167 bool Regionator::RegionateAligned(RegionHandler& rhandler,
168 const RegionPtr& region,
169 const char* output_directory) {
170 kmldom::LatLonAltBoxPtr llab = CloneLatLonAltBox(region->get_latlonaltbox());
171 if (!CreateAlignedAbstractLatLonBox(region->get_latlonaltbox(), llab)) {
172 return false;
173 }
174 kmldom::RegionPtr aligned_region =
175 kmldom::KmlFactory::GetFactory()->CreateRegion();
176 aligned_region->set_latlonaltbox(llab);
177 aligned_region->set_lod(CloneLod(region->get_lod()));
178 boost::scoped_ptr<Regionator> regionator(new Regionator(rhandler,
179 aligned_region));
180 regionator->SetNaturalRegion(region);
181 return regionator->Regionate(output_directory);
182 }
183
184 } // end namespace kmlregionator
185