]> git.street.me.uk Git - andy/viking.git/blob - src/maputils.c
Coverity: Prevent deference after null checks
[andy/viking.git] / src / maputils.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4  *
5  * Copyright (C) 2013-2014, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22 #include "maputils.h"
23 #include "globals.h"
24 #include <math.h>
25
26 // World Scale: VIK_GZ(17)
27 //  down to
28 // Submeter scale: 1/VIK_GZ(5)
29 // No map provider is going to have tiles at the highest zoom in level - but we can interpolate to that.
30
31 static const gdouble scale_mpps[] = { VIK_GZ(0), VIK_GZ(1), VIK_GZ(2), VIK_GZ(3), VIK_GZ(4), VIK_GZ(5),
32                                       VIK_GZ(6), VIK_GZ(7), VIK_GZ(8), VIK_GZ(9), VIK_GZ(10), VIK_GZ(11),
33                                       VIK_GZ(12), VIK_GZ(13), VIK_GZ(14), VIK_GZ(15), VIK_GZ(16), VIK_GZ(17) };
34 static const gint num_scales = (sizeof(scale_mpps) / sizeof(scale_mpps[0]));
35
36 static const gdouble scale_neg_mpps[] = { 1.0/VIK_GZ(0), 1.0/VIK_GZ(1), 1.0/VIK_GZ(2),
37                                           1.0/VIK_GZ(3), 1.0/VIK_GZ(4), 1.0/VIK_GZ(5) };
38 static const gint num_scales_neg = (sizeof(scale_neg_mpps) / sizeof(scale_neg_mpps[0]));
39
40 #define ERROR_MARGIN 0.01
41 /**
42  * map_utils_mpp_to_scale:
43  * @mpp: The so called 'mpp'
44  *
45  * Returns: the zoom scale value which may be negative.
46  */
47 gint map_utils_mpp_to_scale ( gdouble mpp ) {
48         gint i;
49         for ( i = 0; i < num_scales; i++ ) {
50                 if ( ABS(scale_mpps[i] - mpp) < ERROR_MARGIN ) {
51                         return i;
52                 }
53         }
54         for ( i = 0; i < num_scales_neg; i++ ) {
55                 if ( ABS(scale_neg_mpps[i] - mpp) < 0.000001 ) {
56                         return -i;
57                 }
58         }
59
60         return 255;
61 }
62
63 /**
64  * map_utils_mpp_to_zoom_level:
65  * @mpp: The so called 'mpp'
66  *
67  * Returns: a Zoom Level
68  *  See: http://wiki.openstreetmap.org/wiki/Zoom_levels
69  */
70 guint8 map_utils_mpp_to_zoom_level ( gdouble mpp )
71 {
72         gint answer = 17 - map_utils_mpp_to_scale ( mpp );
73         if ( answer < 0 )
74                 answer = 17;
75         return answer;
76 }
77
78 /**
79  * SECTION:maputils
80  * @short_description: Notes about TMS / Spherical Mercator conversion
81  *
82  * VikCoords are in Spherical Mercator projection (#VIK_COORD_LATLON)
83  * MapCoords are in Inverse TMS
84  *
85  * See: http://docs.openlayers.org/library/spherical_mercator.html
86  * See: http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification
87  * NB: the Y axis is inverted, ie the origin is at top-left corner.
88  */
89
90 /**
91  * map_utils_vikcoord_to_iTMS:
92  * @src:   Original #VikCoord in #VIK_COORD_LATLON format
93  * @xzoom: Viking zoom level in x direction
94  * @yzoom: Viking zoom level in y direction (actually needs to be same as xzoom)
95  * @dest:  The resulting Inverse TMS coordinates in #MapCoord
96  *
97  * Convert a #VikCoord in VIK_COORD_LATLON format into Inverse TMS coordinates
98  *
99  * Returns: whether the conversion was performed
100  */
101 gboolean map_utils_vikcoord_to_iTMS ( const VikCoord *src, gdouble xzoom, gdouble yzoom, MapCoord *dest )
102 {
103   if ( src->mode != VIK_COORD_LATLON )
104     return FALSE;
105
106   if ( xzoom != yzoom )
107     return FALSE;
108
109   dest->scale = map_utils_mpp_to_scale ( xzoom );
110   if ( dest->scale == 255 )
111     return FALSE;
112
113   dest->x = (src->east_west + 180) / 360 * VIK_GZ(17) / xzoom;
114   dest->y = (180 - MERCLAT(src->north_south)) / 360 * VIK_GZ(17) / xzoom;
115   dest->z = 0;
116
117   return TRUE;
118 }
119
120 // Internal convenience function
121 static void _to_vikcoord_with_offset ( const MapCoord *src, VikCoord *dest, gdouble offset )
122 {
123   gdouble socalled_mpp;
124   if (src->scale >= 0)
125     socalled_mpp = VIK_GZ(src->scale);
126   else
127     socalled_mpp = 1.0/VIK_GZ(-src->scale);
128   dest->mode = VIK_COORD_LATLON;
129   dest->east_west = ((src->x+offset) / VIK_GZ(17) * socalled_mpp * 360) - 180;
130   dest->north_south = DEMERCLAT(180 - ((src->y+offset) / VIK_GZ(17) * socalled_mpp * 360));
131 }
132
133 /**
134  * map_utils_iTMS_to_center_vikcoord:
135  * @src:   Original #MapCoord in Inverse TMS format
136  * @dest:  The resulting Spherical Mercator coordinates in #VikCoord
137  *
138  * Convert a #MapCoord in Inverse TMS format into Spherical Mercator coordinates for the center of the TMS area
139  *
140  * Returns: whether the conversion was performed
141  */
142 void map_utils_iTMS_to_center_vikcoord ( const MapCoord *src, VikCoord *dest )
143 {
144         _to_vikcoord_with_offset ( src, dest, 0.5 );
145 }
146
147 /**
148  * map_utils_iTMS_to_vikcoord:
149  * @src:   Original #MapCoord in Inverse TMS format
150  * @dest:  The resulting Spherical Mercator coordinates in #VikCoord
151  *
152  * Convert a #MapCoord in Inverse TMS format into Spherical Mercator coordinates
153  *  (for the top left corner of the Inverse TMS area)
154  *
155  * Returns: whether the conversion was performed
156  */
157 void map_utils_iTMS_to_vikcoord ( const MapCoord *src, VikCoord *dest )
158 {
159         _to_vikcoord_with_offset ( src, dest, 0.0 );
160 }