1
/******************************************************************************
4
* Project: libLAS - http://liblas.org - A BSD library for LAS format data.
5
* Purpose: LAS transform implementation for C++ libLAS
6
* Author: Howard Butler, hobu.inc@gmail.com
8
******************************************************************************
9
* Copyright (c) 2010, Howard Butler
11
* All rights reserved.
13
* Redistribution and use in source and binary forms, with or without
14
* modification, are permitted provided that the following
17
* * Redistributions of source code must retain the above copyright
18
* notice, this list of conditions and the following disclaimer.
19
* * Redistributions in binary form must reproduce the above copyright
20
* notice, this list of conditions and the following disclaimer in
21
* the documentation and/or other materials provided
22
* with the distribution.
23
* * Neither the name of the Martin Isenburg or Iowa Department
24
* of Natural Resources nor the names of its contributors may be
25
* used to endorse or promote products derived from this software
26
* without specific prior written permission.
28
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
35
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
36
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
37
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
38
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
40
****************************************************************************/
42
#include <liblas/transform.hpp>
43
#include <liblas/exception.hpp>
44
#include <liblas/header.hpp>
46
#include <boost/concept_check.hpp>
47
#include <boost/tokenizer.hpp>
48
#include <boost/lexical_cast.hpp>
51
# pragma warning(push)
52
# pragma warning(disable: 4100) // unreferenced formal param
54
#include <boost/algorithm/string/erase.hpp>
67
#include <ogr_spatialref.h>
70
typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
76
struct OGRSpatialReferenceDeleter
79
void operator()(T* ptr)
81
::OSRDestroySpatialReference(ptr);
85
struct OSRTransformDeleter
88
void operator()(T* ptr)
90
::OCTDestroyCoordinateTransformation(ptr);
95
struct GDALSourceDeleter
98
void operator()(T* ptr)
108
ReprojectionTransform::ReprojectionTransform(const SpatialReference& inSRS, const SpatialReference& outSRS)
111
Initialize(inSRS, outSRS);
114
ReprojectionTransform::ReprojectionTransform(
115
const SpatialReference& inSRS,
116
const SpatialReference& outSRS,
117
const Header* new_header)
118
: m_new_header(new_header)
120
Initialize(inSRS, outSRS);
123
void ReprojectionTransform::Initialize(const SpatialReference& inSRS, const SpatialReference& outSRS)
127
m_in_ref_ptr = ReferencePtr(OSRNewSpatialReference(0), OGRSpatialReferenceDeleter());
128
m_out_ref_ptr = ReferencePtr(OSRNewSpatialReference(0), OGRSpatialReferenceDeleter());
130
int result = OSRSetFromUserInput(m_in_ref_ptr.get(), inSRS.GetWKT(liblas::SpatialReference::eCompoundOK).c_str());
131
if (result != OGRERR_NONE)
133
std::ostringstream msg;
134
msg << "Could not import input spatial reference for ReprojectionTransform:: "
135
<< CPLGetLastErrorMsg() << " code: " << result
136
<< "wkt: '" << inSRS.GetWKT() << "'";
137
throw std::runtime_error(msg.str());
140
result = OSRSetFromUserInput(m_out_ref_ptr.get(), outSRS.GetWKT(liblas::SpatialReference::eCompoundOK).c_str());
141
if (result != OGRERR_NONE)
143
std::ostringstream msg;
144
msg << "Could not import output spatial reference for ReprojectionTransform:: "
145
<< CPLGetLastErrorMsg() << " code: " << result
146
<< "wkt: '" << outSRS.GetWKT() << "'";
147
std::string message(msg.str());
148
throw std::runtime_error(message);
150
m_transform_ptr = TransformPtr(OCTNewCoordinateTransformation( m_in_ref_ptr.get(), m_out_ref_ptr.get()), OSRTransformDeleter());
153
boost::ignore_unused_variable_warning(inSRS);
154
boost::ignore_unused_variable_warning(outSRS);
158
ReprojectionTransform::~ReprojectionTransform()
164
bool ReprojectionTransform::transform(Point& point)
169
double x = point.GetX();
170
double y = point.GetY();
171
double z = point.GetZ();
173
ret = OCTTransform(m_transform_ptr.get(), 1, &x, &y, &z);
176
std::ostringstream msg;
177
msg << "Could not project point for ReprojectionTransform::" << CPLGetLastErrorMsg() << ret;
178
throw std::runtime_error(msg.str());
181
if (this->ModifiesHeader())
184
point.SetHeader(m_new_header);
195
if (detail::compare_distance(point.GetRawX(), (std::numeric_limits<boost::int32_t>::max)()) ||
196
detail::compare_distance(point.GetRawX(), (std::numeric_limits<boost::int32_t>::min)())) {
197
throw std::domain_error("X scale and offset combination is insufficient to represent the data");
200
if (detail::compare_distance(point.GetRawY(), (std::numeric_limits<boost::int32_t>::max)()) ||
201
detail::compare_distance(point.GetRawY(), (std::numeric_limits<boost::int32_t>::min)())) {
202
throw std::domain_error("Y scale and offset combination is insufficient to represent the data");
205
if (detail::compare_distance(point.GetRawZ(), (std::numeric_limits<boost::int32_t>::max)()) ||
206
detail::compare_distance(point.GetRawZ(), (std::numeric_limits<boost::int32_t>::min)())) {
207
throw std::domain_error("Z scale and offset combination is insufficient to represent the data");
212
boost::ignore_unused_variable_warning(point);
219
TranslationTransform::TranslationTransform(std::string const& expression)
221
if (expression.size() == 0)
222
throw std::runtime_error("no expression was given to TranslationTransform");
224
boost::char_separator<char> sep_space(" ");
226
tokenizer dimensions(expression, sep_space);
227
for (tokenizer::iterator t = dimensions.begin(); t != dimensions.end(); ++t) {
228
std::string const& s = *t;
230
operation op = GetOperation(s);
231
operations.push_back(op);
236
TranslationTransform::operation TranslationTransform::GetOperation(std::string const& expr)
242
std::string star("*");
243
std::string divide("/");
244
std::string plus("+");
245
std::string minus("-");
247
if (expr.find(x) == std::string::npos &&
248
expr.find(y) == std::string::npos &&
249
expr.find(z) == std::string::npos)
250
throw liblas::invalid_expression("expression is invalid -- use x, y, or z to define a dimension. No 'x', 'y', or 'z' was found");
252
operation output("X");
255
std::string expression(expr);
256
boost::erase_all(expression, " "); // Get rid of any spaces in the expression chunk
259
std::size_t found_x = expression.find(x);
260
std::size_t found_y = expression.find(y);
261
std::size_t found_z = expression.find(z);
262
std::size_t found_star = expression.find(star);
263
std::size_t found_divide = expression.find(divide);
264
std::size_t found_plus = expression.find(plus);
265
std::size_t found_minus = expression.find(minus);
267
// if they gave something like 'xyz*34' it's invalid
268
if (found_x != std::string::npos &&
269
found_y != std::string::npos &&
270
found_z != std::string::npos)
271
throw liblas::invalid_expression("expression is invalid");
273
std::string::size_type op_pos=std::string::npos;
274
if (found_x != std::string::npos)
276
output = operation("X");
277
op_pos = expression.find_last_of(x) + 1;
280
if (found_y != std::string::npos)
282
output = operation("Y");
283
op_pos = expression.find_last_of(y) + 1;
286
if (found_z != std::string::npos)
288
output = operation("Z");
289
op_pos = expression.find_last_of(z) + 1;
292
if (op_pos == std::string::npos)
294
std::ostringstream oss;
295
oss << "Expression '" << expression << "' does not have 'x', 'y', or 'z' to denote fields";
296
throw liblas::invalid_expression(oss.str());
299
std::string::size_type data_pos = std::string::npos;
300
if (found_star != std::string::npos)
302
output.oper = eOPER_MULTIPLY;
303
data_pos = expression.find_last_of(star) + 1;
306
if (found_divide != std::string::npos)
308
output.oper = eOPER_DIVIDE;
309
data_pos = expression.find_last_of(divide) + 1;
312
if (found_plus != std::string::npos)
314
output.oper = eOPER_ADD;
315
data_pos = expression.find_last_of(plus) + 1;
317
if (found_minus != std::string::npos)
319
output.oper = eOPER_SUBTRACT;
320
data_pos = expression.find_last_of(minus) + 1;
323
if (data_pos == std::string::npos)
325
std::ostringstream oss;
326
oss << "Expression '" << expression << "' does not have '*', '/', '+', or '-' to denote operations";
327
throw liblas::invalid_expression(oss.str());
331
out = expression.substr(data_pos, expression.size());
333
double value = boost::lexical_cast<double>(out);
334
output.expression = expression;
335
output.value = value;
340
bool TranslationTransform::transform(Point& point)
342
for(std::vector<TranslationTransform::operation>::const_iterator op = operations.begin();
343
op != operations.end();
350
if (!op->dimension.compare("X"))
352
point.SetX(point.GetX() * op->value);
354
if (!op->dimension.compare("Y"))
356
point.SetY(point.GetY() * op->value);
358
if (!op->dimension.compare("Z"))
360
point.SetZ(point.GetZ() * op->value);
364
if (!op->dimension.compare("X"))
366
point.SetX(point.GetX() / op->value);
368
if (!op->dimension.compare("Y"))
370
point.SetY(point.GetY() / op->value);
372
if (!op->dimension.compare("Z"))
374
point.SetZ(point.GetZ() / op->value);
378
if (!op->dimension.compare("X"))
380
point.SetX(point.GetX() + op->value);
382
if (!op->dimension.compare("Y"))
384
point.SetY(point.GetY() + op->value);
386
if (!op->dimension.compare("Z"))
388
point.SetZ(point.GetZ() + op->value);
393
if (!op->dimension.compare("X"))
395
point.SetX(point.GetX() - op->value);
397
if (!op->dimension.compare("Y"))
399
point.SetY(point.GetY() - op->value);
401
if (!op->dimension.compare("Z"))
403
point.SetZ(point.GetZ() - op->value);
408
std::ostringstream oss;
409
oss << "Unhandled expression operation id " << static_cast<boost::int32_t>(op->oper);
410
throw std::runtime_error(oss.str());
413
if (detail::compare_distance(point.GetRawX(), (std::numeric_limits<boost::int32_t>::max)()) ||
414
detail::compare_distance(point.GetRawX(), (std::numeric_limits<boost::int32_t>::min)())) {
415
throw std::domain_error("X scale and offset combination of this file is insufficient to represent the data given the expression ");
418
if (detail::compare_distance(point.GetRawY(), (std::numeric_limits<boost::int32_t>::max)()) ||
419
detail::compare_distance(point.GetRawY(), (std::numeric_limits<boost::int32_t>::min)())) {
420
throw std::domain_error("Y scale and offset combination of this file is insufficient to represent the data given the expression");
423
if (detail::compare_distance(point.GetRawZ(), (std::numeric_limits<boost::int32_t>::max)()) ||
424
detail::compare_distance(point.GetRawZ(), (std::numeric_limits<boost::int32_t>::min)())) {
425
throw std::domain_error("Z scale and offset combination of this file is insufficient to represent the data given the expression");
433
TranslationTransform::~TranslationTransform()
438
void CPL_STDCALL ColorFetchingTransformGDALErrorHandler(CPLErr eErrClass, int err_no, const char *msg)
440
std::ostringstream oss;
442
if (eErrClass == CE_Failure || eErrClass == CE_Fatal) {
443
oss <<"GDAL Failure number=" << err_no << ": " << msg;
444
throw std::runtime_error(oss.str());
451
ColorFetchingTransform::ColorFetchingTransform(
452
std::string const& datasource,
453
std::vector<boost::uint32_t> bands
456
, m_ds(DataSourcePtr())
457
, m_datasource(datasource)
464
ColorFetchingTransform::ColorFetchingTransform(
465
std::string const& datasource,
466
std::vector<boost::uint32_t> bands,
469
: m_new_header(header)
470
, m_ds(DataSourcePtr())
471
, m_datasource(datasource)
478
void ColorFetchingTransform::Initialize()
484
CPLPopErrorHandler();
485
CPLPushErrorHandler(ColorFetchingTransformGDALErrorHandler);
487
m_ds = DataSourcePtr(GDALOpen( m_datasource.c_str(), GA_ReadOnly ), GDALSourceDeleter());
489
// Assume the first three bands are RGB, and not get any more if the
490
// user did not supply any bands
491
if( m_bands.size() == 0 )
493
for( boost::int32_t i = 0; i < GDALGetRasterCount( m_ds.get() ); i++ )
496
m_bands.push_back( i+1 );
500
m_forward_transform.assign(0.0);
501
m_inverse_transform.assign(0.0);
503
if (GDALGetGeoTransform( m_ds.get(), &(m_forward_transform.front()) ) != CE_None )
505
throw std::runtime_error("unable to fetch forward geotransform for raster!");
508
GDALInvGeoTransform( &(m_forward_transform.front()), &(m_inverse_transform.front()) );
513
bool ColorFetchingTransform::transform(Point& point)
517
boost::int32_t pixel = 0;
518
boost::int32_t line = 0;
520
double x = point.GetX();
521
double y = point.GetY();
525
point.SetHeader(m_new_header);
528
pixel = (boost::int32_t) std::floor(
529
m_inverse_transform[0]
530
+ m_inverse_transform[1] * x
531
+ m_inverse_transform[2] * y );
532
line = (boost::int32_t) std::floor(
533
m_inverse_transform[3]
534
+ m_inverse_transform[4] * x
535
+ m_inverse_transform[5] * y );
537
if( pixel < 0 || line < 0
538
|| pixel >= GDALGetRasterXSize( m_ds.get() )
539
|| line >= GDALGetRasterYSize( m_ds.get() )
542
// The x, y is not coincident with this raster, we'll leave whatever
543
// color value might be there alone.
547
boost::array<double, 2> pix;
548
boost::array<liblas::Color::value_type, 3> color;
551
for( std::vector<boost::int32_t>::size_type i = 0;
552
i < m_bands.size(); i++ )
554
GDALRasterBandH hBand = GDALGetRasterBand( m_ds.get(), m_bands[i] );
559
if( GDALRasterIO( hBand, GF_Read, pixel, line, 1, 1,
560
&pix[0], 1, 1, GDT_CFloat64, 0, 0) == CE_None )
562
color[i] = static_cast<liblas::Color::value_type>(pix[0]);
564
color[i] = color[i] * static_cast<liblas::Color::value_type>(m_scale);
569
point.SetColor(Color(color));
573
boost::ignore_unused_variable_warning(point);
577
ColorFetchingTransform::~ColorFetchingTransform()
580
CPLPopErrorHandler();
584
} // namespace liblas