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