28using namespace helios;
34 std::ostringstream oss;
35 oss << std::setw(4) << std::setfill(
'0') << date.year <<
":"
36 << std::setw(2) << std::setfill(
'0') << date.month <<
":"
37 << std::setw(2) << std::setfill(
'0') << date.day <<
" "
38 << std::setw(2) << std::setfill(
'0') << time.hour <<
":"
39 << std::setw(2) << std::setfill(
'0') << time.minute <<
":"
40 << std::setw(2) << std::setfill(
'0') << time.second;
53 const int hour_offset_int =
static_cast<int>(std::floor(utc_offset_helios_west_positive));
54 const float fractional_hour = utc_offset_helios_west_positive -
static_cast<float>(hour_offset_int);
55 const int minute_offset =
static_cast<int>(std::round(fractional_hour * 60.0f));
57 int total_minutes =
static_cast<int>(local_time.
hour) * 60 +
static_cast<int>(local_time.
minute) + hour_offset_int * 60 + minute_offset;
58 const int total_seconds =
static_cast<int>(local_time.
second);
61 while (total_minutes < 0) {
62 total_minutes += 24 * 60;
65 while (total_minutes >= 24 * 60) {
66 total_minutes -= 24 * 60;
71 int year = local_date.
year;
72 int jd = local_date.
JulianDay() + day_shift;
75 const int prev_year_days = (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? 366 : 365;
79 const int year_days = (((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0)) ? 366 : 365;
80 if (jd <= year_days)
break;
86 utc_time.
hour = total_minutes / 60;
87 utc_time.
minute = total_minutes % 60;
88 utc_time.
second = total_seconds;
92 std::string formatStandardOffset(
float utc_offset_helios_west_positive) {
94 float std_offset = -utc_offset_helios_west_positive;
95 char sign = (std_offset >= 0.f) ?
'+' :
'-';
96 float abs_off = std::fabs(std_offset);
97 int hh =
static_cast<int>(std::floor(abs_off));
98 int mm =
static_cast<int>(std::round((abs_off -
static_cast<float>(hh)) * 60.0f));
105 std::snprintf(buf,
sizeof(buf),
"%c%02d:%02d", sign, hh, mm);
106 return std::string(buf);
111void RadiationModel::populateImageEXIF(
const std::string &camera_label,
helios::ImageEXIFData &exif)
const {
113 if (cameras.find(camera_label) == cameras.end()) {
114 helios_runtime_error(
"ERROR (RadiationModel::populateImageEXIF): Camera '" + camera_label +
"' does not exist.");
117 const auto &cam = cameras.at(camera_label);
125 if (!cam.manufacturer.empty()) {
126 exif.
make = cam.manufacturer;
127 const std::string prefix = cam.manufacturer +
" ";
128 if (cam.model.size() > prefix.size() && cam.model.compare(0, prefix.size(), prefix) == 0) {
129 exif.
model = cam.model.substr(prefix.size());
131 exif.
model = cam.model;
134 exif.
make =
"Helios";
135 exif.
model = cam.model.empty() ? std::string(
"HeliosCamera") : cam.model;
144 const std::string local_dt = formatExifDateTime(local_date, local_time);
156 if (cam.sensor_width_mm > 0.f && cam.HFOV_degrees > 0.f) {
157 const float HFOV_rad = cam.HFOV_degrees *
static_cast<float>(
M_PI) / 180.f;
158 const float optical_focal_length_mm = cam.sensor_width_mm / (2.0f * std::tan(HFOV_rad / 2.0f));
162 const float sensor_width_cm = cam.sensor_width_mm / 10.0f;
163 const float sensor_height_mm = (cam.FOV_aspect_ratio > 0.f) ? (cam.sensor_width_mm / cam.FOV_aspect_ratio) : cam.sensor_width_mm;
164 const float sensor_height_cm = sensor_height_mm / 10.0f;
165 if (sensor_width_cm > 0.f && cam.resolution.x > 0) {
168 if (sensor_height_cm > 0.f && cam.resolution.y > 0) {
182 const float lens_diameter_mm = cam.lens_diameter * 1000.0f;
190 if (cam.focal_length > 0.f) {
196 const float fl35 = exif.
focal_length_mm * (36.0f / cam.sensor_width_mm);
204 if (cam.exposure ==
"auto") {
206 }
else if (cam.exposure ==
"manual" ||
207 (cam.exposure.size() > 3 && cam.exposure.compare(0, 3,
"ISO") == 0)) {
212 if (cam.white_balance ==
"auto") {
214 }
else if (!cam.white_balance.empty()) {
219 if (cam.camera_zoom > 0.f) {
225 if (cam.applied_exposure_gain > 0.f) {
231 if (cam.exposure.size() > 3 && cam.exposure.compare(0, 3,
"ISO") == 0) {
233 int iso_int = std::stoi(cam.exposure.substr(3));
235 exif.
iso =
static_cast<unsigned int>(iso_int);
250 const double lat0_deg =
static_cast<double>(loc.
latitude_deg);
251 const double lon0_deg_std = -
static_cast<double>(loc.
longitude_deg);
252 const double lat0_rad = lat0_deg *
M_PI / 180.0;
253 const double meters_per_deg_lat = 111320.0;
254 const double meters_per_deg_lon = 111320.0 * std::cos(lat0_rad);
256 exif.
latitude_deg = lat0_deg + (
static_cast<double>(cam.position.y) / meters_per_deg_lat);
258 if (meters_per_deg_lon > 0.0) {
259 exif.
longitude_deg += (
static_cast<double>(cam.position.x) / meters_per_deg_lon);
266 double yaw_deg = std::atan2(
static_cast<double>(dir.
x),
static_cast<double>(dir.
y)) * 180.0 /
M_PI;
267 if (yaw_deg < 0.0) yaw_deg += 360.0;
273 toUTC(local_date, local_time, loc.
UTC_offset, utc_date, utc_time);
276 std::ostringstream ds;
277 ds << std::setw(4) << std::setfill(
'0') << utc_date.
year <<
":"
278 << std::setw(2) << std::setfill(
'0') << utc_date.
month <<
":"
279 << std::setw(2) << std::setfill(
'0') << utc_date.
day;
283 std::ostringstream ts;
284 ts << std::setw(2) << std::setfill(
'0') << utc_time.
hour <<
":"
285 << std::setw(2) << std::setfill(
'0') << utc_time.
minute <<
":"
286 << std::setw(2) << std::setfill(
'0') << utc_time.
second;
297 exif.
pitch_deg = -
static_cast<double>(std::asin(
static_cast<double>(dir.
z))) * 180.0 / M_PI;