text: // Copyright 2008, Google Inc. All rights reserved.
  //
  // Redistribution and use in source and binary forms, with or without 
  // modification, are permitted provided that the following conditions are met:
  //
  //  1. Redistributions of source code must retain the above copyright notice, 
  //     this list of conditions and the following disclaimer.
  //  2. Redistributions in binary form must reproduce the above copyright notice,
  //     this list of conditions and the following disclaimer in the documentation
  //     and/or other materials provided with the distribution.
  //  3. Neither the name of Google Inc. nor the names of its contributors may be
  //     used to endorse or promote products derived from this software without
  //     specific prior written permission.
  //
  // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
  // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
  // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
  // OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
  // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
  // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
  // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  
  // This file contains the implementation of location-related utility functions.
  
  #include "kml/engine/location_util.h"
  #include "kml/dom.h"
  #include "kml/engine/bbox.h"
  
  using kmlbase::Vec3;
  using kmldom::AbstractLatLonBoxPtr;
  using kmldom::ContainerPtr;
  using kmldom::CoordinatesPtr;
  using kmldom::FeaturePtr;
  using kmldom::GeometryPtr;
  using kmldom::LinearRingPtr;
  using kmldom::LineStringPtr;
  using kmldom::ModelPtr;
  using kmldom::MultiGeometryPtr;
  using kmldom::PhotoOverlayPtr;
  using kmldom::PlacemarkPtr;
  using kmldom::PointPtr;
  using kmldom::PolygonPtr;
  
  namespace kmlengine {
  
  bool GetCoordinatesBounds(const CoordinatesPtr& coordinates, Bbox* bbox) {
    if (!coordinates) {
      return false;
    }
    size_t num_coords = coordinates->get_coordinates_array_size();
    if (bbox) {
      for (size_t i = 0; i < num_coords; ++i) {
        const Vec3& vec3 = coordinates->get_coordinates_array_at(i);
        bbox->ExpandLatLon(vec3.get_latitude(), vec3.get_longitude());
      }
    }
    return num_coords != 0;
  }
  
  bool GetFeatureBounds(const FeaturePtr& feature, Bbox* bbox) {
    if (PlacemarkPtr placemark = kmldom::AsPlacemark(feature)) {
      return GetGeometryBounds(placemark->get_geometry(), bbox);
    } else if (PhotoOverlayPtr photooverlay = kmldom::AsPhotoOverlay(feature)) {
      return GetCoordinatesParentBounds(photooverlay->get_point(), bbox);
    } else if (ContainerPtr container = kmldom::AsContainer(feature)) {
      // TODO: unify feature hierarchy walking with public API for such
      size_t num_features = container->get_feature_array_size();
      for (size_t i = 0; i < num_features; ++num_features) {
        bool has_bounds = false;  // Turns true on any Feature w/ bounds.
        for (size_t i = 0; i < num_features; ++i) {
          if (GetFeatureBounds(container->get_feature_array_at(i), bbox)) {
            has_bounds = true;
          }
        }
        return has_bounds;
      }
    }
    // TODO: other GroundOverlay
    return false;
  }
  
  bool GetFeatureLatLon(const FeaturePtr& feature, double* lat, double* lon) {
    Bbox bbox;
    if (GetFeatureBounds(feature, &bbox)) {
      if (lat) {
        *lat = bbox.GetCenterLat();
      }
      if (lon) {
        *lon = bbox.GetCenterLon();
      }
      return true;
    }
    return false;
  }
  
  bool GetGeometryBounds(const GeometryPtr& geometry, Bbox* bbox) {
    if (!geometry) {
      return false;
    }
    // TODO: Arguably the bounds of a Geometry includes extrusion...
    if (PointPtr point = AsPoint(geometry)) {
      return GetCoordinatesParentBounds(point, bbox);
    } else if (LineStringPtr linestring = AsLineString(geometry)) {
      return GetCoordinatesParentBounds(linestring, bbox);
    } else if (LinearRingPtr linearring = AsLinearRing(geometry)) {
      return GetCoordinatesParentBounds(linearring, bbox);
    } else if (PolygonPtr polygon = AsPolygon(geometry)) {
      return polygon->has_outerboundaryis() &&
          polygon->get_outerboundaryis()->has_linearring() &&
          GetCoordinatesParentBounds(
              polygon->get_outerboundaryis()->get_linearring(), bbox);
    } else if (ModelPtr model = AsModel(geometry)) {
      return GetModelBounds(model, bbox);
    } else if (MultiGeometryPtr multigeometry = AsMultiGeometry(geometry)) {
      bool has_bounds = false;  // Turns true on any Geometry w/ bounds.
      size_t size = multigeometry->get_geometry_array_size();
      for (size_t i = 0; i < size; ++i) {
        if (GetGeometryBounds(multigeometry->get_geometry_array_at(i), bbox)) {
          has_bounds = true;
        }
      }
      return has_bounds;
    }
    return false;
  }
  
  bool GetGeometryLatLon(const GeometryPtr& geometry, double* lat, double* lon) {
    Bbox bbox;
    if (GetGeometryBounds(geometry, &bbox)) {
      if (lat) {
        *lat = bbox.GetCenterLat();
      }
      if (lon) {
        *lon = bbox.GetCenterLon();
      }
      return true;
    }
    return false;
  }
  
  bool GetPlacemarkLatLon(const PlacemarkPtr& placemark,
                          double* lat, double* lon) {
    return GetGeometryLatLon(placemark->get_geometry(), lat, lon);
  }
  
  bool GetModelBounds(const ModelPtr& model, Bbox* bbox) {
    double lat, lon;
    if (GetModelLatLon(model, &lat, &lon)) {
      if (bbox) {
        bbox->ExpandLatLon(lat, lon);
      }
      return true;
    }
    return false;
  }
  
  bool GetModelLatLon(const ModelPtr& model, double* lat, double* lon) {
    if (model) {
      if (model->has_location()) {
        if (lat) {
          *lat = model->get_location()->get_latitude();
        }
        if (lon) {
          *lon = model->get_location()->get_longitude();
        }
        return true;
      }
    }
    return false;
  }
  
  bool GetPointLatLon(const PointPtr& point, double* lat, double* lon) {
    if (point) {
      if (CoordinatesPtr coordinates = point->get_coordinates()) {
        if (coordinates->get_coordinates_array_size() > 0) {
          Vec3 point = coordinates->get_coordinates_array_at(0);
          if (lat) {
            *lat = point.get_latitude();
          }
          if (lon) {
            *lon = point.get_longitude();
          }
          return true;
        }
      }
    }
    return false;
  }
  
  void GetCenter(const AbstractLatLonBoxPtr& allb, double* lat, double* lon) {
    if (!allb) {
      return;
    }
    if (lat) {
      *lat = (allb->get_north() + allb->get_south())/2.0;
    }
    if (lon) {
      *lon = (allb->get_east() + allb->get_west())/2.0;
    }
  }
  
  }  // end namespace kmlengine
  