Line data Source code
1 : /**********************************************************************
2 : *
3 : * Name: geoheif.cpp
4 : * Project: GDAL
5 : * Purpose: OGC GeoHEIF shared implementation.
6 : * Author: Brad Hards <bradh@silvereye.tech>
7 : *
8 : **********************************************************************
9 : * Copyright (c) 2024, Brad Hards
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 : #include "geoheif.h"
14 :
15 : namespace gdal
16 : {
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 0 : GeoHEIF::GeoHEIF() : gcps(0)
21 : {
22 0 : }
23 :
24 0 : GeoHEIF::~GeoHEIF()
25 : {
26 0 : }
27 :
28 0 : bool GeoHEIF::has_SRS() const
29 : {
30 0 : return !m_oSRS.IsEmpty();
31 : }
32 :
33 0 : bool GeoHEIF::has_GCPs() const
34 : {
35 0 : return haveGCPs;
36 : }
37 :
38 0 : static double to_double(const uint8_t *data, uint32_t index)
39 : {
40 0 : double d = 0;
41 0 : memcpy(&d, data + index, sizeof(d));
42 0 : CPL_MSBPTR64(&d);
43 0 : return d;
44 : }
45 :
46 0 : static double int_as_double(const uint8_t *data, uint32_t index)
47 : {
48 0 : uint32_t v = 0;
49 0 : memcpy(&v, data + index, sizeof(uint32_t));
50 0 : CPL_MSBPTR32(&v);
51 0 : return static_cast<double>(v);
52 : }
53 :
54 0 : void GeoHEIF::setModelTransformation(const uint8_t *payload, size_t length)
55 : {
56 : // TODO: this only handles the 2D case.
57 0 : if (length != (6 * 8 + 4))
58 : {
59 0 : return;
60 : }
61 : // Match version
62 0 : if (payload[0] == 0x00)
63 : {
64 0 : uint32_t index = 0;
65 0 : if (payload[index + 3] == 0x01)
66 : {
67 0 : index += 4;
68 0 : modelTransform[1] = to_double(payload, index);
69 0 : index += 8;
70 0 : modelTransform[2] = to_double(payload, index);
71 0 : index += 8;
72 0 : modelTransform[0] = to_double(payload, index);
73 0 : index += 8;
74 0 : modelTransform[4] = to_double(payload, index);
75 0 : index += 8;
76 0 : modelTransform[5] = to_double(payload, index);
77 0 : index += 8;
78 0 : modelTransform[3] = to_double(payload, index);
79 : }
80 : }
81 : else
82 : {
83 0 : CPLDebug("GeoHEIF", "Unsupported mtxf version %d", payload[0]);
84 : }
85 : }
86 :
87 0 : CPLErr GeoHEIF::GetGeoTransform(double *padfTransform) const
88 : {
89 : std::vector<int> axes =
90 0 : has_SRS() ? m_oSRS.GetDataAxisToSRSAxisMapping() : std::vector<int>();
91 :
92 0 : if (axes.size() && axes[0] != 1)
93 : {
94 0 : padfTransform[1] = modelTransform[4];
95 0 : padfTransform[2] = modelTransform[5];
96 0 : padfTransform[0] = modelTransform[3];
97 0 : padfTransform[4] = modelTransform[1];
98 0 : padfTransform[5] = modelTransform[2];
99 0 : padfTransform[3] = modelTransform[0];
100 : }
101 : else
102 : {
103 0 : padfTransform[1] = modelTransform[1];
104 0 : padfTransform[2] = modelTransform[2];
105 0 : padfTransform[0] = modelTransform[0];
106 0 : padfTransform[4] = modelTransform[4];
107 0 : padfTransform[5] = modelTransform[5];
108 0 : padfTransform[3] = modelTransform[3];
109 : }
110 0 : return CE_None;
111 : }
112 :
113 : /************************************************************************/
114 : /* GetSpatialRef() */
115 : /************************************************************************/
116 0 : const OGRSpatialReference *GeoHEIF::GetSpatialRef() const
117 : {
118 0 : return !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
119 : }
120 :
121 0 : void GeoHEIF::extractSRS(const uint8_t *payload, size_t length) const
122 : {
123 : // TODO: more sophisticated length checks
124 0 : if (length < 12)
125 : {
126 0 : CPLDebug("GeoHEIF", "Infeasible length CRS payload %u",
127 : static_cast<unsigned>(length));
128 0 : return;
129 : }
130 0 : std::string crsEncoding(payload + 4, payload + 8);
131 0 : std::string crs(payload + 8, payload + length);
132 0 : if (crsEncoding == "wkt2")
133 : {
134 0 : m_oSRS.importFromWkt(crs.c_str());
135 : }
136 0 : else if (crsEncoding == "crsu")
137 : {
138 0 : m_oSRS.importFromCRSURL(crs.c_str());
139 : }
140 0 : else if (crsEncoding == "curi")
141 : {
142 : // null terminated string in the form "[EPSG:4326]"
143 0 : if ((crs.at(0) != '[') || (crs.at(crs.length() - 2) != ']') ||
144 0 : (crs.at(crs.length() - 1) != '\0'))
145 : {
146 0 : CPLDebug("GeoHEIF", "CRS CURIE is not a safe CURIE");
147 0 : return;
148 : }
149 0 : std::string curie = crs.substr(1, crs.length() - 3);
150 0 : size_t separatorPos = curie.find(':');
151 0 : if (separatorPos == std::string::npos)
152 : {
153 0 : CPLDebug("GeoHEIF",
154 : "CRS CURIE does not contain required separator");
155 0 : return;
156 : }
157 0 : std::string authority = curie.substr(0, separatorPos);
158 0 : std::string code = curie.substr(separatorPos + 1);
159 0 : std::string osURL("http://www.opengis.net/def/crs/");
160 0 : osURL.append(authority);
161 0 : osURL += "/0/";
162 0 : osURL.append(code);
163 0 : m_oSRS.importFromCRSURL(osURL.c_str());
164 : }
165 : else
166 : {
167 0 : CPLDebug("GeoHEIF", "CRS encoding is not supported");
168 0 : return;
169 : }
170 0 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
171 : }
172 :
173 0 : void GeoHEIF::addGCPs(const uint8_t *data, size_t length)
174 : {
175 0 : constexpr size_t MIN_VALID_SIZE = sizeof(uint32_t) + sizeof(uint16_t) +
176 : 2 * sizeof(uint32_t) + 2 * sizeof(double);
177 0 : if (length < MIN_VALID_SIZE)
178 : {
179 0 : CPLDebug("GeoHEIF", "GCP data length is too short");
180 0 : return;
181 : }
182 0 : if (data[0] == 0x00)
183 : {
184 0 : uint32_t index = 0;
185 0 : bool is_3D = (data[index + 3] == 0x00);
186 0 : if (is_3D)
187 : {
188 0 : if (length < MIN_VALID_SIZE + sizeof(double))
189 : {
190 0 : CPLDebug("GeoHEIF", "GCP data length is too short for 3D");
191 0 : return;
192 : }
193 : }
194 0 : index += sizeof(uint32_t);
195 0 : uint16_t count = (data[index] << 8) + (data[index + 1]);
196 0 : index += sizeof(uint16_t);
197 0 : for (uint16_t j = 0; (j < count) && (index < length); j++)
198 : {
199 0 : double dfGCPPixel = int_as_double(data, index);
200 0 : index += sizeof(uint32_t);
201 0 : double dfGCPLine = int_as_double(data, index);
202 0 : index += sizeof(uint32_t);
203 0 : double dfGCPX = to_double(data, index);
204 0 : index += sizeof(double);
205 0 : double dfGCPY = to_double(data, index);
206 0 : index += sizeof(double);
207 0 : double dfGCPZ = 0.0;
208 0 : if (is_3D)
209 : {
210 0 : dfGCPZ = to_double(data, index);
211 0 : index += sizeof(double);
212 : }
213 : gcps.emplace_back("", "", dfGCPPixel, dfGCPLine, dfGCPX, dfGCPY,
214 0 : dfGCPZ);
215 0 : haveGCPs = true;
216 : }
217 : }
218 : else
219 : {
220 0 : CPLDebug("GeoHEIF", "Unsupported tiep version %d", data[0]);
221 : }
222 : }
223 :
224 0 : int GeoHEIF::GetGCPCount() const
225 : {
226 0 : return static_cast<int>(gcps.size());
227 : }
228 :
229 0 : const GDAL_GCP *GeoHEIF::GetGCPs()
230 : {
231 0 : return gdal::GCP::c_ptr(gcps);
232 : }
233 :
234 0 : const OGRSpatialReference *GeoHEIF::GetGCPSpatialRef() const
235 : {
236 0 : return this->GetSpatialRef();
237 : }
238 :
239 : } // namespace gdal
240 :
241 : //! @endcond
|