Skip to content

Commit

Permalink
Change box parsing to allow for more flexible specifications.
Browse files Browse the repository at this point in the history
  • Loading branch information
abellgithub committed Apr 25, 2019
1 parent 7bdc5d3 commit 7737b60
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 156 deletions.
27 changes: 16 additions & 11 deletions pdal/Bounds.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,20 +94,25 @@ void Bounds::set(const BOX2D& box)

std::istream& operator>>(std::istream& in, Bounds& bounds)
{
std::streampos start = in.tellg();
BOX3D b3d;
in >> b3d;
if (in.fail())
std::string s;
std::string::size_type pos(0);

std::getline(in, s);

try
{
BOX3D b3d;
b3d.parse(s, pos);
bounds.set(b3d);
}
catch (BOX3D::error&)
{
in.clear();
in.seekg(start);
BOX2D b2d;
in >> b2d;
if (!in.fail())
bounds.set(b2d);
pos = 0;
b2d.parse(s, pos);
bounds.set(b2d);
}
else
bounds.set(b3d);

return in;
}

Expand Down
253 changes: 108 additions & 145 deletions pdal/util/Box.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,50 +38,7 @@
#include <vector>

#include <pdal/util/Box.hpp>

namespace
{

template <typename PREDICATE>
void eat(std::istream& in, PREDICATE p)
{
while (p((char)in.get()))
;
if (in.eof())
in.clear(in.rdstate() & ~std::ios::failbit);
else
in.unget();
}

bool eat(std::istream& in, char c)
{
if ((char)in.get() == c)
return true;
in.unget();
return false;
}

void readpair(std::istream& istr, double& low, double& high)
{
eat(istr, isspace);
if (!eat(istr,'['))
istr.setstate(std::ios_base::failbit);

eat(istr, isspace);
istr >> low;

eat(istr, isspace);
if (!eat(istr,','))
istr.setstate(std::ios_base::failbit);

eat(istr, isspace);
istr >> high;

if (!eat(istr,']'))
istr.setstate(std::ios_base::failbit);
}

} // unnamed namespace
#include <pdal/util/Utils.hpp>

namespace pdal
{
Expand Down Expand Up @@ -172,111 +129,117 @@ const BOX3D& BOX3D::getDefaultSpatialExtent()
}


std::istream& operator>>(std::istream& istr, BOX2D& bounds)
namespace
{
//ABELL - Not sure the point of this. I get that one can have an "empty"
// BOX2D, but when would it be useful to create one from a string?
// A really dirty way to check for an empty bounds object right off
// the bat
char left_paren = (char)istr.get();
if (!istr.good())
{
istr.setstate(std::ios_base::failbit);
return istr;
}
const char right_paren = (char)istr.get();

if (left_paren == '(' && right_paren == ')')
{
bounds = BOX2D();
return istr;
}
istr.unget();
istr.unget(); // ()

std::vector<double> v;

eat(istr, isspace);
if (!eat(istr,'('))
istr.setstate(std::ios_base::failbit);

bool done = false;
for (int i = 0; i < 2; ++i)
{
double low, high;

readpair(istr, low, high);

eat(istr, isspace);
if (!eat(istr, i == 1 ? ')' : ','))
istr.setstate(std::ios_base::failbit);
v.push_back(low);
v.push_back(high);
}

if (istr.good())
{
bounds.minx = v[0];
bounds.maxx = v[1];
bounds.miny = v[2];
bounds.maxy = v[3];
}
return istr;

template <typename T>
void parsePair(const std::string& s, std::string::size_type& pos,
double& low, double& high)
{
low = high = 0;
const char *start;
char *end;

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != '[')
throw typename T::error("No opening '[' in range.");

pos += Utils::extractSpaces(s, pos);
start = s.data() + pos;
low = std::strtod(start, &end);
if (start == end)
throw typename T::error("No valid minimum value for range.");
pos += (end - start);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ',')
throw typename T::error("No ',' separating minimum/maximum values.");

pos += Utils::extractSpaces(s, pos);
start = s.data() + pos;
high = std::strtod(start, &end);
if (start == end)
throw typename T::error("No valid maximum value for range.");
pos += (end - start);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ']')
throw typename T::error("No closing ']' in range.");
}

std::istream& operator>>(std::istream& istr, BOX3D& bounds)
} // unnamed namespace

