/* diversity-osm.c - DiversityOsm * * Copyright 2008 OpenMoko, Inc. * Authored by Chia-I Wu * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include "diversity-osm.h" #include "diversity-curl.h" #include #include /* for strcmp */ #define N_OSM_LEVELS (18) /* must < 32 */ #define LON_LIMIT (180.0) #define LAT_LIMIT (85.05) #define COORD_VALID(lon, lat) ((lon) >= -LON_LIMIT && (lon) < LON_LIMIT && \ (lat) >= -LAT_LIMIT && (lat) < LAT_LIMIT) #define TILE_VALID(priv, x, y) ((x) >= 0 && (x) < (1 << (priv)->cur_level) && \ (y) >= 0 && (y) < (1 << (priv)->cur_level)) #define RADIANS(d) ((d) * M_PI / 180.0) #define DEGREES(r) (180.0 * (r) / M_PI) #define DIVERSITY_OSM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), DIVERSITY_TYPE_OSM, DiversityOsmPrivate)) typedef struct _DiversityOsmPrivate DiversityOsmPrivate; struct _DiversityOsmPrivate { DiversityCurl *curl; const gchar **sources; const gchar **base_urls; guint levels[N_OSM_LEVELS]; gint tile_width; gint tile_height; gchar *base; gint cur_source; gint cur_level; }; static void diversity_osm_atlas_job_completed(DiversityCurl *curl, DiversityCurlJob *job, gpointer data); static void diversity_osm_iface_init(gpointer g_iface, gpointer iface_data); G_DEFINE_TYPE_EXTENDED(DiversityOsm, diversity_osm, DIVERSITY_TYPE_EQUIPMENT, 0, G_IMPLEMENT_INTERFACE(DIVERSITY_TYPE_ATLAS, diversity_osm_iface_init)); static const gchar *osm_sources[] = { "tah", "ojw", "osm", NULL }; static const gchar *osm_base_urls[] = { "http://tah.openstreetmap.org/Tiles/tile", "http://dev.openstreetmap.org/~ojw/Tiles/tile.php", "http://tile.openstreetmap.org/mapnik", NULL }; static void diversity_osm_dispose(GObject *obj) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(obj); if (priv->curl) { g_object_unref(priv->curl); priv->curl = NULL; } ((GObjectClass *) diversity_osm_parent_class)->dispose(obj); } static void diversity_osm_finalize(GObject *obj) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(obj); g_free(priv->base); ((GObjectClass *) diversity_osm_parent_class)->finalize(obj); } static gboolean diversity_osm_equip(DiversityEquipment *eqp) { return TRUE; } static void diversity_osm_unequip(DiversityEquipment *eqp) { } static gboolean diversity_osm_set_config(DiversityEquipment *eqp, const gchar *key, const GValue *val) { return FALSE; } static gboolean diversity_osm_get_config(DiversityEquipment *eqp, const gchar *key, GValue *val) { return FALSE; } static void diversity_osm_class_init(DiversityOsmClass *klass) { GObjectClass *o_class = (GObjectClass *) klass; DiversityEquipmentClass *eqp_class = (DiversityEquipmentClass *) klass; o_class->dispose = diversity_osm_dispose; o_class->finalize = diversity_osm_finalize; eqp_class->equip = diversity_osm_equip; eqp_class->unequip = diversity_osm_unequip; eqp_class->set_config = diversity_osm_set_config; eqp_class->get_config = diversity_osm_get_config; g_type_class_add_private(klass, sizeof(DiversityOsmPrivate)); } static void diversity_osm_init(DiversityOsm *osm) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(osm); gint i; priv->sources = osm_sources; priv->base_urls = osm_base_urls; for (i = 0; i < N_OSM_LEVELS; i++) priv->levels[i] = 1 << i; priv->tile_width = 256; priv->tile_height = 256; priv->cur_source = 0; priv->cur_level = 0; priv->base = g_strdup("/tmp/diversity-maps"); } DiversityOsm *diversity_osm_new(gint pool_size, gint queue_size) { DiversityOsm *osm; DiversityOsmPrivate *priv; osm = g_object_new(DIVERSITY_TYPE_OSM, "name", "osm", NULL); priv = DIVERSITY_OSM_GET_PRIVATE(osm); priv->curl = diversity_curl_new(pool_size, queue_size); if (!priv->curl) { g_object_unref(osm); return NULL; } g_signal_connect(priv->curl, "job-completed", G_CALLBACK(diversity_osm_atlas_job_completed), osm); return osm; } /* DiversityAtlas method implementations * * Inspired by pyroute and maemo-mapper. */ static void diversity_osm_atlas_job_completed(DiversityCurl *curl, DiversityCurlJob *job, gpointer data) { DiversityAtlas *atlas = DIVERSITY_ATLAS(data); g_signal_emit_by_name(atlas, "tile-completed", GPOINTER_TO_UINT(job), job->status); } static const gchar **diversity_osm_atlas_list_sources(DiversityAtlas *atlas) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); return priv->sources; } static gboolean diversity_osm_atlas_set_source(DiversityAtlas *atlas, const gchar *src) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); const gchar **strv, *p; gint i = 0; strv = priv->sources; while (*strv) { p = *strv; if (*p == *src && strcmp(p, src) == 0) break; strv++; i++; } if (!*strv) return FALSE; priv->cur_source = i; return TRUE; } static const gchar *diversity_osm_atlas_get_source(DiversityAtlas *atlas) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); return priv->sources[priv->cur_source]; } static const guint *diversity_osm_atlas_list_levels(DiversityAtlas *atlas, gint *size) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); if (size) *size = N_OSM_LEVELS; return priv->levels; } static gboolean diversity_osm_atlas_set_level(DiversityAtlas *atlas, gint level) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); if (level < 0 || level >= N_OSM_LEVELS) return FALSE; priv->cur_level = level; return TRUE; } static gint diversity_osm_atlas_get_level(DiversityAtlas *atlas) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); return priv->cur_level; } static void diversity_osm_atlas_get_tile_size(DiversityAtlas *atlas, gint *width, gint *height) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); if (width) *width = priv->tile_width; if (height) *height = priv->tile_height; } static gboolean diversity_osm_atlas_get_atlas_coord(DiversityAtlas *atlas, gdouble lon, gdouble lat, gdouble *x, gdouble *y) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); gdouble tmp; if (!COORD_VALID(lon, lat)) return FALSE; *x = ((lon + 180.0) / 360.0) * (1 << priv->cur_level); lat = RADIANS(lat); tmp = log(tan(lat) + 1.0 / cos(lat)); *y = (1.0 - tmp / M_PI) / 2.0 * (1 << priv->cur_level); return TRUE; } static gboolean diversity_osm_atlas_get_world_coord(DiversityAtlas *atlas, gdouble x, gdouble y, gdouble *lon, gdouble *lat) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); x /= (1 << priv->cur_level); y /= (1 << priv->cur_level); if (x < 0.0 || x >= 1.0 || y < 0.0 || y >= 1.0) return FALSE; *lon = -180.0 + x * 360.0; *lat = DEGREES(atan(sinh((1.0 - y * 2.0) * M_PI))); return TRUE; } #if 0 const gchar *quadstr(DiversityAtlas *atlas, gint x, gint y) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); static gchar buf[64]; gchar *qrts = "qrts"; gchar *p; gint i; p = buf; *p++ = 't'; for (i = priv->cur_level; i > 0; i--) { gint quadx, quady; quadx = (x >> (i - 1)) & 1; quady = (y >> (i - 1)) & 1; *p++ = qrts[quadx + 2 * quady]; } *p = '\0'; g_print("(%d, %d, %d) -> %s\n", priv->cur_level, x, y, buf); return buf; } #endif inline static gchar *osm_get_url(DiversityAtlas *atlas, gint x, gint y) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); const gchar *src; gchar *url; src = priv->base_urls[priv->cur_source]; url = g_strdup_printf("%s/%d/%d/%d.png", src, priv->cur_level, x, y); return url; } static gchar *diversity_osm_atlas_get_uri(DiversityAtlas *atlas, gint x, gint y) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); gchar *uri; if (!TILE_VALID(priv, x, y)) return NULL; uri = osm_get_url(atlas, x, y); return uri; } static guint diversity_osm_atlas_submit_tile(DiversityAtlas *atlas, gint x, gint y, gboolean force) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); DiversityCurlJob *job; gchar *uri; gchar buf[PATH_MAX]; if (!TILE_VALID(priv, x, y)) return 0; g_snprintf(buf, sizeof(buf), "%s/%s/%d/%d/%d.png", priv->base, priv->sources[priv->cur_source], priv->cur_level, x, y); if (g_file_test(buf, G_FILE_TEST_IS_REGULAR) && !force) return 0; uri = osm_get_url(atlas, x, y); if (!uri) return 0; job = diversity_curl_job_get(priv->curl); if (!job) return 0; job->uri = uri; job->path = g_strdup(buf); diversity_curl_job_submit(priv->curl, job); return GPOINTER_TO_UINT(job); } static gboolean diversity_osm_atlas_cancel_tile(DiversityAtlas *atlas, guint tile_id) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); DiversityCurlJob *job = GINT_TO_POINTER(tile_id); diversity_curl_job_cancel(priv->curl, job); return TRUE; } static const gchar *diversity_osm_atlas_get_path(DiversityAtlas *atlas) { DiversityOsmPrivate *priv = DIVERSITY_OSM_GET_PRIVATE(atlas); return priv->base; } static void diversity_osm_iface_init(gpointer g_iface, gpointer iface_data) { DiversityAtlasIface *iface = (DiversityAtlasIface *) g_iface; iface->list_sources = diversity_osm_atlas_list_sources; iface->set_source = diversity_osm_atlas_set_source; iface->get_source = diversity_osm_atlas_get_source; iface->list_levels = diversity_osm_atlas_list_levels; iface->set_level = diversity_osm_atlas_set_level; iface->get_level = diversity_osm_atlas_get_level; iface->get_tile_size = diversity_osm_atlas_get_tile_size; iface->get_atlas_coord = diversity_osm_atlas_get_atlas_coord; iface->get_world_coord = diversity_osm_atlas_get_world_coord; iface->get_uri = diversity_osm_atlas_get_uri; iface->submit_tile = diversity_osm_atlas_submit_tile; iface->cancel_tile = diversity_osm_atlas_cancel_tile; iface->get_path = diversity_osm_atlas_get_path; }