Line data Source code
1 : /******************************************************************************
2 : *
3 : * Purpose: Implementation of access to a PCIDSK GCP2 Segment
4 : *
5 : ******************************************************************************
6 : * Copyright (c) 2009
7 : * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
8 : *
9 : * Permission is hereby granted, free of charge, to any person obtaining a
10 : * copy of this software and associated documentation files (the "Software"),
11 : * to deal in the Software without restriction, including without limitation
12 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 : * and/or sell copies of the Software, and to permit persons to whom the
14 : * Software is furnished to do so, subject to the following conditions:
15 : *
16 : * The above copyright notice and this permission notice shall be included
17 : * in all copies or substantial portions of the Software.
18 : *
19 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 : * DEALINGS IN THE SOFTWARE.
26 : ****************************************************************************/
27 : #include "segment/cpcidskgcp2segment.h"
28 :
29 : #include "pcidsk_gcp.h"
30 : #include "pcidsk_exception.h"
31 : #include "pcidsk_file.h"
32 : #include "core/pcidsk_utils.h"
33 :
34 : #include <cstring>
35 : #include <iostream>
36 : #include <vector>
37 : #include <string>
38 :
39 : using namespace PCIDSK;
40 :
41 0 : CPCIDSKGCP2Segment::CPCIDSKGCP2Segment(PCIDSKFile *fileIn, int segmentIn, const char *segment_pointer)
42 0 : : CPCIDSKSegment(fileIn, segmentIn, segment_pointer), loaded_(false)
43 : {
44 0 : pimpl_ = new PCIDSKGCP2SegInfo;
45 0 : pimpl_->gcps.clear();
46 0 : pimpl_->changed = false;
47 : try
48 : {
49 0 : Load();
50 : }
51 0 : catch( const PCIDSKException& )
52 : {
53 0 : delete pimpl_;
54 0 : pimpl_ = new PCIDSKGCP2SegInfo;
55 0 : pimpl_->gcps.clear();
56 0 : pimpl_->num_gcps = 0;
57 0 : pimpl_->changed = false;
58 0 : this->loaded_ = true;
59 : }
60 0 : }
61 :
62 0 : CPCIDSKGCP2Segment::~CPCIDSKGCP2Segment()
63 : {
64 : try
65 : {
66 0 : RebuildSegmentData();
67 : }
68 0 : catch( const PCIDSKException& )
69 : {
70 : // TODO ?
71 : }
72 0 : delete pimpl_;
73 0 : }
74 :
75 0 : void CPCIDSKGCP2Segment::Load()
76 : {
77 0 : if (loaded_) {
78 0 : return;
79 : }
80 :
81 : // Read the segment in. The first block has information about
82 : // the structure of the GCP segment (how many, the projection, etc.)
83 0 : pimpl_->seg_data.SetSize(static_cast<int>(data_size) - 1024);
84 0 : ReadFromFile(pimpl_->seg_data.buffer, 0, data_size - 1024);
85 :
86 : // check for 'GCP2 ' in the first 8 bytes
87 0 : if (!STARTS_WITH(pimpl_->seg_data.buffer, "GCP2 ")) {
88 : // Assume it is an empty segment, so we can mark loaded_ = true,
89 : // write it out and return
90 0 : pimpl_->changed = true;
91 0 : pimpl_->map_units = "LAT/LONG D000";
92 0 : pimpl_->proj_parms = "";
93 0 : pimpl_->num_gcps = 0;
94 0 : loaded_ = true;
95 0 : return;
96 : }
97 :
98 : // Check the number of blocks field's validity
99 0 : unsigned int num_blocks = pimpl_->seg_data.GetInt(8, 8);
100 :
101 0 : if (((data_size - 1024 - 512) / 512) != num_blocks) {
102 : //ThrowPCIDSKException("Calculated number of blocks (%d) does not match "
103 : // "the value encoded in the GCP2 segment (%d).", ((data_size - 1024 - 512)/512),
104 : // num_blocks);
105 : // Something is messed up with how GDB generates these segments... nice.
106 : }
107 :
108 0 : pimpl_->num_gcps = pimpl_->seg_data.GetInt(16, 8);
109 :
110 : // Extract the map units string:
111 0 : pimpl_->map_units = std::string(pimpl_->seg_data.buffer + 24, 16);
112 :
113 : // Extract the projection parameters string
114 0 : pimpl_->proj_parms = std::string(pimpl_->seg_data.buffer + 256, 256);
115 :
116 : // Get the number of alternative projections (should be 0!)
117 0 : pimpl_->num_proj = pimpl_->seg_data.GetInt(40, 8);
118 0 : if (pimpl_->num_proj != 0) {
119 0 : return ThrowPCIDSKException("There are alternative projections contained in this "
120 0 : "GCP2 segment. This functionality is not supported in libpcidsk.");
121 : }
122 :
123 : // Load the GCPs into the vector of PCIDSK::GCPs
124 0 : for (unsigned int i = 0; i < pimpl_->num_gcps; i++)
125 : {
126 0 : unsigned int offset = 512 + i * 256;
127 0 : bool is_cp = pimpl_->seg_data.buffer[offset] == 'C';
128 0 : bool is_active = pimpl_->seg_data.buffer[offset] != 'I';
129 0 : double pixel = pimpl_->seg_data.GetDouble(offset + 6, 14);
130 0 : double line = pimpl_->seg_data.GetDouble(offset + 20, 14);
131 :
132 0 : double elev = pimpl_->seg_data.GetDouble(offset + 34, 12);
133 0 : double x = pimpl_->seg_data.GetDouble(offset + 48, 22);
134 0 : double y = pimpl_->seg_data.GetDouble(offset + 70, 22);
135 :
136 0 : char cElevDatum = (char)toupper(static_cast<unsigned char>(pimpl_->seg_data.buffer[offset + 47]));
137 0 : PCIDSK::GCP::EElevationDatum elev_datum = cElevDatum != 'M' ?
138 : GCP::EEllipsoidal : GCP::EMeanSeaLevel;
139 :
140 0 : char elev_unit_c = (char)toupper(static_cast<unsigned char>(pimpl_->seg_data.buffer[offset + 46]));
141 0 : PCIDSK::GCP::EElevationUnit elev_unit = elev_unit_c == 'M' ? GCP::EMetres :
142 0 : elev_unit_c == 'F' ? GCP::EInternationalFeet :
143 0 : elev_unit_c == 'A' ? GCP::EAmericanFeet : GCP::EUnknown;
144 :
145 0 : double pix_err = pimpl_->seg_data.GetDouble(offset + 92, 10);
146 0 : double line_err = pimpl_->seg_data.GetDouble(offset + 102, 10);
147 0 : double elev_err = pimpl_->seg_data.GetDouble(offset + 112, 10);
148 :
149 0 : double x_err = pimpl_->seg_data.GetDouble(offset + 122, 14);
150 0 : double y_err = pimpl_->seg_data.GetDouble(offset + 136, 14);
151 :
152 0 : std::string gcp_id(pimpl_->seg_data.buffer + offset + 192, 64);
153 :
154 : PCIDSK::GCP gcp(x, y, elev,
155 0 : line, pixel, gcp_id, pimpl_->map_units,
156 0 : pimpl_->proj_parms,
157 : x_err, y_err, elev_err,
158 0 : line_err, pix_err);
159 0 : gcp.SetElevationUnit(elev_unit);
160 0 : gcp.SetElevationDatum(elev_datum);
161 0 : gcp.SetActive(is_active);
162 0 : gcp.SetCheckpoint(is_cp);
163 :
164 0 : pimpl_->gcps.push_back(gcp);
165 : }
166 :
167 0 : loaded_ = true;
168 : }
169 :
170 : // Return all GCPs in the segment
171 0 : std::vector<PCIDSK::GCP> const& CPCIDSKGCP2Segment::GetGCPs(void) const
172 : {
173 0 : return pimpl_->gcps;
174 : }
175 :
176 : // Write the given GCPs to the segment. If the segment already
177 : // exists, it will be replaced with this one.
178 0 : void CPCIDSKGCP2Segment::SetGCPs(std::vector<PCIDSK::GCP> const& gcps)
179 : {
180 0 : pimpl_->num_gcps = static_cast<unsigned int>(gcps.size());
181 0 : pimpl_->gcps = gcps; // copy them in
182 0 : pimpl_->changed = true;
183 :
184 0 : RebuildSegmentData();
185 0 : }
186 :
187 : // Return the count of GCPs in the segment
188 0 : unsigned int CPCIDSKGCP2Segment::GetGCPCount(void) const
189 : {
190 0 : return pimpl_->num_gcps;
191 : }
192 :
193 0 : void CPCIDSKGCP2Segment::Synchronize()
194 : {
195 0 : if( pimpl_ != nullptr )
196 : {
197 0 : RebuildSegmentData();
198 : }
199 0 : }
200 :
201 0 : void CPCIDSKGCP2Segment::RebuildSegmentData(void)
202 : {
203 0 : if (pimpl_->changed == false || !this->file->GetUpdatable()) {
204 0 : return;
205 : }
206 0 : pimpl_->changed = false;
207 :
208 : // Rebuild the segment data based on the contents of the struct
209 0 : int num_blocks = (pimpl_->num_gcps + 1) / 2;
210 :
211 : // This will have to change when we have proper projections support
212 :
213 0 : if (!pimpl_->gcps.empty())
214 : {
215 0 : pimpl_->gcps[0].GetMapUnits(pimpl_->map_units,
216 0 : pimpl_->proj_parms);
217 : }
218 :
219 0 : pimpl_->seg_data.SetSize(num_blocks * 512 + 512);
220 :
221 : // Write out the first few fields
222 0 : pimpl_->seg_data.Put("GCP2 ", 0, 8);
223 0 : pimpl_->seg_data.Put(num_blocks, 8, 8);
224 0 : pimpl_->seg_data.Put((int)pimpl_->gcps.size(), 16, 8);
225 0 : pimpl_->seg_data.Put(pimpl_->map_units.c_str(), 24, 16);
226 0 : pimpl_->seg_data.Put((int)0, 40, 8);
227 0 : pimpl_->seg_data.Put(pimpl_->proj_parms.c_str(), 256, 256);
228 :
229 : // Time to write GCPs out:
230 : std::vector<PCIDSK::GCP>::const_iterator iter =
231 0 : pimpl_->gcps.begin();
232 :
233 0 : int id = 0;
234 0 : while (iter != pimpl_->gcps.end()) {
235 0 : int offset = 512 + id * 256;
236 :
237 0 : if ((*iter).IsCheckPoint()) {
238 0 : pimpl_->seg_data.Put("C", offset, 1);
239 : }
240 0 : else if ((*iter).IsActive())
241 : {
242 0 : pimpl_->seg_data.Put("G", offset, 1);
243 : }
244 : else
245 : {
246 0 : pimpl_->seg_data.Put("I", offset, 1);
247 : }
248 :
249 0 : pimpl_->seg_data.Put("0", offset + 1, 5);
250 :
251 : // Start writing out the GCP values
252 0 : pimpl_->seg_data.Put((*iter).GetPixel(), offset + 6, 14, "%14.4f");
253 0 : pimpl_->seg_data.Put((*iter).GetLine(), offset + 20, 14, "%14.4f");
254 0 : pimpl_->seg_data.Put((*iter).GetZ(), offset + 34, 12, "%12.4f");
255 :
256 : GCP::EElevationUnit unit;
257 : GCP::EElevationDatum datum;
258 0 : (*iter).GetElevationInfo(datum, unit);
259 :
260 : char unit_c[2];
261 :
262 0 : switch (unit)
263 : {
264 0 : case GCP::EMetres:
265 : case GCP::EUnknown:
266 0 : unit_c[0] = 'M';
267 0 : break;
268 0 : case GCP::EAmericanFeet:
269 0 : unit_c[0] = 'A';
270 0 : break;
271 0 : case GCP::EInternationalFeet:
272 0 : unit_c[0] = 'F';
273 0 : break;
274 : }
275 :
276 : char datum_c[2];
277 :
278 0 : switch(datum)
279 : {
280 0 : case GCP::EEllipsoidal:
281 0 : datum_c[0] = 'E';
282 0 : break;
283 0 : case GCP::EMeanSeaLevel:
284 0 : datum_c[0] = 'M';
285 0 : break;
286 : }
287 :
288 0 : unit_c[1] = '\0';
289 0 : datum_c[1] = '\0';
290 :
291 : // Write out elevation information
292 0 : pimpl_->seg_data.Put(unit_c, offset + 46, 1);
293 0 : pimpl_->seg_data.Put(datum_c, offset + 47, 1);
294 :
295 0 : pimpl_->seg_data.Put((*iter).GetX(), offset + 48, 22, "%22.14e");
296 0 : pimpl_->seg_data.Put((*iter).GetY(), offset + 70, 22, "%22.14e");
297 0 : pimpl_->seg_data.Put((*iter).GetPixelErr(), offset + 92, 10, "%10.4f");
298 0 : pimpl_->seg_data.Put((*iter).GetLineErr(), offset + 102, 10, "%10.4f");
299 0 : pimpl_->seg_data.Put((*iter).GetZErr(), offset + 112, 10, "%10.4f");
300 0 : pimpl_->seg_data.Put((*iter).GetXErr(), offset + 122, 14, "%14.4e");
301 0 : pimpl_->seg_data.Put((*iter).GetYErr(), offset + 136, 14, "%14.4e");
302 0 : pimpl_->seg_data.Put((*iter).GetIDString(), offset + 192, 64, true );
303 :
304 0 : ++id;
305 0 : ++iter;
306 : }
307 :
308 0 : WriteToFile(pimpl_->seg_data.buffer, 0, pimpl_->seg_data.buffer_size);
309 :
310 0 : pimpl_->changed = false;
311 : }
312 :
313 : // Clear a GCP Segment
314 0 : void CPCIDSKGCP2Segment::ClearGCPs(void)
315 : {
316 0 : pimpl_->num_gcps = 0;
317 0 : pimpl_->gcps.clear();
318 0 : pimpl_->changed = true;
319 :
320 0 : RebuildSegmentData();
321 0 : }
|