PostGIS 3.0.6dev-r@@SVN_REVISION@@
Loading...
Searching...
No Matches

◆ lwdouble_to_dms()

static char * lwdouble_to_dms ( double  val,
const char *  pos_dir_symbol,
const char *  neg_dir_symbol,
const char *  format 
)
static

Definition at line 76 of file lwprint.c.

77{
78 /* 3 numbers, 1 sign or compass dir, and 5 possible strings (degree signs, spaces, misc text, etc) between or around them.*/
79# define NUM_PIECES 9
80# define WORK_SIZE 1024
81 char pieces[NUM_PIECES][WORK_SIZE];
82 int current_piece = 0;
83 int is_negative = 0;
84
85 double degrees = 0.0;
86 double minutes = 0.0;
87 double seconds = 0.0;
88
89 int compass_dir_piece = -1;
90
91 int reading_deg = 0;
92 int deg_digits = 0;
93 int deg_has_decpoint = 0;
94 int deg_dec_digits = 0;
95 int deg_piece = -1;
96
97 int reading_min = 0;
98 int min_digits = 0;
99 int min_has_decpoint = 0;
100 int min_dec_digits = 0;
101 int min_piece = -1;
102
103 int reading_sec = 0;
104 int sec_digits = 0;
105 int sec_has_decpoint = 0;
106 int sec_dec_digits = 0;
107 int sec_piece = -1;
108
109 int round_pow = 0;
110
111 int format_length = ((NULL == format) ? 0 : strlen(format));
112
113 char * result;
114
115 int index, following_byte_index;
116 int multibyte_char_width = 1;
117
118 /* Initialize the working strs to blank. We may not populate all of them, and
119 * this allows us to concat them all at the end without worrying about how many
120 * we actually needed. */
121 for (index = 0; index < NUM_PIECES; index++)
122 {
123 pieces[index][0] = '\0';
124 }
125
126 /* If no format is provided, use a default. */
127 if (0 == format_length)
128 {
129 /* C2B0 is UTF-8 for the degree symbol. */
130 format = "D\xC2\xB0""M'S.SSS\"C";
131 format_length = strlen(format);
132 }
133 else if (format_length > WORK_SIZE)
134 {
135 /* Sanity check, we don't want to overwrite an entire piece of work and no one should need a 1K-sized
136 * format string anyway. */
137 lwerror("Bad format, exceeds maximum length (%d).", WORK_SIZE);
138 }
139
140 for (index = 0; index < format_length; index++)
141 {
142 char next_char = format[index];
143 switch (next_char)
144 {
145 case 'D':
146 if (reading_deg)
147 {
148 /* If we're reading degrees, add another digit. */
149 deg_has_decpoint ? deg_dec_digits++ : deg_digits++;
150 }
151 else
152 {
153 /* If we're not reading degrees, we are now. */
154 current_piece++;
155 deg_piece = current_piece;
156 if (deg_digits > 0)
157 {
158 lwerror("Bad format, cannot include degrees (DD.DDD) more than once.");
159 }
160 reading_deg = 1;
161 reading_min = 0;
162 reading_sec = 0;
163 deg_digits++;
164 }
165 break;
166 case 'M':
167 if (reading_min)
168 {
169 /* If we're reading minutes, add another digit. */
170 min_has_decpoint ? min_dec_digits++ : min_digits++;
171 }
172 else
173 {
174 /* If we're not reading minutes, we are now. */
175 current_piece++;
176 min_piece = current_piece;
177 if (min_digits > 0)
178 {
179 lwerror("Bad format, cannot include minutes (MM.MMM) more than once.");
180 }
181 reading_deg = 0;
182 reading_min = 1;
183 reading_sec = 0;
184 min_digits++;
185 }
186 break;
187 case 'S':
188 if (reading_sec)
189 {
190 /* If we're reading seconds, add another digit. */
191 sec_has_decpoint ? sec_dec_digits++ : sec_digits++;
192 }
193 else
194 {
195 /* If we're not reading seconds, we are now. */
196 current_piece++;
197 sec_piece = current_piece;
198 if (sec_digits > 0)
199 {
200 lwerror("Bad format, cannot include seconds (SS.SSS) more than once.");
201 }
202 reading_deg = 0;
203 reading_min = 0;
204 reading_sec = 1;
205 sec_digits++;
206 }
207 break;
208 case 'C':
209 /* We're done reading anything else we might have been reading. */
210 if (reading_deg || reading_min || reading_sec)
211 {
212 /* We were reading something, that means this is the next piece. */
213 reading_deg = 0;
214 reading_min = 0;
215 reading_sec = 0;
216 }
217 current_piece++;
218
219 if (compass_dir_piece >= 0)
220 {
221 lwerror("Bad format, cannot include compass dir (C) more than once.");
222 }
223 /* The compass dir is a piece all by itself. */
224 compass_dir_piece = current_piece;
225 current_piece++;
226 break;
227 case '.':
228 /* If we're reading deg, min, or sec, we want a decimal point for it. */
229 if (reading_deg)
230 {
231 deg_has_decpoint = 1;
232 }
233 else if (reading_min)
234 {
235 min_has_decpoint = 1;
236 }
237 else if (reading_sec)
238 {
239 sec_has_decpoint = 1;
240 }
241 else
242 {
243 /* Not reading anything, just pass through the '.' */
244 strncat(pieces[current_piece], &next_char, 1);
245 }
246 break;
247 default:
248 /* Any other char is just passed through unchanged. But it does mean we are done reading D, M, or S.*/
249 if (reading_deg || reading_min || reading_sec)
250 {
251 /* We were reading something, that means this is the next piece. */
252 current_piece++;
253 reading_deg = 0;
254 reading_min = 0;
255 reading_sec = 0;
256 }
257
258 /* Check if this is a multi-byte UTF-8 character. If so go ahead and read the rest of the bytes as well. */
259 multibyte_char_width = 1;
260 if (next_char & 0x80)
261 {
262 if ((next_char & 0xF8) == 0xF0)
263 {
264 multibyte_char_width += 3;
265 }
266 else if ((next_char & 0xF0) == 0xE0)
267 {
268 multibyte_char_width += 2;
269 }
270 else if ((next_char & 0xE0) == 0xC0)
271 {
272 multibyte_char_width += 1;
273 }
274 else
275 {
276 lwerror("Bad format, invalid high-order byte found first, format string may not be UTF-8.");
277 }
278 }
279 if (multibyte_char_width > 1)
280 {
281 if (index + multibyte_char_width >= format_length)
282 {
283 lwerror("Bad format, UTF-8 character first byte found with insufficient following bytes, format string may not be UTF-8.");
284 }
285 for (following_byte_index = (index + 1); following_byte_index < (index + multibyte_char_width); following_byte_index++)
286 {
287 if ((format[following_byte_index] & 0xC0) != 0x80)
288 {
289 lwerror("Bad format, invalid byte found following leading byte of multibyte character, format string may not be UTF-8.");
290 }
291 }
292 }
293 /* Copy all the character's bytes into the current piece. */
294 strncat(pieces[current_piece], &(format[index]), multibyte_char_width);
295 /* Now increment index past the rest of those bytes. */
296 index += multibyte_char_width - 1;
297 break;
298 }
299 if (current_piece >= NUM_PIECES)
300 {
301 lwerror("Internal error, somehow needed more pieces than it should.");
302 }
303 }
304 if (deg_piece < 0)
305 {
306 lwerror("Bad format, degrees (DD.DDD) must be included.");
307 }
308
309 /* Divvy the number up into D, DM, or DMS */
310 if (val < 0)
311 {
312 val *= -1;
313 is_negative = 1;
314 }
315 degrees = val;
316 if (min_digits > 0)
317 {
318 /* Break degrees to integer and use fraction for minutes */
319 minutes = modf(val, &degrees) * 60;
320 }
321 if (sec_digits > 0)
322 {
323 if (0 == min_digits)
324 {
325 lwerror("Bad format, cannot include seconds (SS.SSS) without including minutes (MM.MMM).");
326 }
327 seconds = modf(minutes, &minutes) * 60;
328 if (sec_piece >= 0)
329 {
330 /* See if the formatted seconds round up to 60. If so, increment minutes and reset seconds. */
331 round_pow = pow(10, sec_dec_digits);
332 if (floorf(seconds * round_pow) / round_pow >= 60)
333 {
334 minutes += 1;
335 seconds = 0;
336 }
337 }
338 }
339
340 /* Handle the compass direction. If not using compass dir, display degrees as a positive/negative number. */
341 if (compass_dir_piece >= 0)
342 {
343 strcpy(pieces[compass_dir_piece], is_negative ? neg_dir_symbol : pos_dir_symbol);
344 }
345 else if (is_negative)
346 {
347 degrees *= -1;
348 }
349
350 /* Format the degrees into their string piece. */
351 if (deg_digits + deg_dec_digits + 2 > WORK_SIZE)
352 {
353 lwerror("Bad format, degrees (DD.DDD) number of digits was greater than our working limit.");
354 }
355 if(deg_piece >= 0)
356 {
357 snprintf(pieces[deg_piece], WORK_SIZE, "%*.*f", deg_digits, deg_dec_digits, degrees);
358 }
359
360 if (min_piece >= 0)
361 {
362 /* Format the minutes into their string piece. */
363 if (min_digits + min_dec_digits + 2 > WORK_SIZE)
364 {
365 lwerror("Bad format, minutes (MM.MMM) number of digits was greater than our working limit.");
366 }
367 snprintf(pieces[min_piece], WORK_SIZE, "%*.*f", min_digits, min_dec_digits, minutes);
368 }
369 if (sec_piece >= 0)
370 {
371 /* Format the seconds into their string piece. */
372 if (sec_digits + sec_dec_digits + 2 > WORK_SIZE)
373 {
374 lwerror("Bad format, seconds (SS.SSS) number of digits was greater than our working limit.");
375 }
376 snprintf(pieces[sec_piece], WORK_SIZE, "%*.*f", sec_digits, sec_dec_digits, seconds);
377 }
378
379 /* Allocate space for the result. Leave plenty of room for excess digits, negative sign, etc.*/
380 result = (char*)lwalloc(format_length + WORK_SIZE);
381 memset(result, 0, format_length + WORK_SIZE);
382
383 /* Append all the pieces together. There may be less than 9, but in that case the rest will be blank. */
384 strcpy(result, pieces[0]);
385 for (index = 1; index < NUM_PIECES; index++)
386 {
387 strcat(result, pieces[index]);
388 }
389
390 return result;
391}
void * lwalloc(size_t size)
Definition lwutil.c:227
void lwerror(const char *fmt,...)
Write a notice out to the error handler.
Definition lwutil.c:190
#define WORK_SIZE
#define NUM_PIECES

References lwalloc(), lwerror(), NUM_PIECES, and WORK_SIZE.

Referenced by lwdoubles_to_latlon().

Here is the call graph for this function:
Here is the caller graph for this function: