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(GDALGeoTransform >) 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 : gt.xscale = modelTransform[4];
95 0 : gt.xrot = modelTransform[5];
96 0 : gt.xorig = modelTransform[3];
97 0 : gt.yrot = modelTransform[1];
98 0 : gt.yscale = modelTransform[2];
99 0 : gt.yorig = modelTransform[0];
100 : }
101 : else
102 : {
103 0 : gt.xscale = modelTransform[1];
104 0 : gt.xrot = modelTransform[2];
105 0 : gt.xorig = modelTransform[0];
106 0 : gt.yrot = modelTransform[4];
107 0 : gt.yscale = modelTransform[5];
108 0 : gt.yorig = modelTransform[3];
109 : }
110 0 : return gt.xscale != 0 || gt.yscale != 0 || gt.xrot != 0 || gt.yrot != 0
111 0 : ? CE_None
112 0 : : CE_Failure;
113 : }
114 :
115 : /************************************************************************/
116 : /* GetSpatialRef() */
117 : /************************************************************************/
118 0 : const OGRSpatialReference *GeoHEIF::GetSpatialRef() const
119 : {
120 0 : return !m_oSRS.IsEmpty() ? &m_oSRS : nullptr;
121 : }
122 :
123 0 : void GeoHEIF::extractSRS(const uint8_t *payload, size_t length) const
124 : {
125 : // TODO: more sophisticated length checks
126 0 : if (length < 12)
127 : {
128 0 : CPLDebug("GeoHEIF", "Infeasible length CRS payload %u",
129 : static_cast<unsigned>(length));
130 0 : return;
131 : }
132 0 : std::string crsEncoding(payload + 4, payload + 8);
133 0 : std::string crs(payload + 8, payload + length);
134 0 : if (crsEncoding == "wkt2")
135 : {
136 0 : m_oSRS.importFromWkt(crs.c_str());
137 : }
138 0 : else if (crsEncoding == "crsu")
139 : {
140 0 : m_oSRS.importFromCRSURL(crs.c_str());
141 : }
142 0 : else if (crsEncoding == "curi")
143 : {
144 : // null terminated string in the form "[EPSG:4326]"
145 0 : if ((crs.at(0) != '[') || (crs.at(crs.length() - 2) != ']') ||
146 0 : (crs.at(crs.length() - 1) != '\0'))
147 : {
148 0 : CPLDebug("GeoHEIF", "CRS CURIE is not a safe CURIE");
149 0 : return;
150 : }
151 0 : std::string curie = crs.substr(1, crs.length() - 3);
152 0 : size_t separatorPos = curie.find(':');
153 0 : if (separatorPos == std::string::npos)
154 : {
155 0 : CPLDebug("GeoHEIF",
156 : "CRS CURIE does not contain required separator");
157 0 : return;
158 : }
159 0 : std::string authority = curie.substr(0, separatorPos);
160 0 : std::string code = curie.substr(separatorPos + 1);
161 0 : std::string osURL("http://www.opengis.net/def/crs/");
162 0 : osURL.append(authority);
163 0 : osURL += "/0/";
164 0 : osURL.append(code);
165 0 : m_oSRS.importFromCRSURL(osURL.c_str());
166 : }
167 : else
168 : {
169 0 : CPLDebug("GeoHEIF", "CRS encoding is not supported");
170 0 : return;
171 : }
172 0 : m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
173 : }
174 :
175 0 : void GeoHEIF::addGCPs(const uint8_t *data, size_t length)
176 : {
177 0 : constexpr size_t MIN_VALID_SIZE = sizeof(uint32_t) + sizeof(uint16_t) +
178 : 2 * sizeof(uint32_t) + 2 * sizeof(double);
179 0 : if (length < MIN_VALID_SIZE)
180 : {
181 0 : CPLDebug("GeoHEIF", "GCP data length is too short");
182 0 : return;
183 : }
184 0 : if (data[0] == 0x00)
185 : {
186 0 : uint32_t index = 0;
187 0 : bool is_3D = (data[index + 3] == 0x00);
188 0 : if (is_3D)
189 : {
190 0 : if (length < MIN_VALID_SIZE + sizeof(double))
191 : {
192 0 : CPLDebug("GeoHEIF", "GCP data length is too short for 3D");
193 0 : return;
194 : }
195 : }
196 0 : index += sizeof(uint32_t);
197 0 : uint16_t count = (data[index] << 8) + (data[index + 1]);
198 0 : index += sizeof(uint16_t);
199 0 : for (uint16_t j = 0; (j < count) && (index < length); j++)
200 : {
201 0 : double dfGCPPixel = int_as_double(data, index);
202 0 : index += sizeof(uint32_t);
203 0 : double dfGCPLine = int_as_double(data, index);
204 0 : index += sizeof(uint32_t);
205 0 : double dfGCPX = to_double(data, index);
206 0 : index += sizeof(double);
207 0 : double dfGCPY = to_double(data, index);
208 0 : index += sizeof(double);
209 0 : double dfGCPZ = 0.0;
210 0 : if (is_3D)
211 : {
212 0 : dfGCPZ = to_double(data, index);
213 0 : index += sizeof(double);
214 : }
215 : gcps.emplace_back("", "", dfGCPPixel, dfGCPLine, dfGCPX, dfGCPY,
216 0 : dfGCPZ);
217 0 : haveGCPs = true;
218 : }
219 : }
220 : else
221 : {
222 0 : CPLDebug("GeoHEIF", "Unsupported tiep version %d", data[0]);
223 : }
224 : }
225 :
226 0 : int GeoHEIF::GetGCPCount() const
227 : {
228 0 : return static_cast<int>(gcps.size());
229 : }
230 :
231 0 : const GDAL_GCP *GeoHEIF::GetGCPs()
232 : {
233 0 : return gdal::GCP::c_ptr(gcps);
234 : }
235 :
236 0 : const OGRSpatialReference *GeoHEIF::GetGCPSpatialRef() const
237 : {
238 0 : return this->GetSpatialRef();
239 : }
240 :
241 : } // namespace gdal
242 :
243 : //! @endcond
|