// This parses the guts of a 2D range.
void BOX2D::parse(const std::string& s, std::string::size_type& pos)
{
//ABELL - Not sure the point of this. I get that one can have an "empty"
// BOX3D, but when would it be useful to create one from a string?
// A really dirty way to check for an empty bounds object right off
// the bat
char left_paren = (char)istr.get();
if (!istr.good())
{
istr.setstate(std::ios_base::failbit);
return istr;
}
const char right_paren = (char)istr.get();

if (left_paren == '(' && right_paren == ')')
{
BOX3D output;
bounds = output;
return istr;
}
istr.unget();
istr.unget(); // ()

std::vector<double> v;

eat(istr, isspace);
if (!eat(istr,'('))
istr.setstate(std::ios_base::failbit);

bool done = false;
for (int i = 0; i < 3; ++i)
{
double low, high;

readpair(istr, low, high);

eat(istr, isspace);
if (!eat(istr, i == 2 ? ')' : ','))
istr.setstate(std::ios_base::failbit);
v.push_back(low);
v.push_back(high);
}

if (istr.good())
{
bounds.minx = v[0];
bounds.maxx = v[1];
bounds.miny = v[2];
bounds.maxy = v[3];
bounds.minz = v[4];
bounds.maxz = v[5];
}
return istr;
pos += Utils::extractSpaces(s, pos);
if (s[pos++] != '(')
throw error("No opening '('.");
parsePair<BOX2D>(s, pos, minx, maxx);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ',')
throw error("No comma separating 'X' and 'Y' dimensions.");
parsePair<BOX2D>(s, pos, miny, maxy);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ')')
throw error("No closing ')'.");

pos += Utils::extractSpaces(s, pos);
}

void BOX3D::parse(const std::string& s, std::string::size_type& pos)
{
pos += Utils::extractSpaces(s, pos);
if (s[pos++] != '(')
throw error("No opening '('.");
parsePair<BOX3D>(s, pos, minx, maxx);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ',')
throw error("No comma separating 'X' and 'Y' dimensions.");
parsePair<BOX3D>(s, pos, miny, maxy);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ',')
throw error("No comma separating 'Y' and 'Z' dimensions.");
parsePair<BOX3D>(s, pos, minz, maxz);

pos += Utils::extractSpaces(s, pos);
if (s[pos++] != ')')
throw error("No closing ')'.");

pos += Utils::extractSpaces(s, pos);
}


std::istream& operator>>(std::istream& in, BOX2D& box)
{
std::string s;

std::getline(in, s);
std::string::size_type pos(0);

box.parse(s, pos);
if (pos != s.size())
throw BOX2D::error("Invalid characters following valid 2d-bounds.");
return in;
}



std::istream& operator>>(std::istream& in, BOX3D& box)
{
std::string s;

std::getline(in, s);
std::string::size_type pos(0);

box.parse(s, pos);
if (pos != s.size())
throw BOX3D::error("Invalid characters following valid 3d-bounds.");
return in;
}

} // namespace pdal
31 changes: 31 additions & 0 deletions pdal/util/Box.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ namespace pdal
class PDAL_DLL BOX2D
{
public:
struct error : public std::runtime_error
{
error(const std::string& err) : std::runtime_error(err)
{}
};

double minx; ///< Minimum X value.
double maxx; ///< Maximum X value.
double miny; ///< Minimum Y value.
Expand Down Expand Up @@ -284,14 +290,30 @@ class PDAL_DLL BOX2D
\return A bounds box with infinite bounds,
*/
static const BOX2D& getDefaultSpatialExtent();

/**
Parse a string as a BOX2D.
\param s String representation of the box.
\param pos Position in the string at which to start parsing.
On return set to parsing end position.
*/
void parse(const std::string& s, std::string::size_type& pos);
};


/**
BOX3D represents a three-dimensional box with double-precision bounds.
*/
class PDAL_DLL BOX3D : private BOX2D
{
public:
struct error : public std::runtime_error
{
error(const std::string& err) : std::runtime_error(err)
{}
};

using BOX2D::minx;
using BOX2D::maxx;
using BOX2D::miny;
Expand Down Expand Up @@ -573,6 +595,15 @@ class PDAL_DLL BOX3D : private BOX2D
\return A bounds box with infinite bounds,
*/
static const BOX3D& getDefaultSpatialExtent();

/**
Parse a string as a BOX3D.
\param s String representation of the box.
\param pos Position in the string at which to start parsing.
On return set to parsing end position.
*/
void parse(const std::string& s, std::string::size_type& pos);
};

/**
Expand Down

0 comments on commit 7737b60

Please sign in to comment.