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