]> Pileus Git - grits/blob - src/objects/gis-tile.c
Replace ugly macros with ugly boilerplate
[grits] / src / objects / 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 "gis-util.h"
20 #include "gis-tile.h"
21
22 gchar *gis_tile_path_table[2][2] = {
23         {"00.", "01."},
24         {"10.", "11."},
25 };
26
27 GisTile *gis_tile_new(GisTile *parent,
28         gdouble n, gdouble s, gdouble e, gdouble w)
29 {
30         GisTile *tile = g_object_new(GIS_TYPE_TILE, NULL);
31         tile->parent = parent;
32         tile->edge.n = n;
33         tile->edge.s = s;
34         tile->edge.e = e;
35         tile->edge.w = w;
36         tile->atime  = time(NULL);
37         return tile;
38 }
39
40 gchar *gis_tile_get_path(GisTile *child)
41 {
42         /* This could be easily cached if necessasairy */
43         int x, y;
44         GList *parts = NULL;
45         for (GisTile *parent = child->parent; parent; child = parent, parent = child->parent)
46                 gis_tile_foreach_index(child, x, y)
47                         if (parent->children[x][y] == child)
48                                 parts = g_list_prepend(parts, gis_tile_path_table[x][y]);
49         GString *path = g_string_new("");
50         for (GList *cur = parts; cur; cur = cur->next)
51                 g_string_append(path, cur->data);
52         g_list_free(parts);
53         return g_string_free(path, FALSE);
54 }
55
56 gdouble _gis_tile_get_min_dist(GisTile *tile,
57                 gdouble lat, gdouble lon, gdouble elev)
58 {
59         gdouble tlat  = lat > tile->edge.n ? tile->edge.n :
60                         lat < tile->edge.s ? tile->edge.s : lat;
61         gdouble tlon  = lon > tile->edge.e ? tile->edge.e :
62                         lon < tile->edge.w ? tile->edge.w : lon;
63         gdouble telev = 0; // TODO: elevation at rlat,rlon
64         //if (lat == tlat && lon == tlon)
65         //      return elev; /* Shortcut? */
66         gdouble a[3], b[3];
67         lle2xyz( lat,  lon,  elev, a+0, a+1, a+2);
68         lle2xyz(tlat, tlon, telev, b+0, b+1, b+2);
69         return distd(a, b);
70 }
71
72 gboolean _gis_tile_needs_split(GisTile *tile,
73                 gdouble max_res, gint width, gint height,
74                 gdouble lat, gdouble lon, gdouble elev)
75 {
76         gdouble lat_point = tile->edge.n < 0 ? tile->edge.n :
77                             tile->edge.s > 0 ? tile->edge.s : 0;
78         gdouble min_dist  = _gis_tile_get_min_dist(tile, lat, lon, elev);
79         gdouble view_res  = MPPX(min_dist);
80         gdouble lon_dist  = tile->edge.e - tile->edge.w;
81         gdouble tile_res  = ll2m(lon_dist, lat_point)/width;
82
83         /* This isn't really right, but it helps with memory since we don't (yet?) test if the tile
84          * would be drawn */
85         gdouble scale = elev / min_dist;
86         view_res /= scale;
87         //g_message("tile=(%7.2f %7.2f %7.2f %7.2f) "
88         //          "eye=(%9.1f %9.1f %9.1f) "
89         //          "elev=%9.1f / dist=%9.1f = %f",
90         //              tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w,
91         //              lat, lon, elev,
92         //              elev, min_dist, scale);
93
94         if (tile_res < max_res)
95                 return FALSE;
96         return view_res < tile_res;
97 }
98
99 void gis_tile_update(GisTile *tile,
100                 gdouble res, gint width, gint height,
101                 gdouble lat, gdouble lon, gdouble elev,
102                 GisTileLoadFunc load_func, gpointer user_data)
103 {
104         tile->atime = time(NULL);
105         //g_debug("GisTile: update - %p->atime = %u", tile, (guint)tile->atime);
106         gdouble lat_dist = tile->edge.n - tile->edge.s;
107         gdouble lon_dist = tile->edge.e - tile->edge.w;
108         if (_gis_tile_needs_split(tile, res, width, height, lat, lon, elev)) {
109                 gdouble lat_step = lat_dist / G_N_ELEMENTS(tile->children);
110                 gdouble lon_step = lon_dist / G_N_ELEMENTS(tile->children[0]);
111                 int x, y;
112                 gis_tile_foreach_index(tile, x, y) {
113                         if (!tile->children[x][y]) {
114                                 tile->children[x][y] = gis_tile_new(tile,
115                                                 tile->edge.n-(lat_step*(x+0)),
116                                                 tile->edge.n-(lat_step*(x+1)),
117                                                 tile->edge.w+(lon_step*(y+1)),
118                                                 tile->edge.w+(lon_step*(y+0)));
119                                 load_func(tile->children[x][y], user_data);
120                         }
121                         gis_tile_update(tile->children[x][y],
122                                         res, width, height,
123                                         lat, lon, elev,
124                                         load_func, user_data);
125                 }
126         }
127 }
128
129 GisTile *gis_tile_find(GisTile *tile, gdouble lat, gdouble lon)
130 {
131         gint    rows = G_N_ELEMENTS(tile->children);
132         gint    cols = G_N_ELEMENTS(tile->children[0]);
133
134         gdouble lat_step = (tile->edge.n - tile->edge.s) / rows;
135         gdouble lon_step = (tile->edge.e - tile->edge.w) / cols;
136
137         gdouble lat_offset = tile->edge.n - lat;;
138         gdouble lon_offset = lon - tile->edge.w;
139
140         gint    row = lat_offset / lat_step;
141         gint    col = lon_offset / lon_step;
142
143         if (lon == 180) col--;
144         if (lat == -90) row--;
145
146         //if (lon == 180 || lon == -180)
147         //      g_message("lat=%f,lon=%f step=%f,%f off=%f,%f row=%d/%d,col=%d/%d",
148         //              lat,lon, lat_step,lon_step, lat_offset,lon_offset, row,rows,col,cols);
149
150         if (row < 0 || row >= rows || col < 0 || col >= cols)
151                 return NULL;
152         else if (tile->children[row][col] && tile->children[row][col]->data)
153                 return gis_tile_find(tile->children[row][col], lat, lon);
154         else
155                 return tile;
156 }
157
158 GisTile *gis_tile_gc(GisTile *tile, time_t atime,
159                 GisTileFreeFunc free_func, gpointer user_data)
160 {
161         if (!tile)
162                 return NULL;
163         gboolean has_children = FALSE;
164         int x, y;
165         gis_tile_foreach_index(tile, x, y) {
166                 tile->children[x][y] = gis_tile_gc(
167                                 tile->children[x][y], atime,
168                                 free_func, user_data);
169                 if (tile->children[x][y])
170                         has_children = TRUE;
171         }
172         //g_debug("GisTile: gc - %p->atime=%u < atime=%u",
173         //              tile, (guint)tile->atime, (guint)atime);
174         if (!has_children && tile->atime < atime && tile->data) {
175                 free_func(tile, user_data);
176                 g_object_unref(tile);
177                 return NULL;
178         }
179         return tile;
180 }
181
182 /* Use GObject for this */
183 void gis_tile_free(GisTile *tile, GisTileFreeFunc free_func, gpointer user_data)
184 {
185         if (!tile)
186                 return;
187         GisTile *child;
188         gis_tile_foreach(tile, child)
189                 gis_tile_free(child, free_func, user_data);
190         if (free_func)
191                 free_func(tile, user_data);
192         g_object_unref(tile);
193 }
194
195 /* GObject code */
196 G_DEFINE_TYPE(GisTile, gis_tile, GIS_TYPE_OBJECT);
197 static void gis_tile_init(GisTile *tile)
198 {
199 }
200
201 static void gis_tile_class_init(GisTileClass *klass)
202 {
203 }