Coverage Report

Created: 2026-02-14 09:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pcidsk/sdk/segment/cpcidskgcp2segment.cpp
Line
Count
Source
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
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
#include "segment/cpcidskgcp2segment.h"
12
13
#include "pcidsk_gcp.h"
14
#include "pcidsk_exception.h"
15
#include "pcidsk_file.h"
16
#include "core/pcidsk_utils.h"
17
18
#include <cstring>
19
#include <iostream>
20
#include <vector>
21
#include <string>
22
23
using namespace PCIDSK;
24
25
0
PCIDSKGCPSegment::~PCIDSKGCPSegment() = default;
26
27
CPCIDSKGCP2Segment::CPCIDSKGCP2Segment(PCIDSKFile *fileIn, int segmentIn, const char *segment_pointer)
28
0
    : CPCIDSKSegment(fileIn, segmentIn, segment_pointer), loaded_(false)
29
0
{
30
0
    pimpl_ = new PCIDSKGCP2SegInfo;
31
0
    pimpl_->gcps.clear();
32
0
    pimpl_->changed = false;
33
0
    try
34
0
    {
35
0
        Load();
36
0
    }
37
0
    catch( const PCIDSKException& )
38
0
    {
39
0
        delete pimpl_;
40
0
        pimpl_ = new PCIDSKGCP2SegInfo;
41
0
        pimpl_->gcps.clear();
42
0
        pimpl_->num_gcps = 0;
43
0
        pimpl_->changed = false;
44
0
        this->loaded_ = true;
45
0
    }
46
0
}
Unexecuted instantiation: PCIDSK::CPCIDSKGCP2Segment::CPCIDSKGCP2Segment(PCIDSK::PCIDSKFile*, int, char const*)
Unexecuted instantiation: PCIDSK::CPCIDSKGCP2Segment::CPCIDSKGCP2Segment(PCIDSK::PCIDSKFile*, int, char const*)
47
48
CPCIDSKGCP2Segment::~CPCIDSKGCP2Segment()
49
0
{
50
0
    try
51
0
    {
52
0
        RebuildSegmentData();
53
0
    }
54
0
    catch( const PCIDSKException& )
55
0
    {
56
        // TODO ?
57
0
    }
58
0
    delete pimpl_;
59
0
}
60
61
void CPCIDSKGCP2Segment::Load()
62
0
{
63
0
    if (loaded_) {
64
0
        return;
65
0
    }
66
67
    // Read the segment in. The first block has information about
68
    // the structure of the GCP segment (how many, the projection, etc.)
69
0
    pimpl_->seg_data.SetSize(static_cast<int>(data_size) - 1024);
70
0
    ReadFromFile(pimpl_->seg_data.buffer, 0, data_size - 1024);
71
72
    // check for 'GCP2    ' in the first 8 bytes
73
0
    if (!STARTS_WITH(pimpl_->seg_data.buffer, "GCP2    ")) {
74
        // Assume it is an empty segment, so we can mark loaded_ = true,
75
        // write it out and return
76
0
        pimpl_->changed = true;
77
0
        pimpl_->map_units = "LAT/LONG D000";
78
0
        pimpl_->proj_parms = "";
79
0
        pimpl_->num_gcps = 0;
80
0
        loaded_ = true;
81
0
        return;
82
0
    }
83
84
    // Check the number of blocks field's validity
85
0
    unsigned int num_blocks = pimpl_->seg_data.GetInt(8, 8);
86
87
0
    if (((data_size - 1024 - 512) / 512) != num_blocks) {
88
        //ThrowPCIDSKException("Calculated number of blocks (%d) does not match "
89
        //    "the value encoded in the GCP2 segment (%d).", ((data_size - 1024 - 512)/512),
90
        //    num_blocks);
91
        // Something is messed up with how GDB generates these segments... nice.
92
0
    }
93
94
0
    pimpl_->num_gcps = pimpl_->seg_data.GetInt(16, 8);
95
96
    // Extract the map units string:
97
0
    pimpl_->map_units = std::string(pimpl_->seg_data.buffer + 24, 16);
98
99
    // Extract the projection parameters string
100
0
    pimpl_->proj_parms = std::string(pimpl_->seg_data.buffer + 256, 256);
101
102
    // Get the number of alternative projections (should be 0!)
103
0
    pimpl_->num_proj = pimpl_->seg_data.GetInt(40, 8);
104
0
    if (pimpl_->num_proj != 0) {
105
0
        return ThrowPCIDSKException("There are alternative projections contained in this "
106
0
            "GCP2 segment. This functionality is not supported in libpcidsk.");
107
0
    }
108
109
    // Load the GCPs into the vector of PCIDSK::GCPs
110
0
    for (unsigned int i = 0; i < pimpl_->num_gcps; i++)
111
0
    {
112
0
        unsigned int offset = 512 + i * 256;
113
0
        bool is_cp = pimpl_->seg_data.buffer[offset] == 'C';
114
0
        bool is_active = pimpl_->seg_data.buffer[offset] != 'I';
115
0
        double pixel = pimpl_->seg_data.GetDouble(offset + 6, 14);
116
0
        double line = pimpl_->seg_data.GetDouble(offset + 20, 14);
117
118
0
        double elev = pimpl_->seg_data.GetDouble(offset + 34, 12);
119
0
        double x = pimpl_->seg_data.GetDouble(offset + 48, 22);
120
0
        double y = pimpl_->seg_data.GetDouble(offset + 70, 22);
121
122
0
        char cElevDatum = (char)toupper(static_cast<unsigned char>(pimpl_->seg_data.buffer[offset + 47]));
123
0
        PCIDSK::GCP::EElevationDatum elev_datum = cElevDatum != 'M' ?
124
0
            GCP::EEllipsoidal : GCP::EMeanSeaLevel;
125
126
0
        char elev_unit_c = (char)toupper(static_cast<unsigned char>(pimpl_->seg_data.buffer[offset + 46]));
127
0
        PCIDSK::GCP::EElevationUnit elev_unit = elev_unit_c == 'M' ? GCP::EMetres :
128
0
            elev_unit_c == 'F' ? GCP::EInternationalFeet :
129
0
            elev_unit_c == 'A' ? GCP::EAmericanFeet : GCP::EUnknown;
130
131
0
        double pix_err = pimpl_->seg_data.GetDouble(offset + 92, 10);
132
0
        double line_err = pimpl_->seg_data.GetDouble(offset + 102, 10);
133
0
        double elev_err = pimpl_->seg_data.GetDouble(offset + 112, 10);
134
135
0
        double x_err = pimpl_->seg_data.GetDouble(offset + 122, 14);
136
0
        double y_err = pimpl_->seg_data.GetDouble(offset + 136, 14);
137
138
0
        std::string gcp_id(pimpl_->seg_data.buffer + offset + 192, 64);
139
140
0
        PCIDSK::GCP gcp(x, y, elev,
141
0
                        line, pixel, gcp_id, pimpl_->map_units,
142
0
                        pimpl_->proj_parms,
143
0
                        x_err, y_err, elev_err,
144
0
                        line_err, pix_err);
145
0
        gcp.SetElevationUnit(elev_unit);
146
0
        gcp.SetElevationDatum(elev_datum);
147
0
        gcp.SetActive(is_active);
148
0
        gcp.SetCheckpoint(is_cp);
149
150
0
        pimpl_->gcps.push_back(gcp);
151
0
    }
152
153
0
    loaded_ = true;
154
0
}
155
156
 // Return all GCPs in the segment
