1 #include <clocale>
2 #include <cstdio>
3 #include <cstdlib>
4 #include <cstring>
5 #include <ctime>
6 #include <fstream>
7 #include <map>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11 using namespace std;
12
13 #include "buildPlanetMap.h"
14 #include "findFile.h"
15 #include "keywords.h"
16 #include "Options.h"
17 #include "parse.h"
18 #include "PlanetProperties.h"
19 #include "sphericalToPixel.h"
20 #include "View.h"
21 #include "xpUtil.h"
22
23 #include "libannotate/libannotate.h"
24 #include "libplanet/Planet.h"
25 #include "libprojection/ProjectionBase.h"
26
27 static void
readMarkerFile(const char * line,Planet * planet,const double pR,View * view,ProjectionBase * projection,const int width,const int height,unsigned char * color,string & font,int fontSize,const double magnify,map<double,Planet * > & planetsFromSunMap,multimap<double,Annotation * > & annotationMap)28 readMarkerFile(const char *line, Planet *planet, const double pR,
29 View *view, ProjectionBase *projection,
30 const int width, const int height, unsigned char *color,
31 string &font, int fontSize, const double magnify,
32 map<double, Planet *> &planetsFromSunMap,
33 multimap<double, Annotation *> &annotationMap)
34 {
35 int i = 0;
36 while (isDelimiter(line[i]))
37 {
38 i++;
39 if (static_cast<unsigned int> (i) > strlen(line)) return;
40 }
41 if (isEndOfLine(line[i])) return;
42
43 Options *options = Options::getInstance();
44
45 int align = AUTO;
46 bool haveLat = false;
47 bool haveLon = false;
48 double lat, lon;
49 string image;
50
51 string lang("");
52 string name("");
53 double opacity = 1.0;
54 bool outlined = true;
55 bool pixelCoords = false;
56 double radius = -1;
57 bool relativeToEdges = true;
58 int symbolSize = 2;
59 bool syntaxError = false;
60 string timezone;
61 bool transparency = false;
62 unsigned char transparent_pixel[3];
63
64 while (static_cast<unsigned int> (i) < strlen(line))
65 {
66 char *returnString = NULL;
67 int val = parse(i, line, returnString);
68
69 switch (val)
70 {
71 case ALIGN:
72 if (returnString == NULL) break;
73 switch (returnString[0])
74 {
75 case 'r':
76 case 'R':
77 align = RIGHT;
78 break;
79 case 'l':
80 case 'L':
81 align = LEFT;
82 break;
83 case 'a':
84 case 'A':
85 align = ABOVE;
86 break;
87 case 'b':
88 case 'B':
89 align = BELOW;
90 break;
91 case 'c':
92 case 'C':
93 align = CENTER;
94 break;
95 default:
96 xpWarn("Unrecognized option for align in marker file\n",
97 __FILE__, __LINE__);
98 syntaxError = true;
99 break;
100 }
101 break;
102 case COLOR:
103 {
104 int r, g, b;
105 if (sscanf(returnString, "%d,%d,%d", &r, &g, &b) == 3)
106 {
107 color[0] = static_cast<unsigned char> (r & 0xff);
108 color[1] = static_cast<unsigned char> (g & 0xff);
109 color[2] = static_cast<unsigned char> (b & 0xff);
110 }
111 else
112 {
113 xpWarn("Need three values for color\n", __FILE__, __LINE__);
114 syntaxError = true;
115 }
116 }
117 break;
118 case FONT:
119 font.assign(returnString);
120 break;
121 case FONTSIZE:
122 sscanf(returnString, "%d", &fontSize);
123 if (fontSize <= 0)
124 {
125 xpWarn("fontSize must be positive.\n",
126 __FILE__, __LINE__);
127 syntaxError = true;
128 }
129 break;
130 case IMAGE:
131 image.assign(returnString);
132 break;
133 case LANGUAGE:
134 lang.assign(returnString);
135 break;
136 case LATLON:
137 checkLocale(LC_NUMERIC, "C");
138 if (haveLon)
139 {
140 syntaxError = true;
141 }
142 else if (haveLat)
143 {
144 sscanf(returnString, "%lf", &lon);
145 haveLon = true;
146 }
147 else
148 {
149 sscanf(returnString, "%lf", &lat);
150 haveLat = true;
151 }
152 checkLocale(LC_NUMERIC, "");
153 break;
154 case MAX_RAD_FOR_MARKERS:
155 {
156 double maxRad;
157 sscanf(returnString, "%lf", &maxRad);
158 maxRad *= height;
159 if (pR > 0 && pR > maxRad) return;
160 }
161 break;
162 case MIN_RAD_FOR_MARKERS:
163 {
164 double minRad;
165 sscanf(returnString, "%lf", &minRad);
166 minRad *= height;
167 if (pR > 0 && pR < minRad) return;
168 }
169 break;
170 case NAME:
171 name.assign(returnString);
172 break;
173 case OPACITY:
174 {
175 int s;
176 sscanf(returnString, "%d", &s);
177 if (s < 0)
178 s = 0;
179 else if (s > 100)
180 s = 100;
181 opacity = s/100.;
182 }
183 break;
184 case OUTLINED:
185 if (strncmp(returnString, "f", 1) == 0
186 || strncmp(returnString, "F", 1) == 0)
187 outlined = false;
188 break;
189 case POSITION:
190 if (strncmp(returnString, "pixel", 2) == 0)
191 {
192 pixelCoords = true;
193 }
194 else if (strncmp(returnString, "absolute", 3) == 0)
195 {
196 pixelCoords = true;
197 relativeToEdges = false;
198 }
199 else
200 {
201 if (planet != NULL)
202 {
203 body pIndex = Planet::parseBodyName(returnString);
204
205 if (pIndex != planet->Index())
206 {
207 const Planet *other = findPlanetinMap(planetsFromSunMap,
208 pIndex);
209 double X, Y, Z;
210 other->getPosition(X, Y, Z);
211 planet->XYZToPlanetographic(X, Y, Z, lat, lon);
212
213 lat /= deg_to_rad;
214 lon /= deg_to_rad;
215 }
216 }
217 }
218 break;
219 case RADIUS:
220 checkLocale(LC_NUMERIC, "C");
221 sscanf(returnString, "%lf", &radius);
222 if (radius < 0)
223 {
224 xpWarn("Radius value must be positive\n",
225 __FILE__, __LINE__);
226 radius = -1;
227 syntaxError = true;
228 }
229 checkLocale(LC_NUMERIC, "");
230 break;
231 case SYMBOLSIZE:
232 sscanf(returnString, "%d", &symbolSize);
233 if (symbolSize < 0) symbolSize = 2;
234 break;
235 case TIMEZONE:
236 timezone.assign(returnString);
237 break;
238 case TRANSPARENT:
239 {
240 int r, g, b;
241 if (sscanf(returnString, "%d,%d,%d", &r, &g, &b) == 3)
242 {
243 transparent_pixel[0] = static_cast<unsigned char> (r & 0xff);
244 transparent_pixel[1] = static_cast<unsigned char> (g & 0xff);
245 transparent_pixel[2] = static_cast<unsigned char> (b & 0xff);
246 }
247 else
248 {
249 xpWarn("Need three values for transparency pixel!\n",
250 __FILE__, __LINE__);
251 syntaxError = true;
252 }
253 transparency = true;
254 }
255 break;
256 case UNKNOWN:
257 syntaxError = true;
258 default:
259 case DELIMITER:
260 break;
261 }
262
263 if (val != DELIMITER && options->Verbosity() > 3)
264 {
265 ostringstream msg;
266 msg << "value is " << keyWordString[val - '?'];
267 if (returnString != NULL)
268 msg << ", returnString is " << returnString;
269 msg << endl;
270 xpMsg(msg.str(), __FILE__, __LINE__);
271 }
272
273 delete [] returnString;
274
275 if (syntaxError)
276 {
277 ostringstream errStr;
278 errStr << "Syntax error in marker file\n"
279 << "line is \"" << line << "\"\n";
280 xpWarn(errStr.str(), __FILE__, __LINE__);
281 return;
282 }
283
284 if (val == ENDOFLINE) break;
285 }
286
287 double X, Y, Z = 0;
288 bool markerVisible = false;
289
290 if (pixelCoords)
291 {
292 X = lon;
293 Y = lat;
294
295 if (relativeToEdges)
296 {
297 if (X < 0) X += width;
298 if (Y < 0) Y += height;
299 }
300 }
301 else
302 {
303 lat *= deg_to_rad;
304 lon *= deg_to_rad;
305
306 if (radius < 0)
307 {
308 if (planet != NULL)
309 {
310 radius = planet->Radius(lat);
311 }
312 else
313 {
314 radius = 1;
315 }
316 }
317
318 markerVisible = sphericalToPixel(lat, lon, radius * magnify,
319 X, Y, Z, planet, view, projection);
320
321 // don't draw markers on the far side of the planet
322 if (planet != NULL && view != NULL)
323 {
324 double mX, mY, mZ;
325 planet->PlanetographicToXYZ(mX, mY, mZ, lat, lon, radius * magnify);
326 double oX, oY, oZ;
327 options->getOrigin(oX, oY, oZ);
328 double tX, tY, tZ;
329 planet->getPosition(tX, tY, tZ);
330 double cosAngle = ndot(tX-mX, tY-mY, tZ-mZ, oX-mX, oY-mY, oZ-mZ);
331 if (cosAngle > 0) markerVisible = false;
332 }
333 }
334
335 if (pixelCoords || markerVisible)
336 {
337 const int ix = static_cast<int> (floor(X + 0.5));
338 const int iy = static_cast<int> (floor(Y + 0.5));
339
340 int iconWidth = 0;
341 int iconHeight = 0;
342 if (image.empty())
343 {
344 Symbol *s = new Symbol(color, ix, iy, symbolSize);
345 s->Outline(outlined);
346 annotationMap.insert(pair<const double, Annotation*>(Z, s));
347 iconWidth = symbolSize * 2;
348 iconHeight = symbolSize * 2;
349 }
350 else if (image.compare("none") != 0)
351 {
352 unsigned char *transparent = (transparency ? transparent_pixel : NULL);
353 Icon *i = new Icon(ix, iy, image, transparent);
354 annotationMap.insert(pair<const double, Annotation*>(Z, i));
355 iconWidth = i->Width();
356 iconHeight = i->Height();
357 }
358
359 // if the name string has time formatting codes, and the
360 // timezone is defined, run the name string through strftime()
361 if (name.find("%") != string::npos && !timezone.empty())
362 {
363 const char *tzEnv = getenv("TZ");
364 string tzSave;
365 if (tzEnv != NULL)
366 {
367 tzSave = "TZ=";
368 tzSave += tzEnv;
369 }
370
371 string tz = "TZ=";
372 tz += timezone;
373 putenv((char *) tz.c_str());
374
375 tzset();
376
377 if (!lang.empty())
378 checkLocale(LC_ALL, lang.c_str());
379
380 // run name string through strftime() and convert to UTF-8
381 strftimeUTF8(name);
382
383 if (tzEnv == NULL)
384 removeFromEnvironment("TZ");
385 else
386 putenv((char *) tzSave.c_str());
387
388 tzset();
389
390 if (!lang.empty())
391 checkLocale(LC_ALL, "");
392 }
393
394 if (!name.empty())
395 {
396 Text *t = new Text(color, ix, iy,
397 iconWidth, iconHeight,
398 align, name);
399
400 t->Opacity(opacity);
401 t->Outline(outlined);
402
403 if (!font.empty()) t->Font(font);
404 if (fontSize > 0) t->FontSize(fontSize);
405
406 annotationMap.insert(pair<const double, Annotation*>(Z, t));
407 }
408 }
409 }
410
411 // Used for labeling planets/moons
412 void
addMarkers(PlanetProperties * planetProperties,Planet * planet,const double pixel_radius,const double X,const double Y,const double Z,View * view,ProjectionBase * projection,const int width,const int height,map<double,Planet * > & planetsFromSunMap,multimap<double,Annotation * > & annotationMap)413 addMarkers(PlanetProperties *planetProperties, Planet *planet,
414 const double pixel_radius,
415 const double X, const double Y, const double Z,
416 View *view, ProjectionBase *projection,
417 const int width, const int height,
418 map<double, Planet *> &planetsFromSunMap,
419 multimap<double, Annotation *> &annotationMap)
420 {
421 vector<string> markerfiles = planetProperties->MarkerFiles();
422 vector<string>::iterator ii = markerfiles.begin();
423
424 while (ii != markerfiles.end())
425 {
426 string markerFile(*ii);
427 bool foundFile = findFile(markerFile, "markers");
428 if (foundFile)
429 {
430 ifstream inFile(markerFile.c_str());
431 char *line = new char[MAX_LINE_LENGTH];
432 while (inFile.getline (line, MAX_LINE_LENGTH, '\n'))
433 {
434 unsigned char color[3];
435 memcpy(color, planetProperties->MarkerColor(), 3);
436 string font(planetProperties->MarkerFont());
437 int fontSize(planetProperties->MarkerFontSize());
438
439 readMarkerFile(line, planet, pixel_radius,
440 view, projection, width, height,
441 color, font, fontSize,
442 planetProperties->Magnify(),
443 planetsFromSunMap, annotationMap);
444 }
445
446 inFile.close();
447 delete [] line;
448 }
449 else
450 {
451 ostringstream errStr;
452 errStr << "Can't load marker file " << markerFile << endl;
453 xpWarn(errStr.str(), __FILE__, __LINE__);
454 }
455 ii++;
456 }
457 }
458
459 // Used for labeling star fields
460 void
addMarkers(View * view,const int width,const int height,map<double,Planet * > & planetsFromSunMap,multimap<double,Annotation * > & annotationMap)461 addMarkers(View *view, const int width, const int height,
462 map<double, Planet *> &planetsFromSunMap,
463 multimap<double, Annotation *> &annotationMap)
464 {
465 Options *options = Options::getInstance();
466
467 vector<string> markerfiles = options->MarkerFiles();
468 vector<string>::iterator ii = markerfiles.begin();
469
470 while (ii != markerfiles.end())
471 {
472 string markerFile(*ii);
473 bool foundFile = findFile(markerFile, "markers");
474 if (foundFile)
475 {
476 ifstream inFile(markerFile.c_str());
477 char *line = new char[MAX_LINE_LENGTH];
478 while (inFile.getline (line, MAX_LINE_LENGTH, '\n'))
479 {
480 unsigned char color[3];
481 memcpy(color, options->Color(), 3);
482 string font(options->Font());
483 int fontSize(options->FontSize());
484
485 readMarkerFile(line, NULL, 0,
486 view, NULL, width, height,
487 color, font, fontSize, 1.0,
488 planetsFromSunMap, annotationMap);
489 }
490 inFile.close();
491 delete [] line;
492 }
493 else
494 {
495 ostringstream errStr;
496 errStr << "Can't load marker file " << markerFile << endl;
497 xpWarn(errStr.str(), __FILE__, __LINE__);
498 }
499 ii++;
500 }
501 }
502