/**
 * champlain_map_source_factory_create_cached_source:
 * @factory: the Factory
 * @id: the wanted map source id
 *
 * Creates a cached map source.
 *
 * Returns: (transfer none): a ready to use #ChamplainMapSourceChain consisting of
 * #ChamplainMemoryCache, #ChamplainFileCache, #ChamplainMapSource matching the given name, and
 * an error tile source created with champlain_map_source_factory_create_error_source ().
 *
 * Since: 0.6
 */
ChamplainMapSource *
champlain_map_source_factory_create_cached_source (ChamplainMapSourceFactory *factory,
    const gchar *id)
{
  ChamplainMapSourceChain *source_chain;
  ChamplainMapSource *tile_source;
  ChamplainMapSource *error_source;
  ChamplainMapSource *memory_cache;
  ChamplainMapSource *file_cache;
  guint tile_size;
  ChamplainRenderer *renderer;

  tile_source = champlain_map_source_factory_create (factory, id);

  tile_size = champlain_map_source_get_tile_size (tile_source);
  error_source = champlain_map_source_factory_create_error_source (factory, tile_size);

  renderer = CHAMPLAIN_RENDERER (champlain_image_renderer_new ());
  file_cache = CHAMPLAIN_MAP_SOURCE (champlain_file_cache_new_full (100000000, NULL, renderer));

  renderer = CHAMPLAIN_RENDERER (champlain_image_renderer_new ());
  memory_cache = CHAMPLAIN_MAP_SOURCE (champlain_memory_cache_new_full (100, renderer));

  source_chain = champlain_map_source_chain_new ();
  champlain_map_source_chain_push (source_chain, error_source);
  champlain_map_source_chain_push (source_chain, tile_source);
  champlain_map_source_chain_push (source_chain, file_cache);
  champlain_map_source_chain_push (source_chain, memory_cache);

  return CHAMPLAIN_MAP_SOURCE (source_chain);
}
static guint
get_tile_size (ChamplainMapSource *map_source)
{
  ChamplainMapSourceChain *source_chain = CHAMPLAIN_MAP_SOURCE_CHAIN (map_source);
  g_return_val_if_fail (source_chain, 0);

  ChamplainMapSourceChainPrivate *priv = source_chain->priv;
  g_return_val_if_fail (priv->stack_top, 0);

  return champlain_map_source_get_tile_size (priv->stack_top);
}
static guint
get_tile_size (ChamplainMapSource *map_source)
{
  g_return_val_if_fail (CHAMPLAIN_IS_TILE_CACHE (map_source), 0);

  ChamplainMapSource *next_source = champlain_map_source_get_next_source (map_source);

  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (next_source), 0);

  return champlain_map_source_get_tile_size (next_source);
}
/**
 * champlain_map_source_get_x:
 * @map_source: a #ChamplainMapSource
 * @zoom_level: the zoom level
 * @longitude: a longitude
 *
 * Gets the x position on the map using this map source's projection.
 * (0, 0) is located at the top left.
 *
 * Returns: the x position
 *
 * Since: 0.4
 */
gdouble
champlain_map_source_get_x (ChamplainMapSource *map_source,
    guint zoom_level,
    gdouble longitude)
{
  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);

  longitude = CLAMP (longitude, CHAMPLAIN_MIN_LONGITUDE, CHAMPLAIN_MAX_LONGITUDE);

  /* FIXME: support other projections */
  return ((longitude + 180.0) / 360.0 * pow (2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
}
/**
 * champlain_map_source_get_y:
 * @map_source: a #ChamplainMapSource
 * @zoom_level: the zoom level
 * @latitude: a latitude
 *
 * Gets the y position on the map using this map source's projection.
 * (0, 0) is located at the top left.
 *
 * Returns: the y position
 *
 * Since: 0.4
 */
gdouble
champlain_map_source_get_y (ChamplainMapSource *map_source,
    guint zoom_level,
    gdouble latitude)
{
  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0);

  latitude = CLAMP (latitude, CHAMPLAIN_MIN_LATITUDE, CHAMPLAIN_MAX_LATITUDE);

  /* FIXME: support other projections */
  return ((1.0 - log (tan (latitude * M_PI / 180.0) + 1.0 / cos (latitude * M_PI / 180.0)) / M_PI) /
          2.0 * pow (2.0, zoom_level)) * champlain_map_source_get_tile_size (map_source);
}
/**
 * champlain_map_source_get_longitude:
 * @map_source: a #ChamplainMapSource
 * @zoom_level: the zoom level
 * @x: a x position
 *
 * Gets the longitude corresponding to this x position in the map source's
 * projection.
 *
 * Returns: the longitude
 *
 * Since: 0.4
 */
gdouble
champlain_map_source_get_longitude (ChamplainMapSource *map_source,
    guint zoom_level,
    gdouble x)
{
  gdouble longitude;

  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
  /* FIXME: support other projections */
  gdouble dx = (gdouble) x / champlain_map_source_get_tile_size (map_source);
  longitude = dx / pow (2.0, zoom_level) * 360.0 - 180.0;

  return CLAMP (longitude, CHAMPLAIN_MIN_LONGITUDE, CHAMPLAIN_MAX_LONGITUDE);
}
static void
get_map_size (ChamplainView *view, gint *width, gint *height)
{
  gint size, rows, cols;
  ChamplainMapSource *map_source = champlain_view_get_map_source (view);
  gint zoom_level = champlain_view_get_zoom_level (view);
  size = champlain_map_source_get_tile_size (map_source);
  rows = champlain_map_source_get_row_count (map_source,
                                                zoom_level);
  cols = champlain_map_source_get_column_count (map_source,
                                                zoom_level);
  *width = size * rows;
  *height = size * cols;

}
/**
 * champlain_map_source_get_latitude:
 * @map_source: a #ChamplainMapSource
 * @zoom_level: the zoom level
 * @y: a y position
 *
 * Gets the latitude corresponding to this y position in the map source's
 * projection.
 *
 * Returns: the latitude
 *
 * Since: 0.4
 */
gdouble
champlain_map_source_get_latitude (ChamplainMapSource *map_source,
    guint zoom_level,
    gdouble y)
{
  gdouble latitude;

  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);
  /* FIXME: support other projections */
  gdouble dy = (gdouble) y / champlain_map_source_get_tile_size (map_source);
  gdouble n = M_PI - 2.0 * M_PI * dy / pow (2.0, zoom_level);
  latitude = 180.0 / M_PI *atan (0.5 * (exp (n) - exp (-n)));

  return CLAMP (latitude, CHAMPLAIN_MIN_LATITUDE, CHAMPLAIN_MAX_LATITUDE);
}
/**
 * champlain_map_source_get_meters_per_pixel:
 * @map_source: a #ChamplainMapSource
 * @zoom_level: the zoom level
 * @latitude: a latitude
 * @longitude: a longitude
 *
 * Gets meters per pixel at the position on the map using this map source's projection.
 *
 * Returns: the meters per pixel
 *
 * Since: 0.4.3
 */
gdouble
champlain_map_source_get_meters_per_pixel (ChamplainMapSource *map_source,
    guint zoom_level,
    gdouble latitude,
    G_GNUC_UNUSED gdouble longitude)
{
  g_return_val_if_fail (CHAMPLAIN_IS_MAP_SOURCE (map_source), 0.0);

  /*
   * Width is in pixels. (1 px)
   * m/px = radius_at_latitude / width_in_pixels
   * k = radius of earth = 6 378.1 km
   * radius_at_latitude = 2pi * k * sin (pi/2-theta)
   */

  gdouble tile_size = champlain_map_source_get_tile_size (map_source);
  /* FIXME: support other projections */
  return 2.0 *M_PI *EARTH_RADIUS *sin (M_PI / 2.0 - M_PI / 180.0 *latitude) /
         (tile_size * champlain_map_source_get_row_count (map_source, zoom_level));
}
/**
 * champlain_map_source_factory_create_cached_source:
 * @factory: the Factory
 * @id: the wanted map source id
 *
 * Creates a cached map source.
 *
 * Returns: a ready to use #ChamplainMapSourceChain consisting of
 * #ChamplainFileCache, #ChamplainMapSource matching the given name, and
 * #ChamplainErrorTileSource.
 *
 * Since: 0.6
 */
ChamplainMapSource * champlain_map_source_factory_create_cached_source (ChamplainMapSourceFactory *factory,
    const gchar *id)
{
  ChamplainMapSourceChain *source_chain;
  ChamplainMapSource *tile_source;
  ChamplainMapSource *error_source;
  ChamplainMapSource *file_cache;
  guint tile_size;

  tile_source = champlain_map_source_factory_create (factory, id);

  tile_size = champlain_map_source_get_tile_size (tile_source);
  error_source = CHAMPLAIN_MAP_SOURCE(champlain_error_tile_source_new_full (tile_size));

  file_cache = CHAMPLAIN_MAP_SOURCE(champlain_file_cache_new ());

  source_chain = champlain_map_source_chain_new ();
  champlain_map_source_chain_push (source_chain, error_source);
  champlain_map_source_chain_push (source_chain, tile_source);
  champlain_map_source_chain_push (source_chain, file_cache);

  return CHAMPLAIN_MAP_SOURCE(source_chain);
}