├── .gitignore ├── LICENSE ├── README.md ├── dcf_ast.py ├── solarEclipseRender ├── CMakeLists.txt ├── Makefile ├── earth_day.jpg ├── earth_night.jpg ├── prettymake ├── src │ ├── argparse │ │ ├── LICENSE │ │ ├── README.md │ │ ├── argparse.c │ │ └── argparse.h │ ├── constants.h │ ├── coreUtils │ │ ├── asciiDouble.c │ │ ├── asciiDouble.h │ │ ├── errorReport.c │ │ ├── errorReport.h │ │ └── strConstants.h │ ├── country_lookup.c │ ├── country_lookup.h │ ├── duration.c │ ├── duration.h │ ├── ephemeris.c │ ├── ephemeris.h │ ├── jpeg │ │ ├── jpeg.h │ │ └── jpeg_in.c │ ├── main.c │ ├── make_binary_map.c │ ├── make_binary_map.h │ ├── map_eclipse_contours.c │ ├── map_eclipse_contours.h │ ├── map_greatest_eclipse.c │ ├── map_greatest_eclipse.h │ ├── mathsTools │ │ ├── julianDate.c │ │ ├── julianDate.h │ │ ├── sphericalAst.c │ │ └── sphericalAst.h │ ├── png │ │ ├── image.h │ │ └── image_in.c │ ├── projection.c │ ├── projection.h │ ├── render_2d.c │ ├── render_2d.h │ ├── render_3d.c │ ├── render_3d.h │ ├── rendering.c │ ├── rendering.h │ ├── settings.c │ ├── settings.h │ ├── shadow_calc.c │ └── shadow_calc.h └── worldMap │ ├── countryList.dat │ └── worldMap.png ├── solarEclipses.dat ├── solarEclipses.py └── solarEclipses_makeKml.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | __pycache__ 3 | 4 | # Distribution / packaging 5 | develop-eggs/ 6 | dist/ 7 | eggs/ 8 | .eggs/ 9 | *.egg-info/ 10 | *.egg 11 | 12 | .idea 13 | cmake-build-debug 14 | obj 15 | bin 16 | 17 | ephemeris-compute 18 | output 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eclipse-simulator 2 | 3 | Eclipse simulator is a command-line tool for producing animations of solar 4 | eclipses. It was written to produce all of the diagrams, KML path files, and 5 | video animations that appear on . An example of the 6 | kinds of media it produces can be seen on this page: 7 | 8 | 9 | The simulations are based on the DE430 planetary ephemeris computed by the Jet 10 | Propulsion Laboratory (JPL). The position of the Sun, Earth and Moon are 11 | extracted from the DE430 files using EphemerisCompute 12 | (), written by the author and 13 | also freely available for download. 14 | 15 | They assume that the Earth and Moon are both ellipsoids with fixed polar and 16 | equatorial radii, and do not take into account the irregular topography of 17 | either body. All eclipse predictions are made at sea level. In practice, this 18 | means that the predictions presented here are inaccurate by at most of few 19 | seconds. 20 | 21 | ### Supported operating systems 22 | 23 | `eclipse-simulator` is written in C and runs in Linux, MacOS, and other 24 | Unix-like operating systems. A front-end user interface is written in python3. 25 | The simulator does not run under Windows. 26 | 27 | ### License 28 | 29 | This code is distributed under the Gnu General Public License. It is (C) 30 | Dominic Ford 2012 - 2020. 31 | 32 | ### Set up 33 | 34 | Before you start, the simulator needs to download various data from the 35 | internet, including the tool `ephemerisCompute` from the author's GitHub 36 | repository, and the DE430 ephemeris files. 37 | 38 | This can be done with the Python script `solarEclipses.py`. The total download 39 | size will be around 500 MB. These files are only downloaded the first time this 40 | script is run. 41 | 42 | The script will then proceed to run some demo simulations; by default it 43 | simulates the eclipses of 2020 and 2021. Each simulation takes around 45 44 | minutes on a single core of a modern computer (e.g. Intel i7 8700), or around 45 | 120 minutes on a machine dating from around 2010. If your computer has multiple 46 | cores, multiple simulations will run on parallel on the various cores. 47 | 48 | ### Simulating particular eclipses 49 | 50 | `eclipse-simulator` may be used to simulate any solar eclipse within the time 51 | span of the DE430 planetary ephemeris - i.e. 1600 to 2200 AD. To simulate a 52 | particular eclipse, pass the command-line switches `--year-min` and 53 | `--year-max` to the Python script `solarEclipses.py`. For example: 54 | 55 | ``` 56 | ./solarEclipses.py --year-min 2025 --year-max 2030 57 | ``` 58 | 59 | ## Author 60 | 61 | This code was developed by Dominic Ford . It is distributed under the Gnu General Public License V3. 62 | 63 | -------------------------------------------------------------------------------- /dcf_ast.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # dcf_ast.py 3 | # 4 | # The python script in this file makes eclipse simulations. 5 | # 6 | # Copyright (C) 2012-2020 Dominic Ford 7 | # 8 | # This code is free software; you can redistribute it and/or modify it under 9 | # the terms of the GNU General Public License as published by the Free Software 10 | # Foundation; either version 2 of the License, or (at your option) any later 11 | # version. 12 | # 13 | # You should have received a copy of the GNU General Public License along with 14 | # this file; if not, write to the Free Software Foundation, Inc., 51 Franklin 15 | # Street, Fifth Floor, Boston, MA 02110-1301, USA 16 | 17 | # ---------------------------------------------------------------------------- 18 | 19 | # Various astronomical helper functions 20 | 21 | from math import floor, fmod, pi, sin, cos, tan 22 | 23 | # The day of the year on which each month begins 24 | month_day = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 999] 25 | 26 | # The three-letter names of each month of the year 27 | month_name = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] 28 | 29 | # The full names of each month of the year 30 | month_name_full = ["January", "February", "March", "April", "May", "June", 31 | "July", "August", "September", "October", "November", "December"] 32 | 33 | 34 | def julian_day(year, month, day, hour=0, minute=0, sec=0): 35 | """ 36 | Convert a calendar date into a Julian Date. 37 | 38 | :param year: 39 | Integer year number. 40 | 41 | :type year: 42 | int 43 | 44 | :param month: 45 | Integer month number (1-12) 46 | 47 | :type month: 48 | int 49 | 50 | :param day: 51 | Integer day of month (1-31) 52 | 53 | :type day: 54 | int 55 | 56 | :param hour: 57 | Integer hour of day (0-23) 58 | 59 | :type hour: 60 | int 61 | 62 | :param minute: 63 | Integer minutes past the hour (0-59) 64 | 65 | :type minute: 66 | int 67 | 68 | :param sec: 69 | Floating point seconds past minute (0-60) 70 | 71 | :type sec: 72 | float 73 | 74 | :return: 75 | float Julian date 76 | """ 77 | last_julian = 15821209.0 78 | first_gregorian = 15821220.0 79 | req_date = 10000.0 * year + 100 * month + day 80 | 81 | if month <= 2: 82 | month += 12 83 | year -= 1 84 | 85 | if req_date <= last_julian: 86 | b = -2 + floor((year + 4716) / 4) - 1179 # Julian calendar 87 | elif req_date >= first_gregorian: 88 | b = floor(year / 400) - floor(year / 100) + floor(year / 4) # Gregorian calendar 89 | else: 90 | raise IndexError("The requested date never happened") 91 | 92 | jd = 365.0 * year - 679004.0 + 2400000.5 + b + floor(30.6001 * (month + 1)) + day 93 | day_fraction = (int(hour) + int(minute) / 60.0 + sec / 3600.0) / 24.0 94 | return jd + day_fraction 95 | 96 | 97 | def inv_julian_day(jd): 98 | """ 99 | Convert a Julian date into a calendar date. 100 | 101 | :param jd: 102 | Julian date 103 | 104 | :type jd: 105 | float 106 | 107 | :return: 108 | Calendar date 109 | """ 110 | day_fraction = (jd + 0.5) - floor(jd + 0.5) 111 | hour = int(floor(24 * day_fraction)) 112 | minute = int(floor(fmod(1440 * day_fraction, 60))) 113 | sec = fmod(86400 * day_fraction, 60) 114 | 115 | # Number of whole Julian days. b = Number of centuries since the Council of Nicaea. 116 | # c = Julian Day number as if century leap years happened. 117 | a = int(jd + 0.5) 118 | if a < 2361222.0: 119 | c = int(a + 1524) # Julian calendar 120 | else: 121 | b = int((a - 1867216.25) / 36524.25) 122 | c = int(a + b - (b // 4) + 1525) # Gregorian calendar 123 | d = int((c - 122.1) / 365.25) # Number of 365.25 periods, starting the year at the end of February 124 | e_ = int(365 * d + d // 4) # Number of days accounted for by these 125 | f = int((c - e_) / 30.6001) # Number of 30.6001 days periods (a.k.a. months) in remainder 126 | day = int(floor(c - e_ - int(30.6001 * f))) 127 | month = int(floor(f - 1 - 12 * (f >= 14))) 128 | year = int(floor(d - 4715 - int(month >= 3))) 129 | return [year, month, day, hour, minute, sec] 130 | 131 | 132 | def date_string(utc): 133 | """ 134 | Create a human-readable date from a unix time. 135 | 136 | :param utc: 137 | Unix time 138 | 139 | :type utc: 140 | float 141 | 142 | :return: 143 | Human-readable string 144 | """ 145 | jd = jd_from_unix(utc) 146 | x = inv_julian_day(jd) 147 | return "{:02d}/{:02d}/{:04d} {:02d}:{:02d}".format(x[2], x[1], x[0], x[3], x[4]) 148 | 149 | 150 | # Returns a Unix timestamp from a Julian Day number 151 | def unix_from_jd(jd): 152 | """ 153 | Convert a Julian date into a unix time. 154 | 155 | :param jd: 156 | Julian date 157 | 158 | :type jd: 159 | float 160 | 161 | :return: 162 | Float unix time 163 | """ 164 | return 86400.0 * (jd - 2440587.5) 165 | 166 | 167 | def jd_from_unix(utc): 168 | """ 169 | Convert a unix time into a Julian date. 170 | 171 | :param utc: 172 | Unix time 173 | 174 | :type utc: 175 | float 176 | 177 | :return: 178 | Float Julian date 179 | """ 180 | return (utc / 86400.0) + 2440587.5 181 | 182 | 183 | def sidereal_time(utc): 184 | """ 185 | Turns a unix time into a sidereal time (in hours, at Greenwich) 186 | 187 | :param utc: 188 | Unix time 189 | 190 | :type utc: 191 | float 192 | 193 | :return: 194 | float, sidereal time in hours 195 | """ 196 | u = utc 197 | j = 40587.5 + u / 86400.0 # Julian date - 2400000 198 | t = (j - 51545.0) / 36525.0 # Julian century (no centuries since 2000.0) 199 | st = (( 200 | 280.46061837 + 201 | 360.98564736629 * (j - 51545.0) + # See pages 87-88 of Astronomical Algorithms, by Jean Meeus 202 | 0.000387933 * t * t + 203 | t * t * t / 38710000.0 204 | ) % 360) * 12 / 180 205 | return st # sidereal time, in hours. RA at zenith in Greenwich. 206 | 207 | 208 | def ra_dec_from_j2000(ra0, dec0, utc_new): 209 | """ 210 | Convert celestial coordinates from J2000 into a new epoch. See Green's Spherical Astronomy, pp 222-225 211 | 212 | :param ra0: 213 | Right ascension, in hours, J2000 214 | 215 | :type ra0: 216 | float 217 | 218 | :param dec0: 219 | Declination, in degrees, J2000 220 | 221 | :type dec0: 222 | float 223 | 224 | :param utc_new: 225 | Unix time of the epoch we are to transform celestial coordinates into 226 | 227 | :type utc_new: 228 | float 229 | 230 | :return: 231 | List of [RA, Dec] in hours and degrees, new epoch 232 | """ 233 | ra0 *= pi / 12 234 | dec0 *= pi / 180 235 | 236 | u = utc_new 237 | j = 40587.5 + u / 86400.0 # Julian date - 2400000 238 | t = (j - 51545.0) / 36525.0 # Julian century (no centuries since 2000.0) 239 | 240 | deg = pi / 180 241 | m = (1.281232 * t + 0.000388 * t * t) * deg 242 | n = (0.556753 * t + 0.000119 * t * t) * deg 243 | 244 | ra_m = ra0 + 0.5 * (m + n * sin(ra0) * tan(dec0)) 245 | dec_m = dec0 + 0.5 * n * cos(ra_m) 246 | 247 | ra_new = ra0 + m + n * sin(ra_m) * tan(dec_m) 248 | dec_new = dec0 + n * cos(ra_m) 249 | 250 | return [ra_new * 12 / pi, dec_new * 180 / pi] 251 | 252 | 253 | def ra_dec_to_j2000(ra1, dec1, utc_old): 254 | """ 255 | Convert celestial coordinates to J2000 from another epoch. See Green's Spherical Astronomy, pp 222-225 256 | 257 | :param ra1: 258 | Right ascension, in hours, original epoch 259 | 260 | :type ra1: 261 | float 262 | 263 | :param dec1: 264 | Declination, in degrees, original epoch 265 | 266 | :type dec1: 267 | float 268 | 269 | :param utc_old: 270 | Unix time of the epoch we are to transform celestial coordinates from 271 | 272 | :type utc_old: 273 | float 274 | 275 | :return: 276 | List of [RA, Dec] in hours and degrees, J2000 277 | """ 278 | ra1 *= pi / 12 279 | dec1 *= pi / 180 280 | 281 | u = utc_old 282 | j = 40587.5 + u / 86400.0 # Julian date - 2400000 283 | t = (j - 51545.0) / 36525.0 # Julian century (no centuries since 2000.0) 284 | 285 | deg = pi / 180 286 | m = (1.281232 * t + 0.000388 * t * t) * deg 287 | n = (0.556753 * t + 0.000119 * t * t) * deg 288 | 289 | ra_m = ra1 - 0.5 * (m + n * sin(ra1) * tan(dec1)) 290 | dec_m = dec1 - 0.5 * n * cos(ra_m) 291 | 292 | ra_new = ra1 - m - n * sin(ra_m) * tan(dec_m) 293 | dec_new = dec1 - n * cos(ra_m) 294 | 295 | return [ra_new * 12 / pi, dec_new * 180 / pi] 296 | 297 | 298 | def ra_dec_switch_epoch(ra0, dec0, utc_old, utc_new): 299 | """ 300 | Convert celestial coordinates from one epoch into a new epoch. See Green's Spherical Astronomy, pp 222-225 301 | 302 | :param ra0: 303 | Right ascension, in hours, original epoch 304 | 305 | :type ra0: 306 | float 307 | 308 | :param dec0: 309 | Declination, in degrees, original epoch 310 | 311 | :type dec0: 312 | float 313 | 314 | :param utc_old: 315 | Unix time of the epoch we are to transform celestial coordinates from 316 | 317 | :type utc_old: 318 | float 319 | 320 | :param utc_new: 321 | Unix time of the epoch we are to transform celestial coordinates into 322 | 323 | :type utc_new: 324 | float 325 | 326 | :return: 327 | List of [RA, Dec] in hours and degrees, new epoch 328 | """ 329 | [ra_j2000, dec_j2000] = ra_dec_to_j2000(ra0, dec0, utc_old) 330 | return ra_dec_from_j2000(ra_j2000, dec_j2000, utc_new) 331 | 332 | 333 | def ra_dec_j2000_from_b1950(ra0, dec0): 334 | """ 335 | Convert celestial coordinates from B1950 into J2000. 336 | 337 | :param ra0: 338 | Right ascension, in hours, B1950 339 | 340 | :type ra0: 341 | float 342 | 343 | :param dec0: 344 | Declination, in degrees, B1950 345 | 346 | :type dec0: 347 | float 348 | 349 | :return: 350 | List of [RA, Dec] in hours and degrees, J2000 351 | """ 352 | return ra_dec_to_j2000(ra0, dec0, -631158660) 353 | 354 | 355 | def ra_dec_b1950_from_j2000(ra0, dec0): 356 | """ 357 | Convert celestial coordinates from J2000 into B1950. 358 | 359 | :param ra0: 360 | Right ascension, in hours, J2000 361 | 362 | :type ra0: 363 | float 364 | 365 | :param dec0: 366 | Declination, in degrees, J2000 367 | 368 | :type dec0: 369 | float 370 | 371 | :return: 372 | List of [RA, Dec] in hours and degrees, B1950 373 | """ 374 | return ra_dec_from_j2000(ra0, dec0, -631158660) 375 | -------------------------------------------------------------------------------- /solarEclipseRender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Makefile for solar eclipse renderer 2 | # ------------------------------------------------- 3 | # Copyright 2019-2020 Dominic Ford. 4 | 5 | # This file is part of EclipseRender. 6 | 7 | # EclipseRender is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # EclipseRender is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with EclipseRender. If not, see . 19 | # ------------------------------------------------- 20 | 21 | cmake_minimum_required(VERSION 3.6) 22 | project(solarEclipseRender) 23 | 24 | include_directories(src) 25 | 26 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O3 -Wall -std=c99 -DSRCDIR='\"${CMAKE_SOURCE_DIR}/src\"' -DDCFVERSION='\"x\"' -DDEBUG=0 -D MEMDEBUG1=0 -D MEMDEBUG2=0 -D NORISESET=0") 27 | 28 | set(SOURCE_FILES 29 | src/argparse/argparse.c 30 | src/argparse/argparse.h 31 | src/coreUtils/asciiDouble.c 32 | src/coreUtils/asciiDouble.h 33 | src/coreUtils/errorReport.c 34 | src/coreUtils/errorReport.h 35 | src/coreUtils/strConstants.h 36 | src/mathsTools/julianDate.c 37 | src/mathsTools/julianDate.h 38 | src/mathsTools/sphericalAst.c 39 | src/mathsTools/sphericalAst.h 40 | src/constants.h 41 | src/country_lookup.c 42 | src/country_lookup.h 43 | src/duration.c 44 | src/duration.h 45 | src/ephemeris.c 46 | src/ephemeris.h 47 | src/jpeg/jpeg.h 48 | src/jpeg/jpeg_in.c 49 | src/make_binary_map.c 50 | src/make_binary_map.h 51 | src/map_eclipse_contours.c 52 | src/map_eclipse_contours.h 53 | src/map_greatest_eclipse.c 54 | src/map_greatest_eclipse.h 55 | src/png/image.h 56 | src/png/image_in.c 57 | src/projection.c 58 | src/projection.h 59 | src/rendering.c 60 | src/rendering.h 61 | src/render_2d.c 62 | src/render_2d.h 63 | src/render_3d.c 64 | src/render_3d.h 65 | src/settings.c 66 | src/settings.c 67 | src/shadow_calc.c 68 | src/shadow_calc.h 69 | src/main.c) 70 | 71 | add_executable(solarEclipseRender ${SOURCE_FILES}) 72 | 73 | target_link_libraries(solarEclipseRender mysqlclient gsl gslcblas cairo png z jpeg m) 74 | -------------------------------------------------------------------------------- /solarEclipseRender/Makefile: -------------------------------------------------------------------------------- 1 | # Makefile for solar eclipse renderer 2 | # ------------------------------------------------- 3 | # Copyright 2019-2020 Dominic Ford. 4 | 5 | # This file is part of EclipseRender. 6 | 7 | # EclipseRender is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # EclipseRender is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with EclipseRender. If not, see . 19 | # ------------------------------------------------- 20 | 21 | CWD=$(shell pwd) 22 | 23 | VERSION = 2.0 24 | DATE = 15/09/2019 25 | PATHLINK= / 26 | 27 | WARNINGS= -Wall -Wno-format-truncation -Wno-unused-result 28 | COMPILE = $(CC) $(WARNINGS) -g -c -I $(CWD)/src `mysql_config --cflags` 29 | LIBS = -lcairo -lgsl -lgslcblas -lpng -lz -ljpeg -lm 30 | LINK = $(CC) $(WARNINGS) -g 31 | 32 | OPTIMISATION = -O3 -march=native 33 | 34 | DEBUG = -D DEBUG=1 -D MEMDEBUG1=1 -D MEMDEBUG2=0 -D NORISESET=0 35 | NODEBUG = -D DEBUG=0 -D MEMDEBUG1=0 -D MEMDEBUG2=0 -D NORISESET=0 36 | 37 | LOCAL_SRCDIR = src 38 | LOCAL_OBJDIR = obj 39 | LOCAL_BINDIR = bin 40 | 41 | CORE_FILES = argparse/argparse.c coreUtils/asciiDouble.c coreUtils/errorReport.c mathsTools/julianDate.c mathsTools/sphericalAst.c country_lookup.c duration.c ephemeris.c jpeg/jpeg_in.c make_binary_map.c map_eclipse_contours.c map_greatest_eclipse.c png/image_in.c projection.c rendering.c render_2d.c render_3d.c settings.c shadow_calc.c main.c 42 | 43 | CORE_HEADERS = argparse/argparse.h coreUtils/asciiDouble.h coreUtils/errorReport.h coreUtils/strConstants.h mathsTools/julianDate.h mathsTools/sphericalAst.h constants.h country_lookup.h duration.h ephemeris.h jpeg/jpeg.h make_binary_map.h map_eclipse_contours.h map_greatest_eclipse.h png/image.h projection.h rendering.h render_2d.h render_3d.h settings.h shadow_calc.h 44 | 45 | CORE_SOURCES = $(CORE_FILES:%.c=$(LOCAL_SRCDIR)/%.c) 46 | CORE_OBJECTS = $(CORE_FILES:%.c=$(LOCAL_OBJDIR)/%.o) 47 | CORE_OBJECTS_DEBUG = $(CORE_OBJECTS:%.o=%.debug.o) 48 | CORE_HFILES = $(CORE_HEADERS:%.h=$(LOCAL_SRCDIR)/%.h) Makefile 49 | 50 | ALL_HFILES = $(CORE_HFILES) 51 | 52 | SWITCHES = -D DCFVERSION=\"$(VERSION)\" -D DATE=\"$(DATE)\" -D PATHLINK=\"$(PATHLINK)\" -D SRCDIR=\"$(CWD)/$(LOCAL_SRCDIR)/\" 53 | 54 | all: $(LOCAL_BINDIR)/eclipseRender.bin $(LOCAL_BINDIR)/debug/eclipseRender.bin 55 | 56 | # 57 | # General macros for the compile steps 58 | # 59 | 60 | $(LOCAL_OBJDIR)/%.o: $(LOCAL_SRCDIR)/%.c $(ALL_HFILES) 61 | mkdir -p $(LOCAL_OBJDIR) $(LOCAL_OBJDIR)/argparse $(LOCAL_OBJDIR)/coreUtils $(LOCAL_OBJDIR)/jpeg $(LOCAL_OBJDIR)/mathsTools $(LOCAL_OBJDIR)/png 62 | $(COMPILE) $(OPTIMISATION) $(NODEBUG) $(SWITCHES) $< -o $@ 63 | 64 | $(LOCAL_OBJDIR)/%.debug.o: $(LOCAL_SRCDIR)/%.c $(ALL_HFILES) 65 | mkdir -p $(LOCAL_OBJDIR) $(LOCAL_OBJDIR)/argparse $(LOCAL_OBJDIR)/coreUtils $(LOCAL_OBJDIR)/jpeg $(LOCAL_OBJDIR)/mathsTools $(LOCAL_OBJDIR)/png 66 | $(COMPILE) $(OPTIMISATION) $(DEBUG) $(SWITCHES) $< -o $@ 67 | 68 | # 69 | # Make the binaries 70 | # 71 | 72 | $(LOCAL_BINDIR)/eclipseRender.bin: $(CORE_OBJECTS) 73 | mkdir -p $(LOCAL_BINDIR) 74 | $(LINK) $(OPTIMISATION) $(CORE_OBJECTS) $(LIBS) -o $(LOCAL_BINDIR)/eclipseRender.bin 75 | 76 | $(LOCAL_BINDIR)/debug/eclipseRender.bin: $(CORE_OBJECTS_DEBUG) 77 | mkdir -p $(LOCAL_BINDIR)/debug 78 | echo "The files in this directory are binaries with debugging options enabled: they produce activity logs called 'ephem.log'. It should be noted that these binaries can up to ten times slower than non-debugging versions." > $(LOCAL_BINDIR)/debug/README 79 | $(LINK) $(OPTIMISATION) $(CORE_OBJECTS_DEBUG) $(LIBS) -o $(LOCAL_BINDIR)/debug/eclipseRender.bin 80 | 81 | # 82 | # Clean macros 83 | # 84 | 85 | clean: 86 | rm -vfR $(LOCAL_OBJDIR) $(LOCAL_BINDIR) 87 | 88 | afresh: clean all 89 | 90 | -------------------------------------------------------------------------------- /solarEclipseRender/earth_day.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcf21/eclipse-simulator/50b43f724c40c5eb731faf5c9cb1e0c7282ad5f9/solarEclipseRender/earth_day.jpg -------------------------------------------------------------------------------- /solarEclipseRender/earth_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcf21/eclipse-simulator/50b43f724c40c5eb731faf5c9cb1e0c7282ad5f9/solarEclipseRender/earth_night.jpg -------------------------------------------------------------------------------- /solarEclipseRender/prettymake: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkfifo stderr 3 | cat stderr | sed 's/\(.*\)/\1/' & 4 | make $@ 2>stderr | sed 's/\(.*\)/\1/' 5 | rm stderr 6 | 7 | -------------------------------------------------------------------------------- /solarEclipseRender/src/argparse/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2012-2013 Yecheng Fu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /solarEclipseRender/src/argparse/README.md: -------------------------------------------------------------------------------- 1 | # argparse [![Build Status](https://travis-ci.org/cofyc/argparse.png)](https://travis-ci.org/cofyc/argparse) 2 | 3 | argparse - A command line arguments parsing library in C (compatible with C++). 4 | 5 | ## Description 6 | 7 | This module is inspired by parse-options.c (git) and python's argparse 8 | module. 9 | 10 | Arguments parsing is common task in cli program, but traditional `getopt` 11 | libraries are not easy to use. This library provides high-level arguments 12 | parsing solutions. 13 | 14 | The program defines what arguments it requires, and `argparse` will figure 15 | out how to parse those out of `argc` and `argv`, it also automatically 16 | generates help and usage messages and issues errors when users give the 17 | program invalid arguments. 18 | 19 | ## Features 20 | 21 | - handles both optional and positional arguments 22 | - produces highly informative usage messages 23 | - issues errors when given invalid arguments 24 | 25 | There are basically three types of options: 26 | 27 | - boolean options 28 | - options with mandatory argument 29 | - options with optional argument 30 | 31 | There are basically two forms of options: 32 | 33 | - short option consist of one dash (`-`) and one alphanumeric character. 34 | - long option begin with two dashes (`--`) and some alphanumeric characters. 35 | 36 | Short options may be bundled, e.g. `-a -b` can be specified as `-ab`. 37 | 38 | Options are case-sensitive. 39 | 40 | Options and non-option arguments can clearly be separated using the `--` option. 41 | 42 | ## Examples 43 | 44 | ```c 45 | #include "argparse.h" 46 | 47 | static const char *const usage[] = { 48 | "test_argparse [options] [[--] args]", 49 | "test_argparse [options]", 50 | NULL, 51 | }; 52 | 53 | #define PERM_READ (1<<0) 54 | #define PERM_WRITE (1<<1) 55 | #define PERM_EXEC (1<<2) 56 | 57 | int 58 | main(int argc, const char **argv) 59 | { 60 | int force = 0; 61 | int test = 0; 62 | int num = 0; 63 | const char *path = NULL; 64 | int perms = 0; 65 | struct argparse_option options[] = { 66 | OPT_HELP(), 67 | OPT_GROUP("Basic options"), 68 | OPT_BOOLEAN('f', "force", &force, "force to do"), 69 | OPT_BOOLEAN('t', "test", &test, "test only"), 70 | OPT_STRING('p', "path", &path, "path to read"), 71 | OPT_INTEGER('n', "num", &num, "selected num"), 72 | OPT_GROUP("Bits options"), 73 | OPT_BIT(0, "read", &perms, "read perm", NULL, PERM_READ, OPT_NONEG), 74 | OPT_BIT(0, "write", &perms, "write perm", NULL, PERM_WRITE), 75 | OPT_BIT(0, "exec", &perms, "exec perm", NULL, PERM_EXEC), 76 | OPT_END(), 77 | }; 78 | 79 | struct argparse argparse; 80 | argparse_init(&argparse, options, usage, 0); 81 | argparse_describe(&argparse, "\nA brief description of what the program does and how it works.", "\nAdditional description of the program after the description of the arguments."); 82 | argc = argparse_parse(&argparse, argc, argv); 83 | if (force != 0) 84 | printf("force: %d\n", force); 85 | if (test != 0) 86 | printf("test: %d\n", test); 87 | if (path != NULL) 88 | printf("path: %s\n", path); 89 | if (num != 0) 90 | printf("num: %d\n", num); 91 | if (argc != 0) { 92 | printf("argc: %d\n", argc); 93 | int i; 94 | for (i = 0; i < argc; i++) { 95 | printf("argv[%d]: %s\n", i, *(argv + i)); 96 | } 97 | } 98 | if (perms) { 99 | printf("perms: %d\n", perms); 100 | } 101 | return 0; 102 | } 103 | ``` 104 | -------------------------------------------------------------------------------- /solarEclipseRender/src/argparse/argparse.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "argparse.h" 14 | 15 | #define OPT_UNSET 1 16 | #define OPT_LONG (1 << 1) 17 | 18 | static const char * 19 | prefix_skip(const char *str, const char *prefix) { 20 | size_t len = strlen(prefix); 21 | return strncmp(str, prefix, len) ? NULL : str + len; 22 | } 23 | 24 | static int 25 | prefix_cmp(const char *str, const char *prefix) { 26 | for (;; str++, prefix++) 27 | if (!*prefix) { 28 | return 0; 29 | } else if (*str != *prefix) { 30 | return (unsigned char) *prefix - (unsigned char) *str; 31 | } 32 | } 33 | 34 | static void 35 | argparse_error(struct argparse *self, const struct argparse_option *opt, 36 | const char *reason, int flags) { 37 | (void) self; 38 | if (flags & OPT_LONG) { 39 | fprintf(stderr, "error: option `--%s` %s\n", opt->long_name, reason); 40 | } else { 41 | fprintf(stderr, "error: option `-%c` %s\n", opt->short_name, reason); 42 | } 43 | exit(1); 44 | } 45 | 46 | static int 47 | argparse_getvalue(struct argparse *self, const struct argparse_option *opt, 48 | int flags) { 49 | const char *s = NULL; 50 | if (!opt->value) 51 | goto skipped; 52 | switch (opt->type) { 53 | case ARGPARSE_OPT_BOOLEAN: 54 | if (flags & OPT_UNSET) { 55 | *(int *) opt->value = *(int *) opt->value - 1; 56 | } else { 57 | *(int *) opt->value = *(int *) opt->value + 1; 58 | } 59 | if (*(int *) opt->value < 0) { 60 | *(int *) opt->value = 0; 61 | } 62 | break; 63 | case ARGPARSE_OPT_BIT: 64 | if (flags & OPT_UNSET) { 65 | *(int *) opt->value &= ~opt->data; 66 | } else { 67 | *(int *) opt->value |= opt->data; 68 | } 69 | break; 70 | case ARGPARSE_OPT_STRING: 71 | if (self->optvalue) { 72 | *(const char **) opt->value = self->optvalue; 73 | self->optvalue = NULL; 74 | } else if (self->argc > 1) { 75 | self->argc--; 76 | *(const char **) opt->value = *++self->argv; 77 | } else { 78 | argparse_error(self, opt, "requires a value", flags); 79 | } 80 | break; 81 | case ARGPARSE_OPT_INTEGER: 82 | errno = 0; 83 | if (self->optvalue) { 84 | *(int *) opt->value = strtol(self->optvalue, (char **) &s, 0); 85 | self->optvalue = NULL; 86 | } else if (self->argc > 1) { 87 | self->argc--; 88 | *(int *) opt->value = strtol(*++self->argv, (char **) &s, 0); 89 | } else { 90 | argparse_error(self, opt, "requires a value", flags); 91 | } 92 | if (errno) 93 | argparse_error(self, opt, strerror(errno), flags); 94 | if (s[0] != '\0') 95 | argparse_error(self, opt, "expects an integer value", flags); 96 | break; 97 | case ARGPARSE_OPT_FLOAT: 98 | errno = 0; 99 | if (self->optvalue) { 100 | *(double *) opt->value = strtod(self->optvalue, (char **) &s); 101 | self->optvalue = NULL; 102 | } else if (self->argc > 1) { 103 | self->argc--; 104 | *(double *) opt->value = strtod(*++self->argv, (char **) &s); 105 | } else { 106 | argparse_error(self, opt, "requires a value", flags); 107 | } 108 | if (errno) 109 | argparse_error(self, opt, strerror(errno), flags); 110 | if (s[0] != '\0') 111 | argparse_error(self, opt, "expects a numerical value", flags); 112 | break; 113 | default: 114 | assert(0); 115 | } 116 | 117 | skipped: 118 | if (opt->callback) { 119 | return opt->callback(self, opt); 120 | } 121 | 122 | return 0; 123 | } 124 | 125 | static void 126 | argparse_options_check(const struct argparse_option *options) { 127 | for (; options->type != ARGPARSE_OPT_END; options++) { 128 | switch (options->type) { 129 | case ARGPARSE_OPT_END: 130 | case ARGPARSE_OPT_BOOLEAN: 131 | case ARGPARSE_OPT_BIT: 132 | case ARGPARSE_OPT_INTEGER: 133 | case ARGPARSE_OPT_FLOAT: 134 | case ARGPARSE_OPT_STRING: 135 | case ARGPARSE_OPT_GROUP: 136 | continue; 137 | default: 138 | fprintf(stderr, "wrong option type: %d", options->type); 139 | break; 140 | } 141 | } 142 | } 143 | 144 | static int 145 | argparse_short_opt(struct argparse *self, const struct argparse_option *options) { 146 | for (; options->type != ARGPARSE_OPT_END; options++) { 147 | if (options->short_name == *self->optvalue) { 148 | self->optvalue = self->optvalue[1] ? self->optvalue + 1 : NULL; 149 | return argparse_getvalue(self, options, 0); 150 | } 151 | } 152 | return -2; 153 | } 154 | 155 | static int 156 | argparse_long_opt(struct argparse *self, const struct argparse_option *options) { 157 | for (; options->type != ARGPARSE_OPT_END; options++) { 158 | const char *rest; 159 | int opt_flags = 0; 160 | if (!options->long_name) 161 | continue; 162 | 163 | rest = prefix_skip(self->argv[0] + 2, options->long_name); 164 | if (!rest) { 165 | // negation disabled? 166 | if (options->flags & OPT_NONEG) { 167 | continue; 168 | } 169 | // only OPT_BOOLEAN/OPT_BIT supports negation 170 | if (options->type != ARGPARSE_OPT_BOOLEAN && options->type != 171 | ARGPARSE_OPT_BIT) { 172 | continue; 173 | } 174 | 175 | if (prefix_cmp(self->argv[0] + 2, "no-")) { 176 | continue; 177 | } 178 | rest = prefix_skip(self->argv[0] + 2 + 3, options->long_name); 179 | if (!rest) 180 | continue; 181 | opt_flags |= OPT_UNSET; 182 | } 183 | if (*rest) { 184 | if (*rest != '=') 185 | continue; 186 | self->optvalue = rest + 1; 187 | } 188 | return argparse_getvalue(self, options, opt_flags | OPT_LONG); 189 | } 190 | return -2; 191 | } 192 | 193 | int 194 | argparse_init(struct argparse *self, struct argparse_option *options, 195 | const char *const *usages, int flags) { 196 | memset(self, 0, sizeof(*self)); 197 | self->options = options; 198 | self->usages = usages; 199 | self->flags = flags; 200 | self->description = NULL; 201 | self->epilog = NULL; 202 | return 0; 203 | } 204 | 205 | void 206 | argparse_describe(struct argparse *self, const char *description, 207 | const char *epilog) { 208 | self->description = description; 209 | self->epilog = epilog; 210 | } 211 | 212 | int 213 | argparse_parse(struct argparse *self, int argc, const char **argv) { 214 | self->argc = argc - 1; 215 | self->argv = argv + 1; 216 | self->out = argv; 217 | 218 | argparse_options_check(self->options); 219 | 220 | for (; self->argc; self->argc--, self->argv++) { 221 | const char *arg = self->argv[0]; 222 | if (arg[0] != '-' || !arg[1]) { 223 | if (self->flags & ARGPARSE_STOP_AT_NON_OPTION) { 224 | goto end; 225 | } 226 | // if it's not option or is a single char '-', copy verbatim 227 | self->out[self->cpidx++] = self->argv[0]; 228 | continue; 229 | } 230 | // short option 231 | if (arg[1] != '-') { 232 | self->optvalue = arg + 1; 233 | switch (argparse_short_opt(self, self->options)) { 234 | case -1: 235 | break; 236 | case -2: 237 | goto unknown; 238 | } 239 | while (self->optvalue) { 240 | switch (argparse_short_opt(self, self->options)) { 241 | case -1: 242 | break; 243 | case -2: 244 | goto unknown; 245 | } 246 | } 247 | continue; 248 | } 249 | // if '--' presents 250 | if (!arg[2]) { 251 | self->argc--; 252 | self->argv++; 253 | break; 254 | } 255 | // long option 256 | switch (argparse_long_opt(self, self->options)) { 257 | case -1: 258 | break; 259 | case -2: 260 | goto unknown; 261 | } 262 | continue; 263 | 264 | unknown: 265 | fprintf(stderr, "error: unknown option `%s`\n", self->argv[0]); 266 | argparse_usage(self); 267 | exit(1); 268 | } 269 | 270 | end: 271 | memmove(self->out + self->cpidx, self->argv, 272 | self->argc * sizeof(*self->out)); 273 | self->out[self->cpidx + self->argc] = NULL; 274 | 275 | return self->cpidx + self->argc; 276 | } 277 | 278 | void 279 | argparse_usage(struct argparse *self) { 280 | if (self->usages) { 281 | fprintf(stdout, "Usage: %s\n", *self->usages++); 282 | while (*self->usages && **self->usages) 283 | fprintf(stdout, " or: %s\n", *self->usages++); 284 | } else { 285 | fprintf(stdout, "Usage:\n"); 286 | } 287 | 288 | // print description 289 | if (self->description) 290 | fprintf(stdout, "%s\n", self->description); 291 | 292 | fputc('\n', stdout); 293 | 294 | const struct argparse_option *options; 295 | 296 | // figure out best width 297 | size_t usage_opts_width = 0; 298 | size_t len; 299 | options = self->options; 300 | for (; options->type != ARGPARSE_OPT_END; options++) { 301 | len = 0; 302 | if ((options)->short_name) { 303 | len += 2; 304 | } 305 | if ((options)->short_name && (options)->long_name) { 306 | len += 2; // separator ", " 307 | } 308 | if ((options)->long_name) { 309 | len += strlen((options)->long_name) + 2; 310 | } 311 | if (options->type == ARGPARSE_OPT_INTEGER) { 312 | len += strlen("="); 313 | } 314 | if (options->type == ARGPARSE_OPT_FLOAT) { 315 | len += strlen("="); 316 | } else if (options->type == ARGPARSE_OPT_STRING) { 317 | len += strlen("="); 318 | } 319 | len = (len + 3) - ((len + 3) & 3); 320 | if (usage_opts_width < len) { 321 | usage_opts_width = len; 322 | } 323 | } 324 | usage_opts_width += 4; // 4 spaces prefix 325 | 326 | options = self->options; 327 | for (; options->type != ARGPARSE_OPT_END; options++) { 328 | size_t pos = 0; 329 | int pad = 0; 330 | if (options->type == ARGPARSE_OPT_GROUP) { 331 | fputc('\n', stdout); 332 | fprintf(stdout, "%s", options->help); 333 | fputc('\n', stdout); 334 | continue; 335 | } 336 | pos = fprintf(stdout, " "); 337 | if (options->short_name) { 338 | pos += fprintf(stdout, "-%c", options->short_name); 339 | } 340 | if (options->long_name && options->short_name) { 341 | pos += fprintf(stdout, ", "); 342 | } 343 | if (options->long_name) { 344 | pos += fprintf(stdout, "--%s", options->long_name); 345 | } 346 | if (options->type == ARGPARSE_OPT_INTEGER) { 347 | pos += fprintf(stdout, "="); 348 | } else if (options->type == ARGPARSE_OPT_FLOAT) { 349 | pos += fprintf(stdout, "="); 350 | } else if (options->type == ARGPARSE_OPT_STRING) { 351 | pos += fprintf(stdout, "="); 352 | } 353 | if (pos <= usage_opts_width) { 354 | pad = usage_opts_width - pos; 355 | } else { 356 | fputc('\n', stdout); 357 | pad = usage_opts_width; 358 | } 359 | fprintf(stdout, "%*s%s\n", pad + 2, "", options->help); 360 | } 361 | 362 | // print epilog 363 | if (self->epilog) 364 | fprintf(stdout, "%s\n", self->epilog); 365 | } 366 | 367 | int 368 | argparse_help_cb(struct argparse *self, const struct argparse_option *option) { 369 | (void) option; 370 | argparse_usage(self); 371 | exit(0); 372 | } 373 | -------------------------------------------------------------------------------- /solarEclipseRender/src/argparse/argparse.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (C) 2012-2015 Yecheng Fu 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a MIT-style license that can be found 6 | * in the LICENSE file. 7 | */ 8 | #ifndef ARGPARSE_H 9 | #define ARGPARSE_H 10 | 11 | /* For c++ compatibility */ 12 | #ifdef __cplusplus 13 | extern "C" { 14 | #endif 15 | 16 | #include 17 | 18 | struct argparse; 19 | struct argparse_option; 20 | 21 | typedef int argparse_callback(struct argparse *self, 22 | const struct argparse_option *option); 23 | 24 | enum argparse_flag { 25 | ARGPARSE_STOP_AT_NON_OPTION = 1, 26 | }; 27 | 28 | enum argparse_option_type { 29 | /* special */ 30 | ARGPARSE_OPT_END, 31 | ARGPARSE_OPT_GROUP, 32 | /* options with no arguments */ 33 | ARGPARSE_OPT_BOOLEAN, 34 | ARGPARSE_OPT_BIT, 35 | /* options with arguments (optional or required) */ 36 | ARGPARSE_OPT_INTEGER, 37 | ARGPARSE_OPT_FLOAT, 38 | ARGPARSE_OPT_STRING, 39 | }; 40 | 41 | enum argparse_option_flags { 42 | OPT_NONEG = 1, /* disable negation */ 43 | }; 44 | 45 | /** 46 | * argparse option 47 | * 48 | * `type`: 49 | * holds the type of the option, you must have an ARGPARSE_OPT_END last in your 50 | * array. 51 | * 52 | * `short_name`: 53 | * the character to use as a short option name, '\0' if none. 54 | * 55 | * `long_name`: 56 | * the long option name, without the leading dash, NULL if none. 57 | * 58 | * `value`: 59 | * stores pointer to the value to be filled. 60 | * 61 | * `help`: 62 | * the short help message associated to what the option does. 63 | * Must never be NULL (except for ARGPARSE_OPT_END). 64 | * 65 | * `callback`: 66 | * function is called when corresponding argument is parsed. 67 | * 68 | * `data`: 69 | * associated data. Callbacks can use it like they want. 70 | * 71 | * `flags`: 72 | * option flags. 73 | */ 74 | struct argparse_option { 75 | enum argparse_option_type type; 76 | const char short_name; 77 | const char *long_name; 78 | void *value; 79 | const char *help; 80 | argparse_callback *callback; 81 | intptr_t data; 82 | int flags; 83 | }; 84 | 85 | /** 86 | * argpparse 87 | */ 88 | struct argparse { 89 | // user supplied 90 | const struct argparse_option *options; 91 | const char *const *usages; 92 | int flags; 93 | const char *description; // a description after usage 94 | const char *epilog; // a description at the end 95 | // internal context 96 | int argc; 97 | const char **argv; 98 | const char **out; 99 | int cpidx; 100 | const char *optvalue; // current option value 101 | }; 102 | 103 | // built-in callbacks 104 | int argparse_help_cb(struct argparse *self, 105 | const struct argparse_option *option); 106 | 107 | // built-in option macros 108 | #define OPT_END() { ARGPARSE_OPT_END, 0, NULL, NULL, 0, NULL, 0, 0 } 109 | #define OPT_BOOLEAN(...) { ARGPARSE_OPT_BOOLEAN, __VA_ARGS__ } 110 | #define OPT_BIT(...) { ARGPARSE_OPT_BIT, __VA_ARGS__ } 111 | #define OPT_INTEGER(...) { ARGPARSE_OPT_INTEGER, __VA_ARGS__ } 112 | #define OPT_FLOAT(...) { ARGPARSE_OPT_FLOAT, __VA_ARGS__ } 113 | #define OPT_STRING(...) { ARGPARSE_OPT_STRING, __VA_ARGS__ } 114 | #define OPT_GROUP(h) { ARGPARSE_OPT_GROUP, 0, NULL, NULL, h, NULL, 0, 0 } 115 | #define OPT_HELP() OPT_BOOLEAN('h', "help", NULL, \ 116 | "show this help message and exit", \ 117 | argparse_help_cb, 0, OPT_NONEG) 118 | 119 | int argparse_init(struct argparse *self, struct argparse_option *options, 120 | const char *const *usages, int flags); 121 | 122 | void argparse_describe(struct argparse *self, const char *description, 123 | const char *epilog); 124 | 125 | int argparse_parse(struct argparse *self, int argc, const char **argv); 126 | 127 | void argparse_usage(struct argparse *self); 128 | 129 | #ifdef __cplusplus 130 | } 131 | #endif 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /solarEclipseRender/src/constants.h: -------------------------------------------------------------------------------- 1 | // constants.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef CONSTANTS_H 23 | #define CONSTANTS_H 1 24 | 25 | /** 26 | * Physical constants 27 | */ 28 | 29 | #define RADIUS_SUN 695700e3 /* metres */ 30 | #define AU 149597871e3 /* metres */ 31 | 32 | // Values taken from WGS84 33 | // https://en.wikipedia.org/wiki/World_Geodetic_System 34 | #define RADIUS_EARTH_EQUATOR 6378137. /* metres */ 35 | #define RADIUS_EARTH_POLE 6356752.314245 /* metres */ 36 | 37 | // Value taken from http://www.eclipsewise.com/help/de405-predictions.html 38 | #define RADIUS_MOON (0.272281 * RADIUS_EARTH_EQUATOR) /* metres */ 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /solarEclipseRender/src/coreUtils/asciiDouble.h: -------------------------------------------------------------------------------- 1 | // asciiDouble.h 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef ASCIIDOUBLE_H 23 | #define ASCIIDOUBLE_H 1 24 | 25 | #include 26 | 27 | int get_digit(const char c); 28 | 29 | double get_float(const char *str, int *Nchars); 30 | 31 | int valid_float(const char *str, int *end); 32 | 33 | char *numeric_display(double in, int N, int sig_fig, int latex); 34 | 35 | unsigned char double_equal(double a, double b); 36 | 37 | void file_readline(FILE *file, char *output); 38 | 39 | void get_word(char *out, const char *in, int max); 40 | 41 | char *next_word(char *in); 42 | 43 | char *friendly_time_string(); 44 | 45 | char *str_strip(const char *in, char *out); 46 | 47 | char *str_upper(const char *in, char *out); 48 | 49 | char *str_lower(const char *in, char *out); 50 | 51 | char *str_underline(const char *in, char *out); 52 | 53 | char *str_slice(const char *in, char *out, int start, int end); 54 | 55 | char *str_comma_separated_list_scan(char **inscan, char *out); 56 | 57 | int str_cmp_no_case(const char *a, const char *b); 58 | 59 | void readConfig_fetchKey(char *line, char *out); 60 | 61 | void readConfig_fetchValue(char *line, char *out); 62 | 63 | #endif 64 | 65 | -------------------------------------------------------------------------------- /solarEclipseRender/src/coreUtils/errorReport.c: -------------------------------------------------------------------------------- 1 | // errorReport.c 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "asciiDouble.h" 28 | #include "strConstants.h" 29 | 30 | #include "errorReport.h" 31 | 32 | static char temp_stringA[LSTR_LENGTH], temp_stringB[LSTR_LENGTH], temp_stringC[LSTR_LENGTH], temp_stringD[LSTR_LENGTH], temp_stringE[LSTR_LENGTH]; 33 | char temp_err_string[FNAME_LENGTH]; 34 | 35 | //! logging_error - Log an error message 36 | //! \param msg The error message 37 | 38 | void logging_error(char *msg) { 39 | if ((msg != temp_stringA) && (msg != temp_stringB)) { 40 | strcpy(temp_stringA, msg); 41 | msg = temp_stringA; 42 | } 43 | if (DEBUG) { 44 | snprintf(temp_stringC, FNAME_LENGTH, "%s%s", "Error: ", msg); 45 | logging_log(temp_stringC); 46 | } 47 | snprintf(temp_stringC, FNAME_LENGTH, "Error: %s\n", msg); 48 | fputs(temp_stringC, stderr); 49 | } 50 | 51 | //! logging_fatal - Log a fatal error message, and exit with status 1 52 | //! \param msg The error message 53 | 54 | void logging_fatal(char *file, int line, char *msg) { 55 | char introline[FNAME_LENGTH]; 56 | if (msg != temp_stringE) strcpy(temp_stringE, msg); 57 | snprintf(introline, FNAME_LENGTH, "Fatal Error encountered in %s at line %d:", file, line); 58 | logging_error(introline); 59 | logging_error(temp_stringE); 60 | if (DEBUG) logging_log("Terminating with error condition 1."); 61 | exit(1); 62 | } 63 | 64 | //! logging_warning - Log a warning message 65 | //! \param msg The warning message 66 | 67 | void logging_warning(char *msg) { 68 | if (msg != temp_stringA) strcpy(temp_stringA, msg); 69 | if (DEBUG) { 70 | snprintf(temp_stringC, FNAME_LENGTH, "%s%s", "Warning: ", temp_stringA); 71 | logging_log(temp_stringC); 72 | } 73 | snprintf(temp_stringC, FNAME_LENGTH, "Warning: %s\n", temp_stringA); 74 | fputs(temp_stringC, stderr); 75 | } 76 | 77 | //! logging_report - Log a report message 78 | //! \param msg The report message 79 | 80 | void logging_report(char *msg) { 81 | if (msg != temp_stringA) strcpy(temp_stringA, msg); 82 | if (DEBUG) { 83 | snprintf(temp_stringC, FNAME_LENGTH, "%s%s", "Reporting: ", temp_stringA); 84 | logging_log(temp_stringC); 85 | } 86 | snprintf(temp_stringC, FNAME_LENGTH, "%s\n", temp_stringA); 87 | fputs(temp_stringC, stdout); 88 | } 89 | 90 | //! logging_log - Log an information message 91 | //! \param msg The information message 92 | 93 | void logging_log(char *msg) { 94 | static FILE *logfile = NULL; 95 | static int latch = 0; 96 | char linebuffer[LSTR_LENGTH]; 97 | 98 | if (latch) return; // Do not allow recursive calls, which might be generated by the call to logging_fatal below 99 | latch = 1; 100 | if (logfile == NULL) { 101 | if ((logfile = fopen("ephem.log", "w")) == NULL) { 102 | logging_fatal(__FILE__, __LINE__, "Could not open log file to write."); 103 | exit(1); 104 | } 105 | setvbuf(logfile, NULL, _IOLBF, 0); // Set log file to be line-buffered, so that log file is always up-to-date 106 | } 107 | 108 | if (msg != temp_stringD) strcpy(temp_stringD, msg); 109 | fprintf(logfile, "[%s] %s\n", str_strip(friendly_time_string(), linebuffer), temp_stringD); 110 | latch = 0; 111 | } 112 | 113 | void dcffread(void *ptr, size_t size, size_t nmemb, FILE *stream) { 114 | if (fread(ptr, size, nmemb, stream) != nmemb) logging_fatal(__FILE__, __LINE__, "file read fail"); 115 | } 116 | -------------------------------------------------------------------------------- /solarEclipseRender/src/coreUtils/errorReport.h: -------------------------------------------------------------------------------- 1 | // errorReport.h 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | // Functions for returning messages to the user 23 | 24 | #ifndef ERRORREPORT_H 25 | #define ERRORREPORT_H 1 26 | 27 | extern char temp_err_string[]; 28 | 29 | void logging_error(char *msg); 30 | 31 | void logging_fatal(char *file, int line, char *msg); 32 | 33 | void logging_warning(char *msg); 34 | 35 | void logging_report(char *msg); 36 | 37 | void logging_log(char *msg); 38 | 39 | void dcffread(void *ptr, size_t size, size_t nmemb, FILE *stream); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /solarEclipseRender/src/coreUtils/strConstants.h: -------------------------------------------------------------------------------- 1 | // strConstants.h 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef STR_CONSTANTS_H 23 | #define STR_CONSTANTS_H 1 24 | 25 | #define LSTR_LENGTH 32768 26 | #define FNAME_LENGTH 4096 27 | #define SSTR_LENGTH 2048 28 | 29 | #endif 30 | 31 | -------------------------------------------------------------------------------- /solarEclipseRender/src/country_lookup.h: -------------------------------------------------------------------------------- 1 | // country_lookup.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef COUNTRY_LOOKUP_H 23 | #define COUNTRY_LOOKUP_H 1 24 | 25 | #include "settings.h" 26 | #include "shadow_calc.h" 27 | #include "map_greatest_eclipse.h" 28 | 29 | typedef struct countryInfo { 30 | unsigned char red, grn, blu; 31 | double lat, lng; 32 | char name[64]; 33 | 34 | // Fields populated with information about eclipse visibility 35 | double max_eclipse; 36 | double jd_partial_start, jd_total_start, jd_total_end, jd_partial_end; // JD; TT 37 | } countryInfo; 38 | 39 | #define COUNTRYLISTLEN 2100 40 | 41 | typedef struct country_lookup_handle { 42 | countryInfo countryList[COUNTRYLISTLEN]; 43 | int worldMapWidth; 44 | int worldMapHeight; 45 | int *worldMapArray; 46 | } country_lookup_handle; 47 | 48 | country_lookup_handle *country_lookup_init(); 49 | 50 | void country_lookup_free(country_lookup_handle *cl); 51 | 52 | int test_if_land_or_sea(const country_lookup_handle *cl, double longitude, double latitude); 53 | 54 | void country_lookup_max_eclipse(country_lookup_handle *cl, const settings *config, const shadow_map *greatest_shadow, 55 | const eclipse_path_list *eclipse_path); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /solarEclipseRender/src/duration.c: -------------------------------------------------------------------------------- 1 | // duration.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "mathsTools/sphericalAst.h" 28 | 29 | #include "duration.h" 30 | #include "ephemeris.h" 31 | #include "settings.h" 32 | #include "shadow_calc.h" 33 | 34 | /// eclipse_duration - Calculate the duration of an eclipse, as viewed from a particular place on Earth 35 | /// 36 | /// \param config [in] - settings 37 | /// \param ephemeris [in] - an ephemeris computed using 38 | /// \param longitude [in] - The latitude at which to calculate the eclipse's duration (radians) 39 | /// \param latitude [in] - The longitude at which to calculate the eclipse's duration (radians) 40 | /// \param jd_midpoint [in] - The approximate midpoint of the eclipse as viewed from that location (Julian day) 41 | /// \param is_total [in] - Boolean flag indicating whether to calculate the duration of totality or annularity 42 | /// \return Duration, seconds 43 | double eclipse_duration(const settings *config, const ephemeris *ephemeris, 44 | double longitude, double latitude, double jd_midpoint, int is_total) { 45 | // search for 10 minutes before / after maximum eclipse 46 | const double search_span = 600; // seconds 47 | 48 | // start and end points of total / annular eclipse at this location 49 | double jd_eclipse_start = jd_midpoint; 50 | double jd_eclipse_end = jd_midpoint; 51 | 52 | // start and end points for our search process 53 | const double jd_search_start = jd_midpoint - (search_span / 86400); 54 | const double jd_search_end = jd_midpoint + (search_span / 86400); 55 | 56 | // first and last point numbers in the ephemeris structure that we need to analyse 57 | int point_start = (int) ((jd_search_start - ephemeris->jd_start) / ephemeris->jd_step); 58 | int point_end = (int) ((jd_search_end - ephemeris->jd_start) / ephemeris->jd_step); 59 | 60 | // check that search limits are within range 61 | if (point_start < 0) point_start = 0; 62 | if (point_end > ephemeris->point_count) point_end = ephemeris->point_count; 63 | 64 | // loop over the ephemeris points that we are to analyse 65 | for (int j = point_start; j < point_end; j++) { 66 | const double jd = ephemeris->jd_start + ephemeris->jd_step * j; 67 | const ephemeris_point p = ephemeris->data[j]; 68 | 69 | // Calculate the latitude and longitude on Earth where the Sun is overhead 70 | double sidereal_time, lat_sun, lng_sun; // all in radians 71 | calculate_where_sun_overhead(&lat_sun, &lng_sun, &sidereal_time, p.sun_pos, p.earth_pos, jd); 72 | 73 | // Shadow fraction is zero if the Sun is below the horizon 74 | const double sun_ang_dist = angDist_RADec(longitude, latitude, lng_sun, lat_sun); 75 | if (sun_ang_dist >= M_PI / 2) continue; 76 | 77 | if (is_total) { 78 | // For total eclipses, the duration of the eclipse is defined by when the shadow fraction is 100% 79 | const double shadow_fraction = getShadowFraction(latitude * 180 / M_PI, longitude * 180 / M_PI, jd, 1, 80 | p.sun_pos, p.moon_pos, p.earth_pos, 81 | sidereal_time); 82 | 83 | if (shadow_fraction == 1) { 84 | if (jd < jd_eclipse_start) jd_eclipse_start = jd; 85 | if (jd > jd_eclipse_end) jd_eclipse_end = jd; 86 | } 87 | } else { 88 | // For annular eclipses, look at whether the Moon is contained within the Sun's disk 89 | const int is_annular = testIfAnnularEclipse(latitude * 180 / M_PI, longitude * 180 / M_PI, jd, 1, 90 | p.sun_pos, p.moon_pos, p.earth_pos, 91 | sidereal_time); 92 | 93 | if (is_annular) { 94 | if (jd < jd_eclipse_start) jd_eclipse_start = jd; 95 | if (jd > jd_eclipse_end) jd_eclipse_end = jd; 96 | } 97 | } 98 | } 99 | return (jd_eclipse_end - jd_eclipse_start) * 86400; // seconds 100 | } 101 | -------------------------------------------------------------------------------- /solarEclipseRender/src/duration.h: -------------------------------------------------------------------------------- 1 | // duration.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef DURATION_H 23 | #define DURATION_H 24 | 25 | #include "ephemeris.h" 26 | #include "settings.h" 27 | 28 | double eclipse_duration(const settings *config, const ephemeris *ephemeris, 29 | double longitude, double latitude, double jd_midpoint, int is_total); 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /solarEclipseRender/src/ephemeris.c: -------------------------------------------------------------------------------- 1 | // ephemeris.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | 25 | #include "coreUtils/strConstants.h" 26 | #include "coreUtils/errorReport.h" 27 | 28 | #include "ephemeris.h" 29 | #include "settings.h" 30 | 31 | void fetch_ephemeris(const settings *config, ephemeris **output) { 32 | 33 | // Path to ephemeris calculator 34 | char ephemeris_compute_path[FNAME_LENGTH]; 35 | 36 | sprintf(ephemeris_compute_path, 37 | "%s/../../ephemeris-compute-de430/bin/serial/ephem.bin", 38 | SRCDIR); 39 | 40 | // Compute data points at this interval (measured in days) 41 | const double jd_step = config->time_resolution / 86400.; 42 | 43 | // Data structure to store ephemeris for Sun, Earth and Moon 44 | ephemeris *x = (ephemeris *) malloc(sizeof(ephemeris)); 45 | 46 | // Generous estimate of how many lines we expect ephemerisCompute to return 47 | const int point_count_max = (int) (2 + (config->jd_max - config->jd_min) / jd_step); 48 | 49 | // Allocate data to hold the ephemeris 50 | x->data = (ephemeris_point *) malloc(point_count_max * sizeof(ephemeris_point)); 51 | 52 | // Use ephemerisCompute to track the path of this object 53 | char ephemeris_compute_command[FNAME_LENGTH]; 54 | 55 | snprintf(ephemeris_compute_command, FNAME_LENGTH, "%.2048s " 56 | "--jd_min %.14f " 57 | "--jd_max %.14f " 58 | "--jd_step %.14f " 59 | "--output_format 0 " 60 | "--output_constellations 0 " 61 | "--output_binary 1 " 62 | "--objects \"sun,moon,earth\" ", 63 | ephemeris_compute_path, 64 | config->jd_min, config->jd_max, jd_step); 65 | 66 | FILE *ephemeris_data = popen(ephemeris_compute_command, "r"); 67 | // printf("%s\n", ephemeris_compute_command); 68 | 69 | // Loop over the lines returned by ephemerisCompute 70 | int line_counter = 0; 71 | while ((!feof(ephemeris_data)) && (!ferror(ephemeris_data))) { 72 | int items_fetched; 73 | 74 | // Read columns of data 75 | items_fetched = (int)fread(&x->data[line_counter].sun_pos, sizeof(double), 3, ephemeris_data); 76 | if (items_fetched == 0) break; 77 | if (items_fetched < 3) logging_fatal(__FILE__, __LINE__, "Too few items"); 78 | items_fetched = (int)fread(&x->data[line_counter].moon_pos, sizeof(double), 3, ephemeris_data); 79 | if (items_fetched < 3) logging_fatal(__FILE__, __LINE__, "Too few items"); 80 | items_fetched = (int)fread(&x->data[line_counter].earth_pos, sizeof(double), 3, ephemeris_data); 81 | if (items_fetched < 3) logging_fatal(__FILE__, __LINE__, "Too few items"); 82 | 83 | // Increment data point counter 84 | line_counter++; 85 | } 86 | 87 | // Throw an error if we got no data 88 | if (line_counter == 0) { 89 | logging_fatal(__FILE__, __LINE__, "ephemerisCompute returned no data"); 90 | exit(1); 91 | } 92 | 93 | // Record how many lines of data were returned 94 | x->point_count = line_counter; 95 | x->jd_start = config->jd_min; 96 | x->jd_end = config->jd_max; 97 | x->jd_step = jd_step; 98 | 99 | if (DEBUG) { 100 | sprintf(temp_err_string, "Read list of %d ephemeris points from ephemeris.", line_counter); 101 | logging_log(temp_err_string); 102 | } 103 | 104 | // Return output 105 | *output = x; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /solarEclipseRender/src/ephemeris.h: -------------------------------------------------------------------------------- 1 | // ephemeris.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef EPHEMERIS_H 23 | #define EPHEMERIS_H 1 24 | 25 | #include "settings.h" 26 | 27 | //! A structure defining a point in an ephemeris 28 | typedef struct ephemeris_point { 29 | double sun_pos[3]; 30 | double earth_pos[3]; 31 | double moon_pos[3]; 32 | } ephemeris_point; 33 | 34 | //! A structure defining an ephemeris of a solar system object 35 | typedef struct ephemeris { 36 | double jd_start, jd_end, jd_step; // Julian day numbers; TT 37 | int point_count; 38 | ephemeris_point *data; 39 | } ephemeris; 40 | 41 | 42 | void fetch_ephemeris(const settings *config, ephemeris **output); 43 | 44 | #endif 45 | 46 | -------------------------------------------------------------------------------- /solarEclipseRender/src/jpeg/jpeg.h: -------------------------------------------------------------------------------- 1 | // jpeg.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef JPEG_H 23 | #define JPEG_H 1 24 | 25 | /* Variable format used to store images */ 26 | 27 | typedef struct { 28 | int xsize; 29 | int ysize; 30 | double *data_red; 31 | double *data_grn; 32 | double *data_blu; 33 | double *data_w; 34 | } jpeg_ptr; 35 | 36 | /* Functions defined in jpeg_in.c */ 37 | void jpeg_alloc(jpeg_ptr *out, int x, int y); 38 | 39 | void jpeg_dealloc(jpeg_ptr *in); 40 | 41 | void jpeg_cp(jpeg_ptr *in, jpeg_ptr *out); 42 | 43 | void jpeg_deweight(jpeg_ptr *out); 44 | 45 | jpeg_ptr jpeg_get(const char *filename); 46 | 47 | /* Functions defined in jpeg_out.c */ 48 | int jpeg_put(const char *filename, jpeg_ptr image); 49 | 50 | #endif 51 | 52 | -------------------------------------------------------------------------------- /solarEclipseRender/src/jpeg/jpeg_in.c: -------------------------------------------------------------------------------- 1 | // jpeg_in.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "jpeg.h" 29 | 30 | //! jpeg_alloc - allocate an jpeg_ptr structure to hold an image of dimensions (x, y) 31 | //! \param [out] out The jpeg_ptr structure to populate 32 | //! \param [in] x The horizontal size of the image 33 | //! \param [in] y The vertical size of the image 34 | 35 | void jpeg_alloc(jpeg_ptr *out, int x, int y) { 36 | int i, j = x * y; 37 | 38 | out->xsize = x; 39 | out->ysize = y; 40 | out->data_red = (double *) malloc(x * y * sizeof(double)); 41 | out->data_grn = (double *) malloc(x * y * sizeof(double)); 42 | out->data_blu = (double *) malloc(x * y * sizeof(double)); 43 | out->data_w = (double *) malloc(x * y * sizeof(double)); 44 | for (i = 0; i < j; i++) out->data_red[i] = 0.0; 45 | for (i = 0; i < j; i++) out->data_grn[i] = 0.0; 46 | for (i = 0; i < j; i++) out->data_blu[i] = 0.0; 47 | for (i = 0; i < j; i++) out->data_w[i] = 0.0; 48 | } 49 | 50 | //! jpeg_dealloc - free the storage associated with an jpeg_ptr structure 51 | //! \param [in] in The jpeg_ptr structure to free 52 | 53 | void jpeg_dealloc(jpeg_ptr *in) { 54 | if (in->data_red != NULL) free(in->data_red); 55 | if (in->data_grn != NULL) free(in->data_grn); 56 | if (in->data_blu != NULL) free(in->data_blu); 57 | if (in->data_w != NULL) free(in->data_w); 58 | } 59 | 60 | //! jpeg_cp - copy the image contained within an jpeg_ptr structure 61 | //! \param [in] in The image to copy 62 | //! \param [out] out The jpeg_ptr structure to populate with the copied image 63 | 64 | void jpeg_cp(jpeg_ptr *in, jpeg_ptr *out) { 65 | jpeg_alloc(out, in->xsize, in->ysize); 66 | memcpy(out->data_red, in->data_red, in->xsize * in->ysize * sizeof(double)); 67 | memcpy(out->data_grn, in->data_grn, in->xsize * in->ysize * sizeof(double)); 68 | memcpy(out->data_blu, in->data_blu, in->xsize * in->ysize * sizeof(double)); 69 | memcpy(out->data_w, in->data_w, in->xsize * in->ysize * sizeof(double)); 70 | } 71 | 72 | //! jpeg_deweight - Divide the pixel data in an image by the weight field. This is useful if N images have been 73 | //! co-added into the pixel data, and the structure member contains the number of images which have been 74 | //! added together. The resulting image is then properly normalised. 75 | //! \param out The image to deweight 76 | 77 | void jpeg_deweight(jpeg_ptr *out) { 78 | int i, j = out->xsize * out->ysize; 79 | for (i = 0; i < j; i++) { 80 | out->data_red[i] /= out->data_w[i]; 81 | if (!gsl_finite(out->data_red[i])) out->data_red[i] = 0.0; 82 | } 83 | for (i = 0; i < j; i++) { 84 | out->data_grn[i] /= out->data_w[i]; 85 | if (!gsl_finite(out->data_grn[i])) out->data_grn[i] = 0.0; 86 | } 87 | for (i = 0; i < j; i++) { 88 | out->data_blu[i] /= out->data_w[i]; 89 | if (!gsl_finite(out->data_blu[i])) out->data_blu[i] = 0.0; 90 | } 91 | } 92 | 93 | //! jpeg_get - Read a JPEG image from disk and convert it into an jpeg_ptr structure 94 | //! \param [in] filename The filename of the JPEG image to read 95 | //! \return jpeg_ptr structure containing the pixel data 96 | 97 | jpeg_ptr jpeg_get(const char *filename) { 98 | jpeg_ptr output; 99 | output.xsize = output.ysize = -1; 100 | output.data_red = output.data_grn = output.data_blu = output.data_w = NULL; 101 | JSAMPARRAY buffer; /* Output row buffer */ 102 | unsigned char *buffer_scan; 103 | double *red_scan, *grn_scan, *blu_scan; 104 | int x; 105 | 106 | FILE *infile; 107 | 108 | if ((infile = fopen(filename, "rb")) == NULL) { 109 | fprintf(stderr, "ERROR: Cannot open input file %s\n", filename); 110 | return output; 111 | } 112 | 113 | /* This struct contains the JPEG compression parameters and pointers to 114 | * working space (which is allocated as needed by the JPEG library). 115 | * It is possible to have several such structures, representing multiple 116 | * compression/decompression processes, in existence at once. We refer 117 | * to any one struct (and its associated working data) as a "JPEG object". 118 | */ 119 | struct jpeg_decompress_struct cinfo; 120 | /* This struct represents a JPEG error handler. It is declared separately 121 | * because applications often want to supply a specialized error handler 122 | * (see the second half of this file for an example). But here we just 123 | * take the easy way out and use the standard error handler, which will 124 | * print a message on stderr and call exit() if compression fails. 125 | * Note that this struct must live as long as the main JPEG parameter 126 | * struct, to avoid dangling-pointer problems. 127 | */ 128 | struct jpeg_error_mgr jerr; 129 | /* More stuff */ 130 | int row_stride; /* physical row width in image buffer */ 131 | 132 | /* Step 1: allocate and initialize JPEG compression object */ 133 | 134 | /* We have to set up the error handler first, in case the initialization 135 | * step fails. (Unlikely, but it could happen if you are out of memory.) 136 | * This routine fills in the contents of struct jerr, and returns jerr's 137 | * address which we place into the link field in cinfo. 138 | */ 139 | cinfo.err = jpeg_std_error(&jerr); 140 | /* Now we can initialize the JPEG compression object. */ 141 | jpeg_create_decompress(&cinfo); 142 | jpeg_stdio_src(&cinfo, infile); 143 | (void) jpeg_read_header(&cinfo, TRUE); 144 | (void) jpeg_start_decompress(&cinfo); 145 | row_stride = cinfo.output_width * cinfo.output_components; 146 | 147 | buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); 148 | 149 | output.xsize = cinfo.output_width; 150 | output.ysize = cinfo.output_height; 151 | red_scan = output.data_red = (double *) (malloc(output.xsize * output.ysize * sizeof(double))); 152 | grn_scan = output.data_grn = (double *) (malloc(output.xsize * output.ysize * sizeof(double))); 153 | blu_scan = output.data_blu = (double *) (malloc(output.xsize * output.ysize * sizeof(double))); 154 | 155 | while (cinfo.output_scanline < cinfo.output_height) { 156 | (void) jpeg_read_scanlines(&cinfo, buffer, 1); 157 | 158 | buffer_scan = buffer[0]; 159 | for (x = 0; x < output.xsize; x++) { 160 | *(red_scan++) = (double) *(buffer_scan++); 161 | *(grn_scan++) = (double) *(buffer_scan++); 162 | *(blu_scan++) = (double) *(buffer_scan++); 163 | } 164 | } 165 | 166 | (void) jpeg_finish_decompress(&cinfo); 167 | jpeg_destroy_decompress(&cinfo); 168 | fclose(infile); 169 | return (output); 170 | } 171 | 172 | -------------------------------------------------------------------------------- /solarEclipseRender/src/main.c: -------------------------------------------------------------------------------- 1 | // main.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "argparse/argparse.h" 33 | 34 | #include "coreUtils/asciiDouble.h" 35 | #include "coreUtils/strConstants.h" 36 | #include "coreUtils/errorReport.h" 37 | 38 | #include "country_lookup.h" 39 | #include "ephemeris.h" 40 | #include "jpeg/jpeg.h" 41 | #include "make_binary_map.h" 42 | #include "map_greatest_eclipse.h" 43 | #include "map_eclipse_contours.h" 44 | #include "render_2d.h" 45 | #include "render_3d.h" 46 | #include "settings.h" 47 | #include "shadow_calc.h" 48 | 49 | /** 50 | * usage - Command-line usage instructions for when user supplies the --help switch 51 | */ 52 | static const char *const usage[] = { 53 | "ephem.bin [options] [[--] args]", 54 | "ephem.bin [options]", 55 | NULL, 56 | }; 57 | 58 | int main(int argc, const char **argv) { 59 | int j; 60 | jpeg_ptr earthDay, earthNight; 61 | 62 | // Initialise sub-modules 63 | if (DEBUG) logging_log("Initialising eclipse renderer."); 64 | 65 | // Turn off GSL's automatic error handler 66 | gsl_set_error_handler_off(); 67 | 68 | // Default options 69 | settings config = default_settings(); 70 | 71 | // Scan commandline options for any switches 72 | struct argparse_option options[] = { 73 | OPT_HELP(), 74 | OPT_GROUP("Basic options"), 75 | OPT_FLOAT('a', "jd_min", &config.jd_min, 76 | "The Julian day number at which the ephemeris should begin; TT"), 77 | OPT_FLOAT('b', "jd_max", &config.jd_max, 78 | "The Julian day number at which the ephemeris should end; TT"), 79 | OPT_STRING('t', "title", &config.title, 80 | "The title of this solar eclipse event."), 81 | OPT_STRING('o', "output", &config.output_dir, 82 | "The directory to store output in."), 83 | 84 | OPT_END(), 85 | }; 86 | 87 | struct argparse argparse; 88 | argparse_init(&argparse, options, usage, 0); 89 | argparse_describe(&argparse, 90 | "\nEclipse Renderer", 91 | "\n"); 92 | argc = argparse_parse(&argparse, argc, argv); 93 | 94 | if (argc != 0) { 95 | int i; 96 | for (i = 0; i < argc; i++) { 97 | printf("Error: unparsed argument <%s>\n", *(argv + i)); 98 | } 99 | logging_fatal(__FILE__, __LINE__, "Unparsed arguments"); 100 | } 101 | 102 | // Create output directory 103 | char cmd[FNAME_LENGTH]; 104 | sprintf(cmd, "mkdir -p %s", config.output_dir); 105 | system(cmd); 106 | 107 | // Create ephemeris for the Sun. Earth and Moon 108 | ephemeris *ephemeris; 109 | fetch_ephemeris(&config, &ephemeris); 110 | 111 | // Calculate the latitude and longitude on Earth where the Sun is overhead at the midpoint of the eclipse 112 | { 113 | const int ephemeris_midpoint = ephemeris->point_count / 2; 114 | const double jd_midpoint = ephemeris->jd_start + ephemeris_midpoint * ephemeris->jd_step; 115 | const ephemeris_point *p = &ephemeris->data[ephemeris_midpoint]; 116 | double sidereal_time, lat_sun, lng_sun; 117 | calculate_where_sun_overhead(&lat_sun, &lng_sun, &sidereal_time, p->sun_pos, p->earth_pos, jd_midpoint); 118 | config.solar_longitude_at_midpoint = lng_sun * 180 / M_PI; // degrees 119 | } 120 | 121 | // Make data structure to store time lapse maps of eclipse magnitude across the world 122 | const int binary_snapshot_count = ceil((config.jd_max - config.jd_min) / 123 | (config.binary_map_time_resolution / 86400)); 124 | unsigned char *binary_eclipse_maps = calloc( 125 | (int) (360 * config.binary_map_angular_resolution) * 126 | (int) (180 * config.binary_map_angular_resolution) * 127 | binary_snapshot_count, 1); 128 | 129 | // Initialise country lookup 130 | country_lookup_handle *cl = country_lookup_init(); 131 | 132 | // Read Earth images 133 | if (DEBUG) { logging_log("Reading Earth images."); } 134 | earthDay = jpeg_get(SRCDIR "/../earth_day.jpg"); 135 | earthNight = jpeg_get(SRCDIR "/../earth_night.jpg"); 136 | if ((earthDay.xsize <= 0) || (earthNight.xsize <= 0)) { 137 | logging_fatal(__FILE__, __LINE__, "Could not open Earth images."); 138 | exit(1); 139 | } 140 | 141 | // Array for storing the greatest fraction of shadow at every point on flat map 142 | shadow_map *greatest_shadow = allocate_shadow_map(config.x_size_2d, config.y_size_2d); 143 | time_span timeSpan = {0, 0, 0, 0, 0, 0, 0}; 144 | 145 | // Work out how many ephemeris steps between each video frame 146 | const int video_ephemeris_stride = (int) round(config.video_time_resolution / config.time_resolution); 147 | 148 | // Select the middle frame of the video to use as a poster image 149 | config.poster_image_frame = ephemeris->point_count / video_ephemeris_stride / 2; 150 | 151 | // Work out the path of greatest eclipse across the world 152 | eclipse_path_list *paths = map_greatest_eclipse(&config, ephemeris, &timeSpan); 153 | // exit(0); 154 | 155 | // Loop over video frames 156 | config.frame_counter = -1; 157 | for (j = 0; j < ephemeris->point_count; j += video_ephemeris_stride) { 158 | config.frame_counter++; 159 | 160 | // Read ephemeris 161 | const double JD = ephemeris->jd_start + ephemeris->jd_step * j; 162 | const double *pos_sun = ephemeris->data[j].sun_pos; 163 | const double *pos_earth = ephemeris->data[j].earth_pos; 164 | const double *pos_moon = ephemeris->data[j].moon_pos; 165 | 166 | shadow_map *shadow_map_2d = calculate_eclipse_map_2d(&config, JD, pos_sun, pos_earth, pos_moon, &timeSpan, 167 | greatest_shadow); 168 | render_2d_eclipse_map(&config, JD, earthDay, earthNight, shadow_map_2d, paths); 169 | update_binary_map(&config, binary_eclipse_maps, shadow_map_2d); 170 | shadow_map_free(shadow_map_2d); 171 | 172 | shadow_map *shadow_map_3d = calculate_eclipse_map_3d(&config, JD, pos_sun, pos_earth, pos_moon); 173 | render_3d_eclipse_map(&config, JD, earthDay, pos_sun, pos_earth, shadow_map_3d, paths); 174 | shadow_map_free(shadow_map_3d); 175 | } 176 | 177 | // Make a small teaser image showing where the eclipse is visible 178 | render_2d_eclipse_icon(cl, &config, greatest_shadow); 179 | 180 | // Compute contours of constant eclipse magnitude across the world 181 | contour_line_list *contours = map_eclipse_contours(&config, ephemeris, paths, &timeSpan); 182 | 183 | // Make images mapping the greatest magnitude of the eclipse across a 2D map of the world 184 | render_2d_maximum_extent(cl, &config, contours, greatest_shadow, paths, "png"); 185 | render_2d_maximum_extent(cl, &config, contours, greatest_shadow, paths, "svg"); 186 | render_2d_maximum_extent(cl, &config, contours, greatest_shadow, paths, "pdf"); 187 | 188 | // Work out the maximum extent of the eclipse by country 189 | country_lookup_max_eclipse(cl, &config, greatest_shadow, paths); 190 | 191 | // Output a binary file containing a series of snapshots of the eclipse's magnitude across the world 192 | output_binary_map(&config, ephemeris, binary_eclipse_maps); 193 | 194 | // Return times when the eclipse begins and ends, in partial and total phases 195 | // All times written as Julian day numbers, in UT 196 | const double delta_t_days = delta_t(timeSpan.partial_start) / 86400; 197 | printf("%.10f %.10f %.10f %.10f %.10f %.10f %.10f\n", 198 | timeSpan.partial_start - delta_t_days, 199 | timeSpan.partial_end - delta_t_days, 200 | timeSpan.total_start - delta_t_days, 201 | timeSpan.total_end - delta_t_days, 202 | timeSpan.greatest_eclipse_magnitude, 203 | timeSpan.greatest_eclipse_latitude, 204 | timeSpan.greatest_eclipse_longitude 205 | ); 206 | 207 | // Clean up and exit 208 | if (DEBUG) logging_log("Freeing data structures."); 209 | shadow_map_free(greatest_shadow); 210 | free(contours); 211 | country_lookup_free(cl); 212 | jpeg_dealloc(&earthDay); 213 | jpeg_dealloc(&earthNight); 214 | if (DEBUG) logging_log("Terminating normally."); 215 | return 0; 216 | } 217 | -------------------------------------------------------------------------------- /solarEclipseRender/src/make_binary_map.c: -------------------------------------------------------------------------------- 1 | // make_binary_map.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "coreUtils/strConstants.h" 29 | 30 | #include "ephemeris.h" 31 | #include "make_binary_map.h" 32 | #include "settings.h" 33 | #include "shadow_calc.h" 34 | 35 | // We make a binary file containing time-lapse maps of the eclipse magnitude across the world. This is used to show 36 | // visitors the progress of the eclipse at a series of time points at their location. These binary maps are at a 37 | // lower time and angular resolution than the videos. 38 | 39 | /** 40 | * update_binary_map - This is called for each video frame we generate, to see if we should store a binary snapshot 41 | * of the eclipse's magnitude across the world at this point in time. 42 | * @param config [in] - The settings for this eclipse simulation, including, e.g. the output image size 43 | * @param eclipse_maps [out] - The data structure in which we store the binary snapshots 44 | * @param shadow_map [in] - A binary map of the eclipse magnitude across the world 45 | */ 46 | void update_binary_map(const settings *config, unsigned char *eclipse_maps, const shadow_map *shadow_map) { 47 | int x, y; 48 | 49 | // Calculate how many video frames lie between subsequent binary snapshots 50 | const int binary_map_video_frame_stride = (int) round(config->binary_map_time_resolution / 51 | config->video_time_resolution); 52 | 53 | // Do not proceed if this frame is not to be made into a binary snapshot 54 | if ((config->frame_counter % binary_map_video_frame_stride) != 0) return; 55 | 56 | // Update eclipse profile data structure 57 | 58 | // Number of pixels along the longitude direction of each binary snapshot 59 | const int stride0 = (int) (360 * config->binary_map_angular_resolution); 60 | 61 | // Number of pixels along the latitude direction of each binary snapshot 62 | const int stride1 = (int) (180 * config->binary_map_angular_resolution); 63 | 64 | // The frame number of this binary snapshot 65 | const int k = config->frame_counter / binary_map_video_frame_stride; 66 | 67 | // Loop over all the pixels in this binary snapshot 68 | for (y = 0; y < stride1; y++) 69 | for (x = 0; x < stride0; x++) { 70 | // Work out offset of this pixel in the output structure 71 | const int p0 = (int) (((double) x) / stride0 * config->x_size_2d); 72 | const int p1 = (int) (((double) y) / stride1 * config->y_size_2d); 73 | 74 | // Set pixel value 75 | const int offset = p0 + p1 * config->x_size_2d; 76 | const double shadow_fraction = gsl_max(0, shadow_map->map[offset]); // may be -1 when in Earth shadow 77 | eclipse_maps[x + stride0 * (y + stride1 * k)] = (int) (100 * shadow_fraction); 78 | } 79 | } 80 | 81 | /** 82 | * output_binary_map - Having finished compiling an array of binary snapshots, now write it to disk. 83 | * @param config [in] - The settings for this eclipse simulation, including, e.g. the output image size 84 | * @param ephemeris [in] - The ephemeris used to construct this eclipse simulation 85 | * @param eclipse_maps [in] - The data structure in which we store the binary snapshots 86 | */ 87 | void output_binary_map(const settings *config, const ephemeris *ephemeris, unsigned char *eclipse_maps) { 88 | int x, y, j; 89 | 90 | // Output eclipse profile binary file 91 | // Binary file commences with array of [lat][lng] with file position pointers for where data starts for each position on the globe 92 | // At this file position is a null-terminated structure of the form [int] [char] [char] [char] ... 93 | // double is the unix time at which data begins. chars are eclipse extent (percent) at 10 minute intervals 94 | // when extent reaches zero, the track stops 95 | 96 | // Number of pixels along the longitude direction of each binary snapshot 97 | const int stride0 = (int) (360 * config->binary_map_angular_resolution); 98 | 99 | // Number of pixels along the latitude direction of each binary snapshot 100 | const int stride1 = (int) (180 * config->binary_map_angular_resolution); 101 | 102 | // How many binary snapshots have we created? 103 | const int binary_snapshot_count = ceil((config->jd_max - config->jd_min) / 104 | (config->binary_map_time_resolution / 86400)); 105 | 106 | // Reprocess the data we have collected into a compacted data structure 107 | unsigned char *binary_eclipse_maps_2 = calloc(stride0 * stride1 * (binary_snapshot_count + 4), 1); 108 | 109 | // How many samples are there in the input ephemeris between successive binary snapshots 110 | const int binary_map_ephemeris_stride = (int) round(config->binary_map_time_resolution / 111 | config->time_resolution); 112 | 113 | const int jmax = ephemeris->point_count / binary_map_ephemeris_stride; 114 | int *ptrArray = (int *) binary_eclipse_maps_2; 115 | int ptr = stride0 * stride1 * sizeof(int); 116 | for (y = 0; y < stride1; y++) 117 | for (x = 0; x < stride0; x++) { 118 | int state = 0; 119 | for (j = 0; j < jmax; j++) { 120 | int c = x + stride0 * (y + stride1 * j); 121 | unsigned char f = eclipse_maps[c]; 122 | if ((!state) && f) { 123 | ptrArray[x + y * stride0] = ptr; 124 | 125 | const int offset = j * binary_map_ephemeris_stride; 126 | 127 | // Julian date; TT 128 | const double JD = ephemeris->jd_start + ephemeris->jd_step * offset; 129 | 130 | // Unix time; UT 131 | const double utc = 86400.0 * (JD - 2440587.5) - delta_t(JD); 132 | 133 | *((double *) (binary_eclipse_maps_2 + ptr)) = utc; 134 | ptr += sizeof(double); 135 | state = 1; 136 | } else if (!state) continue; 137 | binary_eclipse_maps_2[ptr++] = f; 138 | if (state && (!f)) { break; } // End of eclipse has been reached 139 | } 140 | } 141 | 142 | // Write out binary snapshot data 143 | char fname[FNAME_LENGTH]; 144 | sprintf(fname, "%s/maximumEclipse.dat", config->output_dir); 145 | FILE *f = fopen(fname, "wb"); 146 | fwrite((void *) binary_eclipse_maps_2, 1, ptr, f); 147 | fclose(f); 148 | } -------------------------------------------------------------------------------- /solarEclipseRender/src/make_binary_map.h: -------------------------------------------------------------------------------- 1 | // make_binary_map.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef MAKE_BINARY_MAP_H 23 | #define MAKE_BINARY_MAP_H 1 24 | 25 | #include "ephemeris.h" 26 | #include "settings.h" 27 | #include "shadow_calc.h" 28 | 29 | void update_binary_map(const settings *config, unsigned char *eclipse_maps, const shadow_map *shadow_map); 30 | 31 | void output_binary_map(const settings *config, const ephemeris *ephemeris, unsigned char *eclipse_maps); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /solarEclipseRender/src/map_eclipse_contours.h: -------------------------------------------------------------------------------- 1 | // map_eclipse_contours.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef MAP_GREATEST_ECLIPSE_H 23 | #define MAP_GREATEST_ECLIPSE_H 1 24 | 25 | #include "ephemeris.h" 26 | #include "map_greatest_eclipse.h" 27 | #include "settings.h" 28 | 29 | #define CONTOUR_MAX_LENGTH 50000 30 | 31 | typedef struct contour_line { 32 | double longitude[CONTOUR_MAX_LENGTH]; // radians 33 | double latitude[CONTOUR_MAX_LENGTH]; // radians 34 | int point_count; 35 | double eclipse_magnitude; 36 | } contour_line; 37 | 38 | #define CONTOUR_LINE_COUNT_MAX 20 39 | 40 | typedef struct contour_line_list { 41 | contour_line line[CONTOUR_LINE_COUNT_MAX]; 42 | int contour_count; 43 | } contour_line_list; 44 | 45 | contour_line_list *map_eclipse_contours(const settings *config, const ephemeris *ephemeris, 46 | const eclipse_path_list *paths, const time_span *eclipse_time_span); 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /solarEclipseRender/src/map_greatest_eclipse.h: -------------------------------------------------------------------------------- 1 | // map_greatest_eclipse.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef MAP_ECLIPSE_CONTOURS_H 23 | #define MAP_ECLIPSE_CONTOURS_H 1 24 | 25 | #include "ephemeris.h" 26 | #include "settings.h" 27 | 28 | // Data structure for storing the path of greatest eclipse 29 | 30 | #define MAX_PATH_LENGTH 2500 31 | #define MAX_PATH_ITEMS 20 32 | 33 | typedef struct path_point { 34 | double latitude, longitude; // radians 35 | double jd; // TT 36 | double duration; // seconds 37 | } path_point; 38 | 39 | typedef struct eclipse_path { 40 | int is_total; 41 | double jd_start, jd_end; 42 | int point_count; 43 | path_point path[MAX_PATH_LENGTH]; 44 | } eclipse_path; 45 | 46 | typedef struct eclipse_path_list { 47 | eclipse_path paths[MAX_PATH_ITEMS]; 48 | int path_count; 49 | double latitude_midpoint, longitude_midpoint; 50 | double maximum_duration; 51 | } eclipse_path_list; 52 | 53 | double eclipse_duration_from_path(const eclipse_path_list *paths, double jd, int *is_total); 54 | 55 | eclipse_path_list *map_greatest_eclipse(const settings *config, const ephemeris *ephemeris, time_span *span_output); 56 | 57 | void eclipse_position_from_path(const eclipse_path_list *paths, double jd, double *lng_out, double *lat_out); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /solarEclipseRender/src/mathsTools/julianDate.c: -------------------------------------------------------------------------------- 1 | // julianDate.c 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | #include "julianDate.h" 29 | 30 | // Routines for looking up the dates when the transition between the Julian calendar and the Gregorian calendar occurred 31 | 32 | //! switch_over_calendar_date - Return the calendar date when the Julian calendar was replaced with the Gregorian 33 | //! calendar 34 | //! \param [out] lastJulian - The last day of the Julian calendar, expressed as yyyymmdd 35 | //! \param [out] firstGregorian - The first day of the Gregorian calendar, expressed as yyyymmdd 36 | 37 | void switch_over_calendar_date(double *lastJulian, double *firstGregorian) { 38 | *lastJulian = 17520902.0; 39 | *firstGregorian = 17520914.0; // British 40 | } 41 | 42 | //! The Julian day number when the Julian calendar was replaced with the Gregorian 43 | //! \return Julian day number 44 | 45 | double switch_over_jd() { 46 | return 2361222.0; // British 47 | } 48 | 49 | //! get_month_name - Get the name of month number i, where 1 is January and 12 is December 50 | //! \param [in] i - Month number 51 | //! \return Month name 52 | 53 | char *get_month_name(int i) { 54 | switch (i) { 55 | case 1: 56 | return "January"; 57 | case 2: 58 | return "February"; 59 | case 3: 60 | return "March"; 61 | case 4: 62 | return "April"; 63 | case 5: 64 | return "May"; 65 | case 6: 66 | return "June"; 67 | case 7: 68 | return "July"; 69 | case 8: 70 | return "August"; 71 | case 9: 72 | return "September"; 73 | case 10: 74 | return "October"; 75 | case 11: 76 | return "November"; 77 | case 12: 78 | return "December"; 79 | default: 80 | return "???"; 81 | } 82 | } 83 | 84 | //! get_week_day_name - Get the name of the ith day of the week, where 0 is Monday and 6 is Sunday 85 | //! \param [in] i - Number of the day of the week 86 | //! \return Name of the day 87 | 88 | char *get_week_day_name(int i) { 89 | switch (i) { 90 | case 0: 91 | return "Monday"; 92 | case 1: 93 | return "Tuesday"; 94 | case 2: 95 | return "Wednesday"; 96 | case 3: 97 | return "Thursday"; 98 | case 4: 99 | return "Friday"; 100 | case 5: 101 | return "Saturday"; 102 | case 6: 103 | return "Sunday"; 104 | default: 105 | return "???"; 106 | } 107 | } 108 | 109 | //! julian_day - Convert a calendar date into a Julian day number 110 | //! \param [in] year The calendar year 111 | //! \param [in] month The calendar month 112 | //! \param [in] day The day of the month 113 | //! \param [in] hour The hour of the day 114 | //! \param [in] min The minutes within the hour 115 | //! \param [in] sec The seconds within the minute 116 | //! \param [out] status Zero on success; One on failure 117 | //! \param [out] err_text Error message (if status is non-zero) 118 | //! \return Julian day number 119 | 120 | double julian_day(int year, int month, int day, int hour, int min, int sec, int *status, char *err_text) { 121 | double jd, day_fraction, last_julian, first_gregorian, required_date; 122 | int b; 123 | 124 | if ((year < -1e6) || (year > 1e6) || (!gsl_finite(year))) { 125 | *status = 1; 126 | sprintf(err_text, "Supplied year is too big."); 127 | return 0.0; 128 | } 129 | if ((day < 1) || (day > 31)) { 130 | *status = 1; 131 | sprintf(err_text, "Supplied day number should be in the range 1-31."); 132 | return 0.0; 133 | } 134 | if ((hour < 0) || (hour > 23)) { 135 | *status = 1; 136 | sprintf(err_text, "Supplied hour number should be in the range 0-23."); 137 | return 0.0; 138 | } 139 | if ((min < 0) || (min > 59)) { 140 | *status = 1; 141 | sprintf(err_text, "Supplied minute number should be in the range 0-59."); 142 | return 0.0; 143 | } 144 | if ((sec < 0) || (sec > 59)) { 145 | *status = 1; 146 | sprintf(err_text, "Supplied second number should be in the range 0-59."); 147 | return 0.0; 148 | } 149 | if ((month < 1) || (month > 12)) { 150 | *status = 1; 151 | sprintf(err_text, "Supplied month number should be in the range 1-12."); 152 | return 0.0; 153 | } 154 | 155 | switch_over_calendar_date(&last_julian, &first_gregorian); 156 | required_date = 10000.0 * year + 100 * month + day; 157 | 158 | if (month <= 2) { 159 | month += 12; 160 | year--; 161 | } 162 | 163 | // Julian calendar 164 | if (required_date <= last_julian) { b = -2 + ((year + 4716) / 4) - 1179; } 165 | 166 | // Gregorian calendar 167 | else if (required_date >= first_gregorian) { b = (year / 400) - (year / 100) + (year / 4); } 168 | 169 | else { 170 | *status = 1; 171 | sprintf(err_text, 172 | "The requested date never happened in the British calendar: " 173 | "it was lost in the transition from the Julian to the Gregorian calendar."); 174 | return 0.0; 175 | } 176 | 177 | jd = 365.0 * year - 679004.0 + 2400000.5 + b + floor(30.6001 * (month + 1)) + day; 178 | 179 | day_fraction = (fabs(hour) + fabs(min) / 60.0 + fabs(sec) / 3600.0) / 24.0; 180 | 181 | return jd + day_fraction; 182 | } 183 | 184 | //! inv_julian_day - Convert a julian day number into a calendar date 185 | //! \param [in] jd Julian day number 186 | //! \param [out] year The calendar year 187 | //! \param [out] month The calendar month 188 | //! \param [out] day The day of the month 189 | //! \param [out] hour The hour of the day 190 | //! \param [out] min The minutes within the hour 191 | //! \param [out] sec The seconds within the minute 192 | //! \param [out] status Zero on success; One on failure 193 | //! \param [out] err_text Error message (if status is non-zero) 194 | 195 | void inv_julian_day(double jd, int *year, int *month, int *day, int *hour, int *min, double *sec, int *status, 196 | char *err_text) { 197 | long a, b, c, d, e, f; 198 | double day_fraction; 199 | int temp; 200 | if (month == NULL) month = &temp; // Dummy placeholder, since we need month later in the calculation 201 | 202 | if ((jd < -1e8) || (jd > 1e8) || (!gsl_finite(jd))) { 203 | *status = 1; 204 | sprintf(err_text, "Supplied Julian Day number is too big."); 205 | return; 206 | } 207 | 208 | // Work out hours, minutes and seconds 209 | day_fraction = (jd + 0.5) - floor(jd + 0.5); 210 | if (hour != NULL) *hour = (int) floor(24 * day_fraction); 211 | if (min != NULL) *min = (int) floor(fmod(1440 * day_fraction, 60)); 212 | if (sec != NULL) *sec = fmod(86400 * day_fraction, 60); 213 | 214 | // Now work out calendar date 215 | // a = Number of whole Julian days. 216 | // b = Number of centuries since the Council of Nicaea. 217 | // c = Julian Day number as if century leap years happened. 218 | a = jd + 0.5; 219 | 220 | // Julian calendar 221 | if (a < switch_over_jd()) { 222 | b = 0; 223 | c = a + 1524; 224 | } 225 | 226 | // Gregorian calendar 227 | else { 228 | b = (a - 1867216.25) / 36524.25; 229 | c = a + b - (b / 4) + 1525; 230 | } 231 | d = (c - 122.1) / 365.25; // Number of 365.25 periods, starting the year at the end of February 232 | e = 365 * d + d / 4; // Number of days accounted for by these 233 | f = (c - e) / 30.6001; // Number of 30.6001 days periods (a.k.a. months) in remainder 234 | if (day != NULL) *day = (int) floor(c - e - (int) (30.6001 * f)); 235 | *month = (int) floor(f - 1 - 12 * (f >= 14)); 236 | if (year != NULL) *year = (int) floor(d - 4715 - (*month >= 3)); 237 | } 238 | 239 | //! jd_from_unix - Convert a unix time into a Julian day number 240 | //! \param unix_time - Unix time stamp 241 | //! \return - Julian day number 242 | 243 | double jd_from_unix(double unix_time) { 244 | return unix_time / 86400.0 + 2440587.5; 245 | } 246 | 247 | //! unix_from_jd - Convert a Julian day number into a unix time 248 | //! \param jd - Julian day number 249 | //! \return - Unix time stamp 250 | 251 | double unix_from_jd(double jd) { 252 | return (jd - 2440587.5) * 86400.0; 253 | } 254 | -------------------------------------------------------------------------------- /solarEclipseRender/src/mathsTools/julianDate.h: -------------------------------------------------------------------------------- 1 | // julianDate.h 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef JULIANDATE_H 23 | #define JULIANDATE_H 1 24 | 25 | void switch_over_cal_date(double *lastJulian, double *firstGregorian); 26 | 27 | double switch_over_jd(); 28 | 29 | char *get_month_name(int i); 30 | 31 | char *get_week_day_name(int i); 32 | 33 | double julian_day(int year, int month, int day, int hour, int min, int sec, int *status, char *errtext); 34 | 35 | void inv_julian_day(double JD, int *year, int *month, int *day, int *hour, int *min, double *sec, int *status, 36 | char *errtext); 37 | 38 | double jd_from_unix(double unix_time); 39 | 40 | double unix_from_jd(double jd); 41 | 42 | #endif 43 | 44 | -------------------------------------------------------------------------------- /solarEclipseRender/src/mathsTools/sphericalAst.c: -------------------------------------------------------------------------------- 1 | // sphericalAst.c 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | //! angDist_ABC - Calculate the angle between the lines BA and BC, measured at B 29 | //! \param xa - Cartesian coordinates of point A 30 | //! \param ya - Cartesian coordinates of point A 31 | //! \param za - Cartesian coordinates of point A 32 | //! \param xb - Cartesian coordinates of point B 33 | //! \param yb - Cartesian coordinates of point B 34 | //! \param zb - Cartesian coordinates of point B 35 | //! \param xc - Cartesian coordinates of point C 36 | //! \param yc - Cartesian coordinates of point C 37 | //! \param zc - Cartesian coordinates of point C 38 | //! \return Angle ABC (radians) 39 | 40 | double angDist_ABC(double xa, double ya, double za, double xb, double yb, double zb, double xc, double yc, double zc) { 41 | double AB2 = gsl_pow_2(xa - xb) + gsl_pow_2(ya - yb) + gsl_pow_2(za - zb); 42 | double BC2 = gsl_pow_2(xb - xc) + gsl_pow_2(yb - yc) + gsl_pow_2(zb - zc); 43 | double CA2 = gsl_pow_2(xc - xa) + gsl_pow_2(yc - ya) + gsl_pow_2(zc - za); 44 | 45 | // Use the cosine rule 46 | double cosine = (AB2 + BC2 - CA2) / (2 * sqrt(AB2) * sqrt(BC2)); 47 | if (cosine >= 1) return 0; 48 | 49 | return acos(cosine); 50 | } 51 | 52 | //! angDist_RADec - Calculate the angular distance between (RA0, Dec0) and (RA1, Dec1) 53 | //! \param ra0 - Right ascension of the first point (radians) 54 | //! \param dec0 - Declination of the first point (radians) 55 | //! \param ra1 - Right ascension of the second point (radians) 56 | //! \param dec1 - Declination of the second point (radians) 57 | //! \return Angular distance between two points (radians) 58 | 59 | double angDist_RADec(double ra0, double dec0, double ra1, double dec1) { 60 | double p0x = sin(ra0) * cos(dec0); 61 | double p0y = cos(ra0) * cos(dec0); 62 | double p0z = sin(dec0); 63 | 64 | double p1x = sin(ra1) * cos(dec1); 65 | double p1y = cos(ra1) * cos(dec1); 66 | double p1z = sin(dec1); 67 | 68 | double sep2 = gsl_pow_2(p0x - p1x) + gsl_pow_2(p0y - p1y) + gsl_pow_2(p0z - p1z); 69 | if (sep2 <= 0) return 0; 70 | 71 | double sep = sqrt(sep2); 72 | return 2 * asin(sep / 2); 73 | } 74 | 75 | //! ra_dec_from_j2000 - Convert celestial coordinates from J2000 into a new epoch. 76 | //! See Green's Spherical Astronomy, pp 222-225 77 | //! \param [in] ra0 - Right ascension, in hours, J2000 78 | //! \param [in] dec0 - Declination, in degrees, J2000 79 | //! \param [in] utc_new - Unix time of the epoch we are to transform celestial coordinates into 80 | //! \param [out] ra_out - Output right ascension, in hours, epoch 81 | //! \param [out] dec_out - Output declination, in degrees, epoch 82 | void ra_dec_from_j2000(double ra0, double dec0, double utc_new, double *ra_out, double *dec_out) { 83 | 84 | // Convert inputs into radians 85 | ra0 *= M_PI / 12; 86 | dec0 *= M_PI / 180; 87 | 88 | const double u = utc_new; 89 | const double j = 40587.5 + u / 86400.0; // Julian date - 2400000 90 | const double t = (j - 51545.0) / 36525.0; // Julian century (no centuries since 2000.0) 91 | 92 | const double deg = M_PI / 180; 93 | const double m = (1.281232 * t + 0.000388 * t * t) * deg; 94 | const double n = (0.556753 * t + 0.000119 * t * t) * deg; 95 | 96 | const double ra_m = ra0 + 0.5 * (m + n * sin(ra0) * tan(dec0)); 97 | const double dec_m = dec0 + 0.5 * n * cos(ra_m); 98 | 99 | const double ra_new = ra0 + m + n * sin(ra_m) * tan(dec_m); 100 | const double dec_new = dec0 + n * cos(ra_m); 101 | 102 | *ra_out = ra_new * 12 / M_PI; 103 | *dec_out = dec_new * 180 / M_PI; 104 | } 105 | 106 | //! ra_dec_to_j2000 - Convert celestial coordinates from an arbitrary epoch to J2000. 107 | //! See Green's Spherical Astronomy, pp 222-225 108 | //! \param [in] ra0 - Right ascension, in hours, original epoch 109 | //! \param [in] dec0 - Declination, in degrees, original epoch 110 | //! \param [in] utc_old - Unix time of the epoch we are to transform celestial coordinates from 111 | //! \param [out] ra_out - Output right ascension, in hours, epoch J2000 112 | //! \param [out] dec_out - Output declination, in degrees, epoch J2000 113 | void ra_dec_to_j2000(double ra1, double dec1, double utc_old, double *ra_out, double *dec_out) { 114 | 115 | // Convert inputs into radians 116 | ra1 *= M_PI / 12; 117 | dec1 *= M_PI / 180; 118 | 119 | const double u = utc_old; 120 | const double j = 40587.5 + u / 86400.0; // Julian date - 2400000 121 | const double t = (j - 51545.0) / 36525.0; // Julian century (no centuries since 2000.0) 122 | 123 | const double deg = M_PI / 180; 124 | const double m = (1.281232 * t + 0.000388 * t * t) * deg; 125 | const double n = (0.556753 * t + 0.000119 * t * t) * deg; 126 | 127 | const double ra_m = ra1 - 0.5 * (m + n * sin(ra1) * tan(dec1)); 128 | const double dec_m = dec1 - 0.5 * n * cos(ra_m); 129 | 130 | const double ra_new = ra1 - m - n * sin(ra_m) * tan(dec_m); 131 | const double dec_new = dec1 - n * cos(ra_m); 132 | 133 | *ra_out = ra_new * 12 / M_PI; 134 | *dec_out = dec_new * 180 / M_PI; 135 | } 136 | 137 | //! find_mean_position - Find the weighted average of two positions on a sphere 138 | //! \param [in] lng0 - The longitude of the first point (radians) 139 | //! \param [in] lat0 - The latitude of the first point (radians) 140 | //! \param [in] weight0 - The weighting of the first point 141 | //! \param [in] lng1 - The longitude of the second point (radians) 142 | //! \param [in] lat1 - The latitude of the second point (radians) 143 | //! \param [in] weight1 - The weighting of the second point 144 | //! \param [out] lng_mean - The longitude of the midpoint between the input points 145 | //! \param [out] lat_mean - The latitude of the midpoint between the input points 146 | 147 | void find_mean_position(double lng0, double lat0, double weight0, 148 | double lng1, double lat1, double weight1, 149 | double *lng_mean, double *lat_mean) { 150 | 151 | // Convert the first point from spherical polar coordinates to Cartesian coordinates 152 | const double x0 = cos(lng0) * cos(lat0) * weight0; 153 | const double y0 = sin(lng0) * cos(lat0) * weight0; 154 | const double z0 = sin(lat0) * weight0; 155 | 156 | // Convert the second point from spherical polar coordinates to Cartesian coordinates 157 | const double x1 = cos(lng1) * cos(lat1) * weight1; 158 | const double y1 = sin(lng1) * cos(lat1) * weight1; 159 | const double z1 = sin(lat1) * weight1; 160 | 161 | // Work out the centroid of the three points in Cartesian space 162 | const double x3 = x0 + x1; 163 | const double y3 = y0 + y1; 164 | const double z3 = z0 + z1; 165 | 166 | // Work out the magnitude of the centroid vector 167 | const double mag = sqrt(x3 * x3 + y3 * y3 + z3 * z3); 168 | 169 | // Convert the Cartesian coordinates into RA and Dec 170 | *lat_mean = asin(z3 / mag); 171 | *lng_mean = atan2(y3, x3); 172 | } -------------------------------------------------------------------------------- /solarEclipseRender/src/mathsTools/sphericalAst.h: -------------------------------------------------------------------------------- 1 | // sphericalAst.h 2 | // 3 | // ------------------------------------------------- 4 | // Copyright 2015-2020 Dominic Ford 5 | // 6 | // This file is part of EclipseRender. 7 | // 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef SPHERICALAST_H 23 | #define SPHERICALAST_H 1 24 | 25 | double angDist_ABC(double xa, double ya, double za, double xb, double yb, double zb, double xc, double yc, double zc); 26 | 27 | double angDist_RADec(double ra0, double dec0, double ra1, double dec1); 28 | 29 | void ra_dec_from_j2000(double ra0, double dec0, double utc_new, double *ra_out, double *dec_out); 30 | 31 | void ra_dec_to_j2000(double ra1, double dec1, double utc_old, double *ra_out, double *dec_out); 32 | 33 | void find_mean_position(double lng0, double lat0, double weight0, 34 | double lng1, double lat1, double weight1, 35 | double *lng_mean, double *lat_mean); 36 | 37 | #endif 38 | 39 | -------------------------------------------------------------------------------- /solarEclipseRender/src/png/image.h: -------------------------------------------------------------------------------- 1 | // image.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef IMAGE_H 23 | #define IMAGE_H 1 24 | 25 | /* Variable format used to store images */ 26 | 27 | typedef struct { 28 | int xsize; 29 | int ysize; 30 | double *data_w; 31 | double *data_red; 32 | double *data_grn; 33 | double *data_blu; 34 | } image_ptr; 35 | 36 | /* Variable format used to image pixels */ 37 | 38 | typedef struct { 39 | double red; 40 | double grn; 41 | double blu; 42 | } pixel; 43 | 44 | /* Functions defined in image_in.c */ 45 | void image_alloc(image_ptr *out, int x, int y); 46 | 47 | void image_dealloc(image_ptr *in); 48 | 49 | void image_cp(image_ptr *in, image_ptr *out); 50 | 51 | void image_deweight(image_ptr *out); 52 | 53 | image_ptr image_get(const char *filename); 54 | 55 | /* Functions defined in image_out.c */ 56 | int image_put(const char *output_filename, image_ptr image, int greyscale); 57 | 58 | #endif 59 | 60 | -------------------------------------------------------------------------------- /solarEclipseRender/src/png/image_in.c: -------------------------------------------------------------------------------- 1 | // image_in.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "coreUtils/errorReport.h" 30 | 31 | #include "image.h" 32 | 33 | // Colour channel configurations 34 | 35 | #define BMP_COLOUR_BMP 1001 36 | #define BMP_COLOUR_PALETTE 1002 37 | #define BMP_COLOUR_GREY 1003 38 | #define BMP_COLOUR_RGB 1004 39 | 40 | //! image_alloc - allocate an image_ptr structure to hold an image of dimensions (x, y) 41 | //! \param [out] out The image_ptr structure to populate 42 | //! \param [in] x The horizontal size of the image 43 | //! \param [in] y The vertical size of the image 44 | 45 | void image_alloc(image_ptr *out, int x, int y) { 46 | int i, j = x * y; 47 | 48 | out->xsize = x; 49 | out->ysize = y; 50 | out->data_w = (double *) malloc(x * y * sizeof(double)); 51 | out->data_red = (double *) malloc(x * y * sizeof(double)); 52 | out->data_grn = (double *) malloc(x * y * sizeof(double)); 53 | out->data_blu = (double *) malloc(x * y * sizeof(double)); 54 | for (i = 0; i < j; i++) out->data_red[i] = 0.0; 55 | for (i = 0; i < j; i++) out->data_grn[i] = 0.0; 56 | for (i = 0; i < j; i++) out->data_blu[i] = 0.0; 57 | for (i = 0; i < j; i++) out->data_w[i] = 0.0; 58 | } 59 | 60 | //! image_dealloc - free the storage associated with an image_ptr structure 61 | //! \param [in] in The image_ptr structure to free 62 | 63 | void image_dealloc(image_ptr *in) { 64 | if (in->data_red != NULL) free(in->data_red); 65 | if (in->data_grn != NULL) free(in->data_grn); 66 | if (in->data_blu != NULL) free(in->data_blu); 67 | if (in->data_w != NULL) free(in->data_w); 68 | } 69 | 70 | //! image_cp - copy the image contained within an image_ptr structure 71 | //! \param [in] in The image to copy 72 | //! \param [out] out The image_ptr structure to populate with the copied image 73 | 74 | void image_cp(image_ptr *in, image_ptr *out) { 75 | image_alloc(out, in->xsize, in->ysize); 76 | memcpy(out->data_red, in->data_red, in->xsize * in->ysize * sizeof(double)); 77 | memcpy(out->data_grn, in->data_grn, in->xsize * in->ysize * sizeof(double)); 78 | memcpy(out->data_blu, in->data_blu, in->xsize * in->ysize * sizeof(double)); 79 | memcpy(out->data_w, in->data_w, in->xsize * in->ysize * sizeof(double)); 80 | } 81 | 82 | //! image_deweight - Divide the pixel data in an image by the weight field. This is useful if N images have been 83 | //! co-added into the pixel data, and the structure member contains the number of images which have been 84 | //! added together. The resulting image is then properly normalised. 85 | //! \param out The image to deweight 86 | 87 | void image_deweight(image_ptr *out) { 88 | int i, j = out->xsize * out->ysize; 89 | for (i = 0; i < j; i++) { 90 | out->data_red[i] /= out->data_w[i]; 91 | if (!gsl_finite(out->data_red[i])) out->data_red[i] = 0.0; 92 | } 93 | for (i = 0; i < j; i++) { 94 | out->data_grn[i] /= out->data_w[i]; 95 | if (!gsl_finite(out->data_grn[i])) out->data_grn[i] = 0.0; 96 | } 97 | for (i = 0; i < j; i++) { 98 | out->data_blu[i] /= out->data_w[i]; 99 | if (!gsl_finite(out->data_blu[i])) out->data_blu[i] = 0.0; 100 | } 101 | } 102 | 103 | //! image_get - Read a PNG image from disk and convert it into an image_ptr structure 104 | //! \param [in] filename The filename of the PNG image to read 105 | //! \return image_ptr structure containing the pixel data 106 | 107 | image_ptr image_get(const char *filename) { 108 | // Output structure to hold the image that we're about to read 109 | image_ptr output; 110 | output.xsize = output.ysize = -1; 111 | output.data_red = output.data_grn = output.data_blu = output.data_w = NULL; 112 | 113 | // Open the PNG image file 114 | FILE *infile; 115 | 116 | if ((infile = fopen(filename, "rb")) == NULL) { 117 | fprintf(stderr, "ERROR: Cannot open input file %s\n", filename); 118 | return output; 119 | } 120 | 121 | // Read the contents of the image using libpng 122 | unsigned char *data = NULL, *trans = NULL; 123 | uint16_t *palette = NULL; 124 | int pal_len = 0, width = 0, height = 0, colour = 0; 125 | 126 | int depth = 0, ncols = 0, ntrans = 0, png_colour_type = 0, i = 0, j = 0; 127 | unsigned row_bytes = 0; 128 | static unsigned char index[3]; 129 | 130 | png_structp png_ptr; 131 | png_infop info_ptr; 132 | png_bytep *row_ptrs, trans_colours; 133 | png_color_16p trans_val; 134 | png_color_16p backgndp; 135 | png_color_16 background; 136 | png_colorp pngpalette; 137 | 138 | // Initialise libpng data structures 139 | png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 140 | if (png_ptr == NULL) { 141 | logging_error("Out of memory"); 142 | goto finalise; 143 | } 144 | 145 | info_ptr = png_create_info_struct(png_ptr); 146 | if (info_ptr == NULL) { 147 | logging_error("Out of memory"); 148 | goto finalise; 149 | } 150 | 151 | if (setjmp(png_jmpbuf(png_ptr))) { 152 | logging_error("Unexpected error in libpng while trying to decode PNG image file"); 153 | goto finalise; 154 | } 155 | 156 | png_init_io(png_ptr, infile); 157 | png_set_sig_bytes(png_ptr, 0); 158 | 159 | // Let libpng read header, and deal with outcome 160 | png_read_info(png_ptr, info_ptr); 161 | 162 | width = png_get_image_width(png_ptr, info_ptr); 163 | height = png_get_image_height(png_ptr, info_ptr); 164 | depth = png_get_bit_depth(png_ptr, info_ptr); 165 | png_colour_type = png_get_color_type(png_ptr, info_ptr); 166 | 167 | if (DEBUG) { 168 | sprintf(temp_err_string, "Size %dx%d", width, height); 169 | logging_log(temp_err_string); 170 | } 171 | if (DEBUG) { 172 | sprintf(temp_err_string, "Depth %d", depth); 173 | logging_log(temp_err_string); 174 | } 175 | 176 | if (png_colour_type & PNG_COLOR_MASK_ALPHA) { 177 | if (DEBUG) logging_log("PNG uses transparency"); 178 | if (png_get_bKGD(png_ptr, info_ptr, &backgndp)) { 179 | if (DEBUG) logging_log("PNG defines a background colour"); 180 | png_set_background(png_ptr, backgndp, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); 181 | } else { 182 | if (DEBUG) logging_log("PNG does not define a background colour"); 183 | background.red = background.green = background.blue = background.gray = 0xff; // Define background colour to be white 184 | png_set_background(png_ptr, &background, PNG_BACKGROUND_GAMMA_FILE, 0, 1.0); 185 | } 186 | } 187 | 188 | if ((png_colour_type == PNG_COLOR_TYPE_GRAY) || (png_colour_type == PNG_COLOR_TYPE_GRAY_ALPHA)) 189 | colour = BMP_COLOUR_GREY; 190 | if ((png_colour_type == PNG_COLOR_TYPE_RGB) || (png_colour_type == PNG_COLOR_TYPE_RGB_ALPHA)) 191 | colour = BMP_COLOUR_RGB; 192 | 193 | if (png_colour_type == PNG_COLOR_TYPE_PALETTE) { 194 | colour = BMP_COLOUR_PALETTE; 195 | i = png_get_PLTE(png_ptr, info_ptr, &pngpalette, &ncols); 196 | if (i == 0) { 197 | logging_error("PNG image file claims to be paletted, but no palette was found"); 198 | goto finalise; 199 | } 200 | pal_len = ncols; 201 | palette = (uint16_t *) malloc(ncols * 3 * sizeof(uint16_t)); 202 | if (palette == NULL) { 203 | logging_error("Out of memory"); 204 | goto finalise; 205 | } 206 | for (i = 0; i < ncols; i++) { 207 | *(palette + 3 * i) = pngpalette[i].red; 208 | *(palette + 3 * i + 1) = pngpalette[i].green; 209 | *(palette + 3 * i + 2) = pngpalette[i].blue; 210 | } 211 | if (DEBUG) { 212 | sprintf(temp_err_string, "PNG image file contains a palette of %d colours", ncols); 213 | logging_log(temp_err_string); 214 | } 215 | } 216 | 217 | // Update png info to reflect any requested conversions (e.g. 16 bit to 8 bit or alpha to non-alpha) 218 | png_read_update_info(png_ptr, info_ptr); 219 | 220 | // Now rowbytes will reflect what we will get, not what we had originally 221 | row_bytes = png_get_rowbytes(png_ptr, info_ptr); 222 | 223 | // Allocate block of memory for uncompressed image 224 | data = malloc(row_bytes * height); 225 | if (data == NULL) { 226 | logging_error("Out of memory"); 227 | goto finalise; 228 | } 229 | 230 | // libpng requires a separate pointer to each row of image 231 | row_ptrs = (png_bytep *) malloc(height * sizeof(png_bytep)); 232 | if (row_ptrs == NULL) { 233 | logging_error("Out of memory"); 234 | data = NULL; 235 | goto finalise; 236 | } 237 | 238 | for (i = 0; i < height; i++) { 239 | row_ptrs[i] = data + row_bytes * i; 240 | } 241 | 242 | // Get uncompressed image 243 | png_read_image(png_ptr, row_ptrs); 244 | 245 | // Free everything we don't need 246 | png_read_end(png_ptr, NULL); 247 | 248 | // Deal with transparency (we can only support images with single transparent palette entries) 249 | if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { 250 | if (DEBUG) logging_log("PNG has transparency"); 251 | png_get_tRNS(png_ptr, info_ptr, &trans_colours, &ntrans, &trans_val); 252 | if (DEBUG) { 253 | sprintf(temp_err_string, "PNG has %d transparent entries in palette", ntrans); 254 | logging_log(temp_err_string); 255 | } 256 | if (png_colour_type == PNG_COLOR_TYPE_PALETTE) { 257 | // We can cope with just one, fully transparent, entry in palette 258 | j = 0; 259 | for (i = 0; i < ntrans; i++) 260 | if (trans_colours[i] == 0) j++; 261 | else if (trans_colours[i] != 255) j += 10; 262 | if (j != 1) { 263 | logging_warning( 264 | "PNG has transparency, but not in the form of a single fully colour in its palette. Such transparency is not supported."); 265 | } else { 266 | for (i = 0; (i < ntrans) && (trans_colours[i] == 255); i++); 267 | trans = index; 268 | *trans = i; 269 | } 270 | } else { 271 | trans = index; 272 | *trans = trans_val->gray; 273 | if (colour == BMP_COLOUR_RGB) { 274 | trans[0] = trans_val->red; 275 | trans[1] = trans_val->green; 276 | trans[2] = trans_val->blue; 277 | } 278 | } 279 | } 280 | png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); 281 | 282 | // Put all necessary information into the output data structure 283 | image_alloc(&output, width, height); 284 | const int frame_size = width * height; 285 | for (i = 0; i < frame_size; i++) output.data_w[i] = 1; 286 | 287 | if (colour == BMP_COLOUR_RGB) { 288 | for (i = 0; i < frame_size; i++) { 289 | if (depth == 8) { 290 | output.data_red[i] = data[3 * i + 0]; 291 | output.data_grn[i] = data[3 * i + 1]; 292 | output.data_blu[i] = data[3 * i + 2]; 293 | } else { 294 | output.data_red[i] = 256 * data[6 * i + 0] + data[6 * i + 1]; 295 | output.data_grn[i] = 256 * data[6 * i + 2] + data[6 * i + 3]; 296 | output.data_blu[i] = 256 * data[6 * i + 4] + data[6 * i + 5]; 297 | } 298 | } 299 | } else if (colour == BMP_COLOUR_PALETTE) { 300 | for (i = 0; i < frame_size; i++) { 301 | int j = data[i]; 302 | if (j > pal_len - 1) j = pal_len - 1; 303 | output.data_red[i] = palette[3 * j + 0]; 304 | output.data_grn[i] = palette[3 * j + 1]; 305 | output.data_blu[i] = palette[3 * j + 2]; 306 | } 307 | } else { 308 | for (i = 0; i < frame_size; i++) { 309 | if (depth == 8) { 310 | output.data_red[i] = data[i]; 311 | output.data_grn[i] = data[i]; 312 | output.data_blu[i] = data[i]; 313 | } else { 314 | output.data_red[i] = 256 * data[2 * i] + data[2 * i + 1]; 315 | output.data_grn[i] = output.data_red[i]; 316 | output.data_blu[i] = output.data_red[i]; 317 | } 318 | } 319 | } 320 | 321 | finalise: 322 | if (data != NULL) free(data); 323 | if (palette != NULL) free(palette); 324 | if (trans != NULL) free(trans); 325 | if (row_ptrs != NULL) free(row_ptrs); 326 | return output; 327 | } 328 | 329 | -------------------------------------------------------------------------------- /solarEclipseRender/src/projection.c: -------------------------------------------------------------------------------- 1 | // projection.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include "projection.h" 28 | #include "settings.h" 29 | 30 | 31 | void project_3d(const settings *config, int x, int y, double lng_sun, double lat_sun, 32 | double *lng_out, double *lat_out, double *p_radius_out) { 33 | double zn, p_radius; 34 | 35 | // Project point in 3D space where we measure eclipse fraction, measured in Earth radii with z-axis 36 | // pointing out of the screen 37 | 38 | // x-axis starts out point horizontally across the screen 39 | double xn = -(x - config->x_size_3d / 2.) / config->earth_pixel_radius; 40 | 41 | // y-axis starts out pointing vertically up the screen 42 | double yn = -(y - config->y_size_3d / 2.) / config->earth_pixel_radius; 43 | double n = gsl_pow_2(xn) + gsl_pow_2(yn); 44 | 45 | // z-axis points out of the screen 46 | if (n >= 1) { 47 | // This pixel is not on the Earth's surface, so project point above Earth 48 | zn = 0; 49 | p_radius = sqrt(n); 50 | 51 | // Re-normalise (xn,yn,zn) is have unit length 52 | xn /= p_radius; 53 | yn /= p_radius; 54 | } else { 55 | // This pixel is on the Earth's surface 56 | zn = -sqrt(1 - n); 57 | p_radius = 1; 58 | } 59 | 60 | // RA - hour angle 61 | const double rotang1 = -M_PI / 2 + lng_sun; 62 | const double rotang2 = -M_PI / 2 - lat_sun; 63 | 64 | // Tip (y,z) by declination 65 | const double x2 = xn; 66 | const double y2 = yn * cos(rotang2) + zn * sin(rotang2); 67 | const double z2 = -yn * sin(rotang2) + zn * cos(rotang2); 68 | 69 | // Tip (x,y) to get the right central longitude 70 | const double xf = x2 * cos(rotang1) - y2 * sin(rotang1); 71 | const double yf = x2 * sin(rotang1) + y2 * cos(rotang1); 72 | const double zf = z2; 73 | 74 | // Coordinates now orientated with z-axis pointing out of north pole 75 | // longitude 76 | *lng_out = atan2(yf, xf) / M_PI * 180; 77 | *lat_out = asin(zf) / M_PI * 180; 78 | *p_radius_out = p_radius; 79 | } 80 | 81 | void inv_project_3d(const settings *config, int *x_out, int *y_out, double lng_sun, double lat_sun, 82 | double lng, double lat) { 83 | 84 | lng *= M_PI / 180; 85 | lat *= M_PI / 180; 86 | 87 | // RA - hour angle 88 | const double rotang1 = -M_PI / 2 + lng_sun; 89 | const double rotang2 = -M_PI / 2 - lat_sun; 90 | 91 | const double xf = cos(lng) * cos(lat); 92 | const double yf = sin(lng) * cos(lat); 93 | const double zf = sin(lat); 94 | 95 | // Tip (x,y) to get the right central longitude 96 | const double x2 = xf * cos(rotang1) + yf * sin(rotang1); 97 | const double y2 = -xf * sin(rotang1) + yf * cos(rotang1); 98 | const double z2 = zf; 99 | 100 | // Tip (y,z) by declination 101 | const double xn = x2; 102 | const double yn = y2 * cos(rotang2) - z2 * sin(rotang2); 103 | //const double zn = y2 * sin(rotang2) + z2 * cos(rotang2); 104 | 105 | *x_out = (int) (config->x_size_3d / 2. - xn * config->earth_pixel_radius); 106 | *y_out = (int) (config->y_size_3d / 2. - yn * config->earth_pixel_radius); 107 | } -------------------------------------------------------------------------------- /solarEclipseRender/src/projection.h: -------------------------------------------------------------------------------- 1 | // projection.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef PROJECTION_H 23 | #define PROJECTION_H 1 24 | 25 | #include "settings.h" 26 | 27 | void project_3d(const settings *config, int x, int y, double lng_sun, double lat_sun, 28 | double *lng_out, double *lat_out, double *p_radius_out); 29 | 30 | void inv_project_3d(const settings *config, int *x_out, int *y_out, double lng_sun, double lat_sun, 31 | double lng, double lat); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /solarEclipseRender/src/render_2d.h: -------------------------------------------------------------------------------- 1 | // render_2d.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef RENDER_2D_H 23 | #define RENDER_2D_H 1 24 | 25 | #include "country_lookup.h" 26 | #include "map_greatest_eclipse.h" 27 | #include "settings.h" 28 | #include "shadow_calc.h" 29 | #include "map_eclipse_contours.h" 30 | 31 | void render_2d_eclipse_map(settings *config, double jd, jpeg_ptr earthDay, jpeg_ptr earthNight, 32 | const shadow_map *shadow_map, const eclipse_path_list *eclipse_path); 33 | 34 | void render_2d_maximum_extent(const country_lookup_handle *cl, const settings *config, 35 | const contour_line_list *contours, 36 | const shadow_map *greatest_shadow, 37 | const eclipse_path_list *eclipse_path, const char *format); 38 | 39 | void render_2d_eclipse_icon(const country_lookup_handle *cl, const settings *config, 40 | const shadow_map *greatest_shadow); 41 | 42 | #endif -------------------------------------------------------------------------------- /solarEclipseRender/src/render_3d.c: -------------------------------------------------------------------------------- 1 | // render_3d.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | #include 30 | 31 | #include "jpeg/jpeg.h" 32 | 33 | #include "coreUtils/errorReport.h" 34 | #include "coreUtils/strConstants.h" 35 | 36 | #include "mathsTools/julianDate.h" 37 | 38 | #include "map_greatest_eclipse.h" 39 | #include "projection.h" 40 | #include "rendering.h" 41 | #include "render_3d.h" 42 | #include "settings.h" 43 | #include "shadow_calc.h" 44 | 45 | /** 46 | * render_3d_eclipse_map - Render a 3D snapshot of a globe of the world as viewed from the direction of the Sun, with 47 | * the magnitude of a solar eclipse overlaid as contours. This is ideal for turning into an animation of the eclipse's 48 | * process if snapshots are turned into a video. 49 | * 50 | * @param config [in] - The settings for this eclipse simulation, including, e.g. the output image size 51 | * @param jd [in] - The Julian day number of the current point in the simulation (TT) 52 | * @param earthDay [in] - A JPEG image of the world in daylight 53 | * @param pos_sun [in] - The position of the Sun, as returned by ephemerisCompute, in AU, relative to solar system barycentre 54 | * @param pos_earth [in] - The position of the centre of the Earth, as returned by ephemerisCompute 55 | * @param shadow_map [in] - A binary map of the eclipse magnitude across the world 56 | * @param eclipse_path [in] - The path of greatest eclipse, with duration at each point 57 | */ 58 | void render_3d_eclipse_map(settings *config, double jd, jpeg_ptr earthDay, 59 | const double *pos_sun, const double *pos_earth, 60 | const shadow_map *shadow_map, const eclipse_path_list *eclipse_path) { 61 | int x, y; 62 | 63 | // Generate image RGB data 64 | const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, config->x_size_3d); 65 | 66 | // Allocate buffer for image data 67 | unsigned char *pixel_data = malloc(stride * config->y_size_3d); 68 | 69 | // Loop over all the pixels in the image we are to produce 70 | for (y = 0; y < config->y_size_3d; y++) 71 | for (x = 0; x < config->x_size_3d; x++) { 72 | // Pixel's offset inside shadow_map 73 | const int offset = x + y * config->x_size_3d; 74 | 75 | jpeg_ptr *srcimg = &earthDay; 76 | 77 | // Look up eclipse magnitude in this pixel 78 | double shadow = shadow_map->map[offset]; 79 | 80 | colour colour_this = {0, 0, 0}; 81 | 82 | if (!gsl_isnan(shadow_map->lng[offset])) { 83 | // Lat & lng of this pixel are finite, which means pixel lies on surface of Earth 84 | // Otherwise this pixel lies off the side of the globe of the world 85 | int p0 = (int) ((shadow_map->lng[offset] + 180.) * srcimg->xsize / 360.); 86 | int p1 = (int) ((90. - shadow_map->lat[offset]) * srcimg->ysize / 180.); 87 | if (p0 >= srcimg->xsize) p0 -= srcimg->xsize; 88 | if (p1 >= srcimg->ysize) p1 = srcimg->ysize - 1; 89 | 90 | const colour colour_earth = { 91 | ((int) srcimg->data_red[p0 + p1 * srcimg->xsize]), 92 | ((int) srcimg->data_grn[p0 + p1 * srcimg->xsize]), 93 | ((int) srcimg->data_blu[p0 + p1 * srcimg->xsize]) 94 | }; 95 | 96 | colour_this = colour_earth; 97 | } 98 | 99 | // Superimpose shadow map over Earth 100 | if (shadow > 0) { 101 | // If this pixel experiences a partial eclipse, shade it accordingly 102 | colour_this.red = (int) (config->moon_shadow_fade_fraction * colour_this.red + 103 | (1 - config->moon_shadow_fade_fraction) * config->shadow_col_r); 104 | colour_this.grn = (int) (config->moon_shadow_fade_fraction * colour_this.grn + 105 | (1 - config->moon_shadow_fade_fraction) * config->shadow_col_g); 106 | colour_this.blu = (int) (config->moon_shadow_fade_fraction * colour_this.blu + 107 | (1 - config->moon_shadow_fade_fraction) * config->shadow_col_b); 108 | } 109 | 110 | // Set pixel color 111 | const int output_offset = x * 4 + y * stride; 112 | 113 | *(uint32_t *) &pixel_data[output_offset] = ((uint32_t) colour_this.blu + // blue 114 | ((uint32_t) colour_this.grn << (unsigned) 8) + // green 115 | ((uint32_t) colour_this.red << (unsigned) 16) + // red 116 | ((uint32_t) 255 << (unsigned) 24) // alpha 117 | ); 118 | } 119 | 120 | // Overlay contours of eclipse magnitude on top of the map 121 | const double contourList[] = {80, 60, 40, 20, 1e-6, -1}; 122 | int *label_position_x, *label_position_y; 123 | static int previous_label_position_x[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; 124 | static int previous_label_position_y[8] = {-1, -1, -1, -1, -1, -1, -1, -1}; 125 | shadowContoursLabelPositions(contourList, shadow_map, 0, 126 | config->x_size_3d, config->y_size_3d, 127 | &label_position_x, &label_position_y, 128 | previous_label_position_x, previous_label_position_y); 129 | drawShadowContours(pixel_data, contourList, shadow_map, label_position_x, label_position_y, 130 | 0, 0, stride, config->x_size_3d, config->y_size_3d); 131 | 132 | // Turn bitmap data into a Cairo surface 133 | cairo_surface_t *surface = cairo_image_surface_create_for_data(pixel_data, CAIRO_FORMAT_ARGB32, 134 | config->x_size_3d, config->y_size_3d, 135 | stride); 136 | 137 | cairo_t *cairo_draw = cairo_create(surface); 138 | 139 | // Label contours 140 | for (int i = 0; contourList[i] >= 0; i++) 141 | if (label_position_x[i] >= 0) { 142 | const double level = contourList[i]; 143 | char text[8]; 144 | sprintf(text, "%.0f%%", level); 145 | 146 | const colour yellow = {255, 255, 0}; 147 | const colour red = {255, 0, 0}; 148 | const colour colour_this = (level > 0.01) ? yellow : red; 149 | 150 | chart_label(cairo_draw, colour_this, text, label_position_x[i], label_position_y[i], 151 | 0, 0, 13, 1, 0); 152 | } 153 | memcpy(previous_label_position_x, label_position_x, sizeof(previous_label_position_x)); 154 | memcpy(previous_label_position_y, label_position_y, sizeof(previous_label_position_y)); 155 | free(label_position_x); 156 | free(label_position_y); 157 | 158 | // Mark the position of central eclipse 159 | double lng_central, lat_central; 160 | eclipse_position_from_path(eclipse_path, jd, &lng_central, &lat_central); 161 | 162 | if (gsl_finite(lng_central)) { 163 | const int cross_size = 4; 164 | 165 | // Calculate the latitude and longitude on Earth where the Sun is overhead 166 | double sidereal_time, lat_sun, lng_sun; 167 | calculate_where_sun_overhead(&lat_sun, &lng_sun, &sidereal_time, pos_sun, pos_earth, jd); 168 | 169 | int x_centre, y_centre; 170 | inv_project_3d(config, &x_centre, &y_centre, lng_sun, lat_sun, lng_central, lat_central); 171 | 172 | cairo_set_source_rgb(cairo_draw, 0, 1, 0); 173 | cairo_new_path(cairo_draw); 174 | cairo_move_to(cairo_draw, x_centre - cross_size, y_centre - cross_size); 175 | cairo_line_to(cairo_draw, x_centre + cross_size, y_centre + cross_size); 176 | cairo_move_to(cairo_draw, x_centre - cross_size, y_centre + cross_size); 177 | cairo_line_to(cairo_draw, x_centre + cross_size, y_centre - cross_size); 178 | cairo_stroke(cairo_draw); 179 | } 180 | 181 | // Look up duration of the eclipse 182 | int is_total; 183 | const double duration = eclipse_duration_from_path(eclipse_path, jd, &is_total); 184 | 185 | // Get date components (in UT; not TT) 186 | int year, month, day, hour, min, status = 0; 187 | double sec; 188 | inv_julian_day(jd - delta_t(jd) / 86400., 189 | &year, &month, &day, &hour, &min, &sec, &status, temp_err_string); 190 | 191 | // Write the time and date in bottom left corner of the image 192 | char text[FNAME_LENGTH]; 193 | colour yellow = {255, 255, 0}; 194 | 195 | cairo_set_source_rgba(cairo_draw, 0, 0, 0, 0.6); 196 | cairo_rectangle(cairo_draw, 197 | 0, config->y_size_3d - 70, 198 | 190, 70); 199 | cairo_fill(cairo_draw); 200 | 201 | sprintf(text, "%d %s %d UTC", day, config->month_names[month], year); 202 | chart_label(cairo_draw, yellow, text, 95, config->y_size_3d - 17, 0, 0, 17, 1, 0); 203 | 204 | sprintf(text, "%02d:%02d", hour, min); 205 | chart_label(cairo_draw, yellow, text, 95, config->y_size_3d - 49, 0, 0, 28, 1, 0); 206 | 207 | // Write copyright text in bottom right corner of the image 208 | cairo_set_source_rgba(cairo_draw, 0, 0, 0, 0.6); 209 | cairo_rectangle(cairo_draw, 210 | config->x_size_3d - 240, config->y_size_3d - 66, 211 | 240, 66); 212 | cairo_fill(cairo_draw); 213 | 214 | sprintf(text, "\u00A9 Dominic Ford 2012\u20132020"); 215 | chart_label(cairo_draw, yellow, text, config->x_size_3d - 12, config->y_size_3d - 44, 1, 0, 14, 1, 0); 216 | 217 | sprintf(text, "https://in-the-sky.org/"); 218 | chart_label(cairo_draw, yellow, text, config->x_size_3d - 12, config->y_size_3d - 18, 1, 0, 14, 1, 0); 219 | 220 | // Write duration in the top left corner of the image 221 | cairo_set_source_rgba(cairo_draw, 0, 0, 0, 0.6); 222 | cairo_rectangle(cairo_draw, 223 | 0, 0, 224 | 166, 70); 225 | cairo_fill(cairo_draw); 226 | 227 | sprintf(text, "Central duration"); 228 | chart_label(cairo_draw, yellow, text, 83, 20, 0, 0, 15, 1, 0); 229 | 230 | if (duration > 0) { 231 | sprintf(text, is_total ? "Total" : "Annular"); 232 | chart_label(cairo_draw, yellow, text, 45, 50, 0, 0, 15, 1, 0); 233 | 234 | sprintf(text, "%dm%02ds", (int) (duration / 60), (int) duration % 60); 235 | chart_label(cairo_draw, yellow, text, 125, 50, 0, 0, 15, 1, 0); 236 | } else { 237 | sprintf(text, "\u2014"); 238 | chart_label(cairo_draw, yellow, text, 83, 50, 0, 0, 16, 1, 0); 239 | } 240 | 241 | // Write output image 242 | char output_filename[FNAME_LENGTH]; 243 | sprintf(output_filename, "%s/frameB%06d.png", config->output_dir, config->frame_counter); 244 | cairo_surface_write_to_png(surface, output_filename); 245 | 246 | // If this frame is at the midpoint of the eclipse we output a special "poster" frame which acts as a teaser image 247 | // for the animation. 248 | if (config->frame_counter == config->poster_image_frame) { 249 | sprintf(text, "Please wait \u2013 loading..."); 250 | chart_label(cairo_draw, yellow, text, (int) (config->x_size_3d / 2), (int) (config->y_size_3d / 2), 251 | 0, 0, 16, 1, 0); 252 | 253 | // Write out poster image 254 | sprintf(output_filename, "%s/solarEclipseB.png", config->output_dir); 255 | cairo_surface_write_to_png(surface, output_filename); 256 | } 257 | 258 | cairo_destroy(cairo_draw); 259 | cairo_surface_finish(surface); 260 | free(pixel_data); 261 | } 262 | -------------------------------------------------------------------------------- /solarEclipseRender/src/render_3d.h: -------------------------------------------------------------------------------- 1 | // render_3d.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef RENDER_3D_H 23 | #define RENDER_3D_H 1 24 | 25 | #include "map_greatest_eclipse.h" 26 | #include "settings.h" 27 | #include "shadow_calc.h" 28 | 29 | void render_3d_eclipse_map(settings *config, double jd, jpeg_ptr earthDay, 30 | const double *pos_sun, const double *pos_earth, 31 | const shadow_map *shadow_map, const eclipse_path_list *eclipse_path); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /solarEclipseRender/src/rendering.c: -------------------------------------------------------------------------------- 1 | // rendering.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | #include 24 | 25 | #include 26 | 27 | #include 28 | 29 | #include "rendering.h" 30 | 31 | //! X_FIX - Make sure that a horizontal position is within the range 0 -> (x_size-1) 32 | #define X_FIX(x) (x + x_size) % x_size 33 | 34 | //! color_blend - Blend two colours together. 35 | //! Output (new_colour * fraction + old_colour * (1-fraction) ) 36 | //! 37 | //! @param new_colour [in] - The new colour to add to the pixel, specified as a colour structure 38 | //! @param old_colour [in] - The existing colour of the pixel, specified as a Cairo ARGB 24-bit integer 39 | //! @param fraction [in] - The fraction with which to turn the pixel to the new colour 40 | //! \return - The new colour, specified as a Cairo ABGR 24-bit integer 41 | 42 | uint32_t color_blend(const colour *new_colour, const uint32_t *old_colour, double fraction) { 43 | // Unpack the new colour 44 | unsigned int red_1 = (unsigned) new_colour->red; 45 | unsigned int green_1 = (unsigned) new_colour->grn; 46 | unsigned int blue_1 = (unsigned) new_colour->blu; 47 | 48 | // Cairo's ARGB32 pixel format stores pixels as 32-bit ints, with alpha in most significant byte. 49 | unsigned int red_2 = (*old_colour >> (unsigned) 16) & (unsigned) 255; 50 | unsigned int green_2 = (*old_colour >> (unsigned) 8) & (unsigned) 255; 51 | unsigned int blue_2 = (*old_colour) & (unsigned) 255; 52 | 53 | // Mix the two colours together 54 | unsigned int red = (unsigned) (red_1 * fraction + red_2 * (1 - fraction)); 55 | unsigned int green = (unsigned) (green_1 * fraction + green_2 * (1 - fraction)); 56 | unsigned int blue = (unsigned) (blue_1 * fraction + blue_2 * (1 - fraction)); 57 | 58 | // Pack into an output structure 59 | return ((uint32_t) blue + // blue 60 | ((uint32_t) green << (unsigned) 8) + // green 61 | ((uint32_t) red << (unsigned) 16) + // red 62 | ((uint32_t) 255 << (unsigned) 24) // alpha 63 | ); 64 | } 65 | 66 | //! set_pixel - Set the colour of a pixel in an array of Cairo ARGB 24-bit integers. Includes alpha blending. 67 | //! 68 | //! @param frame - The array of Cairo ARGB 24-bit integers 69 | //! @param x_size [in] - The horizontal number of pixels in each row 70 | //! @param stride [in] - The number of bytes separating consecutive rows in . NB: bytes, not pixels. 71 | //! @param x [in] - The horizontal position of the pixel to set 72 | //! @param y [in] - The vertical position of the pixel to set 73 | //! @param colour [in] - The colour to set the pixel 74 | //! @param fraction [in] - The opacity of the new pixel colour (0-1) 75 | 76 | void set_pixel(unsigned char *frame, int x_size, int stride, int x, int y, const colour *colour, double fraction) { 77 | const int x_pos = X_FIX(x); 78 | const int y_pos = y; 79 | const int offset = x_pos * 4 + y_pos * stride; 80 | 81 | // Blend new colour with old colour 82 | uint32_t blended_color = color_blend(colour, (const uint32_t *) &frame[offset], fraction); 83 | 84 | // Set new (blended) colour of pixel 85 | *(uint32_t *) &frame[offset] = blended_color; 86 | } 87 | 88 | //! test_pixel - Test a pixel to see whether it lies on a contour 89 | //! 90 | //! @param shadow [in] - An array of the eclipse magnitude in each pixel of the output image 91 | //! @param x [in] - The horizontal position of the pixel to test 92 | //! @param y [in] - The vertical position of the pixel to test 93 | //! @param x_size [in] - The horizontal number of pixels in each row 94 | //! @param level [in] - The eclipse magnitude (0-1) for which we are drawing a contour 95 | //! @return Boolean flag indicating whether this pixel lies on the contour 96 | 97 | int test_pixel(const shadow_map *shadow, int x, int y, int x_size, double level) { 98 | return ((shadow->map[y * x_size + X_FIX(x)] > level) && 99 | ((shadow->map[(y - 1) * x_size + X_FIX(x)] <= level) || 100 | (shadow->map[(y - 1) * x_size + X_FIX(x - 1)] <= level) || 101 | (shadow->map[(y) * x_size + X_FIX(x - 1)] <= level) || 102 | (shadow->map[(y + 1) * x_size + X_FIX(x - 1)] <= level) || 103 | (shadow->map[(y + 1) * x_size + X_FIX(x)] <= level) || 104 | (shadow->map[(y + 1) * x_size + X_FIX(x + 1)] <= level) || 105 | (shadow->map[(y) * x_size + X_FIX(x + 1)] <= level) || 106 | (shadow->map[(y - 1) * x_size + X_FIX(x + 1)] <= level) 107 | ) 108 | ); 109 | } 110 | 111 | //! shadowContoursLabelPositions - Work out positions for placing the labels on each of the contours 112 | //! @param contourList [in] - An array of the eclipse magnitudes (0-100) to draw contours for. Terminate with any 113 | //! negative value. 114 | //! @param shadow [in] - An array of the eclipse magnitude in each pixel of the output image 115 | //! @param x_offset [in] - Shift the output diagram horizontally by some number of pixels to place a longitude other 116 | //! than zero at the centre. 117 | //! @param x_size [in] - The horizontal pixel size of the output image 118 | //! @param y_size [in] - The vertical pixel size of the output image 119 | //! @param label_position_x [out] - Output a newly-malloced array of the horizontal positions of each of the contour 120 | //! labels 121 | //! @param label_position_y [out] - Output a newly-malloced array of the vertical positions of each of the contour 122 | //! labels 123 | //! @param previous_label_position_x [in] - An array of the positions of each contour labels in the previous frame of 124 | //! a video. Used to avoid too much jitter in the positions. 125 | //! @param previous_label_position_y [in] - An array of the positions of each contour labels in the previous frame of 126 | //! a video. Used to avoid too much jitter in the positions. 127 | 128 | void shadowContoursLabelPositions(const double *contourList, const shadow_map *shadow, 129 | int x_offset, int x_size, int y_size, 130 | int **label_position_x, int **label_position_y, 131 | int *previous_label_position_x, int *previous_label_position_y) { 132 | int y, x_output, i, j; 133 | 134 | *label_position_x = malloc(256 * sizeof(int)); 135 | *label_position_y = malloc(256 * sizeof(int)); 136 | 137 | for (i = 0; i < 256; i++) { 138 | (*label_position_x)[i] = (*label_position_y)[i] = -10000; 139 | } 140 | 141 | for (i = 0; contourList[i] >= 0; i++) { 142 | const double level = contourList[i] / 100.; 143 | double best_radius = 1e6; 144 | 145 | // Do not place contour labels right at the edges of the image 146 | for (y = 25; y < y_size - 25; y++) 147 | for (x_output = 25; x_output < x_size - 25; x_output++) { 148 | const int x = X_FIX(x_output + x_offset); 149 | if (test_pixel(shadow, x, y, x_size, level)) { 150 | 151 | double radius = sqrt(gsl_pow_2(x_output - x_size / 2.) + gsl_pow_2(y - y_size / 2.)); 152 | 153 | if (previous_label_position_x != NULL) { 154 | for (j = 0; contourList[j] >= 0; j++) { 155 | if (previous_label_position_x[j] > 0) { 156 | radius += sqrt(gsl_pow_2(x_output - previous_label_position_x[j]) + 157 | gsl_pow_2(y - previous_label_position_y[j])); 158 | } 159 | } 160 | } 161 | 162 | if (radius < best_radius) { 163 | best_radius = radius; 164 | (*label_position_x)[i] = x_output; 165 | (*label_position_y)[i] = y; 166 | } 167 | } 168 | } 169 | } 170 | } 171 | 172 | /** 173 | * drawShadowContours - Draw contours onto an array of Cairo ARGB 24-bit integers, wherever the values in the array 174 | * pass any of the thresholds in the array . 175 | * @param frame [in] - The array of Cairo ARGB 24-bit integers onto which to trace the contours. 176 | * @param shadow [in] - The array of shadow fractions within each pixel, which we are to draw contours from 177 | * @param x_offset [in] - Shift the output diagram horizontally by some number of pixels to place a longitude other than 178 | * zero at the centre. 179 | * @param x_wrap [in] - Boolean flag indicating whether contours wrap around from the left/right edges. 180 | * @param stride [in] - The number of bytes separating consecutive rows in . 181 | * @param bold [in] - Boolean flag indicating whether to make contours bold 182 | * @param x_size [in] - The horizontal pixel size of the output image 183 | * @param y_size [in] - The vertical pixel size of the output image 184 | */ 185 | void drawShadowContours(unsigned char *frame, const double *contourList, const shadow_map *shadow, 186 | int *label_position_x, int *label_position_y, 187 | int x_offset, int x_wrap, int stride, int x_size, int y_size) { 188 | int x, y, i, j; 189 | 190 | const colour yellow = {255, 255, 0}; 191 | const colour red = {255, 0, 0}; 192 | 193 | for (i = 0; contourList[i] >= 0; i++) { 194 | const double level = contourList[i] / 100.; 195 | 196 | const colour color = (level > 0.01) ? yellow : red; 197 | 198 | const int x_min = x_wrap ? 0 : 1; 199 | const int x_max = x_wrap ? x_size : (x_size - 1); 200 | 201 | for (y = 1; y < y_size - 1; y++) 202 | for (x = x_min; x < x_max; x++) { 203 | if (test_pixel(shadow, x, y, x_size, level)) { 204 | const int x_output = X_FIX(x - x_offset); 205 | 206 | int mask_pixel = 0; 207 | for (j = 0; contourList[j] >= 0; j++) { 208 | const double radius = (gsl_pow_2(x_output - label_position_x[j]) + 209 | gsl_pow_2(y - label_position_y[j])); 210 | const double critical_radius = 16; 211 | if (radius < critical_radius * critical_radius) { 212 | mask_pixel = 1; 213 | } 214 | } 215 | if (mask_pixel) continue; 216 | 217 | set_pixel(frame, x_size, stride, x_output, y, &color, 1); 218 | 219 | set_pixel(frame, x_size, stride, x_output - 1, y, &color, 0.25); 220 | set_pixel(frame, x_size, stride, x_output + 1, y, &color, 0.25); 221 | 222 | set_pixel(frame, x_size, stride, x_output, y - 1, &color, 0.25); 223 | set_pixel(frame, x_size, stride, x_output, y + 1, &color, 0.25); 224 | 225 | set_pixel(frame, x_size, stride, x_output - 1, y - 1, &color, 0.1); 226 | set_pixel(frame, x_size, stride, x_output + 1, y - 1, &color, 0.1); 227 | set_pixel(frame, x_size, stride, x_output - 1, y + 1, &color, 0.1); 228 | set_pixel(frame, x_size, stride, x_output + 1, y + 1, &color, 0.1); 229 | } 230 | } 231 | } 232 | } 233 | 234 | //! chart_label - Write a text label onto a cairo page immediately. 235 | //! @param cairo_draw [in] - The cairo drawing context 236 | //! @param colour [in] - The colour to use to write the text 237 | //! @param label [in] - The string of the text label 238 | //! @param x_canvas [in] - The horizontal position of the label 239 | //! @param y_canvas [in] - The vertical position of the label 240 | //! @param h_align [in] - The horizontal alignment of the label 241 | //! @param v_align [in] - The vertical alignment of the label 242 | //! @param font_size [in] - Font size of label (pixels) 243 | //! @param font_bold [in] - Boolean indicating whether to render label in bold 244 | //! @param font_italic [in] - Boolean indicating whether to render label in italic 245 | //! @return - None 246 | 247 | void chart_label(cairo_t *cairo_draw, colour colour, const char *label, 248 | double x_canvas, double y_canvas, int h_align, int v_align, 249 | double font_size, int font_bold, int font_italic) { 250 | 251 | // Select font 252 | cairo_text_extents_t extents; 253 | cairo_set_font_size(cairo_draw, font_size); 254 | cairo_select_font_face(cairo_draw, "Sans", 255 | font_italic ? CAIRO_FONT_SLANT_ITALIC : CAIRO_FONT_SLANT_NORMAL, 256 | font_bold ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL); 257 | 258 | // Measure text bounding box 259 | cairo_text_extents(cairo_draw, label, &extents); 260 | 261 | switch (h_align) { 262 | case 1: 263 | x_canvas -= extents.width + extents.x_bearing; // right align 264 | break; 265 | case 0: 266 | x_canvas -= extents.width / 2 + extents.x_bearing; // centre align 267 | break; 268 | case -1: 269 | x_canvas -= extents.x_bearing; // left align 270 | break; 271 | default: 272 | break; 273 | } 274 | 275 | switch (v_align) { 276 | case 1: 277 | y_canvas -= extents.height + extents.y_bearing; // top align 278 | break; 279 | case 0: 280 | y_canvas -= extents.height / 2 + extents.y_bearing; // centre align 281 | break; 282 | case -1: 283 | y_canvas -= extents.y_bearing; // bottom align 284 | break; 285 | default: 286 | break; 287 | } 288 | 289 | // Render the text label itself 290 | cairo_set_source_rgb(cairo_draw, colour.red / 255., colour.grn / 255., colour.blu / 255.); 291 | cairo_move_to(cairo_draw, x_canvas, y_canvas); 292 | cairo_show_text(cairo_draw, label); 293 | } 294 | -------------------------------------------------------------------------------- /solarEclipseRender/src/rendering.h: -------------------------------------------------------------------------------- 1 | // rendering.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef RENDERING_H 23 | #define RENDERING_H 1 24 | 25 | #include 26 | #include "shadow_calc.h" 27 | 28 | //! Define an RGB colour to use to draw a particular item on a chart 29 | typedef struct colour { 30 | double red, grn, blu; 31 | } colour; 32 | 33 | void shadowContoursLabelPositions(const double *contourList, const shadow_map *shadow, 34 | int x_offset, int x_size, int y_size, 35 | int **label_position_x, int **label_position_y, 36 | int *previous_label_position_x, int *previous_label_position_y); 37 | 38 | void drawShadowContours(unsigned char *frame, const double *contourList, const shadow_map *shadow, 39 | int *label_position_x, int *label_position_y, 40 | int x_offset, int x_wrap, int stride, int x_size, int y_size); 41 | 42 | void chart_label(cairo_t *cairo_draw, colour colour, const char *label, 43 | double x_canvas, double y_canvas, int h_align, int v_align, 44 | double font_size, int font_bold, int font_italic); 45 | 46 | #endif -------------------------------------------------------------------------------- /solarEclipseRender/src/settings.c: -------------------------------------------------------------------------------- 1 | // settings.c 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #include 23 | 24 | #include "settings.h" 25 | 26 | settings default_settings() { 27 | settings output; 28 | 29 | output.output_dir = "/tmp/eclipse_demo/"; 30 | output.title = "Undefined"; 31 | 32 | output.x_size_2d = 1600; 33 | output.y_size_2d = 800; 34 | 35 | output.x_size_3d = 800; 36 | output.y_size_3d = 800; 37 | 38 | output.earth_pixel_radius = 320; 39 | 40 | output.x_size_teaser = 400; 41 | output.y_size_teaser = 200; 42 | 43 | output.moon_shadow_fade_fraction = 0.5; 44 | 45 | output.shadow_col_r = 16; 46 | output.shadow_col_g = 16; 47 | output.shadow_col_b = 16; 48 | 49 | output.jd_min = 2449483.217363426; 50 | output.jd_max = 2449483.217365426; 51 | 52 | output.time_resolution = 0.1; 53 | 54 | output.eclipse_path_search_time_resolution = 10; 55 | output.video_time_resolution = 100; 56 | output.binary_map_time_resolution = 600; 57 | output.binary_map_angular_resolution = 4; 58 | 59 | static const char *monthNames[] = {"x", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", 60 | "Dec"}; 61 | output.month_names = monthNames; 62 | 63 | return output; 64 | } 65 | -------------------------------------------------------------------------------- /solarEclipseRender/src/settings.h: -------------------------------------------------------------------------------- 1 | // settings.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef SETTINGS_H 23 | #define SETTINGS_H 1 24 | 25 | #include "coreUtils/strConstants.h" 26 | 27 | typedef struct settings { 28 | 29 | /** 30 | * output - The directory to store output in 31 | */ 32 | char *output_dir; 33 | 34 | /** 35 | * title - The title of this eclipse event, e.g. 'Annular Solar Eclipse'. Useful for debugging... 36 | */ 37 | 38 | char *title; 39 | 40 | /** 41 | * Dimensions of the video showing the eclipse on a flat map of the world 42 | */ 43 | int x_size_2d, y_size_2d; 44 | 45 | /** 46 | * Dimensions of the video showing the eclipse on a 3D globe of the world 47 | */ 48 | int x_size_3d, y_size_3d; 49 | 50 | /** 51 | * Radius of the Earth in the 3D video 52 | */ 53 | int earth_pixel_radius; 54 | 55 | /** 56 | * Dimensions of eclipse teaser image, used on the page 57 | */ 58 | int x_size_teaser, y_size_teaser; 59 | 60 | /** 61 | * Shadow of Moon lets 50% of Earth light remain in videos 62 | */ 63 | double moon_shadow_fade_fraction; 64 | 65 | 66 | /** 67 | * Color to use to shade the Moon's shadow on the Earth 68 | */ 69 | int shadow_col_r; 70 | int shadow_col_g; 71 | int shadow_col_b; 72 | 73 | /** 74 | * Time span for eclipse simulation, specified in TT 75 | */ 76 | double jd_min, jd_max; 77 | 78 | /** 79 | * Time resolution for searching for maximum eclipse at any given location 80 | */ 81 | double time_resolution; 82 | 83 | /** 84 | * Time resolution for searching path a maximum eclipse 85 | */ 86 | double eclipse_path_search_time_resolution; 87 | 88 | /** 89 | * Time resolution for video frames 90 | */ 91 | double video_time_resolution; 92 | 93 | /** 94 | * Time resolution for binary time-lapse snapshots of eclipse 95 | */ 96 | 97 | double binary_map_time_resolution; 98 | 99 | /** 100 | * Within the binary time-lapse snapshots, sample this number of points per degree of latitude and longitude 101 | */ 102 | double binary_map_angular_resolution; 103 | 104 | /** 105 | * month_names - Names of the months, as they should be displayed in the date/time display in the bottom corner 106 | */ 107 | const char **month_names; 108 | 109 | /** 110 | * poster_image_frame - The frame number to use as a poster image for the eclipse videos 111 | */ 112 | int poster_image_frame; 113 | 114 | /** 115 | * frame_counter - The frame currently being worked on 116 | */ 117 | int frame_counter; 118 | 119 | /** 120 | * solar_longitude_at_midpoint - The longitude on Earth where the Sun is overhead at the midpoint of the eclipse 121 | * (degrees) 122 | */ 123 | double solar_longitude_at_midpoint; 124 | } settings; 125 | 126 | /** 127 | * time_span - Structure for storing the time span of a solar eclipse. 128 | * All times stored as Julian day numbers, in TT. 129 | */ 130 | typedef struct time_span { 131 | double total_start; 132 | double total_end; 133 | double partial_start; 134 | double partial_end; 135 | 136 | double greatest_eclipse_magnitude; // In range 0-1 137 | double greatest_eclipse_longitude; // degrees 138 | double greatest_eclipse_latitude; // degrees 139 | } time_span; 140 | 141 | settings default_settings(); 142 | 143 | #endif -------------------------------------------------------------------------------- /solarEclipseRender/src/shadow_calc.h: -------------------------------------------------------------------------------- 1 | // shadow_calc.h 2 | 3 | // ------------------------------------------------- 4 | // Copyright 2019-2020 Dominic Ford. 5 | 6 | // This file is part of EclipseRender. 7 | 8 | // EclipseRender is free software: you can redistribute it and/or modify 9 | // it under the terms of the GNU General Public License as published by 10 | // the Free Software Foundation, either version 3 of the License, or 11 | // (at your option) any later version. 12 | // 13 | // EclipseRender is distributed in the hope that it will be useful, 14 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | // GNU General Public License for more details. 17 | // 18 | // You should have received a copy of the GNU General Public License 19 | // along with EclipseRender. If not, see . 20 | // ------------------------------------------------- 21 | 22 | #ifndef SHADOW_CALC_H 23 | #define SHADOW_CALC_H 1 24 | 25 | #include "settings.h" 26 | 27 | typedef struct shadow_map { 28 | double *map; 29 | double *lat, *lng; // degrees 30 | double *jd_partial_start, *jd_total_start, *jd_total_end, *jd_partial_end; // JD; TT 31 | } shadow_map; 32 | 33 | double delta_t(double JD); 34 | 35 | double siderealTime(double JD); 36 | 37 | void earthTopocentricPositionICRF(double *out, double lat, double lng, double radius_in_earth_radii, 38 | const double *pos_earth, double epoch, double sidereal_time); 39 | 40 | int testIfAnnularEclipse(double lat, double lng, double JD, 41 | double radius, const double *pos_sun, const double *pos_moon, const double *pos_earth, 42 | double sidereal_time); 43 | 44 | double getShadowFraction(double lat, double lng, double JD, 45 | double radius, const double *pos_sun, const double *pos_moon, const double *pos_earth, 46 | double sidereal_time); 47 | 48 | shadow_map *allocate_shadow_map(int x_size, int y_size); 49 | 50 | void shadow_map_free(shadow_map *item); 51 | 52 | void calculate_where_sun_overhead(double *lat_sun, double *lng_sun, double *sidereal_time, 53 | const double *pos_sun, const double *pos_earth, double jd); 54 | 55 | shadow_map *calculate_eclipse_map_2d(const settings *config, 56 | double jd, const double *pos_sun, const double *pos_earth, 57 | const double *pos_moon, 58 | time_span *span_output, shadow_map *greatest_shadow); 59 | 60 | shadow_map *calculate_eclipse_map_3d(const settings *config, 61 | double jd, const double *pos_sun, const double *pos_earth, 62 | const double *pos_moon); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /solarEclipseRender/worldMap/countryList.dat: -------------------------------------------------------------------------------- 1 | 1248 40 202 211 60.1 19.9 Aland Islands 2 | 1004 74 149 24 34.5 69.2 Afghanistan 3 | 1008 97 118 21 41.3 19.8 Albania 4 | 1012 108 210 109 36.8 3.0 Algeria 5 | 1016 160 207 154 -14.3 -170.7 American Samoa 6 | 1020 148 7 229 42.5 1.5 Andorra 7 | 1024 152 205 143 -8.8 13.2 Angola 8 | 1660 132 111 232 18.2 -63.1 Anguilla 9 | 2000 172 233 237 -60.5 -45.7 Antarctica 10 | 1028 13 122 240 17.7 -61.8 Antigua and Barbuda 11 | 1032 194 212 114 -34.6 -58.4 Argentina 12 | 1051 27 77 246 40.2 44.5 Armenia 13 | 1533 72 75 135 12.5 -70.0 Aruba 14 | 1036 6 133 216 -35.3 149.1 Australia 15 | 1040 65 78 85 48.2 16.4 Austria 16 | 1031 217 248 244 40.4 49.9 Azerbaijan 17 | 1044 87 51 15 25.1 -77.3 Bahamas 18 | 1048 162 168 145 26.2 50.6 Bahrain 19 | 1096 143 79 54 4.9 114.9 Brunei 20 | 2001 65 45 210 0.2 -176.5 Baker Island 21 | 1050 19 72 196 23.7 90.4 Bangladesh 22 | 1052 104 52 166 13.1 -59.6 Barbados 23 | 1112 83 136 227 53.9 27.6 Belarus 24 | 1056 145 22 55 50.9 4.3 Belgium 25 | 1084 184 62 232 17.2 -88.8 Belize 26 | 1204 133 95 100 6.5 2.6 Benin 27 | 1060 158 64 137 32.3 -64.8 Bermuda 28 | 1064 67 23 229 27.5 89.6 Bhutan 29 | 1068 230 125 25 -19.0 -65.3 Bolivia 30 | 2002 213 91 129 17.6 -63.2 Bonaire, Saint Eustatius and Saba 31 | 1070 211 89 57 43.8 18.4 Bosnia and Herzegovina 32 | 1072 229 196 60 -24.7 25.9 Botswana 33 | 2003 88 39 45 -54.4 3.4 Bouvet Island 34 | 1076 112 104 226 -15.8 -47.9 Brazil 35 | 2004 226 34 204 -5.2 72.0 British Indian Ocean Territory 36 | 1092 53 175 2 18.4 -64.6 British Virgin Islands 37 | 1100 183 148 147 42.7 23.3 Bulgaria 38 | 1854 249 59 198 12.4 -1.5 Burkina Faso 39 | 1104 161 17 150 19.7 96.1 Myanmar 40 | 1108 182 144 15 -3.4 29.4 Burundi 41 | 1116 97 243 234 11.6 104.9 Cambodia 42 | 1120 92 148 221 3.9 11.5 Cameroon 43 | 1124 142 127 100 45.4 -75.7 Canada 44 | 1132 8 221 217 14.9 -23.5 Cape Verde 45 | 1136 54 229 192 19.3 -81.4 Cayman Islands 46 | 1140 39 235 43 4.4 18.6 Central African Republic 47 | 1148 128 115 207 12.1 15.0 Chad 48 | 1152 227 173 132 -33.5 -70.6 Chile 49 | 1156 42 233 56 39.9 116.4 China 50 | 1162 162 54 25 -10.4 105.7 Christmas Island 51 | 2005 216 125 243 10.3 -109.2 Clipperton Island 52 | 1166 109 64 213 -12.2 96.8 Cocos Islands 53 | 1170 22 86 242 4.6 -74.1 Colombia 54 | 1174 211 213 249 -11.7 43.3 Comoros 55 | 1178 180 254 187 -4.3 15.3 Republic of the Congo 56 | 1180 155 129 92 -4.3 15.3 Democratic Republic of the Congo 57 | 1184 81 119 90 -21.2 -159.8 Cook Islands 58 | 1188 111 75 149 9.9 -84.1 Costa Rica 59 | 1191 185 156 175 45.8 16.0 Croatia 60 | 1192 88 18 210 23.1 -82.4 Cuba 61 | 1531 110 228 181 12.1 -68.9 Curacao 62 | 1196 216 36 9 35.2 33.4 Cyprus 63 | 1203 106 202 188 50.1 14.4 Czechia 64 | 1384 149 225 56 6.8 -5.3 Ivory Coast 65 | 1208 195 233 206 55.7 12.6 Denmark 66 | 1262 136 23 223 11.6 43.1 Djibouti 67 | 1212 246 201 242 15.3 -61.4 Dominica 68 | 1214 87 163 234 18.5 -70.0 Dominican Republic 69 | 1218 0 107 97 -0.2 -78.5 Ecuador 70 | 1818 108 181 13 30.1 31.2 Egypt 71 | 1222 114 121 214 13.7 -89.2 El Salvador 72 | 1226 193 135 215 3.8 8.8 Equatorial Guinea 73 | 1232 32 178 136 15.3 38.9 Eritrea 74 | 1233 11 120 38 59.4 24.8 Estonia 75 | 1231 16 49 105 9.0 38.7 Ethiopia 76 | 1260 128 45 155 -49.4 70.2 French Southern Territories 77 | 1238 18 56 12 -51.7 -57.9 Falkland Islands 78 | 1234 166 144 75 62.0 -6.8 Faroe Islands 79 | 2006 236 50 23 11.7 162.2 Federated States of Micronesia 80 | 1242 146 5 180 -18.1 178.4 Fiji 81 | 1246 249 178 140 60.2 24.9 Finland 82 | 1250 177 132 248 48.9 2.3 France 83 | 1254 194 70 177 4.9 -52.3 French Guiana 84 | 1258 24 66 179 -17.5 -149.6 French Polynesia 85 | 1266 118 232 253 0.4 9.5 Gabon 86 | 1270 174 133 224 13.5 -16.6 Gambia 87 | 1275 84 202 62 31.8 35.2 Palestinian Territory 88 | 1268 39 234 116 41.7 44.8 Georgia 89 | 1276 225 250 94 52.5 13.4 Germany 90 | 1288 210 93 222 5.6 -0.2 Ghana 91 | 1292 27 225 20 36.1 -5.4 Gibraltar 92 | 1300 138 88 211 38.0 23.7 Greece 93 | 1304 184 155 70 64.2 -51.7 Greenland 94 | 2007 132 27 241 12.5 -61.4 Grenada 95 | 1312 181 119 147 16.0 -61.7 Guadeloupe 96 | 1316 56 210 98 13.5 144.7 Guam 97 | 1320 17 164 79 14.6 -90.5 Guatemala 98 | 2008 171 196 238 49.5 -2.5 Guernsey 99 | 1324 3 203 204 9.5 -13.7 Guinea 100 | 1624 21 190 135 11.9 -15.6 Guinea-Bissau 101 | 1328 18 113 131 6.8 -58.2 Guyana 102 | 1332 119 64 183 18.5 -72.3 Haiti 103 | 2009 67 187 249 -52.9 73.6 Heard Island and McDonald Islands 104 | 1340 122 154 247 14.1 -87.2 Honduras 105 | 1344 111 12 160 22.3 114.2 Hong Kong 106 | 2010 114 94 121 0.8 -176.6 Howland Island 107 | 1348 166 226 61 47.5 19.0 Hungary 108 | 1352 226 164 184 64.1 -21.9 Iceland 109 | 1356 141 215 63 28.6 77.2 India 110 | 1360 106 201 61 -6.2 106.8 Indonesia 111 | 1364 182 115 215 35.7 51.4 Iran 112 | 1368 249 219 141 33.3 44.4 Iraq 113 | 1372 236 224 163 53.3 -6.2 Ireland 114 | 1833 86 227 52 54.1 -4.5 Isle of Man 115 | 1376 211 68 165 31.8 35.2 Israel 116 | 1380 183 12 151 41.9 12.5 Italy 117 | 1388 18 162 140 18.0 -76.8 Jamaica 118 | 1392 243 106 249 35.7 139.7 Japan 119 | 2011 158 154 144 -0.4 -160.0 Jarvis Island 120 | 1832 211 26 175 49.2 -2.1 Jersey 121 | 2012 137 189 148 16.7 -169.5 Johnston Atoll 122 | 1400 239 56 78 32.0 35.9 Jordan 123 | 1398 42 103 103 51.2 71.4 Kazakhstan 124 | 1404 253 243 17 -1.3 36.8 Kenya 125 | 2013 48 169 114 6.4 -162.3 Kingman Reef 126 | 1296 167 65 22 1.3 173.0 Kiribati 127 | 1414 187 158 225 29.4 48.0 Kuwait 128 | 1417 24 206 40 42.9 74.6 Kyrgyzstan 129 | 1418 28 39 225 18.0 102.6 Laos 130 | 1428 6 178 224 56.9 24.1 Latvia 131 | 1422 184 111 229 33.9 35.5 Lebanon 132 | 1426 6 222 208 -29.3 27.5 Lesotho 133 | 1430 105 90 78 6.3 -10.8 Liberia 134 | 1434 138 135 140 32.9 13.2 Libya 135 | 1438 134 205 75 47.1 9.5 Liechtenstein 136 | 1440 58 114 218 54.7 25.3 Lithuania 137 | 1442 194 96 56 49.6 6.1 Luxembourg 138 | 1446 71 208 151 22.2 113.6 Macao 139 | 1807 208 109 107 42.0 21.4 Macedonia 140 | 1450 140 236 186 -18.9 47.5 Madagascar 141 | 1454 13 2 136 -14.0 33.8 Malawi 142 | 1458 53 240 160 3.1 101.7 Malaysia 143 | 1462 172 23 136 4.2 73.5 Maldives 144 | 1466 62 234 112 12.7 -8.0 Mali 145 | 1470 170 243 228 35.9 14.5 Malta 146 | 1584 225 29 19 7.1 171.4 Marshall Islands 147 | 1474 174 199 70 14.6 -61.1 Martinique 148 | 1478 106 227 125 18.1 -16.0 Mauritania 149 | 1480 27 180 37 -20.2 57.5 Mauritius 150 | 1175 5 210 71 -12.8 45.2 Mayotte 151 | 1484 251 15 121 19.4 -99.1 Mexico 152 | 2014 20 154 139 28.4 -178.3 Midway Islands 153 | 1498 228 202 248 47.0 28.9 Moldova 154 | 1492 208 125 190 43.7 7.4 Monaco 155 | 1496 14 160 232 47.9 106.9 Mongolia 156 | 1499 88 205 171 42.4 19.3 Montenegro 157 | 1500 185 146 176 16.7 -62.2 Montserrat 158 | 1504 27 246 127 34.0 -6.8 Morocco 159 | 1508 173 233 227 -26.0 32.6 Mozambique 160 | 1516 216 151 100 -22.6 17.1 Namibia 161 | 1520 199 251 245 -0.6 166.9 Nauru 162 | 2015 146 212 139 18.4 -75.0 Navassa Island 163 | 1524 23 191 112 27.7 85.3 Nepal 164 | 1528 131 127 30 52.4 4.9 Netherlands 165 | 1540 191 224 18 -22.3 166.5 New Caledonia 166 | 1554 230 25 192 -41.3 174.8 New Zealand 167 | 1558 33 85 13 12.1 -86.3 Nicaragua 168 | 1562 50 183 209 13.5 2.1 Niger 169 | 1566 21 202 97 9.1 7.5 Nigeria 170 | 1570 146 143 65 -19.1 -169.9 Niue 171 | 1574 179 161 135 -29.1 168.0 Norfolk Island 172 | 1408 144 167 109 39.0 125.8 North Korea 173 | 1580 59 79 195 15.2 145.8 Northern Mariana Islands 174 | 1578 44 239 114 59.9 10.7 Norway 175 | 1512 19 152 110 23.6 58.4 Oman 176 | 1586 63 33 237 33.7 73.0 Pakistan 177 | 1585 98 140 21 7.5 134.6 Palau 178 | 1548 244 49 234 -17.7 168.3 Vanuatu 179 | 2016 55 8 188 5.9 -162.1 Palmyra Atoll 180 | 1591 93 184 4 9.6 -79.6 Panama 181 | 1598 61 46 227 -9.4 147.2 Papua New Guinea 182 | 1600 120 128 146 -25.3 -57.6 Paraguay 183 | 1604 196 13 209 -12.0 -77.0 Peru 184 | 1608 232 174 12 14.6 121.0 Philippines 185 | 1612 77 232 183 -25.1 -130.1 Pitcairn 186 | 1616 158 16 134 52.2 21.0 Poland 187 | 1620 253 170 144 38.7 -9.1 Portugal 188 | 1630 170 189 188 18.5 -66.1 Puerto Rico 189 | 1634 116 90 195 25.3 51.5 Qatar 190 | 1638 164 222 78 -20.9 55.5 Reunion 191 | 1642 214 139 64 44.4 26.1 Romania 192 | 1643 214 94 144 55.8 37.6 Russia 193 | 1646 131 8 40 -1.9 30.1 Rwanda 194 | 1652 186 76 160 17.9 -62.8 Saint Barthelemy 195 | 1654 70 54 78 -15.9 -5.7 Saint Helena 196 | 1659 41 22 195 17.3 -62.7 Saint Kitts and Nevis 197 | 1662 140 35 75 14.0 -61.0 Saint Lucia 198 | 1663 138 195 238 18.1 -63.1 Saint Martin 199 | 1666 47 89 9 46.8 -56.2 Saint Pierre and Miquelon 200 | 1670 103 151 66 13.2 -61.2 Saint Vincent and the Grenadines 201 | 1882 211 186 78 -13.8 -171.8 Samoa 202 | 1674 172 84 53 43.9 12.4 San Marino 203 | 1678 58 16 4 0.3 6.7 Sao Tome and Principe 204 | 1682 31 15 8 24.7 46.7 Saudi Arabia 205 | 1686 171 50 242 14.7 -17.4 Senegal 206 | 1688 196 92 78 44.8 20.5 Serbia 207 | 1690 218 134 216 -4.6 55.5 Seychelles 208 | 1694 141 120 149 8.5 -13.2 Sierra Leone 209 | 1702 139 76 244 1.3 103.9 Singapore 210 | 1534 197 152 128 18.0 -63.0 Sint Maarten 211 | 1703 85 97 145 48.1 17.1 Slovakia 212 | 1705 25 124 206 46.1 14.5 Slovenia 213 | 1090 158 105 79 -9.4 159.9 Solomon Islands 214 | 1706 231 134 184 2.0 45.3 Somalia 215 | 1710 63 60 222 -25.7 28.2 South Africa 216 | 1239 19 197 63 -54.3 -36.5 South Georgia and the South Sandwich Islands 217 | 1410 45 184 4 37.6 127.0 South Korea 218 | 1724 211 212 242 40.4 -3.7 Spain 219 | 2017 238 47 175 11.1 115.0 Spratly Islands 220 | 1144 172 3 100 6.9 79.8 Sri Lanka 221 | 1729 90 216 30 15.6 32.5 Sudan 222 | 1740 159 253 192 5.9 -55.2 Suriname 223 | 1744 153 82 207 78.2 15.6 Svalbard and Jan Mayen 224 | 1748 117 85 246 -26.3 31.1 Swaziland 225 | 1752 63 204 77 59.3 18.1 Sweden 226 | 1756 225 223 68 46.9 7.4 Switzerland 227 | 1760 48 1 29 33.5 36.3 Syria 228 | 1158 237 175 104 25.0 121.5 Taiwan 229 | 1762 69 146 222 38.5 68.8 Tajikistan 230 | 1834 157 202 249 -6.2 35.7 Tanzania 231 | 1764 236 148 0 13.8 100.5 Thailand 232 | 1626 248 186 198 -8.6 125.6 East Timor 233 | 1768 66 239 217 6.1 1.2 Togo 234 | 2018 111 183 62 -8.6 -172.5 Tokelau 235 | 1776 236 86 207 -21.1 -175.2 Tonga 236 | 1780 176 145 243 10.7 -61.5 Trinidad and Tobago 237 | 1788 140 246 197 36.8 10.2 Tunisia 238 | 1792 164 6 150 39.9 32.9 Turkey 239 | 1795 184 201 42 38.0 58.4 Turkmenistan 240 | 1796 114 156 217 21.5 -71.1 Turks and Caicos Islands 241 | 1798 157 67 72 -8.5 179.2 Tuvalu 242 | 1800 43 200 48 0.3 32.6 Uganda 243 | 1804 87 158 178 50.5 30.5 Ukraine 244 | 1784 148 18 189 24.5 54.4 United Arab Emirates 245 | 1826 202 158 236 51.5 -0.1 United Kingdom 246 | 1840 39 120 239 37.1 -113.5 United States 247 | 1858 40 118 227 -34.9 -56.2 Uruguay 248 | 1860 35 49 205 41.3 69.2 Uzbekistan 249 | 1336 156 205 88 41.9 12.5 Vatican 250 | 1862 199 76 175 10.5 -66.9 Venezuela 251 | 1704 229 176 242 21.0 105.8 Vietnam 252 | 1850 191 49 173 18.3 -64.9 U.S. Virgin Islands 253 | 2019 157 201 247 19.3 166.6 Wake Island 254 | 1876 50 0 119 -13.3 -176.2 Wallis and Futuna 255 | 1732 75 55 36 20.8 -17.1 Western Sahara 256 | 1887 253 121 231 15.4 44.2 Yemen 257 | 1894 218 196 223 -15.4 28.3 Zambia 258 | 1716 244 245 174 -17.8 31.1 Zimbabwe 259 | 2020 101 216 140 16.9 112.3 Paracel Islands 260 | -------------------------------------------------------------------------------- /solarEclipseRender/worldMap/worldMap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dcf21/eclipse-simulator/50b43f724c40c5eb731faf5c9cb1e0c7282ad5f9/solarEclipseRender/worldMap/worldMap.png -------------------------------------------------------------------------------- /solarEclipses.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | # solarEclipses.py 4 | # 5 | # The python script in this file makes eclipse simulations. 6 | # 7 | # Copyright (C) 2012-2020 Dominic Ford 8 | # 9 | # This code is free software; you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation; either version 2 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # You should have received a copy of the GNU General Public License along with 15 | # this file; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | # Street, Fifth Floor, Boston, MA 02110-1301, USA 17 | 18 | # ---------------------------------------------------------------------------- 19 | 20 | """ 21 | Create videos of where solar eclipses are visible, and also JSON data files needed to simulate them. 22 | """ 23 | 24 | import argparse 25 | import json 26 | import logging 27 | import os 28 | import re 29 | import sys 30 | import time 31 | from math import floor 32 | 33 | import dask 34 | import solarEclipses_makeKml 35 | from dcf_ast import month_name, julian_day, unix_from_jd 36 | 37 | pid = os.getpid() 38 | 39 | src_path = os.getcwd() 40 | out_path = os.path.join(src_path, "output") 41 | 42 | 43 | @dask.delayed 44 | def make_ephemeris(event_key, event_title, event_duration, j_day, year, month, day): 45 | """ 46 | Make a diagram of the visibility of a particular eclipse. 47 | 48 | :param event_key: 49 | The eventKey for this eclipse in the table 50 | :param event_title: 51 | The title for this eclipse event in the table 52 | :param event_duration: 53 | The duration of this event (seconds) as taken from Fred Espenak 54 | :param j_day: 55 | The Julian day of the eclipse 56 | :type j_day: 57 | float 58 | :param year: 59 | The year of the eclipse 60 | :type year: 61 | int 62 | :param month: 63 | The month number of the eclipse (1-12) 64 | :type month: 65 | int 66 | :param day: 67 | The day of the month at the central point of the eclipse (1-31) 68 | :type day: 69 | int 70 | :return: 71 | A dictionary of the start and end times of the eclipse 72 | """ 73 | 74 | id1 = "{}_{}".format(pid, j_day) 75 | id2 = "{:04d}{:02d}{:02d}".format(year, month, day) 76 | 77 | # Status update 78 | logging.info("Working on {:02d}/{:02d}/{:04d}".format(day, month, year)) 79 | 80 | # Create temporary working directory 81 | tmp = "/tmp/eclipse_{}".format(id1) 82 | os.system("mkdir -p {}".format(tmp)) 83 | os.system("rm -Rf {}/*".format(tmp)) 84 | 85 | # Construct command-line arguments to pass to eclipse-rendering tool 86 | eclipse_renderer = os.path.join(src_path, "solarEclipseRender/bin/eclipseRender.bin") 87 | jd_min = j_day - 3.2 / 24 88 | jd_max = j_day + 3.2 / 24 89 | 90 | cmd = "{exe} --jd_min {min:.3f} --jd_max {max:.3f} --title \"{title}\" --output {tmp} > {tmp}/stdout". \ 91 | format(exe=eclipse_renderer, min=jd_min, max=jd_max, title=event_title, tmp=tmp) 92 | logging.info("Running command: {}".format(cmd)) 93 | time_start = time.time() 94 | os.system(cmd) 95 | time_end = time.time() 96 | 97 | # Read time that eclipse starts / end 98 | times = open("{tmp}/stdout".format(tmp=tmp)).read().split() 99 | 100 | if len(times) < 7: 101 | logging.error("FAILURE in command: {}".format(cmd)) 102 | raise RuntimeError("Eclipse simulator failed") 103 | 104 | output = { 105 | "event_key": event_key, 106 | "partial_start": unix_from_jd(float(times[0])), 107 | "partial_end": unix_from_jd(float(times[1])), 108 | "total_start": unix_from_jd(float(times[2])), 109 | "total_end": unix_from_jd(float(times[3])), 110 | "greatest_eclipse_magnitude": float(times[4]), 111 | "greatest_eclipse_latitude": float(times[5]), 112 | "greatest_eclipse_longitude": float(times[6]) 113 | } 114 | 115 | # Convert frames into MP4 video files 116 | os.system("cd {} ; " 117 | "ffmpeg -nostats -loglevel panic -r 10 -i frameA%06d.png -codec:v libx264 -crf 23 {}/solar_{}_A.mp4". 118 | format(tmp, out_path, id2)) 119 | 120 | os.system("cd {} ; " 121 | "ffmpeg -nostats -loglevel panic -r 10 -i frameB%06d.png -codec:v libx264 -crf 23 {}/solar_{}_B.mp4". 122 | format(tmp, out_path, id2)) 123 | 124 | os.system("cd {} ; " 125 | "ffmpeg -nostats -loglevel panic -r 10 -i frameA%06d.png -acodec vorbis -vcodec libtheora -q:v 4 " 126 | "{}/solar_{}_A.ogg".format(tmp, out_path, id2)) 127 | 128 | os.system("cd {} ; " 129 | "ffmpeg -nostats -loglevel panic -r 10 -i frameB%06d.png -acodec vorbis -vcodec libtheora -q:v 4 " 130 | "{}/solar_{}_B.ogg".format(tmp, out_path, id2)) 131 | 132 | # Move images and JSON files to final destination 133 | os.system("cd {} ; mv solarEclipseA.png {}/solar_{}_A.png".format(tmp, out_path, id2)) 134 | os.system("cd {} ; mv solarEclipseB.png {}/solar_{}_B.png".format(tmp, out_path, id2)) 135 | os.system("cd {} ; mv solarEclipseC.png {}/solar_{}_C.png".format(tmp, out_path, id2)) 136 | os.system("cd {} ; mv maximumEclipse.png {}/solar_{}.png".format(tmp, out_path, id2)) 137 | os.system("cd {} ; mv maximumEclipse.pdf {}/solar_{}.pdf".format(tmp, out_path, id2)) 138 | os.system("cd {} ; mv maximumEclipse.svg {}/solar_{}.svg".format(tmp, out_path, id2)) 139 | os.system("cd {} ; mv maximumEclipse.json {}/solar_{}.json".format(tmp, out_path, id2)) 140 | os.system("cd {} ; mv maximumEclipse.dat {}/solar_{}.dat".format(tmp, out_path, id2)) 141 | 142 | # Merge JSON files 143 | path_json = json.loads(open("{}/maximumEclipsePath.json".format(tmp)).read()) 144 | contours_json = json.loads(open("{}/maximumEclipseContours.json".format(tmp)).read()) 145 | 146 | with open("{}/solar_{}_path.json".format(out_path, id2), "w") as f: 147 | f.write(json.dumps({ 148 | 'path': path_json['paths'], 149 | 'contours': contours_json, 150 | 'compute_time': int(time_start), 151 | 'compute_duration': int(time_end - time_start), 152 | 'event_title': event_title, 153 | 'event_month': "{d} {m} {y}".format(d=day, m=month_name[month - 1], y=year), 154 | 'event_duration_espenak': event_duration, # seconds 155 | 'event_duration_ford': path_json['duration'], # seconds 156 | 'path_segments': path_json['path_segments'], 157 | 'path_midpoint_latitude': path_json['midpoint_latitude'], 158 | 'path_midpoint_longitude': path_json['midpoint_longitude'], 159 | "event_key": event_key, 160 | "partial_start": int(output['partial_start']), 161 | "partial_end": int(output['partial_end']), 162 | "total_start": int(output['total_start']), 163 | "total_end": int(output['total_end']), 164 | "greatest_eclipse_magnitude": output['greatest_eclipse_magnitude'], 165 | "greatest_eclipse_latitude": output['greatest_eclipse_latitude'], 166 | "greatest_eclipse_longitude": output['greatest_eclipse_longitude'], 167 | 'host': os.uname()[1] 168 | }, separators=(',', ':'))) 169 | 170 | # Clean up temporary files 171 | os.system("rm -Rf {}".format(tmp)) 172 | 173 | return output 174 | 175 | 176 | def solar_eclipses(year_min, year_max): 177 | """ 178 | Create videos of where solar eclipses are visible, and also JSON data files needed to simulate them. 179 | 180 | :return: 181 | None 182 | """ 183 | 184 | # Create target directory 185 | os.system("mkdir -p {}".format(out_path)) 186 | os.system("rm -f {}/solar*".format(out_path)) 187 | 188 | # Yuck, but this has to be done 189 | os.environ['TZ'] = 'UTC' 190 | time.tzset() 191 | 192 | # Compile renderer 193 | os.system("cd {} ; ./prettymake".format(os.path.join(src_path, "solarEclipseRender"))) 194 | 195 | # Look through list of solar eclipses, and pick out events within specified range of years. Then make animations 196 | # of all these eclipses 197 | data = "solarEclipses.dat" 198 | 199 | # Loop over all events 200 | eclipse_times_delayed = [] 201 | for line in open(data): 202 | line = line.strip() 203 | if (not line) or (line[0] == "#"): 204 | continue 205 | words = line.split() 206 | ev_year = float(words[2]) 207 | if (ev_year < year_min) or (ev_year > year_max): 208 | continue 209 | ev_mc = words[3] 210 | ev_mon = month_name.index(ev_mc) 211 | ev_day = float(words[4]) 212 | ev_hms = words[5] 213 | [hours_str, mins_str, secs_str] = ev_hms.split(":") 214 | ev_hours = float(hours_str) 215 | ev_mins = float(mins_str) 216 | # ev_secs = float(secs_str) 217 | 218 | # Round to start on a round multiple of 10-minutes 219 | ev_mins = floor(ev_mins / 10) * 10 220 | ev_secs = 0 221 | 222 | jd0 = julian_day(year=int(ev_year), month=int(ev_mon + 1), day=int(ev_day), 223 | hour=int(ev_hours), minute=int(ev_mins), sec=float(ev_secs)) 224 | 225 | # Make string describing eclipse type 226 | eclipse_types = { 227 | 'A': 'Annular solar eclipse', 228 | 'H': 'Hybrid solar eclipse', 229 | 'P': 'Partial solar eclipse', 230 | 'T': 'Total solar eclipse' 231 | } 232 | 233 | eclipse_type = eclipse_types[words[9][0]] 234 | 235 | # Look up eclipse duration 236 | eclipse_duration = 0 237 | test = re.match(r"(\d\d)m(\d\d)s", words[-1]) 238 | if test: 239 | eclipse_duration = int(test.group(1)) + int(test.group(2)) / 60 # minutes 240 | 241 | # Make diagrams of eclipse 242 | eclipse_times_delayed.append(make_ephemeris(event_key="eclipse", 243 | event_title=eclipse_type, 244 | event_duration=eclipse_duration * 60, 245 | j_day=jd0, 246 | year=int(ev_year), 247 | month=int(ev_mon + 1), 248 | day=int(ev_day))) 249 | 250 | # Run eclipse simulations in parallel 251 | dask.compute(*eclipse_times_delayed) 252 | 253 | 254 | # Do it right away if we're run as a script 255 | if __name__ == "__main__": 256 | logging.basicConfig(level=logging.INFO, 257 | stream=sys.stdout, 258 | format='[%(asctime)s] %(levelname)s:%(filename)s:%(message)s', 259 | datefmt='%d/%m/%Y %H:%M:%S') 260 | logger = logging.getLogger(__name__) 261 | logger.info(__doc__.strip()) 262 | 263 | # Read command-line arguments 264 | parser = argparse.ArgumentParser(description=__doc__) 265 | parser.add_argument('--year-min', dest='year_min', type=int, default=2020, 266 | help="The earliest year for which to compute eclipse simulations.") 267 | parser.add_argument('--year-max', dest='year_max', type=int, default=2021, 268 | help="The latest year for which to compute eclipse simulations.") 269 | args = parser.parse_args() 270 | 271 | # We require the ephemerisCompute code. Make sure we have this now. 272 | # We run a dummy ephemeris, in order to make sure that binaries versions of text files are generated now. 273 | ephemeris_compute_path = os.path.join(src_path, "ephemeris-compute-de430") 274 | if not os.path.exists(ephemeris_compute_path): 275 | logging.info("Installing dependant code from ") 276 | os.system("git clone https://github.com/dcf21/ephemeris-compute-de430.git") 277 | os.system("cd ephemeris-compute-de430 ; ./setup.sh ; cd bin ; ./ephem.bin") 278 | 279 | # Make eclipse simulations 280 | solar_eclipses(year_min=args.year_min, year_max=args.year_max) 281 | solarEclipses_makeKml.make_kml() 282 | -------------------------------------------------------------------------------- /solarEclipses_makeKml.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # -*- coding: utf-8 -*- 3 | # solarEclipses_makeKml.py 4 | # 5 | # The python script in this file makes eclipse simulations. 6 | # 7 | # Copyright (C) 2012-2020 Dominic Ford 8 | # 9 | # This code is free software; you can redistribute it and/or modify it under 10 | # the terms of the GNU General Public License as published by the Free Software 11 | # Foundation; either version 2 of the License, or (at your option) any later 12 | # version. 13 | # 14 | # You should have received a copy of the GNU General Public License along with 15 | # this file; if not, write to the Free Software Foundation, Inc., 51 Franklin 16 | # Street, Fifth Floor, Boston, MA 02110-1301, USA 17 | 18 | # ---------------------------------------------------------------------------- 19 | 20 | """ 21 | Make KML files describing the paths of solar eclipses, from JSON equivalents 22 | """ 23 | 24 | import glob 25 | import json 26 | import logging 27 | import os 28 | import sys 29 | import zipfile 30 | 31 | src_path = os.getcwd() 32 | out_path = os.path.join(src_path, "output") 33 | 34 | 35 | class KMLTemplate: 36 | """ 37 | Template to use for producing KML files describing the paths of eclipses 38 | """ 39 | 40 | kml_document = """\ 41 | 42 | 43 | 44 | 45 | 46 | 47 | Dominic Ford 48 | 49 | 50 | 61 | 72 | 78 | 84 | {body} 85 | 86 | 87 | """ 88 | 89 | kml_path = """ 90 | 91 | {path_title} 92 | #{path_style} 93 | 94 | 95 | {point_string} 96 | 97 | 98 | 99 | """ 100 | 101 | kml_polygon = """ 102 | 103 | {polygon_title} 104 | #{polygon_style} 105 | 106 | 107 | 108 | 109 | {point_string} 110 | 111 | 112 | 113 | 114 | 115 | """ 116 | 117 | 118 | def approx(flt, n=2): 119 | """ 120 | Round a floating point number to a set number of decimal places in order to make more compact JSON output. 121 | 122 | :param flt: 123 | Input floating-point value 124 | :param n: 125 | Number of decimal places to round to 126 | :return: 127 | Rounded floating point value 128 | """ 129 | divisor = float(pow(10, n)) 130 | if (type(flt) != int) and (type(flt) != float): 131 | print("Warning: approx() passed argument of type <{}>".format(type(flt))) 132 | return 0 133 | return int(flt * divisor) / divisor 134 | 135 | 136 | def generate_point_string(point_list): 137 | """ 138 | Turn a list of (longitude, latitude) points, each in degrees, into a text string for inclusion in a KML file. 139 | 140 | KML files don't like paths switching from longitude +180 to -180 and vice versa, so we smooth over these transitions 141 | by instead going out of the range +/-180 degrees. 142 | 143 | :param point_list: 144 | List of (longitude, latitude) points, each in degrees 145 | :return: 146 | Textual representation 147 | """ 148 | previous_longitude = None 149 | point_string = "" 150 | 151 | for point in point_list: 152 | longitude, latitude = point 153 | 154 | # Check whether longitude has flipped across the discontinuity at +/- 180 degrees 155 | if previous_longitude is not None: 156 | while longitude <= previous_longitude - 180: 157 | longitude += 360 158 | while longitude > previous_longitude + 180: 159 | longitude -= 360 160 | previous_longitude = longitude 161 | 162 | # Add point to KML file 163 | point_string += "{0:.4f},{1:.4f}\n".format(longitude, latitude) 164 | return point_string 165 | 166 | 167 | def make_kml(): 168 | """ 169 | Make KML files describing the paths of solar eclipses, from JSON equivalents. 170 | 171 | :return: 172 | None 173 | """ 174 | 175 | # Read JSON files produced by describing the path of each solar eclipse 176 | json_in_path = os.path.join(out_path, "*_path.json") 177 | filenames = glob.glob(json_in_path) 178 | 179 | json_data = [] 180 | for filename in filenames: 181 | item = json.loads(open(filename).read()) 182 | item['filename'] = filename 183 | json_data.append(item) 184 | 185 | # Sort items in order of computational time, and name the worst offenders 186 | json_data.sort(key=lambda x: x['compute_duration']) 187 | json_data.reverse() 188 | item_count = 10 189 | for number, item in enumerate(json_data[:item_count]): 190 | logging.info("Longest compute times {n}/{c}: {k} - {t} ({d:.1f} min)". 191 | format(n=number + 1, c=len(json_data), k=item['event_key'], t=item['event_title'], 192 | d=item['compute_duration'] / 60.)) 193 | 194 | # Sort items in order of duration error, and name the worst offenders 195 | json_data.sort(key=lambda x: abs(x['event_duration_ford'] - x['event_duration_espenak'])) 196 | json_data.reverse() 197 | item_count = 25 198 | for number, item in enumerate(json_data[:item_count]): 199 | logging.info("Worst durations {n}/{c}: {k} - {t} ({d1:5.1f} vs {d2:5.1f})". 200 | format(n=number + 1, c=len(json_data), k=item['event_key'], t=item['event_title'], 201 | d1=item['event_duration_ford'], d2=item['event_duration_espenak'])) 202 | 203 | # Sort items into chronological order 204 | json_data.sort(key=lambda x: x['filename']) 205 | 206 | # Compile list of paths of all total / annular eclipses 207 | paths_all = [] 208 | for item in json_data: 209 | # Ignore partial eclipses, with no path 210 | if len(item['path']) == 0: 211 | continue 212 | 213 | # Calculate midpoint lat/lng 214 | flattened_path = [point for path in item['path'] for point in path[1]] 215 | midpoint = [approx(x) for x in flattened_path[int(len(flattened_path) / 2)][1:3]] 216 | 217 | path_list = [] 218 | for x in item['path']: 219 | # Shorten paths to include only every fifth point 220 | points = [[approx(p[1]), approx(p[2])] for p in x[1][::5]] 221 | 222 | # Make sure we include the final point 223 | points.append([approx(x[1][-1][1]), approx(x[1][-1][2])]) 224 | 225 | # Add this path to the list of paths 226 | path_list.append([x[0], points]) 227 | 228 | # Append item to list 229 | paths_all.append({ 230 | 'paths': path_list, 231 | 'event_key': item['event_key'], 232 | 'event_title': item['event_title'], 233 | 'month': item['event_month'], 234 | 'year': int(item['event_month'][-4:]), 235 | 'max_duration': item['event_duration_espenak'], 236 | 'mean_position': midpoint 237 | }) 238 | 239 | # Write list of paths 240 | filename = os.path.join(out_path, "paths_all.json") 241 | with open(filename, "w") as f: 242 | f.write(json.dumps(paths_all, separators=(',', ':'))) 243 | 244 | # Produce KML files 245 | kml_template = KMLTemplate() 246 | for item in json_data: 247 | # Write HTML description for this eclipse 248 | event_title = "{}, {}".format(item['event_title'], item['event_month']) 249 | 250 | event_description = """ 251 |
252 |

