Kstars

ksutils.cpp
1 /*
2  SPDX-FileCopyrightText: 2002 Mark Hollomon <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "ksutils.h"
8 #include "config-kstars.h"
9 #include "ksnotification.h"
10 #include "kstars_debug.h"
11 
12 #include "catalogobject.h"
13 #ifndef KSTARS_LITE
14 #include "kswizard.h"
15 #endif
16 #include "Options.h"
17 #include "starobject.h"
18 #include "auxiliary/kspaths.h"
19 
20 #ifndef KSTARS_LITE
21 #include <KMessageBox>
22 #include <zlib.h>
23 #endif
24 
25 #ifdef HAVE_LIBRAW
26 #include <libraw/libraw.h>
27 #endif
28 
29 #if defined(__APPLE__)
30 #include <sys/sysctl.h>
31 #elif defined(_WIN32)
32 #include "windows.h"
33 #else //Linux
34 #include <QProcess>
35 #endif
36 
37 #include <QPointer>
38 #include <QProcessEnvironment>
39 #include <QLoggingCategory>
40 
41 #ifdef HAVE_STELLARSOLVER
42 #include <stellarsolver.h>
43 #endif
44 
45 namespace KSUtils
46 {
47 bool isHardwareLimited()
48 {
49 #ifdef __arm__
50  return true;
51 #else
52  return false;
53 #endif
54 }
55 
56 bool openDataFile(QFile &file, const QString &s)
57 {
58  QString FileName = KSPaths::locate(QStandardPaths::AppLocalDataLocation, s);
59  if (!FileName.isNull())
60  {
61  file.setFileName(FileName);
62  return file.open(QIODevice::ReadOnly);
63  }
64  return false;
65 }
66 
67 QString getDSSURL(const SkyPoint *const p)
68 {
69  double height, width;
70  double dss_default_size = Options::defaultDSSImageSize();
71  double dss_padding = Options::dSSPadding();
72 
73  Q_ASSERT(p);
74  Q_ASSERT(dss_default_size > 0.0 && dss_padding >= 0.0);
75 
76  const auto *dso = dynamic_cast<const CatalogObject *>(p);
77 
78  // Decide what to do about the height and width
79  if (dso)
80  {
81  // For deep-sky objects, use their height and width information
82  double a, b, pa;
83  a = dso->a();
84  b = dso->a() *
85  dso->e(); // Use a * e instead of b, since e() returns 1 whenever one of the dimensions is zero. This is important for circular objects
86  pa = dso->pa() * dms::DegToRad;
87 
88  // We now want to convert a, b, and pa into an image
89  // height and width -- i.e. a dRA and dDec.
90  // DSS uses dDec for height and dRA for width. (i.e. "top" is north in the DSS images, AFAICT)
91  // From some trigonometry, assuming we have a rectangular object (worst case), we need:
92  width = a * sin(pa) + b * cos(pa);
93  height = a * cos(pa) + b * sin(pa);
94  // 'a' and 'b' are in arcminutes, so height and width are in arcminutes
95 
96  // Pad the RA and Dec, so that we show more of the sky than just the object.
97  height += dss_padding;
98  width += dss_padding;
99  }
100  else
101  {
102  // For a generic sky object, we don't know what to do. So
103  // we just assume the default size.
104  height = width = dss_default_size;
105  }
106  // There's no point in tiny DSS images that are smaller than dss_default_size
107  if (height < dss_default_size)
108  height = dss_default_size;
109  if (width < dss_default_size)
110  width = dss_default_size;
111 
112  return getDSSURL(p->ra0(), p->dec0(), width, height);
113 }
114 
115 QString getDSSURL(const dms &ra, const dms &dec, float width, float height,
116  const QString &type)
117 {
118  const QString URLprefix("https://archive.stsci.edu/cgi-bin/dss_search?");
119  QString URLsuffix = QString("&e=J2000&f=%1&c=none&fov=NONE").arg(type);
120  const double dss_default_size = Options::defaultDSSImageSize();
121 
122  char decsgn = (dec.Degrees() < 0.0) ? '-' : '+';
123  int dd = abs(dec.degree());
124  int dm = abs(dec.arcmin());
125  int ds = abs(dec.arcsec());
126 
127  // Infinite and NaN sizes are replaced by the default size and tiny DSS images are resized to default size
128  if (!qIsFinite(height) || height <= 0.0)
129  height = dss_default_size;
130  if (!qIsFinite(width) || width <= 0.0)
131  width = dss_default_size;
132 
133  // DSS accepts images that are no larger than 75 arcminutes
134  if (height > 75.0)
135  height = 75.0;
136  if (width > 75.0)
137  width = 75.0;
138 
139  QString RAString, DecString, SizeString;
140  DecString = QString::asprintf("&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds);
141  RAString = QString::asprintf("r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second());
142  SizeString = QString::asprintf("&h=%02.1f&w=%02.1f", height, width);
143 
144  return (URLprefix + RAString + DecString + SizeString + URLsuffix);
145 }
146 
147 QString toDirectionString(dms angle)
148 {
149  // TODO: Instead of doing it this way, it would be nicer to
150  // compute the string to arbitrary precision. Although that will
151  // not be easy to localize. (Consider, for instance, Indian
152  // languages that have special names for the intercardinal points)
153  // -- asimha
154 
155  static const char *directions[] =
156  {
157  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "N"),
158  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNE"),
159  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NE"),
160  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ENE"),
161  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "E"),
162  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "ESE"),
163  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SE"),
164  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSE"),
165  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "S"),
166  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SSW"),
167  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "SW"),
168  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WSW"),
169  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "W"),
170  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "WNW"),
171  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NW"),
172  I18N_NOOP2("Abbreviated cardinal / intercardinal etc. direction", "NNW"),
173  I18N_NOOP2("Unknown cardinal / intercardinal direction", "???")
174  };
175 
176  int index = (int)((angle.reduce().Degrees() + 11.25) /
177  22.5); // A number between 0 and 16 (inclusive) is expected
178 
179  if (index < 0 || index > 16)
180  index = 16; // Something went wrong.
181  else
182  index = (index == 16 ? 0 : index);
183 
184  return i18nc("Abbreviated cardinal / intercardinal etc. direction",
185  directions[index]);
186 }
187 
188 QList<SkyObject *> *castStarObjListToSkyObjList(QList<StarObject *> *starObjList)
189 {
190  QList<SkyObject *> *skyObjList = new QList<SkyObject *>();
191 
192  foreach (StarObject *so, *starObjList)
193  {
194  skyObjList->append(so);
195  }
196  return skyObjList;
197 }
198 
199 QString constGenetiveFromAbbrev(const QString &code)
200 {
201  if (code == "And")
202  return QString("Andromedae");
203  if (code == "Ant")
204  return QString("Antliae");
205  if (code == "Aps")
206  return QString("Apodis");
207  if (code == "Aqr")
208  return QString("Aquarii");
209  if (code == "Aql")
210  return QString("Aquilae");
211  if (code == "Ara")
212  return QString("Arae");
213  if (code == "Ari")
214  return QString("Arietis");
215  if (code == "Aur")
216  return QString("Aurigae");
217  if (code == "Boo")
218  return QString("Bootis");
219  if (code == "Cae")
220  return QString("Caeli");
221  if (code == "Cam")
222  return QString("Camelopardalis");
223  if (code == "Cnc")
224  return QString("Cancri");
225  if (code == "CVn")
226  return QString("Canum Venaticorum");
227  if (code == "CMa")
228  return QString("Canis Majoris");
229  if (code == "CMi")
230  return QString("Canis Minoris");
231  if (code == "Cap")
232  return QString("Capricorni");
233  if (code == "Car")
234  return QString("Carinae");
235  if (code == "Cas")
236  return QString("Cassiopeiae");
237  if (code == "Cen")
238  return QString("Centauri");
239  if (code == "Cep")
240  return QString("Cephei");
241  if (code == "Cet")
242  return QString("Ceti");
243  if (code == "Cha")
244  return QString("Chamaeleontis");
245  if (code == "Cir")
246  return QString("Circini");
247  if (code == "Col")
248  return QString("Columbae");
249  if (code == "Com")
250  return QString("Comae Berenices");
251  if (code == "CrA")
252  return QString("Coronae Austrinae");
253  if (code == "CrB")
254  return QString("Coronae Borealis");
255  if (code == "Crv")
256  return QString("Corvi");
257  if (code == "Crt")
258  return QString("Crateris");
259  if (code == "Cru")
260  return QString("Crucis");
261  if (code == "Cyg")
262  return QString("Cygni");
263  if (code == "Del")
264  return QString("Delphini");
265  if (code == "Dor")
266  return QString("Doradus");
267  if (code == "Dra")
268  return QString("Draconis");
269  if (code == "Equ")
270  return QString("Equulei");
271  if (code == "Eri")
272  return QString("Eridani");
273  if (code == "For")
274  return QString("Fornacis");
275  if (code == "Gem")
276  return QString("Geminorum");
277  if (code == "Gru")
278  return QString("Gruis");
279  if (code == "Her")
280  return QString("Herculis");
281  if (code == "Hor")
282  return QString("Horologii");
283  if (code == "Hya")
284  return QString("Hydrae");
285  if (code == "Hyi")
286  return QString("Hydri");
287  if (code == "Ind")
288  return QString("Indi");
289  if (code == "Lac")
290  return QString("Lacertae");
291  if (code == "Leo")
292  return QString("Leonis");
293  if (code == "LMi")
294  return QString("Leonis Minoris");
295  if (code == "Lep")
296  return QString("Leporis");
297  if (code == "Lib")
298  return QString("Librae");
299  if (code == "Lup")
300  return QString("Lupi");
301  if (code == "Lyn")
302  return QString("Lyncis");
303  if (code == "Lyr")
304  return QString("Lyrae");
305  if (code == "Men")
306  return QString("Mensae");
307  if (code == "Mic")
308  return QString("Microscopii");
309  if (code == "Mon")
310  return QString("Monocerotis");
311  if (code == "Mus")
312  return QString("Muscae");
313  if (code == "Nor")
314  return QString("Normae");
315  if (code == "Oct")
316  return QString("Octantis");
317  if (code == "Oph")
318  return QString("Ophiuchi");
319  if (code == "Ori")
320  return QString("Orionis");
321  if (code == "Pav")
322  return QString("Pavonis");
323  if (code == "Peg")
324  return QString("Pegasi");
325  if (code == "Per")
326  return QString("Persei");
327  if (code == "Phe")
328  return QString("Phoenicis");
329  if (code == "Pic")
330  return QString("Pictoris");
331  if (code == "Psc")
332  return QString("Piscium");
333  if (code == "PsA")
334  return QString("Piscis Austrini");
335  if (code == "Pup")
336  return QString("Puppis");
337  if (code == "Pyx")
338  return QString("Pyxidis");
339  if (code == "Ret")
340  return QString("Reticuli");
341  if (code == "Sge")
342  return QString("Sagittae");
343  if (code == "Sgr")
344  return QString("Sagittarii");
345  if (code == "Sco")
346  return QString("Scorpii");
347  if (code == "Scl")
348  return QString("Sculptoris");
349  if (code == "Sct")
350  return QString("Scuti");
351  if (code == "Ser")
352  return QString("Serpentis");
353  if (code == "Sex")
354  return QString("Sextantis");
355  if (code == "Tau")
356  return QString("Tauri");
357  if (code == "Tel")
358  return QString("Telescopii");
359  if (code == "Tri")
360  return QString("Trianguli");
361  if (code == "TrA")
362  return QString("Trianguli Australis");
363  if (code == "Tuc")
364  return QString("Tucanae");
365  if (code == "UMa")
366  return QString("Ursae Majoris");
367  if (code == "UMi")
368  return QString("Ursae Minoris");
369  if (code == "Vel")
370  return QString("Velorum");
371  if (code == "Vir")
372  return QString("Virginis");
373  if (code == "Vol")
374  return QString("Volantis");
375  if (code == "Vul")
376  return QString("Vulpeculae");
377  return code;
378 }
379 
380 QString constNameFromAbbrev(const QString &code)
381 {
382  if (code == "And")
383  return QString("Andromeda");
384  if (code == "Ant")
385  return QString("Antlia");
386  if (code == "Aps")
387  return QString("Apus");
388  if (code == "Aqr")
389  return QString("Aquarius");
390  if (code == "Aql")
391  return QString("Aquila");
392  if (code == "Ara")
393  return QString("Ara");
394  if (code == "Ari")
395  return QString("Aries");
396  if (code == "Aur")
397  return QString("Auriga");
398  if (code == "Boo")
399  return QString("Bootes");
400  if (code == "Cae")
401  return QString("Caelum");
402  if (code == "Cam")
403  return QString("Camelopardalis");
404  if (code == "Cnc")
405  return QString("Cancer");
406  if (code == "CVn")
407  return QString("Canes Venatici");
408  if (code == "CMa")
409  return QString("Canis Major");
410  if (code == "CMi")
411  return QString("Canis Minor");
412  if (code == "Cap")
413  return QString("Capricornus");
414  if (code == "Car")
415  return QString("Carina");
416  if (code == "Cas")
417  return QString("Cassiopeia");
418  if (code == "Cen")
419  return QString("Centaurus");
420  if (code == "Cep")
421  return QString("Cepheus");
422  if (code == "Cet")
423  return QString("Cetus");
424  if (code == "Cha")
425  return QString("Chamaeleon");
426  if (code == "Cir")
427  return QString("Circinus");
428  if (code == "Col")
429  return QString("Columba");
430  if (code == "Com")
431  return QString("Coma Berenices");
432  if (code == "CrA")
433  return QString("Corona Australis");
434  if (code == "CrB")
435  return QString("Corona Borealis");
436  if (code == "Crv")
437  return QString("Corvus");
438  if (code == "Crt")
439  return QString("Crater");
440  if (code == "Cru")
441  return QString("Crux");
442  if (code == "Cyg")
443  return QString("Cygnus");
444  if (code == "Del")
445  return QString("Delphinus");
446  if (code == "Dor")
447  return QString("Doradus");
448  if (code == "Dra")
449  return QString("Draco");
450  if (code == "Equ")
451  return QString("Equuleus");
452  if (code == "Eri")
453  return QString("Eridanus");
454  if (code == "For")
455  return QString("Fornax");
456  if (code == "Gem")
457  return QString("Gemini");
458  if (code == "Gru")
459  return QString("Grus");
460  if (code == "Her")
461  return QString("Hercules");
462  if (code == "Hor")
463  return QString("Horologium");
464  if (code == "Hya")
465  return QString("Hydra");
466  if (code == "Hyi")
467  return QString("Hydrus");
468  if (code == "Ind")
469  return QString("Indus");
470  if (code == "Lac")
471  return QString("Lacerta");
472  if (code == "Leo")
473  return QString("Leo");
474  if (code == "LMi")
475  return QString("Leo Minor");
476  if (code == "Lep")
477  return QString("Lepus");
478  if (code == "Lib")
479  return QString("Libra");
480  if (code == "Lup")
481  return QString("Lupus");
482  if (code == "Lyn")
483  return QString("Lynx");
484  if (code == "Lyr")
485  return QString("Lyra");
486  if (code == "Men")
487  return QString("Mensa");
488  if (code == "Mic")
489  return QString("Microscopium");
490  if (code == "Mon")
491  return QString("Monoceros");
492  if (code == "Mus")
493  return QString("Musca");
494  if (code == "Nor")
495  return QString("Norma");
496  if (code == "Oct")
497  return QString("Octans");
498  if (code == "Oph")
499  return QString("Ophiuchus");
500  if (code == "Ori")
501  return QString("Orion");
502  if (code == "Pav")
503  return QString("Pavo");
504  if (code == "Peg")
505  return QString("Pegasus");
506  if (code == "Per")
507  return QString("Perseus");
508  if (code == "Phe")
509  return QString("Phoenix");
510  if (code == "Pic")
511  return QString("Pictor");
512  if (code == "Psc")
513  return QString("Pisces");
514  if (code == "PsA")
515  return QString("Piscis Austrinus");
516  if (code == "Pup")
517  return QString("Puppis");
518  if (code == "Pyx")
519  return QString("Pyxis");
520  if (code == "Ret")
521  return QString("Reticulum");
522  if (code == "Sge")
523  return QString("Sagitta");
524  if (code == "Sgr")
525  return QString("Sagittarius");
526  if (code == "Sco")
527  return QString("Scorpius");
528  if (code == "Scl")
529  return QString("Sculptor");
530  if (code == "Sct")
531  return QString("Scutum");
532  if (code == "Ser")
533  return QString("Serpens");
534  if (code == "Sex")
535  return QString("Sextans");
536  if (code == "Tau")
537  return QString("Taurus");
538  if (code == "Tel")
539  return QString("Telescopium");
540  if (code == "Tri")
541  return QString("Triangulum");
542  if (code == "TrA")
543  return QString("Triangulum Australe");
544  if (code == "Tuc")
545  return QString("Tucana");
546  if (code == "UMa")
547  return QString("Ursa Major");
548  if (code == "UMi")
549  return QString("Ursa Minor");
550  if (code == "Vel")
551  return QString("Vela");
552  if (code == "Vir")
553  return QString("Virgo");
554  if (code == "Vol")
555  return QString("Volans");
556  if (code == "Vul")
557  return QString("Vulpecula");
558  return code;
559 }
560 
561 QString constNameToAbbrev(const QString &fullName_)
562 {
563  QString fullName = fullName_.toLower();
564  if (fullName == "andromeda")
565  return QString("And");
566  if (fullName == "antlia")
567  return QString("Ant");
568  if (fullName == "apus")
569  return QString("Aps");
570  if (fullName == "aquarius")
571  return QString("Aqr");
572  if (fullName == "aquila")
573  return QString("Aql");
574  if (fullName == "ara")
575  return QString("Ara");
576  if (fullName == "aries")
577  return QString("Ari");
578  if (fullName == "auriga")
579  return QString("Aur");
580  if (fullName == "bootes")
581  return QString("Boo");
582  if (fullName == "caelum")
583  return QString("Cae");
584  if (fullName == "camelopardalis")
585  return QString("Cam");
586  if (fullName == "cancer")
587  return QString("Cnc");
588  if (fullName == "canes venatici")
589  return QString("CVn");
590  if (fullName == "canis major")
591  return QString("CMa");
592  if (fullName == "canis minor")
593  return QString("CMi");
594  if (fullName == "capricornus")
595  return QString("Cap");
596  if (fullName == "carina")
597  return QString("Car");
598  if (fullName == "cassiopeia")
599  return QString("Cas");
600  if (fullName == "centaurus")
601  return QString("Cen");
602  if (fullName == "cepheus")
603  return QString("Cep");
604  if (fullName == "cetus")
605  return QString("Cet");
606  if (fullName == "chamaeleon")
607  return QString("Cha");
608  if (fullName == "circinus")
609  return QString("Cir");
610  if (fullName == "columba")
611  return QString("Col");
612  if (fullName == "coma berenices")
613  return QString("Com");
614  if (fullName == "corona australis")
615  return QString("CrA");
616  if (fullName == "corona borealis")
617  return QString("CrB");
618  if (fullName == "corvus")
619  return QString("Crv");
620  if (fullName == "crater")
621  return QString("Crt");
622  if (fullName == "crux")
623  return QString("Cru");
624  if (fullName == "cygnus")
625  return QString("Cyg");
626  if (fullName == "delphinus")
627  return QString("Del");
628  if (fullName == "doradus")
629  return QString("Dor");
630  if (fullName == "draco")
631  return QString("Dra");
632  if (fullName == "equuleus")
633  return QString("Equ");
634  if (fullName == "eridanus")
635  return QString("Eri");
636  if (fullName == "fornax")
637  return QString("For");
638  if (fullName == "gemini")
639  return QString("Gem");
640  if (fullName == "grus")
641  return QString("Gru");
642  if (fullName == "hercules")
643  return QString("Her");
644  if (fullName == "horologium")
645  return QString("Hor");
646  if (fullName == "hydra")
647  return QString("Hya");
648  if (fullName == "hydrus")
649  return QString("Hyi");
650  if (fullName == "indus")
651  return QString("Ind");
652  if (fullName == "lacerta")
653  return QString("Lac");
654  if (fullName == "leo")
655  return QString("Leo");
656  if (fullName == "leo minor")
657  return QString("LMi");
658  if (fullName == "lepus")
659  return QString("Lep");
660  if (fullName == "libra")
661  return QString("Lib");
662  if (fullName == "lupus")
663  return QString("Lup");
664  if (fullName == "lynx")
665  return QString("Lyn");
666  if (fullName == "lyra")
667  return QString("Lyr");
668  if (fullName == "mensa")
669  return QString("Men");
670  if (fullName == "microscopium")
671  return QString("Mic");
672  if (fullName == "monoceros")
673  return QString("Mon");
674  if (fullName == "musca")
675  return QString("Mus");
676  if (fullName == "norma")
677  return QString("Nor");
678  if (fullName == "octans")
679  return QString("Oct");
680  if (fullName == "ophiuchus")
681  return QString("Oph");
682  if (fullName == "orion")
683  return QString("Ori");
684  if (fullName == "pavo")
685  return QString("Pav");
686  if (fullName == "pegasus")
687  return QString("Peg");
688  if (fullName == "perseus")
689  return QString("Per");
690  if (fullName == "phoenix")
691  return QString("Phe");
692  if (fullName == "pictor")
693  return QString("Pic");
694  if (fullName == "pisces")
695  return QString("Psc");
696  if (fullName == "piscis austrinus")
697  return QString("PsA");
698  if (fullName == "puppis")
699  return QString("Pup");
700  if (fullName == "pyxis")
701  return QString("Pyx");
702  if (fullName == "reticulum")
703  return QString("Ret");
704  if (fullName == "sagitta")
705  return QString("Sge");
706  if (fullName == "sagittarius")
707  return QString("Sgr");
708  if (fullName == "scorpius")
709  return QString("Sco");
710  if (fullName == "sculptor")
711  return QString("Scl");
712  if (fullName == "scutum")
713  return QString("Sct");
714  if (fullName == "serpens")
715  return QString("Ser");
716  if (fullName == "sextans")
717  return QString("Sex");
718  if (fullName == "taurus")
719  return QString("Tau");
720  if (fullName == "telescopium")
721  return QString("Tel");
722  if (fullName == "triangulum")
723  return QString("Tri");
724  if (fullName == "triangulum australe")
725  return QString("TrA");
726  if (fullName == "tucana")
727  return QString("Tuc");
728  if (fullName == "ursa major")
729  return QString("UMa");
730  if (fullName == "ursa minor")
731  return QString("UMi");
732  if (fullName == "vela")
733  return QString("Vel");
734  if (fullName == "virgo")
735  return QString("Vir");
736  if (fullName == "volans")
737  return QString("Vol");
738  if (fullName == "vulpecula")
739  return QString("Vul");
740  return fullName_;
741 }
742 
743 QString constGenetiveToAbbrev(const QString &genetive_)
744 {
745  QString genetive = genetive_.toLower();
746  if (genetive == "andromedae")
747  return QString("And");
748  if (genetive == "antliae")
749  return QString("Ant");
750  if (genetive == "apodis")
751  return QString("Aps");
752  if (genetive == "aquarii")
753  return QString("Aqr");
754  if (genetive == "aquilae")
755  return QString("Aql");
756  if (genetive == "arae")
757  return QString("Ara");
758  if (genetive == "arietis")
759  return QString("Ari");
760  if (genetive == "aurigae")
761  return QString("Aur");
762  if (genetive == "bootis")
763  return QString("Boo");
764  if (genetive == "caeli")
765  return QString("Cae");
766  if (genetive == "camelopardalis")
767  return QString("Cam");
768  if (genetive == "cancri")
769  return QString("Cnc");
770  if (genetive == "canum venaticorum")
771  return QString("CVn");
772  if (genetive == "canis majoris")
773  return QString("CMa");
774  if (genetive == "canis minoris")
775  return QString("CMi");
776  if (genetive == "capricorni")
777  return QString("Cap");
778  if (genetive == "carinae")
779  return QString("Car");
780  if (genetive == "cassiopeiae")
781  return QString("Cas");
782  if (genetive == "centauri")
783  return QString("Cen");
784  if (genetive == "cephei")
785  return QString("Cep");
786  if (genetive == "ceti")
787  return QString("Cet");
788  if (genetive == "chamaeleontis")
789  return QString("Cha");
790  if (genetive == "circini")
791  return QString("Cir");
792  if (genetive == "columbae")
793  return QString("Col");
794  if (genetive == "comae berenices")
795  return QString("Com");
796  if (genetive == "coronae austrinae")
797  return QString("CrA");
798  if (genetive == "coronae borealis")
799  return QString("CrB");
800  if (genetive == "corvi")
801  return QString("Crv");
802  if (genetive == "crateris")
803  return QString("Crt");
804  if (genetive == "crucis")
805  return QString("Cru");
806  if (genetive == "cygni")
807  return QString("Cyg");
808  if (genetive == "delphini")
809  return QString("Del");
810  if (genetive == "doradus")
811  return QString("Dor");
812  if (genetive == "draconis")
813  return QString("Dra");
814  if (genetive == "equulei")
815  return QString("Equ");
816  if (genetive == "eridani")
817  return QString("Eri");
818  if (genetive == "fornacis")
819  return QString("For");
820  if (genetive == "geminorum")
821  return QString("Gem");
822  if (genetive == "gruis")
823  return QString("Gru");
824  if (genetive == "herculis")
825  return QString("Her");
826  if (genetive == "horologii")
827  return QString("Hor");
828  if (genetive == "hydrae")
829  return QString("Hya");
830  if (genetive == "hydri")
831  return QString("Hyi");
832  if (genetive == "indi")
833  return QString("Ind");
834  if (genetive == "lacertae")
835  return QString("Lac");
836  if (genetive == "leonis")
837  return QString("Leo");
838  if (genetive == "leonis minoris")
839  return QString("LMi");
840  if (genetive == "leporis")
841  return QString("Lep");
842  if (genetive == "librae")
843  return QString("Lib");
844  if (genetive == "lupi")
845  return QString("Lup");
846  if (genetive == "lyncis")
847  return QString("Lyn");
848  if (genetive == "lyrae")
849  return QString("Lyr");
850  if (genetive == "mensae")
851  return QString("Men");
852  if (genetive == "microscopii")
853  return QString("Mic");
854  if (genetive == "monocerotis")
855  return QString("Mon");
856  if (genetive == "muscae")
857  return QString("Mus");
858  if (genetive == "normae")
859  return QString("Nor");
860  if (genetive == "octantis")
861  return QString("Oct");
862  if (genetive == "ophiuchi")
863  return QString("Oph");
864  if (genetive == "orionis")
865  return QString("Ori");
866  if (genetive == "pavonis")
867  return QString("Pav");
868  if (genetive == "pegasi")
869  return QString("Peg");
870  if (genetive == "persei")
871  return QString("Per");
872  if (genetive == "phoenicis")
873  return QString("Phe");
874  if (genetive == "pictoris")
875  return QString("Pic");
876  if (genetive == "piscium")
877  return QString("Psc");
878  if (genetive == "piscis austrini")
879  return QString("PsA");
880  if (genetive == "puppis")
881  return QString("Pup");
882  if (genetive == "pyxidis")
883  return QString("Pyx");
884  if (genetive == "reticuli")
885  return QString("Ret");
886  if (genetive == "sagittae")
887  return QString("Sge");
888  if (genetive == "sagittarii")
889  return QString("Sgr");
890  if (genetive == "scorpii")
891  return QString("Sco");
892  if (genetive == "sculptoris")
893  return QString("Scl");
894  if (genetive == "scuti")
895  return QString("Sct");
896  if (genetive == "serpentis")
897  return QString("Ser");
898  if (genetive == "sextantis")
899  return QString("Sex");
900  if (genetive == "tauri")
901  return QString("Tau");
902  if (genetive == "telescopii")
903  return QString("Tel");
904  if (genetive == "trianguli")
905  return QString("Tri");
906  if (genetive == "trianguli australis")
907  return QString("TrA");
908  if (genetive == "tucanae")
909  return QString("Tuc");
910  if (genetive == "ursae majoris")
911  return QString("UMa");
912  if (genetive == "ursae minoris")
913  return QString("UMi");
914  if (genetive == "velorum")
915  return QString("Vel");
916  if (genetive == "virginis")
917  return QString("Vir");
918  if (genetive == "volantis")
919  return QString("Vol");
920  if (genetive == "vulpeculae")
921  return QString("Vul");
922  return genetive_;
923 }
924 
925 QString Logging::_filename;
926 
928 {
929  if (_filename.isEmpty())
930  {
931  QDir dir;
932  QString path =
933  QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation))
934  .filePath("logs" + QDir::separator() + QDateTime::currentDateTime().toString("yyyy-MM-dd"));
935  dir.mkpath(path);
936  QString name =
937  "log_" + QDateTime::currentDateTime().toString("HH-mm-ss") + ".txt";
938  _filename = path + QDir::separator() + name;
939 
940  // Clear file contents
941  QFile file(_filename);
942  file.open(QFile::WriteOnly);
943  file.close();
944  }
945 
946  qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} "
947  "%{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{"
948  "endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] "
949  "%{if-category}[%{category}]%{endif} - %{message}");
950  qInstallMessageHandler(File);
951 }
952 
953 void Logging::File(QtMsgType type, const QMessageLogContext &context, const QString &msg)
954 {
955  QFile file(_filename);
956  if (file.open(QFile::Append | QIODevice::Text))
957  {
958  QTextStream stream(&file);
959  Write(stream, type, context, msg);
960  }
961 }
962 
964 {
965  qSetMessagePattern("[%{time yyyy-MM-dd h:mm:ss.zzz t} "
966  "%{if-debug}DEBG%{endif}%{if-info}INFO%{endif}%{if-warning}WARN%{"
967  "endif}%{if-critical}CRIT%{endif}%{if-fatal}FATL%{endif}] "
968  "%{if-category}[%{category}]%{endif} - %{message}");
969  qInstallMessageHandler(Stdout);
970 }
971 
972 void Logging::Stdout(QtMsgType type, const QMessageLogContext &context,
973  const QString &msg)
974 {
975  QTextStream stream(stdout, QIODevice::WriteOnly);
976  Write(stream, type, context, msg);
977 }
978 
980 {
981  qInstallMessageHandler(Stderr);
982 }
983 
984 void Logging::Stderr(QtMsgType type, const QMessageLogContext &context,
985  const QString &msg)
986 {
987  QTextStream stream(stderr, QIODevice::WriteOnly);
988  Write(stream, type, context, msg);
989 }
990 
991 void Logging::Write(QTextStream &stream, QtMsgType type,
992  const QMessageLogContext &context, const QString &msg)
993 {
994  stream << QDateTime::currentDateTime().toString("[yyyy-MM-ddThh:mm:ss.zzz t ");
995 
996  switch (type)
997  {
998  case QtInfoMsg:
999  stream << "INFO ]";
1000  break;
1001  case QtDebugMsg:
1002  stream << "DEBG ]";
1003  break;
1004  case QtWarningMsg:
1005  stream << "WARN ]";
1006  break;
1007  case QtCriticalMsg:
1008  stream << "CRIT ]";
1009  break;
1010  case QtFatalMsg:
1011  stream << "FATL ]";
1012  break;
1013  default:
1014  stream << "UNKN ]";
1015  }
1016 
1017  stream << "[" << qSetFieldWidth(30) << context.category << qSetFieldWidth(0)
1018  << "] - ";
1019  stream << msg << '\n';
1020  stream.flush();
1021  //stream << qFormatLogMessage(type, context, msg) << Qt::endl;
1022 }
1023 
1025 {
1026  qInstallMessageHandler(nullptr);
1027 }
1028 
1030 {
1031  qInstallMessageHandler(Disabled);
1032 }
1033 
1034 void Logging::Disabled(QtMsgType, const QMessageLogContext &, const QString &) {}
1035 
1037 {
1038  // QString rules = QString("org.kde.kstars.ekos.debug=%1\n"
1039  // "org.kde.kstars.indi.debug=%2\n"
1040  // "org.kde.kstars.fits.debug=%3\n"
1041  // "org.kde.kstars.ekos.capture.debug=%4\n"
1042  // "org.kde.kstars.ekos.focus.debug=%5\n"
1043  // "org.kde.kstars.ekos.guide.debug=%6\n"
1044  // "org.kde.kstars.ekos.align.debug=%7\n"
1045  // "org.kde.kstars.ekos.mount.debug=%8\n"
1046  // "org.kde.kstars.ekos.scheduler.debug=%9\n").arg(
1047  // Options::verboseLogging() ? "true" : "false",
1048  // Options::iNDILogging() ? "true" : "false",
1049  // Options::fITSLogging() ? "true" : "false",
1050  // Options::captureLogging() ? "true" : "false",
1051  // Options::focusLogging() ? "true" : "false",
1052  // Options::guideLogging() ? "true" : "false",
1053  // Options::alignmentLogging() ? "true" : "false",
1054  // Options::mountLogging() ? "true" : "false",
1055  // Options::schedulerLogging() ? "true" : "false")
1056  // .append(QString("org.kde.kstars.ekos.observatory.debug=%2\n"
1057  // "org.kde.kstars.debug=%1").arg(
1058  // Options::verboseLogging() ? "true" : "false",
1059  // Options::observatoryLogging() ? "true" : "false"));
1060 
1061  QStringList rules;
1062 
1063  rules << "org.kde.kstars.ekos.debug"
1064  << (Options::verboseLogging() ? "true" : "false");
1065  rules << "org.kde.kstars.indi.debug" << (Options::iNDILogging() ? "true" : "false");
1066  rules << "org.kde.kstars.fits.debug" << (Options::fITSLogging() ? "true" : "false");
1067  rules << "org.kde.kstars.ekos.capture.debug"
1068  << (Options::captureLogging() ? "true" : "false");
1069  rules << "org.kde.kstars.ekos.focus.debug"
1070  << (Options::focusLogging() ? "true" : "false");
1071  rules << "org.kde.kstars.ekos.guide.debug"
1072  << (Options::guideLogging() ? "true" : "false");
1073  rules << "org.kde.kstars.ekos.align.debug"
1074  << (Options::alignmentLogging() ? "true" : "false");
1075  rules << "org.kde.kstars.ekos.mount.debug"
1076  << (Options::mountLogging() ? "true" : "false");
1077  rules << "org.kde.kstars.ekos.scheduler.debug"
1078  << (Options::schedulerLogging() ? "true" : "false");
1079  rules << "org.kde.kstars.ekos.observatory.debug"
1080  << (Options::observatoryLogging() ? "true" : "false");
1081  rules << "org.kde.kstars.debug" << (Options::verboseLogging() ? "true" : "false");
1082 
1083  QString formattedRules;
1084  for (int i = 0; i < rules.size(); i += 2)
1085  formattedRules.append(QString("%1=%2\n").arg(rules[i], rules[i + 1]));
1086 
1087  QLoggingCategory::setFilterRules(formattedRules);
1088 }
1089 
1090 /**
1091  This method provides a centralized location for the default paths to important external files used in the Options
1092  on different operating systems. Note that on OS X, if the user builds the app without indi, astrometry, and xplanet internally
1093  then the options below will be used. If the user drags the app from a dmg and has to install the KStars data directory,
1094  then most of these paths will be overwritten since it is preferred to use the internal versions.
1095 **/
1096 
1097 QString getDefaultPath(const QString &option)
1098 {
1099  // We support running within Snaps, Flatpaks, and AppImage
1100  // The path should accomodate the differences between the different
1101  // packaging solutions
1103  QString flat = QProcessEnvironment::systemEnvironment().value("FLATPAK_DEST");
1105 
1106  // User prefix is the primary mounting point
1107  QString userPrefix = "/usr";
1108  // By default /usr is the prefix
1109  QString prefix = userPrefix;
1110  // Detect if we are within an App Image
1111  if (QProcessEnvironment::systemEnvironment().value("APPIMAGE").isEmpty() == false &&
1112  appimg.isEmpty() == false)
1113  prefix = appimg + userPrefix;
1114  else if (flat.isEmpty() == false)
1115  // Detect if we are within a Flatpak
1116  prefix = flat;
1117  // Detect if we are within a snap
1118  else if (snap.isEmpty() == false)
1119  prefix = snap + userPrefix;
1120 
1121  if (option == "fitsDir")
1122  {
1124  }
1125  else if (option == "indiServer")
1126  {
1127 #if defined(INDI_PREFIX)
1128  return QString(INDI_PREFIX "/bin/indiserver");
1129 #elif defined(Q_OS_OSX)
1130  return "/usr/local/bin/indiserver";
1131 #endif
1132  return prefix + "/bin/indiserver";
1133  }
1134  else if (option == "PlaceholderFormat")
1135  {
1136 #if defined(Q_OS_WIN)
1137  return "\\%t\\%T\\%F\\%t_%T_%F";
1138 #else
1139  return "/%t/%T/%F/%t_%T_%F";
1140 #endif
1141  }
1142  else if (option == "INDIHubAgent")
1143  {
1144 #if defined(INDI_PREFIX)
1145  return QString(INDI_PREFIX "/bin/indihub-agent");
1146 #elif defined(Q_OS_OSX)
1147  return "/usr/local/bin/indihub-agent";
1148 #endif
1149  return prefix + "/bin/indihub-agent";
1150  }
1151  else if (option == "indiDriversDir")
1152  {
1153 #if defined(INDI_PREFIX)
1154  return QString(INDI_PREFIX "/share/indi");
1155 #elif defined(Q_OS_OSX)
1156  return "/usr/local/share/indi";
1157 #elif defined(Q_OS_LINUX)
1158  return prefix + "/share/indi";
1159 #else
1162 #endif
1163  }
1164  else if (option == "AstrometrySolverBinary")
1165  {
1166 #if defined(ASTROMETRY_PREFIX)
1167  return QString(ASTROMETRY_PREFIX "/bin/solve-field");
1168 #elif defined(Q_OS_OSX)
1169  return "/usr/local/bin/solve-field";
1170 #elif defined(Q_OS_WIN)
1171  return QDir::homePath() +
1172  "/AppData/Local/cygwin_ansvr/lib/astrometry/bin/solve-field.exe";
1173 #endif
1174  return prefix + "/bin/solve-field";
1175  }
1176  else if (option == "WatneyBinary")
1177  {
1178 #if defined(ASTROMETRY_PREFIX)
1179  return QString(ASTROMETRY_PREFIX "/opt/watney/watney-solve");
1180 #elif defined(Q_OS_OSX)
1181  return "/usr/local/bin/watney-solve";
1182 #elif defined(Q_OS_WIN)
1183  return "C:/watney/watney-solve.exe";
1184 #endif
1185  return prefix + "/opt/watney/watney-solve";
1186  }
1187  else if (option == "SextractorBinary")
1188  {
1189 #if defined(SEXTRACTOR_PREFIX)
1190  return QString(SEXTRACTOR_PREFIX "/bin/sextractor");
1191 #elif defined(Q_OS_OSX)
1192  return "/usr/local/bin/sex";
1193 #endif
1194  return prefix + "/bin/sextractor";
1195  }
1196  else if (option == "AstrometryWCSInfo")
1197  {
1198 #if defined(ASTROMETRY_PREFIX)
1199  return QString(ASTROMETRY_PREFIX "/bin/wcsinfo");
1200 #elif defined(Q_OS_OSX)
1201  return "/usr/local/bin/wcsinfo";
1202 #elif defined(Q_OS_WIN)
1203  return QDir::homePath() +
1204  "/AppData/Local/cygwin_ansvr/lib/astrometry/bin/wcsinfo.exe";
1205 #endif
1206  return prefix + "/bin/wcsinfo";
1207  }
1208  else if (option == "AstrometryConfFile")
1209  {
1210 #if defined(ASTROMETRY_CONF_IN_PREFIX) && defined(ASTROMETRY_PREFIX)
1211  return QString(ASTROMETRY_PREFIX "/etc/astrometry.cfg");
1212 #elif defined(Q_OS_OSX)
1213  return "/usr/local/etc/astrometry.cfg";
1214 #elif defined(Q_OS_WIN)
1215  return QDir::homePath() +
1216  "/AppData/Local/cygwin_ansvr/etc/astrometry/backend.cfg";
1217 #endif
1218  // We move /usr
1219  prefix.remove(userPrefix);
1220  return prefix + "/etc/astrometry.cfg";
1221  }
1222  else if (option == "AstrometryIndexFileLocation")
1223  {
1224 #if defined(ASTROMETRY_PREFIX)
1225  return QString(ASTROMETRY_PREFIX "/share/astrometry");
1226 #elif defined(Q_OS_OSX)
1228  .filePath("Astrometry/");
1229 #endif
1230  return prefix + "/share/astrometry/";
1231  }
1232  else if (option == "AstrometryLogFilepath")
1233  {
1234  return QDir::tempPath() + "/astrometryLog.txt";
1235  }
1236  else if (option == "XplanetPath")
1237  {
1238 #if defined(XPLANET_PREFIX)
1239  return QString(XPLANET_PREFIX "/bin/xplanet");
1240 #elif defined(Q_OS_OSX)
1241  return "/usr/local/bin/xplanet";
1242 #endif
1243  return prefix + "/bin/xplanet";
1244  }
1245  else if (option == "ASTAP")
1246  {
1247 #if defined(Q_OS_OSX)
1248  return "/Applications/ASTAP.app/Contents/MacOS/astap";
1249 #elif defined(Q_OS_WIN)
1250  return "C:/Program Files/astap/astap.exe";
1251 #endif
1252  return "/opt/astap/astap";
1253  }
1254 
1255  return QString();
1256 }
1257 
1258 QStringList getAstrometryDefaultIndexFolderPaths()
1259 {
1260  QStringList folderPaths;
1261  const QString confDir =
1262  QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation))
1263  .filePath(QLatin1String("astrometry"));
1264  folderPaths << confDir;
1265  // Check if directory already exists, if it doesn't create one
1266  QDir writableDir(confDir);
1267  if (writableDir.exists() == false)
1268  {
1269  if (writableDir.mkdir(confDir) == false)
1270  {
1271  qCCritical(KSTARS) << "Failed to create local astrometry directory";
1272  folderPaths.clear();
1273  }
1274  }
1275 
1276 #ifdef HAVE_STELLARSOLVER
1277  folderPaths.append(StellarSolver::getDefaultIndexFolderPaths());
1278 #endif
1279  return folderPaths;
1280 }
1281 
1282 #if defined(Q_OS_OSX)
1283 //Note that this will copy and will not overwrite, so that the user's changes in the files are preserved.
1284 void copyResourcesFolderFromAppBundle(QString folder)
1285 {
1286  QString folderLocation = QStandardPaths::locate(
1288  QDir folderSourceDir;
1289  if (folder == "kstars")
1290  folderSourceDir =
1291  QDir(QCoreApplication::applicationDirPath() + "/../Resources/kstars")
1292  .absolutePath();
1293  else
1294  folderSourceDir =
1295  QDir(QCoreApplication::applicationDirPath() + "/../Resources/" + folder)
1296  .absolutePath();
1297  if (folderSourceDir.exists())
1298  {
1299  folderLocation =
1301  folder;
1302  QDir writableDir;
1303  writableDir.mkdir(folderLocation);
1304  copyRecursively(folderSourceDir.absolutePath(), folderLocation);
1305  }
1306 }
1307 
1308 bool setupMacKStarsIfNeeded() // This method will return false if the KStars data directory doesn't exist when it's done
1309 {
1310  //This will copy the locale folder, the notifications folder, and the sounds folder and any missing files in them to Application Support if needed.
1311  copyResourcesFolderFromAppBundle("locale");
1312  copyResourcesFolderFromAppBundle("knotifications5");
1313  copyResourcesFolderFromAppBundle("sounds");
1314 
1315  //This will copy the KStars data directory
1316  copyResourcesFolderFromAppBundle("kstars");
1317  copyResourcesFolderFromAppBundle("kstars/xplanet");
1318 
1319  if (Options::kStarsFirstRun())
1320  {
1321  //This sets some important OS X options.
1322  Options::setIndiServerIsInternal(true);
1323  Options::setIndiServer("*Internal INDI Server*");
1324  Options::setIndiDriversAreInternal(true);
1325  Options::setIndiDriversDir("*Internal INDI Drivers*");
1326  Options::setXplanetIsInternal(true);
1327  Options::setXplanetPath("*Internal XPlanet*");
1328  }
1329 
1330  QString dataLocation = QStandardPaths::locate(
1332  if (dataLocation.isEmpty()) //If there is no kstars user data directory
1333  return false;
1334 
1335  return true;
1336 }
1337 
1338 bool configureAstrometry()
1339 {
1340  QStringList astrometryDataDirs = getAstrometryDataDirs();
1341  if (astrometryDataDirs.count() == 0)
1342  return false;
1343  QString defaultAstrometryDataDir = getDefaultPath("AstrometryIndexFileLocation");
1344  if (astrometryDataDirs.contains("IndexFileLocationNotYetSet"))
1345  replaceIndexFileNotYetSet();
1346  if (QDir(defaultAstrometryDataDir).exists() == false)
1347  {
1349  nullptr,
1350  i18n("The selected Astrometry Index File Location:\n %1 \n does not "
1351  "exist. Do you want to make the directory?",
1352  defaultAstrometryDataDir),
1353  i18n("Make Astrometry Index File Directory?")) == KMessageBox::Yes)
1354  {
1355  if (QDir(defaultAstrometryDataDir).mkdir(defaultAstrometryDataDir))
1356  {
1357  KSNotification::info(
1358  i18n("The Default Astrometry Index File Location was created."));
1359  }
1360  else
1361  {
1362  KSNotification::sorry(
1363  i18n("The Default Astrometry Index File Directory does not exist and "
1364  "was not able to be created."));
1365  }
1366  }
1367  else
1368  {
1369  return false;
1370  }
1371  }
1372 
1373  return true;
1374 }
1375 
1376 bool replaceIndexFileNotYetSet()
1377 {
1378  QString confPath = KSUtils::getAstrometryConfFilePath();
1379 
1380  QFile confFile(confPath);
1381  QString contents;
1382  if (confFile.open(QIODevice::ReadOnly) == false)
1383  {
1384  KSNotification::error(i18n("Astrometry Configuration File Read Error."));
1385  return false;
1386  }
1387  else
1388  {
1389  QByteArray fileContent = confFile.readAll();
1390  confFile.close();
1391  QString contents = QString::fromLatin1(fileContent);
1392  contents.replace("IndexFileLocationNotYetSet",
1393  getDefaultPath("AstrometryIndexFileLocation"));
1394 
1395  if (confFile.open(QIODevice::WriteOnly) == false)
1396  {
1397  KSNotification::error(
1398  i18n("Internal Astrometry Configuration File Write Error."));
1399  return false;
1400  }
1401  else
1402  {
1403  QTextStream out(&confFile);
1404  out << contents;
1405  confFile.close();
1406  }
1407  }
1408  return true;
1409 }
1410 
1411 bool copyRecursively(QString sourceFolder, QString destFolder)
1412 {
1413  QDir sourceDir(sourceFolder);
1414 
1415  if (!sourceDir.exists())
1416  return false;
1417 
1418  QDir destDir(destFolder);
1419  if (!destDir.exists())
1420  destDir.mkdir(destFolder);
1421 
1422  QStringList files = sourceDir.entryList(QDir::Files);
1423  for (int i = 0; i < files.count(); i++)
1424  {
1425  QString srcName = sourceFolder + QDir::separator() + files[i];
1426  QString destName = destFolder + QDir::separator() + files[i];
1427  QFile::copy(srcName, destName); //Note this does not overwrite files
1428  }
1429 
1430  files.clear();
1431  files = sourceDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
1432  for (int i = 0; i < files.count(); i++)
1433  {
1434  QString srcName = sourceFolder + QDir::separator() + files[i];
1435  QString destName = destFolder + QDir::separator() + files[i];
1436  copyRecursively(srcName, destName);
1437  }
1438 
1439  return true;
1440 }
1441 #endif
1442 
1443 QString getAstrometryConfFilePath()
1444 {
1445  return Options::astrometryConfFile();
1446 }
1447 
1448 QStringList getAstrometryDataDirs()
1449 {
1450  QStringList optionsDataDirs = Options::astrometryIndexFolderList();
1451 
1452  bool updated = false;
1453 
1454  // Cleaning up the list of directories in options.
1455  for (int dir = 0; dir < optionsDataDirs.count(); dir++)
1456  {
1457  QString optionsDataDirName = optionsDataDirs.at(dir);
1458  QDir optionsDataDir(optionsDataDirName);
1459  if (optionsDataDir.exists())
1460  {
1461  //This will replace directory names that aren't the absolute path
1462  if (optionsDataDir.absolutePath() != optionsDataDirName)
1463  {
1464  optionsDataDirs.replace(dir, optionsDataDir.absolutePath());
1465  updated = true;
1466  }
1467  }
1468  else
1469  {
1470  //This removes directories that do not exist from the list.
1471  optionsDataDirs.removeAt(dir);
1472  dir--;
1473  updated = true;
1474  }
1475  }
1476 
1477  //This will load the conf file if it exists
1478  QFile confFile(KSUtils::getAstrometryConfFilePath());
1479  if (confFile.open(QIODevice::ReadOnly))
1480  {
1481  QStringList confDataDirs;
1482  QTextStream in(&confFile);
1483  QString line;
1484 
1485  //This will find the index file paths in the conf file
1486  while (!in.atEnd())
1487  {
1488  line = in.readLine();
1489  if (line.isEmpty() || line.startsWith('#'))
1490  continue;
1491 
1492  line = line.trimmed();
1493  if (line.startsWith(QLatin1String("add_path")))
1494  {
1495  confDataDirs << line.mid(9).trimmed();
1496  }
1497  }
1498 
1499  //This will search through the paths and compare them to the index folder list
1500  //It will add them if they aren't in there.
1501  for (QString astrometryDataDirName : confDataDirs)
1502  {
1503  QDir astrometryDataDir(astrometryDataDirName);
1504  //This rejects any that do not exist
1505  if (!astrometryDataDir.exists())
1506  continue;
1507  QString astrometryDataDirPath = astrometryDataDir.absolutePath();
1508  if (!optionsDataDirs.contains(astrometryDataDirPath))
1509  {
1510  optionsDataDirs.append(astrometryDataDirPath);
1511  updated = true;
1512  }
1513  }
1514  }
1515 
1516  //This will remove any duplicate entries.
1517  if (optionsDataDirs.removeDuplicates() != 0)
1518  updated = true;
1519 
1520  //This updates the list in Options if it changed.
1521  if (updated)
1522  Options::setAstrometryIndexFolderList(optionsDataDirs);
1523 
1524  return optionsDataDirs;
1525 }
1526 
1527 bool addAstrometryDataDir(const QString &dataDir)
1528 {
1529  //This will need to be fixed!
1530  //if(Options::astrometryIndexFileLocation() != dataDir)
1531  // Options::setAstrometryIndexFileLocation(dataDir);
1532 
1533  QString confPath = KSUtils::getAstrometryConfFilePath();
1534  QStringList astrometryDataDirs = getAstrometryDataDirs();
1535 
1536  QFile confFile(confPath);
1537  QString contents;
1538  if (confFile.open(QIODevice::ReadOnly) == false)
1539  {
1540  KSNotification::error(i18n("Astrometry Configuration File Read Error."));
1541  return false;
1542  }
1543  else
1544  {
1545  QTextStream in(&confFile);
1546  QString line;
1547  bool foundSpot = false;
1548  while (!in.atEnd())
1549  {
1550  line = in.readLine();
1551 
1552  if (line.trimmed().startsWith(QLatin1String("add_path")))
1553  {
1554  if (!foundSpot)
1555  {
1556  foundSpot = true;
1557  for (QString astrometryDataDir : astrometryDataDirs)
1558  contents += "add_path " + astrometryDataDir + '\n';
1559  contents += "add_path " + dataDir + '\n';
1560  }
1561  else
1562  {
1563  //Do not keep adding the other add_paths because they just got added in the seciton above.
1564  }
1565  }
1566  else
1567  {
1568  contents += line + '\n';
1569  }
1570  }
1571  if (!foundSpot)
1572  {
1573  for (QString astrometryDataDir : astrometryDataDirs)
1574  contents += "add_path " + astrometryDataDir + '\n';
1575  contents += "add_path " + dataDir + '\n';
1576  }
1577 
1578  confFile.close();
1579 
1580  if (confFile.open(QIODevice::WriteOnly) == false)
1581  {
1582  KSNotification::error(
1583  i18n("Internal Astrometry Configuration File Write Error."));
1584  return false;
1585  }
1586  else
1587  {
1588  QTextStream out(&confFile);
1589  out << contents;
1590  confFile.close();
1591  }
1592  }
1593  return true;
1594 }
1595 
1596 bool removeAstrometryDataDir(const QString &dataDir)
1597 {
1598  QString confPath = KSUtils::getAstrometryConfFilePath();
1599  QStringList astrometryDataDirs = getAstrometryDataDirs();
1600 
1601  QFile confFile(confPath);
1602  QString contents;
1603  if (confFile.open(QIODevice::ReadOnly) == false)
1604  {
1605  KSNotification::error(i18n("Astrometry Configuration File Read Error."));
1606  return false;
1607  }
1608  else
1609  {
1610  QTextStream in(&confFile);
1611  QString line;
1612  while (!in.atEnd())
1613  {
1614  line = in.readLine();
1615  if (line.mid(9).trimmed() != dataDir)
1616  {
1617  contents += line + '\n';
1618  }
1619  }
1620  confFile.close();
1621 
1622  if (confFile.open(QIODevice::WriteOnly) == false)
1623  {
1624  KSNotification::error(
1625  i18n("Internal Astrometry Configuration File Write Error."));
1626  return false;
1627  }
1628  else
1629  {
1630  QTextStream out(&confFile);
1631  out << contents;
1632  confFile.close();
1633  }
1634  }
1635  return true;
1636 }
1637 
1638 QByteArray getJPLQueryString(const QByteArray &kind, const QByteArray &dataFields,
1639  const QVector<JPLFilter> &filters)
1640 {
1641  /* For example:
1642  https://ssd-api.jpl.nasa.gov/sbdb_query.api?fields=full_name,neo,H,G,diameter,extent,albedo,rot_per,orbit_id,epoch.mjd,e,a,q,i,om,w,ma,per.y,moid,class&full-prec=false&sb-cdata=%7B%22AND%22:%5B%22H%7CLT%7C10%22%5D%7D&sb-kind=a&sb-ns=n&www=1
1643  */
1644 
1645  QByteArray query("sb-kind=" + kind + "&full-prec=true");
1646 
1647  // Apply filters:
1648  if (filters.size() > 0)
1649  {
1650  QByteArray filter_string("{\"AND\":[");
1651  for (const auto &item : filters)
1652  {
1653  filter_string += "\"" + item.item + "|" + item.op + "|" + item.value + "\",";
1654  }
1655 
1656  filter_string.chop(1);
1657  filter_string += "]}";
1658 
1659  query += "&sb-cdata=" + filter_string;
1660  }
1661 
1662  // Apply query data fields...
1663  query += "&fields=" + dataFields;
1664 
1665  return query;
1666 }
1667 
1668 bool RAWToJPEG(const QString &rawImage, const QString &output, QString &errorMessage)
1669 {
1670 #ifndef HAVE_LIBRAW
1671  errorMessage = i18n("Unable to find dcraw and cjpeg. Please install the required "
1672  "tools to convert CR2/NEF to JPEG.");
1673  return false;
1674 #else
1675  int ret = 0;
1676  // Creation of image processing object
1677  LibRaw RawProcessor;
1678 
1679  // Let us open the file
1680  if ((ret = RawProcessor.open_file(rawImage.toLatin1().data())) != LIBRAW_SUCCESS)
1681  {
1682  errorMessage = i18n("Cannot open %1: %2", rawImage, libraw_strerror(ret));
1683  RawProcessor.recycle();
1684  return false;
1685  }
1686 
1687  // Let us unpack the thumbnail
1688  if ((ret = RawProcessor.unpack_thumb()) != LIBRAW_SUCCESS)
1689  {
1690  errorMessage = i18n("Cannot unpack_thumb %1: %2", rawImage, libraw_strerror(ret));
1691  RawProcessor.recycle();
1692  return false;
1693  }
1694  else
1695  // We have successfully unpacked the thumbnail, now let us write it to a file
1696  {
1697  //snprintf(thumbfn,sizeof(thumbfn),"%s.%s",av[i],T.tformat == LIBRAW_THUMBNAIL_JPEG ? "thumb.jpg" : "thumb.ppm");
1698  if (LIBRAW_SUCCESS !=
1699  (ret = RawProcessor.dcraw_thumb_writer(output.toLatin1().data())))
1700  {
1701  errorMessage = i18n("Cannot write %s %1: %2", output, libraw_strerror(ret));
1702  RawProcessor.recycle();
1703  return false;
1704  }
1705  }
1706  return true;
1707 #endif
1708 }
1709 
1710 double getAvailableRAM()
1711 {
1712 #if defined(Q_OS_OSX)
1713  int mib[] = { CTL_HW, HW_MEMSIZE };
1714  size_t length;
1715  length = sizeof(int64_t);
1716  int64_t RAMcheck;
1717  if (sysctl(mib, 2, &RAMcheck, &length, NULL, 0))
1718  return false; // On Error
1719  //Until I can figure out how to get free RAM on Mac
1720  return RAMcheck;
1721 #elif defined(Q_OS_LINUX)
1722  QProcess p;
1723  p.start("awk", QStringList() << "/MemAvailable/ { print $2 }"
1724  << "/proc/meminfo");
1725  p.waitForFinished();
1726  QString memory = p.readAllStandardOutput();
1727  p.close();
1728  //kB to bytes
1729  return (memory.toLong() * 1024.0);
1730 #elif defined(Q_OS_WIN32)
1731  MEMORYSTATUSEX memory_status;
1732  ZeroMemory(&memory_status, sizeof(MEMORYSTATUSEX));
1733  memory_status.dwLength = sizeof(MEMORYSTATUSEX);
1734  if (GlobalMemoryStatusEx(&memory_status))
1735  {
1736  return memory_status.ullAvailPhys;
1737  }
1738  else
1739  {
1740  return 0;
1741  }
1742 #endif
1743  return 0;
1744 }
1745 
1746 JPLParser::JPLParser(const QString &path)
1747 {
1748  QFile jpl_file(path);
1749  if (!jpl_file.open(QIODevice::ReadOnly))
1750  {
1751  throw std::runtime_error("Could not open file.");
1752  }
1753 
1754  const auto &ast_json = QJsonDocument::fromJson(jpl_file.readAll());
1755  const auto &fields = ast_json["fields"].toArray();
1756  m_data = ast_json["data"].toArray();
1757 
1758  {
1759  int i = 0;
1760  for (const auto &field : fields)
1761  {
1762  m_field_map[field.toString()] = i++;
1763  }
1764  }
1765 }
1766 
1767 MPCParser::MPCParser(const QString &path)
1768 {
1769  QFile mpc_file(path);
1770  if (!mpc_file.open(QIODevice::ReadOnly))
1771  {
1772  throw std::runtime_error("Could not open file.");
1773  }
1774 
1775  gzFile file = gzopen(path.toLatin1().constData(), "r");
1776  if (file)
1777  {
1778 
1779  QByteArray data;
1780  const uint32_t len = 32768;
1781 
1782  while (!gzeof(file))
1783  {
1784  char buffer[len] = {0};
1785  int bytes_read = gzread(file, buffer, len - 1);
1786  if (bytes_read < 0)
1787  break;
1788  buffer[bytes_read] = 0;
1789  data.append(buffer);
1790  }
1791 
1792  gzclose(file);
1793  const auto &ast_json = QJsonDocument::fromJson(data);
1794  m_data = ast_json.array();
1795  }
1796  else
1797  qCritical(KSTARS) << "Failed to read MPC comets data file" << path;
1798 }
1799 
1800 void setGlobalSettings(const QVariantMap &settings)
1801 {
1802  for (auto &key : settings.keys())
1803  {
1804  auto property = key;
1805  // Anything starting with kcfg_ must be processed to remove the
1806  // prefix and ensure first letter is lower case.
1807  if (property.startsWith("kcfg_"))
1808  {
1809  property.remove("kcfg_");
1810  property.replace(0, 1, property.at(0).toLower());
1811  }
1812  Options::self()->setProperty(property.toLatin1(), settings[key]);
1813  }
1814 
1815  Options::self()->save();
1816 }
1817 
1818 QString sanitize(const QString &text)
1819 {
1820  static const QRegularExpression re1("\\s|/|\\(|\\)|:|\\*|\\+|~|\"" );
1821  static const QRegularExpression re2("_{2,}");
1822  static const QRegularExpression re3("_$");
1823 
1824  QString sanitized = text;
1825  if (sanitized != i18n("unnamed"))
1826  {
1827  // Remove illegal characters that can be problematic
1828  sanitized = sanitized.replace(re1, "_" )
1829  // Remove any two or more __
1830  .replace( re2, "_")
1831  // Remove any _ at the end
1832  .replace( re3, "");
1833  }
1834  return sanitized;
1835 }
1836 
1837 double rangePA(double pa)
1838 {
1839  while (pa > 180)
1840  pa -= 360;
1841  while (pa < -180)
1842  pa += 360;
1843  return pa;
1844 }
1845 
1846 double range360(double r)
1847 {
1848  double res = r;
1849  while (res < 0.00)
1850  res += 360.00;
1851  while (res > 359.99) // uniqueness of angle (360 = 0)
1852  res -= 360.00;
1853  return res;
1854 }
1855 
1856 double rotationToPositionAngle(double value)
1857 {
1858  double pa = value + 180;
1859  while (pa > 180)
1860  pa -= 360;
1861  while (pa < -180)
1862  pa += 360;
1863  return pa;
1864 }
1865 
1866 double positionAngleToRotation(double value)
1867 {
1868  double rotation = value - 180;
1869  while (rotation > 180)
1870  rotation -= 360;
1871  while (rotation < -180)
1872  rotation += 360;
1873  return rotation;
1874 }
1875 
1876 
1877 } // namespace KSUtils
void start(const QString &program, const QStringList &arguments, QIODevice::OpenMode mode)
void append(const T &value)
bool isNull() const const
bool exists() const const
std::optional< QSqlQuery > query(const QString &queryStatement)
QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error)
bool waitForFinished(int msecs)
QByteArray & append(char ch)
virtual bool open(QIODevice::OpenMode mode) override
QDateTime currentDateTime()
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
bool copy(const QString &newName)
int count(const T &value) const const
QChar separator()
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition: dms.h:390
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QString trimmed() const const
QProcessEnvironment systemEnvironment()
bool mkdir(const QString &dirName) const const
QByteArray readAllStandardOutput()
QString applicationDirPath()
QString writableLocation(QStandardPaths::StandardLocation type)
QByteArray toLatin1() const const
QString homePath()
static void UseStderr()
Output logs to stderr.
Definition: ksutils.cpp:979
static void UseStdout()
Output logs to stdout.
Definition: ksutils.cpp:963
void replace(int i, const T &value)
QString locate(QStandardPaths::StandardLocation type, const QString &fileName, QStandardPaths::LocateOptions options)
ButtonCode warningYesNo(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonYes=KStandardGuiItem::yes(), const KGuiItem &buttonNo=KStandardGuiItem::no(), const QString &dontAskAgainName=QString(), Options options=Options(Notify|Dangerous))
void removeAt(int i)
QString tempPath()
KCALUTILS_EXPORT QString errorMessage(const KCalendarCore::Exception &exception)
float a() const
#define I18N_NOOP2(context, text)
void flush()
int size() const const
static void UseFile()
Store all logs into the specified file.
Definition: ksutils.cpp:927
QString i18n(const char *text, const TYPE &arg...)
int mkdir(const QString &pathname, mode_t mode)
subclass of SkyObject specialized for stars.
Definition: starobject.h:32
static void SyncFilterRules()
SyncFilterRules Sync QtLogging filter rules from Options.
Definition: ksutils.cpp:1036
bool isEmpty() const const
QString toNativeSeparators(const QString &pathName)
const T & at(int i) const const
void setFileName(const QString &name)
virtual void close() override
QTextStream & dec(QTextStream &stream)
virtual void close() override
KStars utility functions.
QString absolutePath() const const
int second() const
Definition: dms.cpp:231
QString & replace(int position, int n, QChar after)
QString & remove(int position, int n)
static void Disable()
Disable logging.
Definition: ksutils.cpp:1029
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const const
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
int removeDuplicates()
QString toLower() const const
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
const char * constData() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString path(const QString &relativePath)
const CachingDms & dec0() const
Definition: skypoint.h:257
QString fromLatin1(const char *str, int size)
const double & Degrees() const
Definition: dms.h:141
static void UseDefault()
Use the default logging mechanism.
Definition: ksutils.cpp:1024
QString filePath(const QString &fileName) const const
const CachingDms & ra0() const
Definition: skypoint.h:251
int hour() const
Definition: dms.h:147
void clear()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
int size() const const
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition: dms.cpp:251
QString asprintf(const char *cformat,...)
QString toString(Qt::DateFormat format) const const
int minute() const
Definition: dms.cpp:221
QString mid(int position, int n) const const
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
Definition: catalogobject.h:40
void setFilterRules(const QString &rules)
QString fullName(const PartType &type)
QString & append(QChar ch)
QString value(const QString &name, const QString &defaultValue) const const
char * data()
long toLong(bool *ok, int base) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sat Sep 30 2023 04:02:44 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.