]> Pileus Git - grits/blob - src/gis-tile.c
Update API docs
[grits] / src / gis-tile.c
1 /*
2  * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <config.h>
19 #include <glib.h>
20
21 #include "gis-tile.h"
22 #include "gis-util.h"
23
24 gchar *gis_tile_path_table[2][2] = {
25         {"00.", "01."},
26         {"10.", "11."},
27 };
28
29 GisTile *gis_tile_new(GisTile *parent,
30         gdouble n, gdouble s, gdouble e, gdouble w)
31 {
32         GisTile *self = g_object_new(GIS_TYPE_TILE, NULL);
33         self->parent = parent;
34         self->edge.n = n;
35         self->edge.s = s;
36         self->edge.e = e;
37         self->edge.w = w;
38         self->atime  = time(NULL);
39         return self;
40 }
41
42 gchar *gis_tile_get_path(GisTile *child)
43 {
44         /* This could be easily cached if necessasairy */
45         int x, y;
46         GList *parts = NULL;
47         for (GisTile *parent = child->parent; parent; child = parent, parent = child->parent)
48                 gis_tile_foreach_index(child, x, y)
49                         if (parent->children[x][y] == child)
50                                 parts = g_list_prepend(parts, gis_tile_path_table[x][y]);
51         GString *path = g_string_new("");
52         for (; parts; parts = parts->next)
53                 g_string_append(path, parts->data);
54         g_list_free(parts);
55         return g_string_free(path, FALSE);
56 }
57
58 gdouble _gis_tile_get_min_dist(GisTile *self,
59                 gdouble lat, gdouble lon, gdouble elev)
60 {
61         gdouble tlat  = lat > self->edge.n ? self->edge.n :
62                         lat < self->edge.s ? self->edge.s : lat;
63         gdouble tlon  = lon > self->edge.e ? self->edge.e :
64                         lon < self->edge.w ? self->edge.w : lon;
65         gdouble telev = 0; // TODO: elevation at rlat,rlon
66         //if (lat == tlat && lon == tlon)
67         //      return elev; /* Shortcut? */
68         gdouble a[3], b[3];
69         lle2xyz( lat,  lon,  elev, a+0, a+1, a+2);
70         lle2xyz(tlat, tlon, telev, b+0, b+1, b+2);
71         return distd(a, b);
72 }
73
74 gboolean _gis_tile_needs_split(GisTile *self,
75                 gdouble max_res, gint width, gint height,
76                 gdouble lat, gdouble lon, gdouble elev)
77 {
78         gdouble lat_point = self->edge.n < 0 ? self->edge.n :
79                             self->edge.s > 0 ? self->edge.s : 0;
80         gdouble min_dist  = _gis_tile_get_min_dist(self, lat, lon, elev);
81         gdouble view_res  = MPPX(min_dist);
82         gdouble lon_dist  = self->edge.e - self->edge.w;
83         gdouble tile_res  = ll2m(lon_dist, lat_point)/width;
84
85         /* This isn't really right, but it helps with memory since we don't (yet?) test if the tile
86          * would be drawn */
87         gdouble scale = elev / min_dist;
88         view_res /= scale;
89         //g_message("tile=(%7.2f %7.2f %7.2f %7.2f) "
90         //          "eye=(%9.1f %9.1f %9.1f) "
91         //          "elev=%9.1f / dist=%9.1f = %f",
92         //              self->edge.n, self->edge.s, self->edge.e, self->edge.w,
93         //              lat, lon, elev,
94         //              elev, min_dist, scale);
95
96         if (tile_res < max_res)
97                 return FALSE;
98         return view_res < tile_res;
99 }
100
101 void gis_tile_update(GisTile *self,
102                 gdouble res, gint width, gint height,
103                 gdouble lat, gdouble lon, gdouble elev,
104                 GisTileLoadFunc load_func, gpointer user_data)
105 {
106         self->atime = time(NULL);
107         //g_debug("GisTile: update - %p->atime = %u", self, (guint)self->atime);
108         gdouble lat_dist = self->edge.n - self->edge.s;
109         gdouble lon_dist = self->edge.e - self->edge.w;
110         if (_gis_tile_needs_split(self, res, width, height, lat, lon, elev)) {
111                 gdouble lat_step = lat_dist / G_N_ELEMENTS(self->children);
112                 gdouble lon_step = lon_dist / G_N_ELEMENTS(self->children[0]);
113                 int x, y;
114                 gis_tile_foreach_index(self, x, y) {
115                         if (!self->children[x][y]) {
116                                 self->children[x][y] = gis_tile_new(self,
117                                                 self->edge.n-(lat_step*(x+0)),
118                                                 self->edge.n-(lat_step*(x+1)),
119                                                 self->edge.w+(lon_step*(y+1)),
120                                                 self->edge.w+(lon_step*(y+0)));
121                                 load_func(self->children[x][y], user_data);
122                         }
123                         gis_tile_update(self->children[x][y],
124                                         res, width, height,
125                                         lat, lon, elev,
126                                         load_func, user_data);
127                 }
128         }
129 }
130
131 GisTile *gis_tile_find(GisTile *self, gdouble lat, gdouble lon)
132 {
133         gint    rows = G_N_ELEMENTS(self->children);
134         gint    cols = G_N_ELEMENTS(self->children[0]);
135
136         gdouble lat_step = (self->edge.n - self->edge.s) / rows;
137         gdouble lon_step = (self->edge.e - self->edge.w) / cols;
138
139         gdouble lat_offset = self->edge.n - lat;;
140         gdouble lon_offset = lon - self->edge.w;
141
142         gint    row = lat_offset / lat_step;
143         gint    col = lon_offset / lon_step;
144
145         if (lon == 180) col--;
146         if (lat == -90) row--;
147
148         //if (lon == 180 || lon == -180)
149         //      g_message("lat=%f,lon=%f step=%f,%f off=%f,%f row=%d/%d,col=%d/%d",
150         //              lat,lon, lat_step,lon_step, lat_offset,lon_offset, row,rows,col,cols);
151
152         if (row < 0 || row >= rows || col < 0 || col >= cols)
153                 return NULL;
154         else if (self->children[row][col] && self->children[row][col]->data)
155                 return gis_tile_find(self->children[row][col], lat, lon);
156         else
157                 return self;
158 }
159
160 GisTile *gis_tile_gc(GisTile *self, time_t atime,
161                 GisTileFreeFunc free_func, gpointer user_data)
162 {
163         if (!self)
164                 return NULL;
165         gboolean has_children = FALSE;
166         int x, y;
167         gis_tile_foreach_index(self, x, y) {
168                 self->children[x][y] = gis_tile_gc(
169                                 self->children[x][y], atime,
170                                 free_func, user_data);
171                 if (self->children[x][y])
172                         has_children = TRUE;
173         }
174         //g_debug("GisTile: gc - %p->atime=%u < atime=%u",
175         //              self, (guint)self->atime, (guint)atime);
176         if (!has_children && self->atime < atime && self->data) {
177                 free_func(self, user_data);
178                 g_free(self);
179                 return NULL;
180         }
181         return self;
182 }
183
184 void gis_tile_free(GisTile *self, GisTileFreeFunc free_func, gpointer user_data)
185 {
186         if (!self)
187                 return;
188         GisTile *child;
189         gis_tile_foreach(self, child)
190                 gis_tile_free(child, free_func, user_data);
191         if (free_func)
192                 free_func(self, user_data);
193         g_object_unref(self);
194 }
195
196 /* GObject code */
197 G_DEFINE_TYPE(GisTile, gis_tile, GIS_TYPE_OBJECT);
198 static void gis_tile_init(GisTile *self) { }
199 static void gis_tile_class_init(GisTileClass *klass) { }