Files
secondo/Algebras/ColumnSpatial/ColumnSpatialAlgebra.cpp

3286 lines
98 KiB
C++
Raw Normal View History

2026-01-23 17:03:45 +08:00
/*
----
This file is part of SECONDO.
Copyright (C) 2016,
Faculty of Mathematics and Computer Science,
Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
//paragraph [1] Title: [{\Large \bf \begin {center}] [\end {center}}]
//paragraph [2] Centered: [\begin{center}] [\end{center}]
//[_] [\_]
//[<] [\ensuremath{<}]
//[>] [\ensuremath{>}]
[1] Implementation of a column oriented spatial algebra
[2] May 2017 by Sascha Radke for bachelor thesis
1 Includes and global settings
*/
// TODO: remove relative paths and int64_t.
// they are only needed for a better syntax hilighting with qt-creator.
#include "ColumnSpatialAlgebra.h" // header for this algebra
#include "Symbols.h" // predefined strings
#include "ListUtils.h" // useful nested list functions
#include "NestedList.h" // required at many places
#include "QueryProcessor.h" // needed for value mappings
#include "TypeConstructor.h" // constructor for Secondo Types
#include "StandardTypes.h"
#include "Algebras/Spatial/SpatialAlgebra.h" // spatial types and operators
#include "Algebras/Spatial/RegionTools.h" // cycles and region building
#include "Algebras/Relation-C++/RelationAlgebra.h" // use of tuples
#include "Stream.h" // wrapper for secondo streams
#include "Algebras/CRel/Ints.h" // type for id result list
#include "Algebras/CRel/TypeConstructors/LongIntsTC.h"
#include <ctime>
#include <fstream>
#include <cstring>
#include <stdint.h>
using std::vector;
using std::fstream;
using namespace CRelAlgebra;
extern NestedList *nl;
extern QueryProcessor *qp;
namespace col {
/*
Benchmark cycles and nanoseconds.
*/
inline void benchmark(long &cycles, long &ns) {
struct timespec ttime;
uint64_t lo, hi;
// get nanoseconds from timer
clock_gettime(CLOCK_MONOTONIC, &ttime);
ns = (long)ttime.tv_sec * 1.0e9 + ttime.tv_nsec;
// get timestamp counter (tsc) from cpu
asm( "rdtsc" : "=a" (lo), "=d" (hi) );
cycles = lo | (hi << 32);
}
/*
2 Class ColPoint for column-oriented representation of points
*/
ColPoint::ColPoint() {} // standard constructor doing nothing
ColPoint::ColPoint(sPoint* newArray, long newCount) { // main constructor
aPoint = newArray;
count = newCount;
}
ColPoint::ColPoint(bool min) { // constructor with minimum array
aPoint = NULL;
aPoint = static_cast<sPoint*>(calloc(1, sizeof(sPoint)));
count = 0;
step = 0;
}
ColPoint::~ColPoint() { // destructor
free(aPoint);
}
// returns the corresponding basic type
const string ColPoint::BasicType() { return "apoint"; }
// compares the type of the given object with the class type
const bool ColPoint::checkType(const ListExpr list) {
return listutils::isSymbol(list, BasicType());
}
// description of the Secondo type for the user
ListExpr ColPoint::Property() {
return (nl->TwoElemList(
nl->FiveElemList(
nl->StringAtom("Signature"),
nl->StringAtom("Example Type List"),
nl->StringAtom("List Rep"),
nl->StringAtom("Example List"),
nl->StringAtom("Remarks")),
nl->FiveElemList(
nl->StringAtom("-> SIMPLE"),
nl->StringAtom(BasicType()),
nl->StringAtom("((real real) .. (real real))"),
nl->StringAtom("((3.5 -6.0) (1.0 2) (-9.1 5))"),
nl->StringAtom("created by nested list or stream of points."))));
}
// returns the number of elements in the point array
long ColPoint::getCount() {
return count;
}
// returns the x coordinate of the indexed point entry
double ColPoint::getX(long index) {
return aPoint[index].x;
}
// returns the x coordinate of the indexed point entry
double ColPoint::getY(long index) {
return aPoint[index].y;
}
// returns the address of the first element of the apoint array
void* ColPoint::getArrayAddress() {
return &aPoint[0].x;
}
// appends one point to the point array
bool ColPoint::append(Point* point) {
if(!point->IsDefined()) {
cout << "point is undefined!" << endl;
return false;
}
if(point->IsEmpty()) {
cout << "point is empty" << endl;
return false;
}
// calculate and allocate sufficient memory for the tuple array
while ( (count * sizeof(sPoint)) >= allocBytes[step]) step++;
aPoint = static_cast<sPoint*> (realloc(aPoint, allocBytes[step]));
if (aPoint == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all lines!");
return false;
}
// initialize tuple array with next line
aPoint[count].x = point->GetX();
aPoint[count].y = point->GetY();
count++;
return true;
}
// add terminator entries to arrays and finalize counters
void ColPoint::finalize() {
aPoint = static_cast<sPoint*>(realloc(aPoint, count * sizeof(sPoint)));
cout << count * sizeof(sPoint) << " bytes used.\n";
}
/*
The ~merge~ - function concatenates two apoints and returns a single apoint.
It uses the memcpy - function of the string-library
*/
// merges two apoints into one apoint
bool ColPoint::merge(ColPoint* cPoint1, ColPoint* cPoint2) {
// calculate and allocate sufficient memory for the result array
long cc1 = cPoint1->getCount();
long cc2 = cPoint2->getCount();
aPoint = static_cast<sPoint*>
(realloc(aPoint, (cc1 + cc2) * sizeof(sPoint)));
if (aPoint == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all points!");
return false;
}
memcpy(&aPoint[0].x, cPoint1->getArrayAddress(), cc1 * sizeof(sPoint));
memcpy(&aPoint[cc1].x, cPoint2->getArrayAddress(), cc2 * sizeof(sPoint));
count = cc1 + cc2;
return true;
}
// test output of the array aPoint and there parameters
void ColPoint::showArray(string title) {
cout << "\n--------------------------------------------\n" << title << "\n";
// output of the array aPoint
cout << "aPoint (" << count << "):\n";
cout << "Bytes allocated: " << count * sizeof(sPoint) << "\n";
cout << "index\tx\ty\n";
for (long cp = 0; cp < count; cp++) {
cout << cp << ":\t" << aPoint[cp].x << "\t" << aPoint[cp].y << "\n";
}
cout << "--------------------------------------------\n";
}
// reads a nested list and stores it into the array format
Word ColPoint::In(const ListExpr typeInfo, ListExpr instance,
const int errorPos, ListExpr &errorInfo, bool &correct) {
correct = false;
sPoint* inArray = NULL; // array which contains the input point values
long inCount = 0; // counter of input points, grows with each point
long step = 0; // index of actual allocated memory (allocBytes)
Word error(static_cast<void*>(0)); // create an error result pointing at 0
errorInfo = listutils::typeError("Error in ColPoint.In!!!");
if (listutils::isSymbolUndefined(instance)) {
cmsg.inFunError("Symbol is undefined!");
return error;
}
if (nl->IsAtom(instance)) { // exit when the nested list is an atom
cmsg.inFunError("Nested list must not be an atom!");
return error;
}
while (!nl->IsEmpty(instance)) { // as long as there are points left
ListExpr pointLeaf = nl->First(instance); // get the point
if ((!nl->HasLength(pointLeaf, 2)) ||
(!listutils::isNumeric(nl->First(pointLeaf))) ||
(!listutils::isNumeric(nl->Second(pointLeaf)))) {
// exit on invalid point
cmsg.inFunError("expected an atom with two numeric values!");
return error;
}
// allocate more memory if the actual allocated memory is insufficient
if (((inCount + 1) * sizeof(sPoint)) >= allocBytes[step]) {
// allocate more memory - in C++ a type casting is necessary unlike in C
inArray = static_cast<sPoint*>(realloc(inArray, allocBytes[++step]));
if (inArray == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return error;
}
}
// append the x- and y-Coordinates from the actual point to the array
inArray[inCount].x = listutils::getNumValue((nl->First(pointLeaf)));
inArray[inCount].y = listutils::getNumValue((nl->Second(pointLeaf)));
inCount++;
instance = nl->Rest(instance); // move to next entry
}
// truncate oversized memory to allocate the real used memory
inArray = static_cast<sPoint*>(realloc(inArray, inCount * sizeof(sPoint)));
cout << inCount * sizeof(sPoint) << " bytes used\n";
Word answer(static_cast<void*>(0));
answer.addr = new ColPoint(inArray, inCount); // create new point object
correct = true;
return answer;
}
// converts the internal array structure int a nested list
ListExpr ColPoint::Out(ListExpr typeInfo, Word value) {
ColPoint* cPoint = static_cast<ColPoint*>(value.addr);
if (cPoint->count == 0) {
return listutils::emptyErrorInfo();
}
ListExpr res = nl->OneElemList(nl->TwoElemList( // init first point
nl->RealAtom(cPoint->aPoint[0].x),
nl->RealAtom(cPoint->aPoint[0].y)));
ListExpr last = res; // set actual list element
for (long i = 1; i < cPoint->count; i++) { // read all aPoint elements
last = nl->Append(last, // append the point to the nested list
nl->TwoElemList(
nl->RealAtom(cPoint->aPoint[i].x),
nl->RealAtom(cPoint->aPoint[i].y)));
}
return res; // pointer to the beginning of the nested list
}
Word ColPoint::Create(const ListExpr typeInfo) {
Word answer(static_cast<void*>(0));
sPoint* inArray = NULL;
answer.addr = new ColPoint(inArray, 0); // create a new point object
return answer; // return its adress
}
void ColPoint::Delete(const ListExpr typeInfo, Word& w) {
ColPoint* cPoint = static_cast<ColPoint*>(w.addr);
delete cPoint;
w.addr = 0;
}
bool ColPoint::Open(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "open apoint...\n";
sPoint* aPoint = NULL; // array which contains the input point values
long count; // amount of points
double x, y; // actual read coordinates
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
bool ok = (valueRecord.Read(&count, sizeL, offset) == sizeL);
offset += sizeL;
// allocate memory depending on the ammount of points
aPoint = static_cast<sPoint*>(malloc(count * sizeof(sPoint)));
if (aPoint == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory (??? Bytes needed)!");
return false;
}
// read each point and store it into the array
for (long i = 0; i < count; i++) {
ok = ok && (valueRecord.Read(&x, sizeD, offset) == sizeD);
offset += sizeD;
aPoint[i].x = x;
ok = ok && (valueRecord.Read(&y, sizeD, offset) == sizeD);
offset += sizeD;
aPoint[i].y = y;
if (!ok) { break; }
}
if (ok) { // create a new ColPoint and store the read values in it
value.addr = new ColPoint(aPoint, count);
} else { // error
value.addr = 0;
}
cout << count * sizeof(sPoint) << " bytes used." << endl;
return ok;
}
bool ColPoint::Save(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "save apoint...\n";
ColPoint* cPoint = static_cast<ColPoint*>(value.addr);
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
long c = cPoint->count;
double x, y;
bool ok = valueRecord.Write(&c, sizeL, offset);
offset += sizeL;
for (long i = 0; i < cPoint->count; i++) {
x = cPoint->aPoint[i].x;
y = cPoint->aPoint[i].y;
ok = ok && valueRecord.Write(&x, sizeL, offset);
offset += sizeD;
ok = ok && valueRecord.Write(&y, sizeL, offset);
offset += sizeD;
}
return ok;
}
void ColPoint::Close(const ListExpr typeInfo, Word& w) {
delete static_cast<ColPoint*>( w.addr);
w.addr = 0;
}
/*
~Clone~ makes a copy of a ColPoint object. Therefore it allocates memory
for the clone object and scans the array of the source object and copies
each entry to the clone object.
*/
Word ColPoint::Clone(const ListExpr typeInfo, const Word& w) {
ColPoint* cPoint = static_cast<ColPoint*>(w.addr);
long tmpCount = cPoint->count;
sPoint* tmpPoint = NULL;
tmpPoint = static_cast<sPoint*>(realloc(tmpPoint,sizeof(sPoint)*tmpCount));
if (tmpPoint == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
for (long cp = 0; cp < tmpCount; cp++)
tmpPoint[cp] = cPoint->aPoint[cp];
Word res((void*)0);
res.addr = new ColPoint(tmpPoint, tmpCount);
return res;
}
void* ColPoint::Cast(void* addr) {
return (new (addr) ColPoint);
}
bool ColPoint::TypeCheck(ListExpr type, ListExpr& errorInfo) {
return nl->IsEqual(type, BasicType());
}
int ColPoint::SizeOf() {
return sizeof(sPoint);
}
//------------------------------------------------------------------------------
/*
3 Class ColLine for column-oriented representation of lines
*/
ColLine::ColLine() {} // standard constructor doing nothing
ColLine::ColLine(sLine *newLine, sSegment *newSegment,
long newCountLine, long newCountSegment) {
aLine = newLine;
aSegment = newSegment;
countLine = newCountLine;
countSegment = newCountSegment;
}
ColLine::ColLine(bool min) { // constructor with minimum initialized arrays
aLine = NULL;
aSegment = NULL;
aLine = static_cast<sLine*>(calloc(1, sizeof(sLine)));
aSegment = static_cast<sSegment*>(calloc(1, sizeof(sSegment)));
countLine = 0;
countSegment = 0;
stepLine = 0;
stepSegment = 0;
}
ColLine::~ColLine() { // destructor
free(aLine);
free(aSegment);
}
// returns the corresponding basic type
const string ColLine::BasicType() { return "aline"; }
// compares the type of the given object with class type
const bool ColLine::checkType(const ListExpr list) {
return listutils::isSymbol(list, BasicType());
}
// description of the Secondo type for the user
ListExpr ColLine::Property() {
return (nl->TwoElemList(
nl->FiveElemList(
nl->StringAtom("Signature"),
nl->StringAtom("Example Type List"),
nl->StringAtom("List Rep"),
nl->StringAtom("Example List"),
nl->StringAtom("Remarks")),
nl->FiveElemList(
nl->StringAtom("-> SIMPLE"),
nl->StringAtom(BasicType()),
nl->StringAtom("((x1 y1 x2 y2) (x2 y2 x3 y3) ... ) ...)"),
nl->StringAtom("(((3.5 -6.0 5 3) (5 3 5 2) (5 3 4 7)))"),
nl->StringAtom("all coordinates must be of type real"))));
}
// returns the number of elements in the line array
long ColLine::getCount() {
return countLine - 1; // subtract the terminator element
}
// returns the total number of segments in the segments array
long ColLine::getSegments() {
return countSegment - 1; // subtract the terminator element
}
// returns the number of segmets for a single line
long ColLine::getSegments(long index) {
return aLine[index + 1].index - aLine[index].index;
}
// returns the indices of the first and last segment of a single line
void ColLine::getLineSegments(long index, long &first, long &last) {
first = aLine[index].index;
last = aLine[index + 1].index;
}
// returns the coordinates of a single segment
void ColLine::getSegmentPoints(long index,
double &x1, double &y1,
double &x2, double &y2) {
x1 = aSegment[index].x1;
y1 = aSegment[index].y1;
x2 = aSegment[index].x2;
y2 = aSegment[index].y2;
}
void* ColLine::getLineAddress() {
return &aLine[0].index;
}
void* ColLine::getSegmentAddress() {
return &aSegment[0].x1;
}
// returns the line found at index of aline
bool ColLine::createLine(Line* line, long index) {
long start = aLine[index].index;
long stop = aLine[index + 1].index;
double x1, y1, x2, y2; // work variables
Point* lp = new Point(true, 0, 0); // actual point
Point* rp = new Point(true, 1, 1); // actual point
HalfSegment* hs = new HalfSegment(true, *lp, *rp); // actual halfsegment
line->StartBulkLoad(); // bulk load for easy filling of line type
for (long i = start; i < stop; i++) { // scan all segments
x1 = aSegment[i].x1;
y1 = aSegment[i].y1;
x2 = aSegment[i].x2;
y2 = aSegment[i].y2;
lp->Set(x1, y1);
rp->Set(x2, y2);
hs->Set(true, *lp, *rp);
line->Put(i - start, *hs); // insert segment to line
}
line->EndBulkLoad(true, true, true);
return true;
}
// appends a complete line to an aline object
bool ColLine::append(Line* line) {
if(!line->IsDefined()) {
cout << "line is undefined!" << endl;
return false;
}
if(line->IsEmpty()) {
cout << "line is empty" << endl;
return false;
}
// calculate and allocate sufficient memory for the tuple array
while ( ((countLine + 2) * sizeof(sLine)) >= allocBytes[stepLine]) {
stepLine++;
}
aLine = static_cast<sLine*> (realloc(aLine, allocBytes[stepLine]));
if (aLine == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all lines!");
return false;
}
// initialize tuple array with next line
aLine[countLine].index = countSegment;
Point lp, rp;
HalfSegment hs;
// make a copy of the input line to avoid modifying the original data
Line *lCopy = new Line(*line);
for (int i = 0; i < lCopy->Size(); i++) {
lCopy->Get(i, hs); // extract actual halfsegment
if(hs.IsLeftDomPoint() == true) {
// allocates memory for the point array and appends the given segment
if (((countSegment + 2) * sizeof(sSegment)) >= allocBytes[stepSegment]){
// allocate more memory - in C++ a type casting is necessary
aSegment = static_cast<sSegment*>
(realloc(aSegment, allocBytes[++stepSegment]));
if (aSegment == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memoryfor new segment!");
return false;
}
}
lp = hs.GetLeftPoint();
rp = hs.GetRightPoint();
aSegment[countSegment].x1 = lp.GetX();
aSegment[countSegment].y1 = lp.GetY();
aSegment[countSegment].x2 = rp.GetX();
aSegment[countSegment].y2 = rp.GetY();
countSegment++;
}
}
countLine++;
return true;
}
// add terminator entries to arrays and finalize counters
void ColLine::finalize() {
aLine[countLine].index = countSegment;
aSegment[countSegment].x1 = 0;
aSegment[countSegment].y1 = 0;
aSegment[countSegment].x2 = 0;
aSegment[countSegment].y2 = 0;
countLine++;
countSegment++;
aLine = static_cast<sLine*>
(realloc(aLine, countLine * sizeof(sLine)));
aSegment = static_cast<sSegment*>
(realloc(aSegment, countSegment * sizeof(sSegment)));
cout << countLine * sizeof(sLine) + countSegment * sizeof(sSegment)
<< " bytes used.\n";
}
// merges two alines into one aline
bool ColLine::merge(ColLine* cLine1, ColLine* cLine2) {
// calculate and allocate sufficient memory for the result array
long ccl1 = cLine1->getCount();
long ccl2 = cLine2->getCount();
long ccs1 = cLine1->getSegments();
long ccs2 = cLine2->getSegments();
aLine = static_cast<sLine*>
(realloc(aLine, (ccl1 + ccl2 + 1) * sizeof(sLine)));
if (aLine == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all lines!");
return false;
}
aSegment = static_cast<sSegment*>
(realloc(aSegment, (ccs1 + ccs2 + 1) * sizeof(sSegment)));
if (aSegment == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all segments!");
return false;
}
// fast block copy
memcpy(&aLine[0].index, cLine1->getLineAddress(),
ccl1 * sizeof(sLine));
memcpy(&aLine[ccl1].index, cLine2->getLineAddress(),
(ccl2 + 1) * sizeof(sLine));
memcpy(&aSegment[0].x1, cLine1->getSegmentAddress(),
ccs1 * sizeof(sSegment));
memcpy(&aSegment[ccs1].x1, cLine2->getSegmentAddress(),
(ccs2 + 1) * sizeof(sSegment));
// correct indices of second aline
for (long i = ccl1; i < (ccl1 + ccl2 + 1); i++)
aLine[i].index += ccs1;
countLine = ccl1 + ccl2 + 1;
countSegment = ccs1 + ccs2 + 1;
return true;
}
// test output of the arrays aLine and aSegment and there parameters
void ColLine::showArrays(string title) {
cout << "\n--------------------------------------------\n" << title << "\n";
// output of the array aLine
cout << "aLine (" << countLine << "):\n";
cout << "Bytes allocated: " << countLine * sizeof(sLine) << "\n";
cout << "index\tsegment\n";
for (long cl = 0; cl < countLine; cl++) {
cout << cl << ":\t" << aLine[cl].index << "\n";
}
// output of the array aSegment
cout << "aSegment (" << countSegment << "):\n";
cout << "Bytes allocated: " << countSegment * sizeof(sSegment) << "\n";
cout << "index\tx1\ty1\tx2\ty2\n";
for (long cs = 0; cs < countSegment; cs++) {
cout << cs << ":\t"
<< aSegment[cs].x1 << "\t"
<< aSegment[cs].y1 << "\t"
<< aSegment[cs].x2 << "\t"
<< aSegment[cs].y2 << "\n";
}
cout << "--------------------------------------------\n";
}
// scans a nested-list and converts it into an array of lines.
Word ColLine::In(const ListExpr typeInfo, ListExpr instance,
const int errorPos, ListExpr &errorInfo, bool &correct) {
correct = false;
Word error(static_cast<void*>(0)); // create an error result pointing at 0
errorInfo = listutils::typeError("Error in ColLine.In!!!");
if (listutils::isSymbolUndefined(instance)) {
cmsg.inFunError("Symbol is undefined!");
return error;
}
if (nl->IsAtom(instance)) { // exit when the nested list is an atom
cmsg.inFunError("Nested list must not be an atom!");
return error;
}
sLine* inLine = NULL; // array which contains the input line values
sSegment* inSeg = NULL; // array which contains the input line values
long cl = 0; // counter of input lines, grows with each line
long cs = 0; // counter of input lines, grows with each line
// index to the array allocbytes of actual allocated memory for each array
long stepLine = 0;
long stepSegment = 0;
// allocate memory for the input arrays
inLine = static_cast<sLine*>(realloc(inLine, allocBytes[stepLine]));
inSeg = static_cast<sSegment*>(realloc(inSeg, allocBytes[stepSegment]));
ListExpr lineNL = instance; // get all lines
while (!nl->IsEmpty(lineNL)) { // as long as there are lines left
// allocate more memory if the actual allocated memory is insufficient
if (((cl + 2) * sizeof(sLine)) >= allocBytes[stepLine]) {
// allocate more memory - C++ needs type casting unlike C
inLine = static_cast<sLine*>(realloc(inLine, allocBytes[++stepLine]));
if (inLine == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for line indices!");
return error;
}
}
inLine[cl].index = cs;
cl++;
ListExpr segmentNL = nl->First(lineNL); // get first segment
if (nl->IsAtom(segmentNL)) {
cmsg.inFunError("expected a nested list of segments for each line!");
return error;
}
while(!nl->IsEmpty(segmentNL)) {
ListExpr segmentLeaf = nl->First(segmentNL); // get first segment
if ((!nl->HasLength(segmentLeaf, 4)) ||
(!listutils::isNumeric(nl->First(segmentLeaf))) ||
(!listutils::isNumeric(nl->Second(segmentLeaf))) ||
(!listutils::isNumeric(nl->Third(segmentLeaf))) ||
(!listutils::isNumeric(nl->Fourth(segmentLeaf)))) {
// exit on invalid segment
cmsg.inFunError("expected an atom with four numeric values!");
return error;
}
// allocate more memory if the actual allocated memory is insufficient
if (((cs + 2) * sizeof(sSegment)) >= allocBytes[stepSegment]) {
// allocate more memory - C++ needs type casting unlike C
inSeg = static_cast<sSegment*>
(realloc(inSeg, allocBytes[++stepSegment]));
if (inSeg == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for line segments!");
return error;
}
}
// append the coordinates from the actual line to the array
inSeg[cs].x1 = listutils::getNumValue((nl->First(segmentLeaf)));
inSeg[cs].y1 = listutils::getNumValue((nl->Second(segmentLeaf)));
inSeg[cs].x2 = listutils::getNumValue((nl->Third(segmentLeaf)));
inSeg[cs].y2 = listutils::getNumValue((nl->Fourth(segmentLeaf)));
cs++;
segmentNL = nl->Rest(segmentNL);
}
lineNL = nl->Rest(lineNL); // move to next line
}
// add terminator entries to arrays. they are needed for the Out-function.
inLine[cl].index = cs;
inSeg[cs].x1 = 0;
inSeg[cs].y1 = 0;
inSeg[cs].x2 = 0;
inSeg[cs].y2 = 0;
// truncate oversized memory to allocate the real used memory
inLine = static_cast<sLine*>(realloc(inLine, ++cl * sizeof(sLine)));
inSeg = static_cast<sSegment*>(realloc(inSeg, ++cs * sizeof(sSegment)));
cout << cl * sizeof(sLine) + cs * sizeof(sSegment) << " bytes used\n";
Word answer(static_cast<void*>(0));
answer.addr = new ColLine(inLine, inSeg, cl, cs); // create new line object
correct = true;
return answer; // return the object
}
// converts a line array into a nested list format
ListExpr ColLine::Out(ListExpr typeInfo, Word value) {
ColLine* cLine = static_cast<ColLine*>(value.addr);
if (cLine->countLine == 0) {
return listutils::emptyErrorInfo();
}
long cl = 1; // counter for index array aLine starting at second entry
// initialize heads and tails for working lists
ListExpr lineNL = nl->TheEmptyList();
ListExpr lineNLLast = lineNL;
// init tuple with first segment
ListExpr tupleNL = nl->OneElemList(nl->FourElemList(
nl->RealAtom(cLine->aSegment[0].x1),
nl->RealAtom(cLine->aSegment[0].y1),
nl->RealAtom(cLine->aSegment[0].x2),
nl->RealAtom(cLine->aSegment[0].y2)));
ListExpr tupleNLLast = tupleNL;
// main loop: traverse all segments in the segment array
for (long cs = 1; cs < cLine->countSegment; cs++) { // read all segments
// check whether a new line appears
if (cs == cLine->aLine[cl].index) {
if (nl->IsEmpty(lineNL)) { // append tuple to first line
lineNL = nl->OneElemList(tupleNL);
lineNLLast = lineNL;
} else { // append tuple to line
lineNLLast = nl->Append(lineNLLast, tupleNL);
}
tupleNL = nl->OneElemList(nl->FourElemList(
nl->RealAtom(cLine->aSegment[cs].x1),
nl->RealAtom(cLine->aSegment[cs].y1),
nl->RealAtom(cLine->aSegment[cs].x2),
nl->RealAtom(cLine->aSegment[cs].y2)));
tupleNLLast = tupleNL;
cl++;
} else { // append new segment to segment list
tupleNLLast = nl->Append(tupleNLLast, nl->FourElemList(
nl->RealAtom(cLine->aSegment[cs].x1),
nl->RealAtom(cLine->aSegment[cs].y1),
nl->RealAtom(cLine->aSegment[cs].x2),
nl->RealAtom(cLine->aSegment[cs].y2)));
}
}
return lineNL;
}
Word ColLine::Create(const ListExpr typeInfo) {
Word answer(static_cast<void*>(0));
sLine* inLine = NULL;
sSegment* inSeg = NULL;
answer.addr = new ColLine(inLine, inSeg, 0, 0); // create new line object
return answer; // return its adress
}
// Removes the complete object including disc parts if there are any
void ColLine::Delete(const ListExpr typeInfo, Word& w) {
ColLine* cLine = static_cast<ColLine*>(w.addr);
delete cLine;
w.addr = 0;
}
// Reads an array from disc via an ~SmiRecord~.
bool ColLine::Open(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "open aline...\n";
sLine* inLine = NULL; // array which contains the input line values
sSegment* inSegment = NULL; // array which contains the input line values
long cl; // number of lines
long cs; // number of segments
long valueL;
double x1, y1, x2, y2; // actual read coordinates
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
// allocate memory depending on the ammount of lines
bool ok = (valueRecord.Read(&cl, sizeL, offset) == sizeL);
offset += sizeL;
inLine = static_cast<sLine*>(malloc(cl * sizeof(sLine)));
if (inLine == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for line indices!");
return false;
}
// allocate memory depending on the ammount of segments
ok = (valueRecord.Read(&cs, sizeL, offset) == sizeL);
offset += sizeL;
inSegment = static_cast<sSegment*>(malloc(cs * sizeof(sSegment)));
if (inSegment == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for segments!");
return false;
}
// read each line and store it into the array
for (long i = 0; i < cl; i++) {
ok = ok && (valueRecord.Read(&valueL, sizeL, offset) == sizeL);
offset += sizeL;
inLine[i].index = valueL;
if (!ok) break;
}
// read each segment and store it into the array
for (long i = 0; i < cs; i++) {
ok = ok && (valueRecord.Read(&x1, sizeD, offset) == sizeD);
offset += sizeD;
ok = ok && (valueRecord.Read(&y1, sizeD, offset) == sizeD);
offset += sizeD;
ok = ok && (valueRecord.Read(&x2, sizeD, offset) == sizeD);
offset += sizeD;
ok = ok && (valueRecord.Read(&y2, sizeD, offset) == sizeD);
offset += sizeD;
inSegment[i].x1 = x1;
inSegment[i].y1 = y1;
inSegment[i].x2 = x2;
inSegment[i].y2 = y2;
if (!ok) break;
}
if (ok) { // create a new ColLine and store the read values in it
value.addr = new ColLine(inLine, inSegment, cl, cs);
} else { // error
value.addr = 0;
}
cout << cl * sizeof(sLine) + cs * sizeof(sSegment)
<< " bytes used." << endl;
return ok;
}
// Saves an array of lines into a SmiRecord.
bool ColLine::Save(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "save aline...\n";
ColLine* cLine = static_cast<ColLine*>(value.addr);
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
long cl = cLine->countLine;
long cs = cLine->countSegment;
long valueL;
double x1, y1, x2, y2;
bool ok = valueRecord.Write(&cl, sizeL, offset);
offset += sizeL;
ok = valueRecord.Write(&cs, sizeL, offset);
offset += sizeL;
// write all line indices to smi record
for (long i = 0; i < cLine->countLine; i++) {
valueL = cLine->aLine[i].index;
ok = ok && valueRecord.Write(&valueL, sizeL, offset);
offset += sizeL;
}
// write all segments to smi record
for (long i = 0; i < cLine->countSegment; i++) {
x1 = cLine->aSegment[i].x1;
ok = ok && valueRecord.Write(&x1, sizeL, offset);
offset += sizeD;
y1 = cLine->aSegment[i].y1;
ok = ok && valueRecord.Write(&y1, sizeL, offset);
offset += sizeD;
x2 = cLine->aSegment[i].x2;
ok = ok && valueRecord.Write(&x2, sizeL, offset);
offset += sizeD;
y2 = cLine->aSegment[i].y2;
ok = ok && valueRecord.Write(&y2, sizeL, offset);
offset += sizeD;
}
return ok;
}
void ColLine::Close(const ListExpr typeInfo, Word& w) {
delete static_cast<ColLine*>(w.addr);
w.addr = 0;
}
/*
~Clone~ makes a copy of a ColLine object. Therefore it allocates memory
for the clone object and scans the arrays of the source object and copies
each entry to the clone object.
*/
Word ColLine::Clone(const ListExpr typeInfo, const Word& w) {
ColLine* cLine = static_cast<ColLine*>(w.addr);
long tmpCL = cLine->countLine;
sLine* tmpLine = NULL;
tmpLine = static_cast<sLine*>(realloc(tmpLine, sizeof(sLine) * tmpCL));
if (tmpLine == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
long tmpCS = cLine->countSegment;
sSegment* tmpSeg = NULL;
tmpSeg = static_cast<sSegment*>(realloc(tmpSeg, sizeof(sSegment) * tmpCS));
if (tmpSeg == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
for (long cl = 0; cl < tmpCL; cl++)
tmpLine[cl] = cLine->aLine[cl];
for (long cs = 0; cs < tmpCS; cs++)
tmpSeg[cs] = cLine->aSegment[cs];
Word res((void*)0);
res.addr = new ColLine(tmpLine, tmpSeg, tmpCL, tmpCS);
return res;
}
void* ColLine::Cast(void* addr) {
return (new (addr) ColLine);
}
bool ColLine::TypeCheck(ListExpr type, ListExpr& errorInfo) {
return nl->IsEqual(type, BasicType());
}
int ColLine::SizeOf() {
return sizeof(sLine) + sizeof(sSegment); // the result isn't meaningful
}
//-----------------------------------------------------------------------------
/*
4 Class ColRegion for column-oriented representation of Regions
*/
ColRegion::ColRegion() {} // standard constructor doing nothing
// non-standard constructor initializing the object with parameters
ColRegion::ColRegion(sRegion* newTuple, sCycle* newCycle, sPoint* newPoint,
long newCountTuple, long newCountCycle, long newCountPoint) {
aRegion = newTuple;
aCycle = newCycle;
aPoint = newPoint;
countRegion = newCountTuple;
countCycle = newCountCycle;
countPoint = newCountPoint;
}
// non-standard constructor initializing the object with minimum data
ColRegion::ColRegion(bool min) {
aRegion = NULL;
aCycle = NULL;
aPoint = NULL;
aRegion = static_cast<sRegion*>(calloc(1, sizeof(sRegion)));
aCycle = static_cast<sCycle*>(calloc(1, sizeof(sCycle)));
aPoint = static_cast<sPoint*>(calloc(1, sizeof(sPoint)));
countRegion = 0;
countCycle = 0;
countPoint = 0;
stepRegion = 0;
stepCycle = 0;
stepPoint = 0;
}
// destructor - free allocated menory
ColRegion::~ColRegion() {
free(aRegion);
free(aCycle);
free(aPoint);
}
// returns the corresponding basic type
const string ColRegion::BasicType() { return "aregion"; }
// compares the type of the given object with class type
const bool ColRegion::checkType(const ListExpr list) {
return listutils::isSymbol(list, BasicType());
}
// description of the Secondo type for the user
ListExpr ColRegion::Property() {
return (nl->TwoElemList(
nl->FiveElemList(
nl->StringAtom("Signature"),
nl->StringAtom("Example Type List"),
nl->StringAtom("List Rep"),
nl->StringAtom("Example List"),
nl->StringAtom("Remarks")),
nl->FiveElemList(
nl->StringAtom("-> SIMPLE"),
nl->StringAtom(BasicType()),
nl->StringAtom("((real real) .. (real real))"),
nl->StringAtom("((3.5 -6.0) (1.0 2) (-9.1 5))"),
nl->StringAtom("aregion is defined by a list of regions."))));
}
// returns the number of regions without terminator
long ColRegion::getCount() {
return countRegion - 1;
}
// returns the number of cycles within the aCycle array
long ColRegion::getCountCycles() {
return countCycle - 1; // subtract the terminator element
}
// returns the number of points within the aPoint array
long ColRegion::getCountPoints() {
return countPoint - 1; // subtract the terminator element
}
// returns the number of points of a given region
long ColRegion::getCountPoints(const long index) {
return aRegion[index + 1].indexPoint - aRegion[index].indexPoint - 1;
}
void ColRegion::getRegion(const long index,
long &indexCycle, long &indexPoint,
double &mbbX1, double &mbbY1,
double &mbbX2, double &mbbY2) {
indexCycle = aRegion[index].indexCycle;
indexPoint = aRegion[index].indexPoint;
mbbX1 = aRegion[index].mbbX1;
mbbY1 = aRegion[index].mbbY1;
mbbX2 = aRegion[index].mbbX2;
mbbY2 = aRegion[index].mbbY2;
}
long ColRegion::getCycle(const long index) {
return aCycle[index].index;
}
void ColRegion::getPoint(const long index, double &x, double &y) {
x = aPoint[index].x;
y = aPoint[index].y;
}
void* ColRegion::getRegionAddress() {
return &aRegion[0].indexCycle;
}
void* ColRegion::getCycleAddress() {
return &aCycle[0].index;
}
void* ColRegion::getPointAddress() {
return &aPoint[0].x;
}
// extracts one region from aregion to region of the spatial algebra.
bool ColRegion::createRegion(Region*& region, const long index) {
vector<vector<Point> > cycles;
long firstCyc = aRegion[index].indexCycle;
long lastCyc = aRegion[index + 1].indexCycle; // first cycle of next region
// scan all cycles of the region
for (long indexCyc = firstCyc; indexCyc < lastCyc; indexCyc++) {
vector<Point> cycle;
long firstPnt = abs(aCycle[indexCyc].index);
long lastPnt = abs(aCycle[indexCyc + 1].index);
// append all points of the actual cycle to the vector
for (long indexPnt = firstPnt; indexPnt < lastPnt; indexPnt++) {
Point p(true, aPoint[indexPnt].x, aPoint[indexPnt].y);
cycle.push_back(p);
}
// append first point to close the cycle as demanded in RegionTools.h
Point p(true, aPoint[firstPnt].x, aPoint[firstPnt].y);
cycle.push_back(p);
// ensure that faces are clockwise and holes are counter-clockwise
if (((aCycle[indexCyc].index < 0) && getDir(cycle)) ||
((aCycle[indexCyc].index >= 0) && !getDir(cycle)))
reverseCycle(cycle);
cycles.push_back(cycle);
}
region = buildRegion(cycles);
// region->Print(cout); // returns a valid value
return true;
}
// allocates memory for the point array and appends the given point
int ColRegion::appendPoint(const Point p, long &stepPoint) {
double x = p.GetX();
double y = p.GetY();
// allocate more memory if actual allocated memory is insufficient
if (((countPoint + 2) * sizeof(sPoint)) >= allocBytes[stepPoint]) {
// allocate more memory - in C++ a type casting is necessary
stepPoint++;
aPoint = static_cast<sPoint*> (realloc(aPoint, allocBytes[stepPoint]));
if (aPoint == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all points!");
return false;
}
}
aPoint[countPoint].x = x;
aPoint[countPoint].y = y;
// adjust mbb coordinates if neccessary
if (x < aRegion[countRegion].mbbX1) aRegion[countRegion].mbbX1 = x;
if (x > aRegion[countRegion].mbbX2) aRegion[countRegion].mbbX2 = x;
if (y < aRegion[countRegion].mbbY1) aRegion[countRegion].mbbY1 = y;
if (y > aRegion[countRegion].mbbY2) aRegion[countRegion].mbbY2 = y;
countPoint++; // increase index of point array
return true;
}
// allocates memory for the cycle array and appends the give cycle
int ColRegion::appendCycle(long cp, long &stepCycle) {
// allocate more memory if actual allocated memory is insufficient
if (
((countCycle + 2) * sizeof(sCycle)) >= allocBytes[stepCycle]) {
// allocate more memory - in C++ a type casting is necessary
aCycle =static_cast<sCycle*>
(realloc(aCycle, allocBytes[++stepCycle]));
if (aCycle == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all cycles!");
return false;
}
}
aCycle[countCycle].index = cp;
countCycle++;
return true;
}
// This function appends a region datatype of the spatial algebra
// to an attrarray of regions of the column spatial algebra.
bool ColRegion::append(Region* region) {
if(!region->IsDefined()) {
cout << "region is undefined!" << endl;
return false;
}
if(region->IsEmpty()) {
cout << "region is empty" << endl;
return false;
}
// calculate and allocate sufficient memory for the tuple array
while (
((countRegion + 2) * sizeof(sRegion)) >= allocBytes[stepRegion]) {
stepRegion++;
}
aRegion = static_cast<sRegion*> (realloc(aRegion, allocBytes[stepRegion]));
if (aRegion == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory for all regions!");
return false;
}
// initialize tuple array with region
aRegion[countRegion].indexCycle = countCycle;
aRegion[countRegion].indexPoint = countPoint;
// mbb set to extremes, so they will be adapted in any case
aRegion[countRegion].mbbX1 = 999999;
aRegion[countRegion].mbbY1 = 999999;
aRegion[countRegion].mbbX2 = -999999;
aRegion[countRegion].mbbY2 = -999999;
Point outputP, leftoverP;
HalfSegment hs, hsnext;
// make a copy of the input region to avoid modifying the original data
Region *rCopy=new Region(*region, true);
rCopy->LogicSort(); // sort is important for consecutive iterations
rCopy->Get(0, hs); // extract first hs
int currFace = hs.attr.faceno; // set actual face to face of first hs
int currCycle = hs.attr.cycleno; // set actual cycle to cycle of first hs
// calculate coordinates of first point in region
rCopy->Get(1, hsnext); // extract second hs
if ((hs.GetLeftPoint() == hsnext.GetLeftPoint()) ||
((hs.GetLeftPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetRightPoint();
leftoverP = hs.GetLeftPoint();
} else if ((hs.GetRightPoint() == hsnext.GetLeftPoint()) ||
((hs.GetRightPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetLeftPoint();
leftoverP = hs.GetRightPoint();
} else {
cmsg.inFunError("Wrong data format: discontiguous segments!");
return false;
}
// append first point to aPoint[]
if (!appendPoint(outputP, stepPoint)) {
cmsg.inFunError("error on appending first point!");
return false;
}
// append first cycle (face) to aCycle[]
if (!appendCycle(countPoint - 1, stepCycle)) {
cmsg.inFunError("error on appending first face!");
return false;
}
// main loop: scan all remaining halfsegments
for (int i = 1; i < rCopy->Size(); i++) {
rCopy->Get(i, hs); // extract actual hs
if (hs.attr.faceno == currFace) { // hs belongs to actual face
if (hs.attr.cycleno == currCycle) { // hs belongs to actual cycle
// calculate coordinates of actual point in region
outputP=leftoverP;
if (hs.GetLeftPoint() == leftoverP)
leftoverP = hs.GetRightPoint();
else if (hs.GetRightPoint() == leftoverP) {
leftoverP = hs.GetLeftPoint();
} else {
cmsg.inFunError("Wrong data format: discontiguous segments!");
return false;
}
// append actual point to aPoint[]
if (!appendPoint(outputP, stepPoint)) {
cmsg.inFunError("error on appending point!");
return false;
}
} else { // hs belongs to new cycle = hole
currCycle = hs.attr.cycleno;
rCopy->Get(i+1, hsnext);
if ((hs.GetLeftPoint() == hsnext.GetLeftPoint()) ||
((hs.GetLeftPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetRightPoint();
leftoverP = hs.GetLeftPoint();
} else if ((hs.GetRightPoint() == hsnext.GetLeftPoint()) ||
((hs.GetRightPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetLeftPoint();
leftoverP = hs.GetRightPoint();
} else {
cmsg.inFunError("Wrong data format: discontiguous segments!");
return false;
}
// append new point to aPoint[]
if (!appendPoint(outputP, stepPoint)) {
cmsg.inFunError("error on appending point!");
return false;
}
// append new cycle (hole = -index) to aCycle[]
if (!appendCycle((countPoint - 1) * (-1), stepCycle)) {
cmsg.inFunError("error on appending hole!");
return false;
}
}
} else { // hs belongs to new face
currFace = hs.attr.faceno;
currCycle = hs.attr.cycleno;
rCopy->Get( i+1, hsnext );
if ((hs.GetLeftPoint() == hsnext.GetLeftPoint()) ||
((hs.GetLeftPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetRightPoint();
leftoverP = hs.GetLeftPoint();
} else if ((hs.GetRightPoint() == hsnext.GetLeftPoint()) ||
((hs.GetRightPoint() == hsnext.GetRightPoint()))) {
outputP = hs.GetLeftPoint();
leftoverP = hs.GetRightPoint();
} else {
cmsg.inFunError("Wrong data format: discontiguous segments!");
return false;
}
if (!appendPoint(outputP, stepPoint)) {
cmsg.inFunError("error on appending point!");
return false;
}
if (!appendCycle(countPoint - 1, stepCycle)) {
cmsg.inFunError("error on appending face!");
return false;
}
}
}
countRegion++;
return true;
}
/*
The ~finalize~ function appends a terminator to each array of the aregion type.
*/
void ColRegion::finalize() {
aPoint[countPoint].x = 0;
aPoint[countPoint].y = 0;
aCycle[countCycle].index = countPoint;
aRegion[countRegion].indexCycle = countCycle;
aRegion[countRegion].indexPoint = countPoint;
aRegion[countRegion].mbbX1 = 0;
aRegion[countRegion].mbbY1 = 0;
aRegion[countRegion].mbbX2 = 0;
aRegion[countRegion].mbbY2 = 0;
countRegion++;
countCycle++;
countPoint++;
// truncate oversized memory to allocate only the real used memory
aRegion = static_cast<sRegion*>
(realloc(aRegion, countRegion * sizeof(sRegion)));
aCycle = static_cast<sCycle*>
(realloc(aCycle, countCycle * sizeof(sCycle)));
aPoint = static_cast<sPoint*>
(realloc(aPoint, countPoint * sizeof(sPoint)));
}
/*
merges two aregions into one aregion.
*/
bool ColRegion::merge(ColRegion* cRegion1, ColRegion* cRegion2) {
long ccr1 = cRegion1->getCount();
long ccr2 = cRegion2->getCount();
long ccc1 = cRegion1->getCountCycles();
long ccc2 = cRegion2->getCountCycles();
long ccp1 = cRegion1->getCountPoints();
long ccp2 = cRegion2->getCountPoints();
// allocate memory for the input arrays
aRegion = static_cast<sRegion*>
(realloc(aRegion, (ccr1 + ccr2 + 1) * sizeof(sRegion)));
aCycle = static_cast<sCycle*>
(realloc(aCycle, (ccc1 + ccc2 + 1) * sizeof(sCycle)));
aPoint = static_cast<sPoint*>
(realloc(aPoint, (ccp1 + ccp2 + 1) * sizeof(sPoint)));
/*
The following instructions are commented out because there is still
a problem with memory allocation. Instead the loops below are used.
----
// fast block copy
memcpy(&aRegion[0].indexCycle, cRegion1->getRegionAddress(),
ccr1 * sizeof(sRegion));
memcpy(&aRegion[ccr1].indexCycle, cRegion2->getRegionAddress(),
(ccr1 + 1) * sizeof(sRegion));
memcpy(&aCycle[0].index, cRegion1->getCycleAddress(),
ccc1 * sizeof(sCycle));
memcpy(&aCycle[ccc1].index, cRegion2->getCycleAddress(),
(ccc1 + 1) * sizeof(sCycle));
memcpy(&aPoint[0].x, cRegion1->getPointAddress(),
ccp1 * sizeof(sPoint));
memcpy(&aPoint[ccp1].x, cRegion2->getPointAddress(),
(ccp1 + 1) * sizeof(sPoint));
// correct indices of second aRegion
for (long i = ccr1; i < (ccr1 + ccr2 + 1); i++) {
aRegion[i].indexCycle += ccc1;
aRegion[i].indexPoint += ccp1;
}
// correct indices of second aCycle
for (long i = ccc1; i < (ccc1 + ccc2 + 1); i++)
aCycle[i].index += ccp1;
----
*/
// scan all regions of first aregion
for (long i = 0; i < ccr1; i++)
cRegion1->getRegion(i, aRegion[i].indexCycle, aRegion[i].indexPoint,
aRegion[i].mbbX1, aRegion[i].mbbY1,
aRegion[i].mbbX2, aRegion[i].mbbY2);
// scan all regions of second aregion including terminator
long iCycle, iPoint;
for (long i = 0; i <= ccr2; i++) {
cRegion2->getRegion(i, iCycle, iPoint,
aRegion[i+ccr1].mbbX1, aRegion[i+ccr1].mbbY1,
aRegion[i+ccr1].mbbX2, aRegion[i+ccr1].mbbY2);
aRegion[i+ccr1].indexCycle = iCycle + ccc1;
aRegion[i+ccr1].indexPoint = iPoint + ccp1;
}
// scan all cycles of first aregion
for (long i = 0; i < ccc1; i++)
aCycle[i].index = cRegion1->getCycle(i);
// scan all cycles of second aregion including terminator
for (long i = 0; i <= ccc2; i++)
aCycle[i+ccc1].index = cRegion2->getCycle(i) + ccp1;
// scan all points of first aregion
for (long i = 0; i < ccp1; i++)
cRegion1->getPoint(i, aPoint[i].x, aPoint[i].y);
// scan all points of second aregion including terminator
for (long i = 0; i <= ccp2; i++)
cRegion2->getPoint(i, aPoint[i+ccp1].x, aPoint[i+ccp1].y);
countRegion = ccr1 + ccr2 + 1;
countCycle = ccc1 + ccc2 + 1;
countPoint = ccp1 + ccp2 + 1;
return true;
}
// test output of the arrays aRegion, aCycle and aPoint and there parameters
void ColRegion::showArrays(string title, bool showPoints) {
cout << "\n--------------------------------------------\n" << title << "\n";
// output of the array aRegion
cout << "aRegion (" << countRegion << "):\n";
cout << "Bytes allocated: " << countRegion * sizeof(aRegion[0]) << "\n";
cout << "index\tcycle\tpoint\tmbb-l\tmbb-b\tmbb-r\tmbb-t\n";
for (long cr = 0; cr < countRegion; cr++) {
cout << cr << ":\t" << aRegion[cr].indexCycle << "\t"
<< aRegion[cr].indexPoint << "\t"
<< aRegion[cr].mbbX1 << "\t"
<< aRegion[cr].mbbY1 << "\t"
<< aRegion[cr].mbbX2 << "\t"
<< aRegion[cr].mbbY2 << "\n";
}
// output of the array aCycle
cout << "aCycle (" << countCycle << "):\n";
cout << "Bytes allocated: " << countCycle * sizeof(aCycle[0]) << "\n";
cout << "index\tpoint\n";
for (long cc = 0; cc < countCycle; cc++) {
cout << cc << ":\t" << aCycle[cc].index << "\n";
}
// output of the array aPoint
cout << "aPoint (" << countPoint << "):\n";
cout << "Bytes allocated: " << countPoint * sizeof(aPoint[0]) << "\n";
if (showPoints) {
cout << "index\tx\ty\n";
for (long cp = 0; cp < countPoint; cp++) {
cout << cp << ":\t" << aPoint[cp].x << "\t" << aPoint[cp].y << "\n";
}
}
cout << "--------------------------------------------\n";
}
/*
Checks whether two segments intersect. It uses the vector equation of two
lines, which are derived from the two
segments s1 = (x1, y1, x2, y2) and s2 = (x3, y3, x4, y4).
*/
inline bool ColRegion::intersects(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4) {
double s1x = x2-x1;
double s1y = y2-y1;
double s2x = x4-x3;
double s2y = y4-y3;
// vector equations of both straight lines solved for scalars t and s
double s = (s1x*(y1-y3) - s1y*(x1-x3)) / (s1x*s2y - s2x*s1y);
double t = (s2x*(y1-y3) - s2y*(x1-x3)) / (s1x*s2y - s2x*s1y);
// return true if a point lies on both segments, otherwise return false
return (s >= 0 && s <= 1 && t >= 0 && t <= 1);
}
/*
The following function checks whether a single point is inside a region
Needs x and y of the point and the index of aRegion.
*/
inline bool ColRegion::pointInsideRegion(double x, double y, long idReg) {
// define a x coordinate outside the region
long x2 = aRegion[idReg].mbbX1 - 1;
// first and last cycle within the region
long ccStart = aRegion[idReg].indexCycle;
long ccStop = aRegion[idReg + 1].indexCycle;
long cpStart, cpStop;
// number of intersections
long isCount = 0;
double x3, y3, x4, y4;
// scan all cycles of the actual region
for (long idCyc = ccStart; idCyc < ccStop; idCyc++) {
// set first and last point of the actual cycle
cpStart = abs(aCycle[idCyc].index);
cpStop = abs(aCycle[idCyc + 1].index);
// scan all points
for (long idPnt = cpStart; idPnt < cpStop; idPnt++) {
x3 = aPoint[idPnt].x;
y3 = aPoint[idPnt].y;
// set next point of cycle
if ((idPnt + 1) < cpStop) {
x4 = aPoint[idPnt + 1].x;
y4 = aPoint[idPnt + 1].y;
} else {
// if cycle's last point, then set first point to close the cycle
x4 = aPoint[cpStart].x;
y4 = aPoint[cpStart].y;
}
// avoid double counting of vertexes on vertical segments
// the intersection counts only if the second vertex of the segment
// lies below the tested point
if ((y == y3 && y3 > y4) || (y == y4 && y4 > y3))
continue;
if (intersects(x, y, x2, y, x3, y3, x4, y4))
isCount++;
}
}
// if isCount is odd then segment intersects with region
return isCount & 1;
}
/*
checks for each point of the ~ColPoint~ object whether it is inside
one of the regions of the actual ~ColRegions~ object.
Precondition:
- cycle points must be in clockwise or in counterclockwise order,
no matter if they are faces or holes. this precondition is always true.
- the first point of a region must be the leftmost/bottom point.
this precondition is always true.
Complexity: $O(m . n)$,
where ~m~ is the number of points and ~n~ is the number of regions.
known weakness:
if a tested region overlaps the 180th longitude then the function fails.
*/
LongInts* ColRegion::pointsInside(ColPoint* cPoint) {
const long cp1 = cPoint->getCount(); // number of points
long cr = countRegion - 1; // number of regions without terminator
double idPx1;
double idPy1;
// result array of matching indices
LongInts* id = new LongInts();
// scan all points
for (long idP = 0; idP < cp1; idP++) {
// this is the actual point to be checked
idPx1 = cPoint->getX(idP);
idPy1 = cPoint->getY(idP);
// scan all regions
for (long idReg = 0; idReg < cr; idReg++) {
// check if actual point is inside the bounding box of actual region
if ((idPx1 < aRegion[idReg].mbbX1) || (idPx1 > aRegion[idReg].mbbX2) ||
(idPy1 < aRegion[idReg].mbbY1) || (idPy1 > aRegion[idReg].mbbY2))
continue; // if not inside bbox then continue with next region
if (pointInsideRegion(idPx1, idPy1, idReg)) {
id->Append(idP);
// no need to check further regions, continue with next point
break;
}
}
}
return id;
}
/*
Checks for each line of the ~ColLine~ object whether it is inside
one of the regions of the actual ~ColRegions~ object.
To do so, the first point of the line is checked whether it is inside or
outside the boundig box of the region. If it is outside the line
can not be inside the region and the next line is processed.
If it is inside, the number of intersections are countes. if the
number is even then the line is completely inside one of the regions.
any other case the line only crosses the region.
*/
LongInts* ColRegion::linesInside(ColLine* cLine) {
const long cl1 = cLine->getCount(); // number of lines
long cr = countRegion -1; // number of regions without terminator
long cc, cp, idSegStart, idSegStop;
cc = cp = idSegStart = idSegStop = 0;
double x1, y1, x2, y2, x4, y4;
x1 = y1 = x2 = y2 = x4 = y4 = 42;
bool intersect_flag = false; // controls the loops
// result array of matching indices
LongInts* id = new LongInts();
// scan all lines
for (long idL = 0; idL < cl1; idL++) {
// set first and last segment of the line
cLine->getLineSegments(idL, idSegStart, idSegStop);
// scan all regions
for (long idReg = 0; idReg < cr; idReg++) {
// if first point is outside region -> continue with next region
cLine->getSegmentPoints(idSegStart, x1, y1, x2, y2);
if (!pointInsideRegion(x1, y1, idReg)) break;
intersect_flag = false;
// scan all line segments
for (long idLS = idSegStart; ((idLS < idSegStop) && !intersect_flag);
idLS++) {
// get points of actual line segment
cLine->getSegmentPoints(idSegStart, x1, y1, x2, y2);
// number of cycles within the actual region
cc = aRegion[idReg + 1].indexCycle;
// scan all cycles of the actual region
for (long idCyc = aRegion[idReg].indexCycle;
(idCyc < cc) && !intersect_flag; idCyc++) {
// set last point of the actual cycle
cp = aCycle[idCyc + 1].index;
// scan all points
for (long idPnt = aCycle[idCyc].index; idPnt < cp; idPnt++) {
// set next point of cycle
if ((idPnt + 1) < cp) { // next point
x4 = aPoint[idPnt + 1].x;
y4 = aPoint[idPnt + 1].y;
} else { // first point to close the cycle
x4 = aPoint[aCycle[idCyc].index].x;
y4 = aPoint[aCycle[idCyc].index].y;
}
// if intersection between line segment and region segment
// -> continue with next region
if (intersects(x1, y1, x2, y2,
aPoint[idPnt].x, aPoint[idPnt].y, x4, y4)) {
intersect_flag = true;
break;
}
}
}
}
// if no intersection has occured -> append
if (!intersect_flag) id->Append(idL);
}
}
return id;
}
/*
checks for each point of the ~ColPoint~ object whether it is inside
one of the regions of the actual ~ColRegions~ object
and returns the region indices.
*/
LongInts* ColRegion::containsPoints(ColPoint* cPoint) {
const long cp1 = cPoint->getCount(); // number of points
long cr = countRegion - 1; // number of regions without terminator
double idPx1;
double idPy1;
// result array of matching indices
LongInts* id = new LongInts();
// scan all regions
for (long idReg = 0; idReg < cr; idReg++) {
// scan all points
for (long idP = 0; idP < cp1; idP++) {
// this is the actual point to be checked
idPx1 = cPoint->getX(idP);
idPy1 = cPoint->getY(idP);
// check if actual point is inside the bounding box of actual region
if ((idPx1 < aRegion[idReg].mbbX1) || (idPx1 > aRegion[idReg].mbbX2) ||
(idPy1 < aRegion[idReg].mbbY1) || (idPy1 > aRegion[idReg].mbbY2))
continue; // if not inside bbox then continue with next point
if (pointInsideRegion(idPx1, idPy1, idReg)) {
id->Append(idReg);
// no need to check further points, continue with next region
break;
}
}
}
return id;
}
Word ColRegion::In(const ListExpr typeInfo, const ListExpr instance,
const int errorPos, ListExpr& errorInfo, bool& correct) {
correct = false; // assume that there will be an error
Word error(static_cast<void*>(0)); // create an error result pointing at 0
errorInfo = listutils::typeError("Error in ColRegion::In!!!");
if (listutils::isSymbolUndefined(instance)) {
cmsg.inFunError("Symbol is undefined!");
return error;
}
if (nl->IsAtom(instance)) { // exit when the nested list is an atom
cmsg.inFunError("Nested list must not be an atom!");
return error;
}
// initialize the arrays which contain the latter input values
sRegion* inRegion = NULL;
sCycle* inCycle = NULL;
sPoint* inPoint = NULL;
long cr = 0; // index counter of the tuple array
long cc = 0; // index counter of the cycle array
long cp = 0; // index counter of the point array
// index to the array allocbytes of actual allocated memory for each array
long stepRegion = 0;
long stepCycle = 0;
long stepPoint = 0;
// allocate memory for the input arrays
inRegion = static_cast<sRegion*>(realloc(inRegion, allocBytes[stepRegion]));
inCycle = static_cast<sCycle*>(realloc(inCycle, allocBytes[stepCycle]));
inPoint = static_cast<sPoint*>(realloc(inPoint, allocBytes[stepPoint]));
int cycleSign = 1; // 1 marks an outer cycle and -1 marks an inner cycle
// loops to parse a nested list of regions
ListExpr regionNL = instance;
while (!nl->IsEmpty(regionNL)) { // as long as there are regions left
// allocate more memory if the actual allocated memory is insufficient
if (
((cr + 2) * sizeof(sRegion)) >= allocBytes[stepRegion]) {
// allocate more memory - in C++ a type casting is necessary
inRegion = static_cast<sRegion*>
(realloc(inRegion, allocBytes[++stepRegion]));
if (inRegion == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory!");
return error;
}
}
inRegion[cr].indexCycle = cc;
inRegion[cr].indexPoint = cp;
// values for the actual minimum bounding rectangle per region
inRegion[cr].mbbX1 = 999999;
inRegion[cr].mbbY1 = 999999;
inRegion[cr].mbbX2 = -999999;
inRegion[cr].mbbY2 = -999999;
ListExpr tupleNL = nl->First(regionNL);
if (nl->IsAtom(tupleNL)) {
cmsg.inFunError("expected a nested list for regions!");
return error;
}
while (!nl->IsEmpty(tupleNL)) { // faces
ListExpr faceNL = nl->First(tupleNL);
if (nl->IsAtom(faceNL)) {
cmsg.inFunError("expected a nested list for faces!");
return error;
}
cycleSign = 1; // the first cycle of a face is always an outer cycle
while (!nl->IsEmpty(faceNL)) { // cycles
// allocate more memory if actual allocated memory is insufficient
if (
((cc + 2) * sizeof(sCycle)) >= allocBytes[stepCycle]) {
// allocate more memory - in C++ a type casting is necessary
inCycle =static_cast<sCycle*>
(realloc(inCycle, allocBytes[++stepCycle]));
if (inCycle == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory!");
return error;
}
}
inCycle[cc++].index = cycleSign * cp;
cycleSign = -1; // any other cycles within a face are holes
ListExpr cycleNL = nl->First(faceNL);
if (nl->IsAtom(cycleNL)) {
cmsg.inFunError("expected a nested list for cycles!");
return error;
}
while (!nl->IsEmpty(cycleNL)) { // points
ListExpr pointNL = nl->First(cycleNL);
if ((!nl->HasLength(pointNL, 2)) ||
(!listutils::isNumeric(nl->First(pointNL))) ||
(!listutils::isNumeric(nl->Second(pointNL)))) {
cmsg.inFunError("expected an atom with two numeric values!");
return error; // exit on invalid point
}
// allocate more memory if actual allocated memory is insufficient
if ( ((cp + 2) * sizeof(sPoint)) >= allocBytes[stepPoint]) {
// allocate more memory - in C++ a type casting is necessary
inPoint = static_cast<sPoint*>
(realloc(inPoint, allocBytes[++stepPoint]));
if (inPoint == NULL) { // exit on memory overflow
cmsg.inFunError("not enough memory!");
return error;
}
}
double x = listutils::getNumValue((nl->First(pointNL)));
double y = listutils::getNumValue((nl->Second(pointNL)));
inPoint[cp].x = x;
inPoint[cp].y = y;
// calculate the mbb
if (x < inRegion[cr].mbbX1) inRegion[cr].mbbX1 = x;
if (x > inRegion[cr].mbbX2) inRegion[cr].mbbX2 = x;
if (y < inRegion[cr].mbbY1) inRegion[cr].mbbY1 = y;
if (y > inRegion[cr].mbbY2) inRegion[cr].mbbY2 = y;
cp++;
cycleNL = nl->Rest(cycleNL); // "move" to next point
}
faceNL = nl->Rest(faceNL); // "move" to next cycle
}
tupleNL = nl->Rest(tupleNL); // "move" to next face
}
cr++;
regionNL = nl->Rest(regionNL); // "move" to next region
}
// add terminator entries to arrays.
inPoint[cp].x = 0;
inPoint[cp].y = 0;
inCycle[cc].index = cp;
inRegion[cr].indexCycle = cc;
inRegion[cr].indexPoint = cp;
inRegion[cr].mbbX1 = 0;
inRegion[cr].mbbY1 = 0;
inRegion[cr].mbbX2 = 0;
inRegion[cr].mbbY2 = 0;
// truncate oversized memory to allocate only the real used memory
inRegion = static_cast<sRegion*>(realloc(inRegion, ++cr * sizeof(sRegion)));
inCycle = static_cast<sCycle*>(realloc(inCycle, ++cc * sizeof(sCycle)));
inPoint = static_cast<sPoint*>(realloc(inPoint, ++cp * sizeof(sPoint)));
cout << cr * sizeof(sRegion) + cc * sizeof(sCycle) + cp * sizeof(sPoint)
<< " bytes used\n";
Word answer(static_cast<void*>(0));
// create new region object with the just read in arrays
answer.addr = new ColRegion(inRegion, inCycle, inPoint, cr, cc, cp);
correct = true;
return answer;
}
ListExpr ColRegion::Out(ListExpr typeInfo, Word value) {
ColRegion* cRegion = static_cast<ColRegion*>(value.addr);
if (cRegion->countRegion == 0) { // no elements lead to error
return listutils::emptyErrorInfo();
}
// counters for index arrays aRegion and aCycle starting at second entries
long cr = 1;
long cc = 1;
// initialize heads and tails for working lists
ListExpr regionNL = nl->TheEmptyList();
ListExpr regionNLLast = regionNL;
ListExpr tupleNL = nl->TheEmptyList();
ListExpr tupleNLLast = tupleNL;
ListExpr faceNL = nl->TheEmptyList();
ListExpr faceNLLast = faceNL;
ListExpr cycleNL = nl->OneElemList(nl->TwoElemList(
nl->RealAtom(cRegion->aPoint[0].x),
nl->RealAtom(cRegion->aPoint[0].y)));
ListExpr cycleNLLast = cycleNL;
// main loop: traverse all points in the point array
for (long cp = 1; cp < cRegion->countPoint; cp++) {
// check whether a new cycle appears
if (cp == abs(cRegion->aCycle[cc].index)) {
// check whether the last cycle was an outer cycle
if (cRegion->aCycle[cc - 1].index >= 0) { // positive value = face
faceNL = nl->OneElemList(cycleNL);
faceNLLast = faceNL;
} else { // negative value = hole = append to last face
faceNLLast = nl->Append(faceNLLast, cycleNL);
}
// check whether the actual cycle is an outer cycle = new face
if (cRegion->aCycle[cc].index >= 0) {
if (nl->IsEmpty(tupleNL)) { // append last face to new tuple
tupleNL = nl->OneElemList(faceNL);
tupleNLLast = tupleNL;
} else { // append last face to last tuple
tupleNLLast = nl->Append(tupleNLLast, faceNL);
}
// check whether a new tuple appears
if (cp == cRegion->aRegion[cr].indexPoint) {
if (nl->IsEmpty(regionNL)) { // append last tuple to new region
regionNL = nl->OneElemList(tupleNL);
regionNLLast = regionNL;
} else { // append last tuple to last region
regionNLLast = nl->Append(regionNLLast, tupleNL);
}
// clear tuple and increase region counter
tupleNL = nl->TheEmptyList();
tupleNLLast = tupleNL;
cr++;
}
}
// initialize new cycle and increase cycle counter
// note: terminator will be created too, but not appended
cycleNL = nl->OneElemList(nl->TwoElemList(
nl->RealAtom(cRegion->aPoint[cp].x),
nl->RealAtom(cRegion->aPoint[cp].y)));
cycleNLLast = cycleNL;
cc++;
} else { // append new point to actual cycle
cycleNLLast = nl->Append(cycleNLLast, nl->TwoElemList(
nl->RealAtom(cRegion->aPoint[cp].x),
nl->RealAtom(cRegion->aPoint[cp].y)));
}
}
return regionNL;
}
Word ColRegion::Create(const ListExpr typeInfo) {
Word answer(static_cast<void*>(0));
sPoint *inPoint = NULL;
sCycle *inCycle = NULL;
sRegion *inRegion = NULL;
// create a new region object
answer.addr = new ColRegion(inRegion, inCycle, inPoint, 0, 0, 0);
return answer; // return its adress
}
void ColRegion::Delete(const ListExpr typeInfo, Word& w) {
ColRegion* cRegion = static_cast<ColRegion*>(w.addr);
delete cRegion;
w.addr = 0;
}
bool ColRegion::Open(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "open aregion...\n";
// initialize arrays
sRegion* region = NULL;
sCycle* cycle = NULL;
sPoint* point = NULL;
// declare counters
long cr;
long cc;
long cp;
// declare read in values
long valueL;
double valueD;
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
// 1. read the counters from disc
bool ok = (valueRecord.Read(&cr, sizeL, offset) == sizeL);
offset += sizeL;
ok = ok && (valueRecord.Read(&cc, sizeL, offset) == sizeL);
offset += sizeL;
ok = ok && (valueRecord.Read(&cp, sizeL, offset) == sizeL);
offset += sizeL;
// allocate memory depending on the counters
region = static_cast<sRegion*>(malloc(cr * sizeof(sRegion)));
if (region == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory!");
return false;
}
cycle = static_cast<sCycle*>(malloc(cc * sizeof(sCycle)));
if (cycle == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory!");
return false;
}
point = static_cast<sPoint*>(malloc(cp * sizeof(sPoint)));
if (point == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory!");
return false;
}
// 2. read the tuples and store them into the array tuple
for (long i = 0; i < cr; i++) {
ok = ok && (valueRecord.Read(&valueL, sizeL, offset) == sizeL);
region[i].indexCycle = valueL;
offset += sizeL;
ok = ok && (valueRecord.Read(&valueL, sizeL, offset) == sizeL);
region[i].indexPoint = valueL;
offset += sizeL;
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
region[i].mbbX1 = valueD;
offset += sizeD;
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
region[i].mbbY1 = valueD;
offset += sizeD;
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
region[i].mbbX2 = valueD;
offset += sizeD;
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
region[i].mbbY2 = valueD;
offset += sizeD;
if (!ok) { break; }
}
// 3. read the cycles and store them into the array tuple
for (long i = 0; i < cc; i++) {
ok = ok && (valueRecord.Read(&valueL, sizeL, offset) == sizeL);
offset += sizeL;
cycle[i].index = valueL;
if (!ok) { break; }
}
// 4. read the points and store them into the array tuple
for (long i = 0; i < cp; i++) {
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
point[i].x = valueD;
offset += sizeD;
ok = ok && (valueRecord.Read(&valueD, sizeD, offset) == sizeD);
point[i].y = valueD;
offset += sizeD;
if (!ok) { break; }
}
if (ok) { // create a new ColRegion and store the read values in it
value.addr = new ColRegion(region, cycle, point, cr, cc, cp);
} else { // error
value.addr = 0;
}
cout << cr * sizeof(sRegion) + cc * sizeof(sCycle) + cp * sizeof(sPoint)
<< " bytes used." << endl;
return ok; // ok can be true or false
}
bool ColRegion::Save(SmiRecord& valueRecord, size_t& offset,
const ListExpr typeInfo, Word& value) {
cout << "save aregion...\n";
ColRegion* cRegion = static_cast<ColRegion*>(value.addr);
size_t sizeL = sizeof(long);
size_t sizeD = sizeof(double);
// order of counters and arrays are tuples - cycles - points
long cr = cRegion->countRegion;
long cc = cRegion->countCycle;
long cp = cRegion->countPoint;
long valueL;
double valueD;
// 1. write the counters to disc
bool ok = valueRecord.Write(&cr, sizeL, offset);
offset += sizeL;
ok = ok && valueRecord.Write(&cc, sizeL, offset);
offset += sizeL;
ok = ok && valueRecord.Write(&cp, sizeL, offset);
offset += sizeL;
// 2. write the tuple array to disc
for (long i = 0; i < cr; i++) {
valueL = cRegion->aRegion[i].indexCycle;
ok = ok && valueRecord.Write(&valueL, sizeL, offset);
offset += sizeL;
valueL = cRegion->aRegion[i].indexPoint;
ok = ok && valueRecord.Write(&valueL, sizeL, offset);
offset += sizeL;
valueD = cRegion->aRegion[i].mbbX1;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
valueD = cRegion->aRegion[i].mbbY1;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
valueD = cRegion->aRegion[i].mbbX2;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
valueD = cRegion->aRegion[i].mbbY2;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
}
// 3. write the cycle array to disc
for (long i = 0; i < cc; i++) {
valueL = cRegion->aCycle[i].index;
ok = ok && valueRecord.Write(&valueL, sizeL, offset);
offset += sizeL;
}
// 4. write the point array to disc
for (long i = 0; i < cp; i++) {
valueD = cRegion->aPoint[i].x;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
valueD = cRegion->aPoint[i].y;
ok = ok && valueRecord.Write(&valueD, sizeD, offset);
offset += sizeD;
}
return ok;
}
void ColRegion::Close(const ListExpr typeInfo, Word& w) {
delete static_cast<ColRegion*>(w.addr);
w.addr = 0;
}
/*
~Clone~ makes a copy of a ~ColRegion~ object. Therefore it allocates memory
for the clone object and scans the arrays of the source object and copies
each entry to the clone object.
*/
Word ColRegion::Clone(const ListExpr typeInfo, const Word& w) {
ColRegion* cRegion = static_cast<ColRegion*>(w.addr);
long tmpCR = cRegion->getCount() + 1; // add 1 for terminator
sRegion* tmpRegion = NULL;
tmpRegion = static_cast<sRegion*>
(realloc(tmpRegion, sizeof(sRegion) * tmpCR));
if (tmpRegion == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
long tmpCC = cRegion->getCountCycles() + 1; // add terminator entry
sCycle* tmpCycle = NULL;
tmpCycle = static_cast<sCycle*>
(realloc(tmpCycle, sizeof(sCycle) * tmpCC));
if (tmpCycle == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
long tmpCP = cRegion->getCountPoints() + 1; // add terminator entry
sPoint* tmpPoint = NULL;
tmpPoint = static_cast<sPoint*>
(realloc(tmpPoint, sizeof(sPoint) * tmpCP));
if (tmpPoint == NULL) { // exit on memory overflow
cmsg.inFunError("out of memory");
return (void*)0;
}
cout << "sizeof(tmpRegion) = " << sizeof(sRegion) * tmpCR << endl
<< "sizeof(tmpCycle) = " << sizeof(sCycle) * tmpCC << endl
<< "sizeof(tmppoint) = " << sizeof(sPoint) * tmpCP << endl;
for (long cr = 0; cr < tmpCR; cr++)
tmpRegion[cr] = cRegion->aRegion[cr];
for (long cc = 0; cc < tmpCC; cc++)
tmpCycle[cc] = cRegion->aCycle[cc];
for (long cp = 0; cp < tmpCP; cp++)
tmpPoint[cp] = cRegion->aPoint[cp];
Word res((void*)0);
res.addr = new ColRegion(tmpRegion, tmpCycle, tmpPoint,
tmpCR, tmpCC, tmpCP);
return res;
}
void* ColRegion::Cast(void* addr) {
return (new (addr) ColRegion);
}
bool ColRegion::TypeCheck(ListExpr type, ListExpr& errorInfo) {
return nl->IsEqual(type, BasicType());
}
int ColRegion::SizeOf() {
return sizeof(sRegion) + sizeof(sCycle) + sizeof(sPoint);
}
//------------------------------ Operators ------------------------------------
/*
5 Operators
5.1 Type Mapping
*/
ListExpr insideTM(ListExpr args) {
string err = "{apoint | aline} x aregion expected\n";
if (!nl->HasLength(args, 2)) {
return listutils::typeError(err + " (wrong number of arguments)");
}
if ((ColPoint::checkType(nl->First(args)) ||
ColLine::checkType(nl->First(args))) &&
ColRegion::checkType(nl->Second(args))) {
return LongIntsTI(false).GetTypeExpr(); // list of long ints for id
}
return listutils::typeError(err);
}
ListExpr containsTM(ListExpr args) {
string err = "aregion x apoint expected\n";
if (!nl->HasLength(args, 2)) {
return listutils::typeError(err + " (wrong number of arguments)");
}
if (ColRegion::checkType(nl->First(args)) &&
ColPoint::checkType(nl->Second(args))) {
return LongIntsTI(false).GetTypeExpr(); // list of long ints for id
}
return listutils::typeError(err);
}
ListExpr mapTM(ListExpr args) {
if (!nl->HasLength(args,2)) {
return listutils::typeError("Wrong number of arguments!");
}
// check for tuple stream
if (!Stream<Tuple>::checkType(nl->First(args))) {
return listutils::typeError("tuple stream "
"with spatial attribute expected!");
}
if (nl->AtomType(nl->Second(args))!=SymbolType) {
return listutils::typeError("Second arg is no valid attribute name!");
}
// extract the attribute list
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
ListExpr type;
string name = nl->SymbolValue(nl->Second(args));
int j = listutils::findAttribute(attrList, name, type);
if (j==0) {
return listutils::typeError("Attribute " + name + " not found!");
};
// check type and return corresponding attribute number
// and result type using append mechanism
ListExpr returnType = nl->Empty();
if (Point::checkType(type))
returnType = listutils::basicSymbol<ColPoint>();
if (Line::checkType(type))
returnType = listutils::basicSymbol<ColLine>();
if (Region::checkType(type))
returnType = listutils::basicSymbol<ColRegion>();
if (!nl->IsEmpty(returnType)) {
cout << "'" << name << "' is attribute " << j << ", type is '"
<< nl->ToString(type) << "'." << endl;
return nl->ThreeElemList(nl->SymbolAtom(Symbols::APPEND()),
nl->OneElemList(nl->IntAtom(j)), returnType);
};
return listutils::typeError("Attribute type point, line or region"
"not found!");
}
ListExpr mapColTM(ListExpr args) {
if (!nl->HasLength(args,2)) {
return listutils::typeError("Wrong number of arguments!");
}
// check for column spatial object and return corresponding spatial type
if (ColPoint::checkType(nl->First(args)))
return listutils::basicSymbol<Point>();
if (ColLine::checkType(nl->First(args)))
return listutils::basicSymbol<Line>();
if (ColRegion::checkType(nl->First(args)))
return listutils::basicSymbol<Region>();
return listutils::typeError("First arg is no valid column spatial type!");
}
ListExpr countTM(ListExpr args) {
if (!nl->HasLength(args,1)) {
return listutils::typeError("Wrong number of arguments!");
}
// check for column spatial object and return corresponding spatial type
if (ColPoint::checkType(nl->First(args)) ||
ColLine::checkType(nl->First(args)) ||
ColRegion::checkType(nl->First(args)))
return listutils::basicSymbol<CcInt>();
return listutils::typeError("First arg is no valid column spatial type!");
}
ListExpr plusTM(ListExpr args) {
if (!nl->HasLength(args,2)) {
return listutils::typeError("Expected two column spatial types!");
}
// check for column spatial object and return corresponding spatial type
if (ColPoint::checkType(nl->First(args)) &&
ColPoint::checkType(nl->Second(args)))
return listutils::basicSymbol<ColPoint>();
if (ColLine::checkType(nl->First(args)) &&
ColLine::checkType(nl->Second(args)))
return listutils::basicSymbol<ColLine>();
if (ColRegion::checkType(nl->First(args)) &&
ColRegion::checkType(nl->Second(args)))
return listutils::basicSymbol<ColRegion>();
return listutils::typeError("Expected two column spatial types!");
}
ListExpr showarrayTM(ListExpr args) {
if (!nl->HasLength(args,1)) {
return listutils::typeError("Expected a column spatial type!");
}
// check for column spatial object and return corresponding spatial type
if ((ColPoint::checkType(nl->First(args))) ||
(ColLine::checkType(nl->First(args))) ||
(ColRegion::checkType(nl->First(args))))
return listutils::basicSymbol<CcBool>();
return listutils::typeError("Expected a column spatial type!");
}
/*
5.2 Value Mapping
checks for each point of the apoint array if it's inside each region
of a aregion array. If only one point or one region should be checked,
then the arrays have to contain only one element.
*/
int pointsInsideVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
ColPoint* cPoint = (ColPoint*) args[0].addr;
ColRegion* cRegion = (ColRegion*) args[1].addr;
cout << "check " << cPoint->getCount() << " points "
<< "and " << cRegion->getCount() << " regions..." << endl;
result = qp->ResultStorage(s);
LongInts* id = cRegion->pointsInside(cPoint);
result.addr = id;
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
fstream f;
f.open("benchmark.dat", std::fstream::out|std::fstream::app);
f << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
f.close();
return 0;
}
int linesInsideVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
ColLine* cLine = (ColLine*) args[0].addr;
ColRegion* cRegion = (ColRegion*) args[1].addr;
cout << "check " << cLine->getCount() << " lines "
<< "and " << cRegion->getCount() << " regions..." << endl;
result = qp->ResultStorage(s);
LongInts* id = cRegion->linesInside(cLine);
result.addr = id;
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int regionsInsideVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s);
// initialized and empty result array
LongInts* id = new LongInts();
result.addr = id;
return 0;
}
/*
~contain~ functions
*/
int containsPointsVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
ColRegion* cRegion = (ColRegion*) args[0].addr;
ColPoint* cPoint = (ColPoint*) args[1].addr;
cout << "check " << cPoint->getCount() << " points "
<< "and " << cRegion->getCount() << " regions..." << endl;
result = qp->ResultStorage(s);
LongInts* id = cRegion->containsPoints(cPoint);
result.addr = id;
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
/*
The operator ~mapPointVM~ expects a stream of tuples as input parameter.
It extracts the ~point~ attribute of each tuple and hands it over to the
append function of the column spatial ~apoint~ object.
*/
int mapPointVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s); // use result storage for the result
result.addr = new ColPoint(true);
ColPoint* cPoint = static_cast<ColPoint*> (result.addr);
int index = ((CcInt*) args[2].addr)->GetValue(); // append attributes index
Stream<Tuple> stream(args[0]); // get the tuples
stream.open(); // open the stream
Tuple* tuple; // actual tuple element
Point* point; // actual line attribute
while( (tuple = stream.request()) ) { // if exists, get next tuple
// extract point from tuple
point = (Point*) tuple->GetAttribute(index - 1);
if (!cPoint->append(point)) { // append line to aline
cout << "Error in mapping stream(point) to apoint!" << endl;
return 0;
}
tuple->DeleteIfAllowed(); // remove tuple from stream
}
stream.close(); // close the stream
cPoint->finalize();
return 0;
}
/*
The operator ~mapLineVM~ expects a stream of tuples as input parameter.
It extracts the ~line~ attribute of each tuple and hands it over to the
append function of the column spatial ~aline~ object.
*/
int mapLineVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s); // use result storage for the result
result.addr = new ColLine(true);
ColLine* cLine = static_cast<ColLine*> (result.addr);
int index = ((CcInt*) args[2].addr)->GetValue(); // append attributes index
Stream<Tuple> stream(args[0]); // get the tuples
stream.open(); // open the stream
Tuple* tuple; // actual tuple element
Line* line; // actual line attribute
while( (tuple = stream.request()) ) { // if exists, get next tuple
// extract line from tuple
line = (Line*) tuple->GetAttribute(index - 1);
if (!cLine->append(line)) { // append line to aline
cout << "Error in mapping stream(line) to aline!" << endl;
return 0;
}
tuple->DeleteIfAllowed(); // remove tuple from stream
}
stream.close(); // close the stream
cLine->finalize();
return 0;
}
/*
The operator ~mapRegionVM~ expects a stream of tuples as input parameter.
It extracts the ~region~ attribute of each tuple and hands it over to the
append function of the column spatial ~aregion~ object.
*/
int mapRegionVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s); // use result storage for the result
result.addr = new ColRegion(true); // result set to cRegion object
ColRegion* cRegion = static_cast<ColRegion*> (result.addr);
int index = ((CcInt*) args[2].addr)->GetValue(); // append attributes index
Stream<Tuple> stream(args[0]); // get the tuples
stream.open(); // open the stream
Tuple* tuple; // actual tuple element
Region* region; // actual region attribute
while( (tuple = stream.request()) ) { // if exists, get next tuple
// extract region from tuple
region = (Region*) tuple->GetAttribute(index - 1);
if (!cRegion->append(region)) { // append region to aregion
cout << "Error in mapping stream(region) to aregion!" << endl;
return 0;
}
tuple->DeleteIfAllowed(); // remove tuple from stream
}
stream.close(); // close the stream
cRegion->finalize();
return 0;
}
/*
The operator ~mapColPointVM~ expects an apoint type of the column spatial
algebra and an index. Then it extracts the entry specified by index and
converts it into the spatial object ~point~.
*/
int mapColPointVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s);
ColPoint* cPoint = static_cast<ColPoint*> (args[0].addr);
int index = ((CcInt*) args[1].addr)->GetValue(); // append attributes index
if ((index < 0) || (index >= cPoint->getCount())) {
cout << "Error: apoint index " << index << " out of bounds!" << endl;
return 0;
}
cout << "map apoint[" << index << "] to point.\n";
result.addr = new Point(true, cPoint->getX(index), cPoint->getY(index));
return 0;
}
/*
maps the column spatial type ~aline~ to a single spatial type ~line~.
needs an index.
*/
int mapColLineVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s);
ColLine* cLine = static_cast<ColLine*> (args[0].addr);
int index = ((CcInt*) args[1].addr)->GetValue(); // append attributes index
if ((index < 0) || (index >= cLine->getCount())) {
cout << "Error: aline index " << index << " out of bounds!" << endl;
return false;
}
cout << "map aline[" << index << "] to line\n";
long segCount = cLine->getSegments(index);
result.addr = new Line(segCount);
Line* line = static_cast<Line*> (result.addr);
if (!cLine->createLine(line, index)) {
cout << "Error mapping aline to line!" << endl;
return 0;
}
return 0;
}
/*
maps column spatial type ~aregion~ to a single spatial type ~region~.
needs an index.
*/
int mapColRegionVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
result = qp->ResultStorage(s);
ColRegion* cRegion = static_cast<ColRegion*> (args[0].addr);
int index = ((CcInt*) args[1].addr)->GetValue(); // append attributes index
if ((index < 0) || (index >= cRegion->getCount())) {
cout << "Error: aregion index " << index << " out of bounds!" << endl;
return 0;
}
cout << "map aregion[" << index << "] to region.\n";
Region* region = 0;
if (!cRegion->createRegion(region, index)) {
cout << "Error mapping aregion to region!" << endl;
return 0;
}
Region* res = static_cast<Region*> (result.addr);
*res = *region;
region->DeleteIfAllowed();
res->Print(cout);
return 0;
}
/*
value mapping of the count functions
*/
int countPointVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
ColPoint* cPoint = static_cast<ColPoint*> (args[0].addr);
CcInt* res = (CcInt*) result.addr;
res->Set(true, cPoint->getCount());
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int countLineVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
ColLine* cLine = static_cast<ColLine*> (args[0].addr);
CcInt* res = (CcInt*) result.addr;
res->Set(true, cLine->getCount());
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int countRegionVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
ColRegion* cRegion = static_cast<ColRegion*> (args[0].addr);
CcInt* res = (CcInt*) result.addr;
res->Set(true, cRegion->getCount());
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
/*
value mappings of the plus functions
*/
int plusPointVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
result.addr = new ColPoint(true);
ColPoint* cPoint = static_cast<ColPoint*> (result.addr);
ColPoint* cPoint1 = (ColPoint*) (args[0].addr);
ColPoint* cPoint2 = (ColPoint*) (args[1].addr);
if (!cPoint->merge(cPoint1, cPoint2)) {
cout << "Error in merging two apoint types!" << endl;
return 0;
}
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int plusLineVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
result.addr = new ColLine(true);
ColLine* cLine = static_cast<ColLine*> (result.addr);
ColLine* cLine1 = (ColLine*) (args[0].addr);
ColLine* cLine2 = (ColLine*) (args[1].addr);
if (!cLine->merge(cLine1, cLine2)) {
cout << "Error in merging two aline types!" << endl;
return 0;
}
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int plusRegionVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
long cStart, cStop, tStart, tStop;
benchmark(cStart, tStart);
result = qp->ResultStorage(s);
result.addr = new ColRegion(true); // result set to cRegion object
ColRegion* cRegion = static_cast<ColRegion*> (result.addr);
ColRegion* cRegion1 = (ColRegion*) (args[0].addr);
ColRegion* cRegion2 = (ColRegion*) (args[1].addr);
if (!cRegion->merge(cRegion1, cRegion2)) {
cout << "Error in merging two aregion types!" << endl;
return 0;
}
benchmark(cStop, tStop);
cout << "Benchmark: " << (long)(cStop - cStart) << " cycles / "
<< (long) tStop - tStart << " nanoseconds." << endl;
return 0;
}
int showarrayPointVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
ColPoint* cPoint = (ColPoint*) (args[0].addr);
cPoint->showArray("show internal array of apoint");
result.addr = new CcBool();
result = qp->ResultStorage(s);
CcBool* res = (CcBool*) result.addr;
res->Set(true, true);
return 0;
}
int showarrayLineVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
ColLine* cLine = (ColLine*) (args[0].addr);
cLine->showArrays("show internal arrays of aregion");
result.addr = new CcBool();
result = qp->ResultStorage(s);
CcBool* res = (CcBool*) result.addr;
res->Set(true, true);
return 0;
}
int showarrayRegionVM (Word* args, Word& result, int message,
Word& local, Supplier s) {
ColRegion* cRegion = (ColRegion*) (args[0].addr);
cRegion->showArrays("show internal arrays of aregion", true);
result.addr = new CcBool();
result = qp->ResultStorage(s);
CcBool* res = (CcBool*) result.addr;
res->Set(true, true);
return 0;
}
/*
5.3 Selection Function
*/
int insideSelect(ListExpr args) {
if (ColPoint::checkType(nl->First(args))) { return 0; }
if (ColLine::checkType(nl->First(args))) { return 1; }
return 2; // ColRegion
}
int containsSelect(ListExpr args) {
if (ColPoint::checkType(nl->Second(args))) { return 0; }
if (ColLine::checkType(nl->Second(args))) { return 1; }
return 2; // ColRegion
}
/*
If the first argument is a tuple type then there are three different mappings
of spatial types within a relation to column spatial types.
*/
int mapSelect(ListExpr args) {
ListExpr arg1 = nl->First( args );
// check for tuple stream
if (Stream<Tuple>::checkType(arg1)) {
// extract the attribute list
ListExpr attrList = nl->Second(nl->Second(nl->First(args)));
string name = nl->SymbolValue(nl->Second(args));
ListExpr type;
listutils::findAttribute(attrList, name, type);
if (Point::checkType(type)) return 0; // mapPointVM
if (Line::checkType(type)) return 1; // mapLineVM
if (Region::checkType(type)) return 2; // mapRegionVM
}
return 0; // if no match then return dummy, type mapping will be done later
}
/*
If the first argument is a colum spatial type then there are three mappings
of column spatial types to spatial types.
*/
int mapColSelect(ListExpr args) {
// first check for column spatial objects
ListExpr arg1 = nl->First( args );
if (ColPoint::checkType(arg1)) return 0; // mapColPointVM
if (ColLine::checkType(arg1)) return 1; // mapColLineVM
if (ColRegion::checkType(arg1)) return 2; // mapColRegionVM
return 0; // if no match then return dummy, type mapping will be done later
}
int countSelect(ListExpr args) {
// first check for column spatial objects
ListExpr arg1 = nl->First( args );
if (ColPoint::checkType(arg1)) return 0; // countPointVM
if (ColLine::checkType(arg1)) return 1; // countLineVM
if (ColRegion::checkType(arg1)) return 2; // countRegionVM
return 0; // if no match then return dummy, type mapping will be done later
}
int plusSelect(ListExpr args) {
// first check for column spatial objects
ListExpr arg1 = nl->First( args );
if (ColPoint::checkType(arg1)) return 0; // plusColPointVM
if (ColLine::checkType(arg1)) return 1; // plusColLineVM
if (ColRegion::checkType(arg1)) return 2; // plusColRegionVM
return 0; // if no match then return dummy, type mapping will be done later
}
int showarraySelect(ListExpr args) {
// first check for column spatial objects
ListExpr arg1 = nl->First( args );
if (ColPoint::checkType(arg1)) return 0; // showarrayColPointVM
if (ColLine::checkType(arg1)) return 1; // showarrayColLineVM
if (ColRegion::checkType(arg1)) return 2; // showarrayColRegionVM
return 0; // if no match then return dummy, type mapping will be done later
}
} // namespace col