157
std::vector<PCIDSK::GCP> const& CPCIDSKGCP2Segment::GetGCPs(void) const
158
0
{
159
0
    return pimpl_->gcps;
160
0
}
161
162
// Write the given GCPs to the segment. If the segment already
163
// exists, it will be replaced with this one.
164
void CPCIDSKGCP2Segment::SetGCPs(std::vector<PCIDSK::GCP> const& gcps)
165
0
{
166
0
    pimpl_->num_gcps = static_cast<unsigned int>(gcps.size());
167
0
    pimpl_->gcps = gcps; // copy them in
168
0
    pimpl_->changed = true;
169
170
0
    RebuildSegmentData();
171
0
}
172
173
// Return the count of GCPs in the segment
174
unsigned int  CPCIDSKGCP2Segment::GetGCPCount(void) const
175
0
{
176
0
    return pimpl_->num_gcps;
177
0
}
178
179
void CPCIDSKGCP2Segment::Synchronize()
180
0
{
181
0
    if( pimpl_ != nullptr )
182
0
    {
183
0
        RebuildSegmentData();
184
0
    }
185
0
}
186
187
void CPCIDSKGCP2Segment::RebuildSegmentData(void)
188
0
{
189
0
    if (pimpl_->changed == false || !this->file->GetUpdatable()) {
190
0
        return;
191
0
    }
192
0
    pimpl_->changed = false;
193
194
    // Rebuild the segment data based on the contents of the struct
195
0
    int num_blocks = (pimpl_->num_gcps + 1) / 2;
196
197
    // This will have to change when we have proper projections support
198
199
0
    if (!pimpl_->gcps.empty())
200
0
    {
201
0
        pimpl_->gcps[0].GetMapUnits(pimpl_->map_units,
202
0
            pimpl_->proj_parms);
203
0
    }
204
205
0
    pimpl_->seg_data.SetSize(num_blocks * 512 + 512);
206
207
    // Write out the first few fields
208
0
    pimpl_->seg_data.Put("GCP2    ", 0, 8);
209
0
    pimpl_->seg_data.Put(num_blocks, 8, 8);
210
0
    pimpl_->seg_data.Put((int)pimpl_->gcps.size(), 16, 8);
211
0
    pimpl_->seg_data.Put(pimpl_->map_units.c_str(), 24, 16);
212
0
    pimpl_->seg_data.Put((int)0, 40, 8);
213
0
    pimpl_->seg_data.Put(pimpl_->proj_parms.c_str(), 256, 256);
214
215
    // Time to write GCPs out:
216
0
    std::vector<PCIDSK::GCP>::const_iterator iter =
217
0
        pimpl_->gcps.begin();
218
219
0
    int id = 0;
220
0
    while (iter != pimpl_->gcps.end()) {
221
0
        int offset = 512 + id * 256;
222
223
0
        if ((*iter).IsCheckPoint()) {
224
0
            pimpl_->seg_data.Put("C", offset, 1);
225
0
        }
226
0
        else if ((*iter).IsActive())
227
0
        {
228
0
            pimpl_->seg_data.Put("G", offset, 1);
229
0
        }
230
0
        else
231
0
        {
232
0
            pimpl_->seg_data.Put("I", offset, 1);
233
0
        }
234
235
0
        pimpl_->seg_data.Put("0", offset + 1, 5);
236
237
        // Start writing out the GCP values
238
0
        pimpl_->seg_data.Put((*iter).GetPixel(), offset + 6, 14, "%14.4f");
239
0
        pimpl_->seg_data.Put((*iter).GetLine(), offset + 20, 14, "%14.4f");
240
0
        pimpl_->seg_data.Put((*iter).GetZ(), offset + 34, 12, "%12.4f");
241
242
0
        GCP::EElevationUnit unit;
243
0
        GCP::EElevationDatum datum;
244
0
        (*iter).GetElevationInfo(datum, unit);
245
246
0
        char unit_c[2];
247
248
0
        switch (unit)
249
0
        {
250
0
        case GCP::EMetres:
251
0
        case GCP::EUnknown:
252
0
            unit_c[0] = 'M';
253
0
            break;
254
0
        case GCP::EAmericanFeet:
255
0
            unit_c[0] = 'A';
256
0
            break;
257
0
        case GCP::EInternationalFeet:
258
0
            unit_c[0] = 'F';
259
0
            break;
260
0
        }
261
262
0
        char datum_c[2];
263
264
0
        switch(datum)
265
0
        {
266
0
        case GCP::EEllipsoidal:
267
0
            datum_c[0] = 'E';
268
0
            break;
269
0
        case GCP::EMeanSeaLevel:
270
0
            datum_c[0] = 'M';
271
0
            break;
272
0
        }
273
274
0
        unit_c[1] = '\0';
275
0
        datum_c[1] = '\0';
276
277
        // Write out elevation information
278
0
        pimpl_->seg_data.Put(unit_c, offset + 46, 1);
279
0
        pimpl_->seg_data.Put(datum_c, offset + 47, 1);
280
281
0
        pimpl_->seg_data.Put((*iter).GetX(), offset + 48, 22, "%22.14e");
282
0
        pimpl_->seg_data.Put((*iter).GetY(), offset + 70, 22, "%22.14e");
283
0
        pimpl_->seg_data.Put((*iter).GetPixelErr(), offset + 92, 10, "%10.4f");
284
0
        pimpl_->seg_data.Put((*iter).GetLineErr(), offset + 102, 10, "%10.4f");
285
0
        pimpl_->seg_data.Put((*iter).GetZErr(), offset + 112, 10, "%10.4f");
286
0
        pimpl_->seg_data.Put((*iter).GetXErr(), offset + 122, 14, "%14.4e");
287
0
        pimpl_->seg_data.Put((*iter).GetYErr(), offset + 136, 14, "%14.4e");
288
0
        pimpl_->seg_data.Put((*iter).GetIDString(), offset + 192, 64, true );
289
290
0
        ++id;
291
0
        ++iter;
292
0
    }
293
294
0
    WriteToFile(pimpl_->seg_data.buffer, 0, pimpl_->seg_data.buffer_size);
295
296
0
    pimpl_->changed = false;
297
0
}
298
299
// Clear a GCP Segment
300
void  CPCIDSKGCP2Segment::ClearGCPs(void)
301
0
{
302
0
    pimpl_->num_gcps = 0;
303
0
    pimpl_->gcps.clear();
304
0
    pimpl_->changed = true;
305
306
0
    RebuildSegmentData();
307
0
}