253 | © 2011-2019 Dominic Ford / In-The-Sky.org 254 |

255 | Calculated using Dominic Ford's Eclipse Simulator 256 |
257 | based on the JPL DE430 ephemeris. 258 |

259 | Downloaded from In-The-Sky.org 260 |

261 |
262 | """ 263 | kml_body = "" 264 | 265 | # Add paths where eclipse is total or annular 266 | for path in item['path']: 267 | point_string = generate_point_string(point_list=[point[1:3] for point in path[1]]) 268 | kml_body += kml_template.kml_path.format(path_title="Central path, {}".format(item['event_month']), 269 | path_style="eclipseTotal" if path[0] else "eclipseAnnular", 270 | point_string=point_string) 271 | 272 | # Add contours 273 | for contour in item['contours']: 274 | if len(contour[1]) > 0: 275 | point_string = generate_point_string(point_list=[point[0:2] for point in contour[1]]) 276 | kml_body += kml_template.kml_polygon.format(polygon_title=contour[0], 277 | polygon_style=( 278 | "eclipseOuter" if contour[0] == "Partial eclipse" 279 | else "eclipseInner"), 280 | point_string=point_string) 281 | 282 | # Write KMZ output 283 | file_stub = os.path.split(item['filename'])[1][:-5] 284 | filename = os.path.join(out_path, "{}.kmz".format(file_stub)) 285 | with zipfile.ZipFile(filename, "w", zipfile.ZIP_DEFLATED) as myzip: 286 | kml_filename = "{}.kml".format(file_stub) 287 | with myzip.open(kml_filename, "w") as f: 288 | f.write(kml_template.kml_document.format( 289 | title=event_title, 290 | description=event_description, 291 | body=kml_body 292 | ).encode('utf-8')) 293 | 294 | 295 | # Do it right away if we're run as a script 296 | if __name__ == "__main__": 297 | logging.basicConfig(level=logging.INFO, 298 | stream=sys.stdout, 299 | format='[%(asctime)s] %(levelname)s:%(filename)s:%(message)s', 300 | datefmt='%d/%m/%Y %H:%M:%S') 301 | logger = logging.getLogger(__name__) 302 | logger.info(__doc__.strip()) 303 | 304 | make_kml() 305 | --------------------------------------------------------------------------------