19189 lines
499 KiB
C++
19189 lines
499 KiB
C++
/*
|
|
----
|
|
This file is part of SECONDO.
|
|
|
|
Copyright (C) 2004, University in Hagen, Department of 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}}]
|
|
//[TOC] [\tableofcontents]
|
|
//[_] [\_]
|
|
|
|
[1] Implementation of the Spatial Algebra
|
|
|
|
February, 2003. Victor Teixeira de Almeida
|
|
|
|
March-July, 2003. Zhiming Ding
|
|
|
|
January, 2005 Leonardo Guerreiro Azevedo
|
|
|
|
[TOC]
|
|
|
|
1 Overview
|
|
|
|
This implementation file essentially contains the implementation of the classes
|
|
~Point~, ~Points~, ~Line~, and ~Region~ used in the Spatial Algebra. These
|
|
classes respectively correspond to the memory representation for the type
|
|
constructors ~point~, ~points~, ~line~, and ~region~.
|
|
|
|
For more detailed information see SpatialAlgebra.h.
|
|
|
|
2 Defines and Includes
|
|
|
|
*/
|
|
|
|
#undef __TRACE__
|
|
//#define __TRACE__ cout << __FILE__ << "::" << __LINE__;
|
|
#define __TRACE__
|
|
|
|
|
|
|
|
#include "Label.h"
|
|
#include "../../Tools/Flob/Flob.h"
|
|
#include "../../Tools/Flob/DbArray.h"
|
|
#include "Algebra.h"
|
|
#include "NestedList.h"
|
|
#include "ListUtils.h"
|
|
#include "GenericTC.h"
|
|
#include "QueryProcessor.h"
|
|
#include "StandardTypes.h"
|
|
#include "SpatialAlgebra.h"
|
|
#include "Algebras/FText/FTextAlgebra.h"
|
|
#include "SecondoConfig.h"
|
|
#include "AvlTree.h"
|
|
#include "AVLSegment.h"
|
|
#include "AlmostEqual.h"
|
|
#include "Algebras/Relation-C++/RelationAlgebra.h"
|
|
#include "RegionTools.h"
|
|
#include "Symbols.h"
|
|
#include "Stream.h"
|
|
#include "RegionCreator.h"
|
|
#include "StringUtils.h"
|
|
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <stdexcept>
|
|
#include <iostream>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <cmath>
|
|
#include <algorithm>
|
|
#include <ctime>
|
|
#include <queue>
|
|
#include <iterator>
|
|
#include <sstream>
|
|
#include <limits>
|
|
#include <errno.h>
|
|
#include <cerrno>
|
|
#include "../TopRel/Tree.h"
|
|
|
|
#include "RobustSetOps.h"
|
|
|
|
#include "DLine.h"
|
|
#include "Segment.h"
|
|
|
|
#include "DRM.h"
|
|
#include "Disc.h"
|
|
#include "Stack.h"
|
|
|
|
#include "Algebras/Geoid/GeoDist.h"
|
|
|
|
|
|
using namespace std;
|
|
|
|
extern NestedList* nl;
|
|
extern QueryProcessor* qp;
|
|
|
|
|
|
typedef RegionCreator<DbArray> PRegionCreator;
|
|
typedef robust::RealmChecker<DbArray> PRealmChecker;
|
|
|
|
|
|
/*
|
|
3 Type investigation auxiliaries
|
|
|
|
Within this algebra module, we have to handle with values of four different
|
|
types: ~point~ and ~points~, ~line~ and ~region~.
|
|
|
|
Later on we will
|
|
examine nested list type descriptions. In particular, we
|
|
are going to check whether they describe one of the four types just introduced.
|
|
In order to simplify dealing with list expressions describing these types, we
|
|
declare an enumeration, ~SpatialType~, containing the four types, and a
|
|
function, ~SpatialTypeOfSymbol~, taking a nested list as argument and returning
|
|
the corresponding ~SpatialType~ type name.
|
|
|
|
*/
|
|
enum SpatialType { stpoint, stpoints, stline, stregion,
|
|
stbox, sterror, stsline, stcpoint };
|
|
|
|
SpatialType
|
|
SpatialTypeOfSymbol( ListExpr symbol )
|
|
{
|
|
if ( nl->AtomType( symbol ) == SymbolType )
|
|
{
|
|
string s = nl->SymbolValue( symbol );
|
|
if ( s == Point::BasicType() ) return (stpoint);
|
|
if ( s == Points::BasicType() ) return (stpoints);
|
|
if ( s == Line::BasicType() ) return (stline);
|
|
if ( s == Region::BasicType() ) return (stregion);
|
|
if ( s == Rectangle<2>::BasicType() ) return (stbox);
|
|
if ( s == SimpleLine::BasicType() ) return (stsline);
|
|
if ( s == CPoint::BasicType() ) return (stcpoint);
|
|
}
|
|
return (sterror);
|
|
}
|
|
|
|
/*
|
|
9 Object Traversal functions
|
|
|
|
These functions are utilities useful for traversing objects. They are basic
|
|
functions to be called by the operations defined below.
|
|
|
|
There are 6 combinations, pp, pl, pr, ll, lr, rr
|
|
|
|
*/
|
|
|
|
|
|
void SelectFirst_pl( const Points& P, const Line& L,
|
|
object& obj, status& stat )
|
|
{
|
|
P.SelectFirst();
|
|
L.SelectFirst();
|
|
|
|
Point p1(true);
|
|
Point p2(true);
|
|
HalfSegment hs;
|
|
|
|
bool gotPt = P.GetPt( p1 ),
|
|
gotHs = L.GetHs( hs );
|
|
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( p1 < p2 )
|
|
obj = first;
|
|
else if( p1 > p2 )
|
|
obj = second;
|
|
else
|
|
obj=both;
|
|
}
|
|
}
|
|
|
|
void SelectNext_pl( const Points& P, const Line& L,
|
|
object& obj, status& stat )
|
|
{
|
|
// 1. get the current elements
|
|
Point p1(true);
|
|
Point p2(true);
|
|
HalfSegment hs;
|
|
|
|
bool gotPt = P.GetPt( p1 ),
|
|
gotHs = L.GetHs( hs );
|
|
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
|
|
//2. move the pointers
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
//do nothing
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
L.SelectNext();
|
|
gotHs = L.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
}
|
|
else //both currently defined
|
|
{
|
|
if( p1 < p2 ) //then hs1 is the last output
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
}
|
|
else if( p1 > p2 )
|
|
{
|
|
L.SelectNext();
|
|
gotHs = L.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
else
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
L.SelectNext();
|
|
gotHs = L.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
}
|
|
|
|
//3. generate the outputs
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat=endnone;
|
|
if( p1 < p2 )
|
|
obj = first;
|
|
else if( p1 > p2 )
|
|
obj = second;
|
|
else
|
|
obj = both;
|
|
}
|
|
}
|
|
|
|
void SelectFirst_pr( const Points& P, const Region& R,
|
|
object& obj, status& stat )
|
|
{
|
|
P.SelectFirst();
|
|
R.SelectFirst();
|
|
|
|
Point p1(true);
|
|
Point p2(true);
|
|
HalfSegment hs;
|
|
|
|
bool gotPt = P.GetPt( p1 ),
|
|
gotHs = R.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( p1 < p2 )
|
|
obj = first;
|
|
else if( p1 > p2 )
|
|
obj = second;
|
|
else
|
|
obj=both;
|
|
}
|
|
}
|
|
|
|
void SelectNext_pr( const Points& P, const Region& R,
|
|
object& obj, status& stat )
|
|
{
|
|
// 1. get the current elements
|
|
Point p1(true);
|
|
Point p2(true);
|
|
HalfSegment hs;
|
|
|
|
bool gotPt = P.GetPt( p1 ),
|
|
gotHs = R.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
|
|
//2. move the pointers
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
//do nothing
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
R.SelectNext();
|
|
gotHs = R.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
}
|
|
else //both currently defined
|
|
{
|
|
if( p1 < p2 ) //then hs1 is the last output
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
}
|
|
else if( p1 > p2 )
|
|
{
|
|
R.SelectNext();
|
|
gotHs = R.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
else
|
|
{
|
|
P.SelectNext();
|
|
gotPt = P.GetPt( p1 );
|
|
R.SelectNext();
|
|
gotHs = R.GetHs( hs );
|
|
if( gotHs )
|
|
p2 = hs.GetDomPoint();
|
|
}
|
|
}
|
|
|
|
//3. generate the outputs
|
|
if( !gotPt && !gotHs )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotPt )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( p1 < p2 )
|
|
obj = first;
|
|
else if( p1 > p2 )
|
|
obj = second;
|
|
else
|
|
obj = both;
|
|
}
|
|
}
|
|
|
|
|
|
void SelectFirst_lr( const Line& L, const Region& R,
|
|
object& obj, status& stat )
|
|
{
|
|
L.SelectFirst();
|
|
R.SelectFirst();
|
|
|
|
HalfSegment hs1, hs2;
|
|
bool gotHs1 = L.GetHs( hs1 ),
|
|
gotHs2 = R.GetHs( hs2 );
|
|
|
|
if( !gotHs1 && !gotHs2 )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( hs1 < hs2 )
|
|
obj = first;
|
|
else if( hs1 > hs2 )
|
|
obj = second;
|
|
else
|
|
obj = both;
|
|
}
|
|
}
|
|
|
|
void SelectNext_lr( const Line& L, const Region& R,
|
|
object& obj, status& stat )
|
|
{
|
|
// 1. get the current elements
|
|
HalfSegment hs1, hs2;
|
|
bool gotHs1 = L.GetHs( hs1 ),
|
|
gotHs2 = R.GetHs( hs2 );
|
|
|
|
//2. move the pointers
|
|
if( !gotHs1 && !gotHs2 )
|
|
{
|
|
//do nothing
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
R.SelectNext();
|
|
gotHs2 = R.GetHs( hs2 );
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
L.SelectNext();
|
|
gotHs1 = L.GetHs( hs1 );
|
|
}
|
|
else //both currently defined
|
|
{
|
|
if( hs1 < hs2 ) //then hs1 is the last output
|
|
{
|
|
L.SelectNext();
|
|
gotHs1 = L.GetHs( hs1 );
|
|
}
|
|
else if( hs1 > hs2 )
|
|
{
|
|
R.SelectNext();
|
|
gotHs2 = R.GetHs( hs2 );
|
|
}
|
|
else
|
|
{
|
|
L.SelectNext();
|
|
gotHs1 = L.GetHs( hs1 );
|
|
R.SelectNext();
|
|
gotHs2 = R.GetHs( hs2 );
|
|
}
|
|
}
|
|
|
|
//3. generate the outputs
|
|
if( !gotHs1 && gotHs2 )
|
|
{
|
|
obj = none;
|
|
stat = endboth;
|
|
}
|
|
else if( !gotHs1 )
|
|
{
|
|
obj = second;
|
|
stat = endfirst;
|
|
}
|
|
else if( !gotHs2 )
|
|
{
|
|
obj = first;
|
|
stat = endsecond;
|
|
}
|
|
else //both defined
|
|
{
|
|
stat = endnone;
|
|
if( hs1 < hs2 )
|
|
obj = first;
|
|
else if( hs1 > hs2 )
|
|
obj = second;
|
|
else
|
|
obj=both;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
3 Auxiliary classes
|
|
|
|
*/
|
|
|
|
// Generic print-function object for printing with STL::for_each
|
|
template<class T> struct print : public unary_function<T, void>
|
|
{
|
|
print(ostream& out) : os(out) {}
|
|
void operator() (T x) { os << "\t" << x << "\n"; }
|
|
ostream& os;
|
|
};
|
|
|
|
|
|
/*
|
|
4 Type Constructor ~point~
|
|
|
|
A value of type ~point~ represents a point in the Euclidean plane or is
|
|
undefined.
|
|
|
|
4.1 Implementation of the class ~Point~
|
|
|
|
*/
|
|
ostream& operator<<( ostream& o, const Point& p )
|
|
{
|
|
ios_base::fmtflags oldOptions = o.flags();
|
|
o.setf(ios_base::fixed,ios_base::floatfield);
|
|
o.precision(8);
|
|
if( p.IsDefined() )
|
|
o << "(" << p.GetX() << ", " << p.GetY() << ")";
|
|
else
|
|
o << Symbol::UNDEFINED();
|
|
o.flags(oldOptions);
|
|
return o;
|
|
}
|
|
|
|
const Rectangle<2> Point::BoundingBox(const Geoid* geoid /*=0*/) const
|
|
{
|
|
assert( IsDefined() );
|
|
if( IsDefined() ) {
|
|
if( geoid && geoid->IsDefined() ){ // spherical case
|
|
double minx = MAX(-180, x - ApplyFactor(x));
|
|
double maxx = MIN(180, x+ ApplyFactor(x));
|
|
double miny = MAX(-90, y-ApplyFactor(y));
|
|
double maxy = MIN(90,y+ApplyFactor(y));
|
|
double minMax[] = {minx,maxx,miny,maxy};
|
|
return Rectangle<2>( true, minMax);
|
|
} else if(!geoid){
|
|
double minMax[] = { x - ApplyFactor(x),
|
|
x + ApplyFactor(x),
|
|
y - ApplyFactor(y),
|
|
y + ApplyFactor(y) };
|
|
return Rectangle<2>( true, minMax);
|
|
}
|
|
} // else:
|
|
return Rectangle<2>( false );
|
|
}
|
|
ostream& Point::Print( ostream &os ) const
|
|
{
|
|
return os << *this;
|
|
}
|
|
|
|
string Point::toString(const Geoid* geoid /*=0*/) const {
|
|
if(!IsDefined()){
|
|
return Symbol::UNDEFINED();
|
|
}
|
|
stringstream s;
|
|
s.setf(ios_base::fixed,ios_base::floatfield);
|
|
s.precision(8);
|
|
if(geoid){ // print as geographic coords
|
|
double lat = GetY();
|
|
double lon = GetX();
|
|
string ns = (lat>=0)?"N":"S";
|
|
string ew = (lon>=0)?"E":"W";
|
|
|
|
lat = fabs(lat);
|
|
int degLat = (int)lat;
|
|
double resLat = (lat - degLat)*60.0;
|
|
int minLat = ((int) resLat);
|
|
double secLat = (resLat - minLat)*60.0;
|
|
|
|
lon = fabs(lon);
|
|
int degLon = (int)lon;
|
|
double resLon = (lon - degLon)*60.0;
|
|
int minLon = ((int) resLon);
|
|
double secLon = (resLon - minLon)*60.0;
|
|
s << "(" << degLat << "°" << minLat << "'" << secLat << "\"" << ns << ", "
|
|
<< degLon << "°" << minLon << "'" << secLon << "\"" << ew << ")";
|
|
} else { // print as euclidean coords
|
|
s << *this;
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
|
|
bool Point::Inside( const Rectangle<2>& r, const Geoid* geoid /*=0*/ ) const
|
|
{
|
|
assert( r.IsDefined() );
|
|
if( !IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
return false;
|
|
}
|
|
if( x < r.MinD(0) || x > r.MaxD(0) )
|
|
return false;
|
|
else if( y < r.MinD(1) || y > r.MaxD(1) )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void Point::ReadFromString(string value) {
|
|
|
|
ListExpr list;
|
|
if(!nl->ReadFromString(value,list)){
|
|
if(!nl->ReadFromString("("+value+")",list)){
|
|
SetDefined(false);
|
|
return;
|
|
}
|
|
}
|
|
if(listutils::isSymbolUndefined(list)){
|
|
SetDefined(false);
|
|
return;
|
|
}
|
|
if( nl->HasLength(list,2)
|
|
&& listutils::isNumeric(nl->First(list))
|
|
&& listutils::isNumeric(nl->Second(list))){
|
|
Set(listutils::getNumValue(nl->First(list)),
|
|
listutils::getNumValue(nl->Second(list)));
|
|
return;
|
|
}
|
|
if( nl->HasLength(list,3)
|
|
&& listutils::isNumeric(nl->First(list))
|
|
&& listutils::isNumeric(nl->Third(list))){
|
|
Set(listutils::getNumValue(nl->First(list)),
|
|
listutils::getNumValue(nl->Third(list)));
|
|
return;
|
|
}
|
|
SetDefined(false);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Intersection(const Point& p, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/ ) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(AlmostEqual(*this, p)){
|
|
result += *this;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Intersection(const PointsT<Array>& ps, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/ ) const{
|
|
ps.Intersection(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Intersection(const LineT<Array>& l, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
l.Intersection(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Intersection(const RegionT<Array>& r, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
r.Intersection(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Intersection(const SimpleLineT<Array>& l, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
l.Intersection(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Minus(const Point& p, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(!AlmostEqual(*this, p)){
|
|
result += *this;
|
|
}
|
|
|
|
}
|
|
template<template<typename T>class Array>
|
|
void Point::Minus(const PointsT<Array>& ps, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !ps.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
|
|
if(!ps.Contains(*this, geoid)){
|
|
result += *this;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Minus(const LineT<Array>& l, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !l.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(!l.Contains(*this, geoid)){
|
|
result += *this;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Minus(const RegionT<Array>& r, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(!r.Contains(*this, geoid)){
|
|
result += *this;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Minus(const SimpleLineT<Array>& l, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !l.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
if(!l.Contains(*this, geoid)){
|
|
result += *this;
|
|
}
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Union(const Point& p, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
result.Clear();
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined())){
|
|
result.SetDefined(false);
|
|
return;
|
|
}
|
|
result.SetDefined(true);
|
|
result.StartBulkLoad();
|
|
result += *this;
|
|
result += p;
|
|
result.EndBulkLoad();
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Union(const PointsT<Array>& ps, PointsT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
ps.Union(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Union(const LineT<Array>& l, LineT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
l.Union(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Union(const RegionT<Array>& r, RegionT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
r.Union(*this, result, geoid);
|
|
}
|
|
|
|
template<template<typename T>class Array>
|
|
void Point::Union(const SimpleLineT<Array>& l, SimpleLineT<Array>& result,
|
|
const Geoid* geoid /*=0*/) const{
|
|
l.Union(*this, result, geoid);
|
|
}
|
|
|
|
double Point::Distance( const Point& p, const Geoid* geoid /* = 0 */ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
bool ok = false;
|
|
double bearInitial = 0, bearFinal = 0;
|
|
double d = DistanceOrthodromePrecise(p,*geoid,ok,bearInitial,bearFinal);
|
|
if(ok){
|
|
return d;
|
|
} else{
|
|
assert(false);
|
|
return -1.0;
|
|
}
|
|
} else {
|
|
double dx = p.x - x,
|
|
dy = p.y - y;
|
|
return sqrt( pow( dx, 2 ) + pow( dy, 2 ) );
|
|
}
|
|
}
|
|
|
|
double Point::Distance( const Rectangle<2>& r, const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( IsDefined() );
|
|
assert( r.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented!"
|
|
<< endl; // TODO: implement spherical gemetry case
|
|
assert(false);
|
|
}
|
|
double rxmin = r.MinD(0), rxmax = r.MaxD(0),
|
|
rymin = r.MinD(1), rymax = r.MaxD(1);
|
|
double dx =
|
|
( (x > rxmin || AlmostEqual(x,rxmin))
|
|
&& (x < rxmax || AlmostEqual(x,rxmax))) ? (0.0) :
|
|
(min(abs(x-rxmin),abs(x-rxmax)));
|
|
double dy =
|
|
( (y > rymin || AlmostEqual(y,rymin))
|
|
&& (y < rymax || AlmostEqual(y,rymax))) ? (0.0) :
|
|
(min(abs(y-rymin),abs(y-rymax)));
|
|
|
|
return sqrt( pow( dx, 2 ) + pow( dy, 2 ) );
|
|
}
|
|
|
|
bool Point::Intersects(const Rectangle<2>& r, const Geoid* geoid/*=0*/) const{
|
|
assert(IsDefined());
|
|
assert(r.IsDefined());
|
|
assert(!geoid); // not implemented yet
|
|
return (x>=r.MinD(0) ) && (x<=r.MaxD(0))
|
|
&& (y>=r.MinD(1) ) && (y<=r.MaxD(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
// calculate the enclosed angle between (a,b) and (b,c) in degrees
|
|
double Point::calcEnclosedAngle( const Point &a,
|
|
const Point &b,
|
|
const Point &c,
|
|
const Geoid* geoid /* = 0 */){
|
|
double beta = 0.0;
|
|
errno = 0;
|
|
if(geoid){ // use sperical trigonometry
|
|
// very simplistic, works for short distances, but
|
|
// should be improved!
|
|
bool ok = true;
|
|
double la = b.DistanceOrthodrome(c,*geoid, ok); // la = |(b,c)|
|
|
assert(ok);
|
|
double lb = a.DistanceOrthodrome(c,*geoid, ok); // lb = |(a,c)|
|
|
assert(ok);
|
|
double lc = a.DistanceOrthodrome(b,*geoid, ok); // lc = |(a,b)|
|
|
assert(ok);
|
|
assert(la != 0.0);
|
|
assert(lc != 0.0);
|
|
double cosb = (la*la + lc*lc - lb*lb) / (2*la*lc);
|
|
cosb = max(cosb, -1.0);
|
|
cosb = min(cosb, 1.0);
|
|
beta = radToDeg( acos(cosb) );
|
|
assert(errno == 0);
|
|
} else { // use euclidean geometry
|
|
double la = b.Distance(c);
|
|
double lb = a.Distance(c);
|
|
double lc = a.Distance(b);
|
|
double cosb = (la*la + lc*lc - lb*lb) / (2*la*lc);
|
|
cosb = max(cosb, -1.0);
|
|
cosb = min(cosb, 1.0);
|
|
beta = radToDeg( acos(cosb) );
|
|
assert(errno == 0);
|
|
}
|
|
return beta;
|
|
}
|
|
|
|
|
|
// return the direction at starting point ~this~
|
|
double Point::Direction(const Point& p ,
|
|
const bool returnHeading,
|
|
const Geoid* geoid,
|
|
const bool atEndpoint /*=false*/ ) const{
|
|
|
|
if(!IsDefined() || !p.IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
throw( new SecondoException(string("invalid Arguments") + __FILE__ +
|
|
" " + stringutils::int2str(__LINE__)));
|
|
return -1.0;
|
|
}
|
|
if(geoid && (!checkGeographicCoord() || !p.checkGeographicCoord()) ){
|
|
cerr << __PRETTY_FUNCTION__ << ": Invalid geographic coordinate." << endl;
|
|
throw( new SecondoException(string("invalid coordinate") + __FILE__ + " "
|
|
+ stringutils::int2str(__LINE__)));
|
|
return -1.0;
|
|
}
|
|
if(AlmostEqual(*this, p)){
|
|
throw( new SecondoException(string("equal points") + __FILE__ + " "
|
|
+ stringutils::int2str(__LINE__)));
|
|
return -1.0;
|
|
}
|
|
errno = 0;
|
|
double direction = 0;
|
|
if(!geoid){ //euclidean geometry
|
|
Coord x1 = x,
|
|
y1 = y,
|
|
x2 = p.x,
|
|
y2 = p.y;
|
|
|
|
if( AlmostEqual( x1, x2 ) ){
|
|
if( y2 > y1 ){
|
|
direction = 90.0;
|
|
} else {
|
|
direction = 270.0;
|
|
}
|
|
} else if( AlmostEqual( y1, y2 ) ){
|
|
if( x2 > x1 ){
|
|
direction = 0.0;
|
|
} else {
|
|
direction = 180.0;
|
|
}
|
|
} else {
|
|
direction = radToDeg( atan2( (y2 - y1) , (x2 - x1) ) );
|
|
direction = (direction <0.0)?direction+360.0:direction;
|
|
}
|
|
if(errno != 0) {
|
|
cerr << __PRETTY_FUNCTION__ << ": Numerical error in euclidean." << endl;
|
|
throw( new SecondoException(string("numerical error") + __FILE__ + " "
|
|
+ stringutils::int2str(__LINE__)));
|
|
return -1.0;
|
|
}
|
|
// normalize return value:
|
|
if(returnHeading){ // return result as standard heading
|
|
direction = directionToHeading(direction); // already normalized
|
|
} else { // return result as standard direction: Normalize to 0<=DIR<360
|
|
direction = AlmostEqual(direction,360.0)?0.0:direction;
|
|
}
|
|
return direction;
|
|
} else {// spherical geometry
|
|
double tc1=0.0, tc2=0.0; // true course at *this, p
|
|
bool valid = true;
|
|
double d = DistanceOrthodromePrecise(p, *geoid, valid, tc1, tc2);
|
|
if(!valid || (d<0)){
|
|
throw( new SecondoException(string("error in distance computation")
|
|
+ __FILE__ + " " + stringutils::int2str(__LINE__)));
|
|
return -1.0;
|
|
}
|
|
if(returnHeading){
|
|
return (atEndpoint)?tc2:tc1;
|
|
} else {
|
|
return headingToDirection((atEndpoint)?tc2:tc1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Returns the vertex, i.e. the southernmost/northernmost point
|
|
// of the orthodrome *this -> p
|
|
Point Point::orthodromeVertex(const double& rcourseDEG) const {
|
|
if( !checkGeographicCoord() || (rcourseDEG<0) || (rcourseDEG>360) ){
|
|
return Point(false, 0.0, 0.0);
|
|
}
|
|
double Latv = 0;
|
|
double Lov = 0;
|
|
// Latitude Latv of Vertex
|
|
double rcourse = directionToHeading(rcourseDEG); // orthodr. course in degree
|
|
double cosLatv = cos(degToRad(this->GetY()))*sin(degToRad(rcourse));
|
|
Latv = radToDeg(acos(cosLatv));
|
|
if (AlmostEqual(rcourse,0) || AlmostEqual(rcourse,180)){
|
|
Latv = 90;
|
|
}
|
|
if (Latv > 90){
|
|
Latv = 180 - Latv;
|
|
}
|
|
if (this->GetY()<0){
|
|
Latv = - Latv;
|
|
}
|
|
// Longitude Lov of Vertex
|
|
if (!AlmostEqual(Latv,0)){
|
|
double sinDlov = cos(degToRad(rcourse))/sin(degToRad(Latv));
|
|
double Dlov = radToDeg(asin(sinDlov));
|
|
if (rcourse > 180){
|
|
Dlov = -Dlov;
|
|
}
|
|
Lov = Dlov + this->GetX();
|
|
} else {
|
|
Lov = 0;
|
|
}
|
|
return Point(true, Lov, Latv);
|
|
}
|
|
|
|
int Point::orthodromeAtLatitude( const Point &other, const double& latitudeDEG,
|
|
double& lonMinDEG, double& lonMaxDEG) const{
|
|
if( !checkGeographicCoord() || !other.checkGeographicCoord()
|
|
|| AlmostEqual(*this,other)
|
|
|| (latitudeDEG<-90) || (latitudeDEG>90) ){
|
|
throw( new SecondoException(string("invalid parameter") + __FILE__ + " "
|
|
+ stringutils::int2str(__LINE__)));
|
|
return 0;
|
|
}
|
|
|
|
double lon1 = degToRad(GetX());
|
|
double lat1 = degToRad(GetY());
|
|
double lon2 = degToRad(other.GetX());
|
|
double lat2 = degToRad(other.GetY());
|
|
double lat3 = degToRad(latitudeDEG);
|
|
|
|
double l12 = lon1-lon2;
|
|
double A = sin(lat1)*cos(lat2)*cos(lat3)*sin(l12);
|
|
double B = sin(lat1)*cos(lat2)*cos(lat3)*cos(l12)
|
|
- cos(lat1)*sin(lat2)*cos(lat3);
|
|
double C = cos(lat1)*cos(lat2)*sin(lat3)*sin(l12);
|
|
double lon = atan2(B,A);
|
|
if(fabs(C) <= sqrt(A*A + B*B)){
|
|
double dlon = acos(C/sqrt(A*A+B*B));
|
|
double lon3_1=fmod2(lon1+dlon+lon+M_PI, 2*M_PI) - M_PI;
|
|
double lon3_2=fmod2(lon1-dlon+lon+M_PI, 2*M_PI) - M_PI;
|
|
lonMinDEG = radToDeg(MIN(lon3_1,lon3_2));
|
|
lonMaxDEG = radToDeg(MAX(lon3_1,lon3_2));
|
|
if(AlmostEqual(lon3_1,lon3_2)){
|
|
return 1;
|
|
}
|
|
return 2;
|
|
}
|
|
throw( new SecondoException(string("numerical error") + __FILE__
|
|
+ " " + stringutils::int2str(__LINE__)));
|
|
return 0;
|
|
}
|
|
|
|
bool isBetween(const double value, const double bound1, const double bound2){
|
|
return ( (MIN(bound1,bound2)<=value) && (value<=MAX(bound1,bound2)) );
|
|
}
|
|
|
|
bool Point::orthodromeExtremeLatitudes(const Point &other, const Geoid &geoid,
|
|
double &minLat, double &maxLat) const
|
|
{
|
|
if(!checkGeographicCoord() || !other.checkGeographicCoord()
|
|
|| !geoid.IsDefined() ){
|
|
minLat = 1000.0;
|
|
maxLat = -1000.0;
|
|
// cerr << __PRETTY_FUNCTION__ << ": Invalid parameter." << endl;
|
|
throw( new SecondoException(string("invalid parameter") + __FILE__
|
|
+ " " + stringutils::int2str(__LINE__)));
|
|
return false;
|
|
}
|
|
if(AlmostEqual(*this,other)){
|
|
// cerr << __PRETTY_FUNCTION__ << ": *this == other." << endl;
|
|
minLat=MIN(this->GetY(),other.GetY());
|
|
maxLat=MAX(this->GetY(),other.GetY());
|
|
// cerr << __PRETTY_FUNCTION__ << ": minLat=" << minLat << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": maxLat=" << maxLat << endl;
|
|
return true;
|
|
}
|
|
double trueCourse = Direction(other,false,&geoid);
|
|
if(AlmostEqual(trueCourse,0) || AlmostEqual(trueCourse,180)){
|
|
// cerr << __PRETTY_FUNCTION__ << ": Polar course." << endl;
|
|
minLat=MIN(this->GetY(),other.GetY());
|
|
maxLat=MAX(this->GetY(),other.GetY());
|
|
// cerr << __PRETTY_FUNCTION__ << ": minLat=" << minLat << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": maxLat=" << maxLat << endl;
|
|
return true;
|
|
}
|
|
Point vertex = orthodromeVertex(trueCourse);
|
|
if(!vertex.IsDefined()){
|
|
// cerr << __PRETTY_FUNCTION__ << ": Cannot compute vertex." << endl;
|
|
minLat=MIN(this->GetY(),other.GetY());
|
|
maxLat=MAX(this->GetY(),other.GetY());
|
|
// cerr << __PRETTY_FUNCTION__ << ": minLat=" << minLat << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": maxLat=" << maxLat << endl;
|
|
throw( new SecondoException(string("invalid parameter") + __FILE__
|
|
+ " " + stringutils::int2str(__LINE__)));
|
|
return false;
|
|
}
|
|
// cerr << __PRETTY_FUNCTION__ << ": vertex=" << vertex << endl;
|
|
|
|
double vertexReachedLonMin = 6666.6666;
|
|
double vertexReachedLonMax = -6666.6666;
|
|
int notransgressions = orthodromeAtLatitude(other,vertex.GetY(),
|
|
vertexReachedLonMin,
|
|
vertexReachedLonMax);
|
|
if(notransgressions == 0){ // vertex not passed, should not happen
|
|
cerr << __PRETTY_FUNCTION__ << ": Vertex not passed, should not happen."
|
|
<< endl;
|
|
assert(false);
|
|
return false;
|
|
} else if(notransgressions == 1) { // vertex passed once.
|
|
vertexReachedLonMax = vertexReachedLonMin; //
|
|
} // else: vertex passed twice
|
|
if( ( isBetween(directionToHeading(trueCourse),0.0,180.0)
|
|
&& ( (vertexReachedLonMin<MIN(GetX(),other.GetX()))
|
|
|| (vertexReachedLonMax>MAX(GetX(),other.GetX())) ) )
|
|
|| ( isBetween(directionToHeading(trueCourse),180.0,360.0)
|
|
&& !( (vertexReachedLonMin<MIN(GetX(),other.GetX()))
|
|
||(vertexReachedLonMax>MAX(GetX(),other.GetX())) ) ) ){
|
|
// vertex is not reached by this orthodrome
|
|
// cerr << __PRETTY_FUNCTION__ << ": Vertex not passed." << endl;
|
|
minLat = MIN(GetY(),other.GetY());
|
|
maxLat = MAX(GetY(),other.GetY());
|
|
} else if( (GetY()>=0) && (other.GetY()>=0) ){// both in northern hemissphere
|
|
// vertex may be north of this and p
|
|
// cerr << __PRETTY_FUNCTION__ << ": Both northern hemissphere." << endl;
|
|
minLat = MIN(GetY(),other.GetY());
|
|
maxLat = MAX(vertex.GetY(),MAX(GetY(),other.GetY()));
|
|
} else if( (GetY()<0) && (other.GetY()<0) ){// both in southern hemissphere
|
|
// vertex may be south of this and p
|
|
// cerr << __PRETTY_FUNCTION__ << ": Both southern hemissphere." << endl;
|
|
minLat = MIN(vertex.GetY(),MIN(GetY(),other.GetY()));
|
|
maxLat = MAX(GetY(),other.GetY());
|
|
} else { // both in different hemisspheres
|
|
// vertex is either this or p
|
|
// cerr << __PRETTY_FUNCTION__ << ": Both in different hemisspheres."<<endl;
|
|
minLat = MIN(vertex.GetY(),MIN(GetY(),other.GetY()));
|
|
maxLat = MAX(vertex.GetY(),MAX(GetY(),other.GetY()));
|
|
}
|
|
// cerr << __PRETTY_FUNCTION__ << ": minLat=" << minLat << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": maxLat=" << maxLat << endl;
|
|
return true;
|
|
}
|
|
|
|
Rectangle<2> Point::GeographicBBox(const Point &other, const Geoid &geoid) const
|
|
{
|
|
if(!checkGeographicCoord() || !other.checkGeographicCoord()
|
|
|| !geoid.IsDefined())
|
|
{
|
|
return Rectangle<2>(false);
|
|
}
|
|
double northmostLAT;
|
|
double southmostLAT;
|
|
orthodromeExtremeLatitudes(other, geoid, southmostLAT, northmostLAT);
|
|
// cerr << __PRETTY_FUNCTION__ << ": northmostLAT = " << northmostLAT << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": southmostLAT = " << southmostLAT << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": GetX()=" << GetX() << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": other.GetX()=" << other.GetX() << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": MIN(GetX(),other.GetX()) = "
|
|
// << MIN(GetX(),other.GetX()) << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << ": MAX(GetX(),other.GetX()) = "
|
|
// << MAX(GetX(),other.GetX()) << endl;
|
|
|
|
Point p1( true, MIN(GetX(),other.GetX()), southmostLAT );
|
|
Point p2( true, MAX(GetX(),other.GetX()), northmostLAT );
|
|
// cerr << __PRETTY_FUNCTION__ << "p1=" << p1 << ", bbox="
|
|
/// << p1.BoundingBox() << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << "p2=" << p2 << ", bbox="
|
|
// << p2.BoundingBox() << endl;
|
|
// cerr << __PRETTY_FUNCTION__ << "res="
|
|
// << p1.BoundingBox().Union(p2.BoundingBox()) << endl;
|
|
return p1.BoundingBox().Union(p2.BoundingBox());
|
|
}
|
|
|
|
void Point::Rotate(const Coord& x, const Coord& y,
|
|
const double alpha, Point& res) const{
|
|
|
|
if(!IsDefined()){
|
|
res.SetDefined(false);
|
|
return;
|
|
}
|
|
|
|
double s = sin(alpha);
|
|
double c = cos(alpha);
|
|
|
|
double m00 = c;
|
|
double m01 = -s;
|
|
double m02 = x - x*c + y*s;
|
|
double m10 = s;
|
|
double m11 = c;
|
|
double m12 = y - x*s-y*c;
|
|
|
|
res.Set( m00*this->x + m01*this->y + m02,
|
|
m10*this->x + m11*this->y + m12);
|
|
|
|
}
|
|
|
|
/*
|
|
4.1 Spherical geometry operations
|
|
|
|
While the preceding operations use euclidic geometry, the following operations
|
|
use Spherical geometry.
|
|
|
|
*/
|
|
|
|
bool Point::checkGeographicCoord() const {
|
|
return IsDefined() && Geoid::checkGeographicCoord(x,y);
|
|
}
|
|
|
|
/*
|
|
Distance between two points given in geodetic (Lat, Lon)-coordinates.
|
|
The distance is measured along a geoid passed as an argument.
|
|
The result uses the same unit as the geoid'd radius.
|
|
|
|
If an undefined Point or a Point with an invalid geographic coordinate is used,
|
|
~valid~ is set to false, otherwise the result is calculated and ~valid~ is set
|
|
to true.
|
|
|
|
*/
|
|
double Point::DistanceOrthodrome( const Point& p,
|
|
const Geoid& g,
|
|
bool& valid) const
|
|
{
|
|
valid = IsDefined() && p.IsDefined();
|
|
if(!valid){
|
|
return 0.0;
|
|
}
|
|
return g.DistanceOrthodrome(x,y,p.x,p.y,valid);
|
|
}
|
|
|
|
|
|
double Point::DistanceOrthodromePrecise( const Point& p,
|
|
const Geoid& g,
|
|
bool& valid,
|
|
double& initialBearingDEG,
|
|
double& finalBearingDEG,
|
|
const double epsilon /* = 1 */) const
|
|
{
|
|
/*
|
|
* Vincenty Inverse Solution of Geodesics on the Ellipsoid
|
|
*
|
|
* from: Vincenty inverse formula - T Vincenty, "Direct and Inverse Solutions
|
|
* of Geodesics on the Ellipsoid with application of nested equations",
|
|
* Survey Review, vol XXII no 176, 1975
|
|
* http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
|
|
* Calculates geodetic distance between two points specified by
|
|
* latitude/longitude using Vincenty inverse formula for ellipsoids
|
|
*
|
|
*/
|
|
|
|
valid = this->checkGeographicCoord() && p.checkGeographicCoord()
|
|
&& g.IsDefined();
|
|
if(!valid){
|
|
cout << __PRETTY_FUNCTION__ << ": Invalid coordinates." << endl;
|
|
return-666.666;
|
|
}
|
|
initialBearingDEG = -666.666;
|
|
finalBearingDEG = -666.666;
|
|
if(AlmostEqual(*this,p)){
|
|
return 0;
|
|
}
|
|
errno = 0;
|
|
double lat1 = GetY();
|
|
double lon1 = GetX();
|
|
double lat2 = p.GetY();
|
|
double lon2 = p.GetX();
|
|
double a = g.getR();// = 6378137
|
|
double f = g.getF();// = 1/298.257223563
|
|
double b = (1-f)*a; // = 6356752.314245
|
|
double L = degToRad(lon2-lon1);
|
|
double U1 = atan( (1-f)*tan(degToRad(lat1)) );
|
|
double U2 = atan( (1-f)*tan(degToRad(lat2)) );
|
|
double sinU1 = sin(U1);
|
|
double cosU1 = cos(U1);
|
|
double sinU2 = sin(U2);
|
|
double cosU2 = cos(U2);
|
|
if(errno!=0){
|
|
cout << __PRETTY_FUNCTION__ << ": Error at (1)." << endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
|
|
double lambda = L;
|
|
double lambdaP;
|
|
int iterLimit = 100;
|
|
int iter = 1;
|
|
double sinLambda, cosLambda,
|
|
sinSigma, cosSigma, sigma,
|
|
sinAlpha, cosSqAlpha,
|
|
cos2SigmaM, C;
|
|
do{
|
|
sinLambda = sin(lambda);
|
|
cosLambda = cos(lambda);
|
|
sinSigma = sqrt(
|
|
(cosU2*sinLambda)
|
|
* (cosU2*sinLambda)
|
|
+ (cosU1*sinU2-sinU1*cosU2*cosLambda)
|
|
* (cosU1*sinU2-sinU1*cosU2*cosLambda));
|
|
if (sinSigma==0){ // co-incident points
|
|
valid = false;
|
|
cout << __PRETTY_FUNCTION__ << ": Error: Co-incident points: *this="
|
|
<< *this << ", p=" << p << endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
if(errno!=0){
|
|
cout << __PRETTY_FUNCTION__ << ": Error (1) in iteration " <<iter<< endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
|
|
sigma = atan2(sinSigma, cosSigma);
|
|
sinAlpha = cosU1 * cosU2 * sinLambda / sinSigma;
|
|
cosSqAlpha = 1 - sinAlpha*sinAlpha;
|
|
cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
|
|
if(cos2SigmaM!=cos2SigmaM){ // i.e.(cos2SigmaM == nan)||(cos2SigmaM == -nan)
|
|
// equatorial line: cosSqAlpha=0 (§6)
|
|
cos2SigmaM = 0;
|
|
errno = 0;
|
|
}
|
|
C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
|
|
lambdaP = lambda;
|
|
lambda = L + (1-C) * f * sinAlpha *
|
|
( sigma
|
|
+ C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)) );
|
|
if(errno!=0){
|
|
cout << __PRETTY_FUNCTION__ << ": Error (2) in iteration " <<iter<< endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
} while( (fabs(lambda-lambdaP) > epsilon) && (iter++<iterLimit) );
|
|
if(iter>iterLimit) { // formula failed to converge
|
|
cout << __PRETTY_FUNCTION__ << ": Formula failed to converge after "
|
|
<< iter << " iterations. Delta=" << fabs(lambda-lambdaP)
|
|
<< " > epsilon=" << epsilon << "." << endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
double uSq = cosSqAlpha * (a*a - b*b) / (b*b);
|
|
double A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
|
|
double B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
|
|
double deltaSigma = B*sinSigma*(cos2SigmaM
|
|
+(B/4)*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
|
|
(B/6)*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
|
|
double s = b*A*(sigma-deltaSigma);
|
|
if(errno!=0){
|
|
cout << __PRETTY_FUNCTION__ << ": Error (3) " << iter << endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
|
|
// calculate initial/final bearings in addition to distance
|
|
double fwdAz = atan2(cosU2*sinLambda, cosU1*sinU2-sinU1*cosU2*cosLambda);
|
|
double revAz = atan2(cosU1*sinLambda, -sinU1*cosU2+cosU1*sinU2*cosLambda);
|
|
initialBearingDEG = radToDeg(fwdAz);
|
|
finalBearingDEG = radToDeg(revAz);
|
|
// normalize heading results to 0<x<=360
|
|
while( initialBearingDEG<0.0 ) {
|
|
initialBearingDEG += 360.0;
|
|
}
|
|
if(initialBearingDEG > 360.0){
|
|
initialBearingDEG = fmod(initialBearingDEG,360.0);
|
|
}
|
|
if( AlmostEqual(initialBearingDEG,0.0) ){
|
|
initialBearingDEG = 360.0; // Map NORTH to heading 360, not to 0
|
|
}
|
|
|
|
while( finalBearingDEG<0.0 ){
|
|
finalBearingDEG += 360.0;
|
|
}
|
|
if(finalBearingDEG > 360.0){
|
|
finalBearingDEG = fmod(finalBearingDEG,360.0);
|
|
}
|
|
if( AlmostEqual(finalBearingDEG,0.0) ){
|
|
finalBearingDEG = 360.0; // Map NORTH to heading 360, not to 0
|
|
}
|
|
|
|
if(errno!=0){
|
|
cout << __PRETTY_FUNCTION__ << ": Error (4) " << iter << endl;
|
|
valid = false;
|
|
return -666.666;
|
|
}
|
|
// cout << __PRETTY_FUNCTION__ << ": After " << iter
|
|
// << " Iterations: Distance[m]=" << s
|
|
// << ", initialBearingDEG=" << initialBearingDEG
|
|
// << ", finalBearingDEG=" << finalBearingDEG << endl;
|
|
valid = true;
|
|
return s;
|
|
}
|
|
|
|
|
|
|
|
Point Point::MidpointTo(const Point& p, const Geoid* geoid /* = 0 */ ) const
|
|
{
|
|
if ( !this->IsDefined() || !p.IsDefined() ) {
|
|
return Point( false );
|
|
}
|
|
|
|
if( AlmostEqual(*this,p) ){ // no calculation required.
|
|
return *this;
|
|
}
|
|
if(geoid){ // spherical geometry
|
|
// TODO: Use ellipsoid ~geoid~ instead of ideal sphere!
|
|
bool ok = this->checkGeographicCoord() && p.checkGeographicCoord();
|
|
double lon1 = degToRad( GetX() ); // X <-> geogr. Laenge (Longitude)
|
|
double lat1 = degToRad( GetY() ); // Y <-> geogr. Breite (Latitude)
|
|
double lat2 = degToRad( p.GetY() );
|
|
double dLon = degToRad( p.GetX() - GetX() );
|
|
errno = 0;
|
|
double Bx = cos(lat2) * cos(dLon);
|
|
double By = cos(lat2) * sin(dLon);
|
|
double lat3 = atan2( sin(lat1)+sin(lat2),
|
|
sqrt( (cos(lat1)+Bx)*(cos(lat1)+Bx) + By*By) );
|
|
double lon3 = lon1 + atan2(By, cos(lat1) + Bx);
|
|
Point p( true, radToDeg(lon3), radToDeg(lat3) );
|
|
p.SetDefined( ok && (errno==0) && p.checkGeographicCoord() );
|
|
return p;
|
|
} else { // euclidean geometry
|
|
return Point( true, ( GetX() + p.GetX() )/2.0, ( GetY() + p.GetY() )/2.0 );
|
|
}
|
|
}
|
|
|
|
Point Point::MidpointTo(const Point& p, const Coord& f,
|
|
const Geoid* geoid /*=0*/) const{
|
|
if( !IsDefined() || !p.IsDefined() ||
|
|
(geoid && !geoid->IsDefined()) || (f<0.0) || (f>1.0) ) {
|
|
return Point(false);
|
|
}
|
|
if(AlmostEqual(f,0.0)) {
|
|
return *this;
|
|
}
|
|
if(AlmostEqual(f,1.0)) {
|
|
return p;
|
|
}
|
|
if(geoid){ // spherical TODO: use ellipsoid instead of ideal sphere
|
|
double lon1 = degToRad(GetX()); // X <-> geogr. Laenge (Longitude)
|
|
double lat1 = degToRad(GetY()); // Y <-> geogr. Breite (Latitude)
|
|
double lon2 = degToRad(p.GetX());
|
|
double lat2 = degToRad(p.GetY());
|
|
if( AlmostEqual(lat1,lat2) && AlmostEqual(fabs(lon1-lon2),M_PI) ){
|
|
return Point(false);
|
|
}
|
|
errno = 0;
|
|
double A = sin(1 - f) / sin(1);
|
|
double B = sin(f) / sin(1);
|
|
double C = A * cos(lat1) * cos(lon1) + B * cos(lat2)*cos(lon2);
|
|
double D = A * cos(lat1) * sin(lon1) + B * cos(lat2) * sin(lon2);
|
|
double lat = atan2(A * sin(lat1) + B * sin(lat2),sqrt(C*C + D*D));
|
|
double lon = atan2(D, C);
|
|
Point p( true, radToDeg(lon), radToDeg(lat) );
|
|
p.SetDefined( (errno==0) && p.checkGeographicCoord() );
|
|
return p;
|
|
} else { // euclidean
|
|
double x1 = GetX();
|
|
double y1 = GetY();
|
|
double x2 = p.GetX();
|
|
double y2 = p.GetY();
|
|
double dx = x2-x1;
|
|
double dy = y2-y1;
|
|
double x = x1 + (f*dx);
|
|
double y = y1 + (f*dy);
|
|
return Point( true, x, y );
|
|
}
|
|
}
|
|
|
|
/*
|
|
4.2 List Representation
|
|
|
|
The list representation of a point is
|
|
|
|
---- (x y)
|
|
----
|
|
|
|
4.3 ~Out~-function
|
|
|
|
*/
|
|
ListExpr
|
|
OutPoint( ListExpr typeInfo, Word value )
|
|
{
|
|
Point* point = (Point*)(value.addr);
|
|
if( point->IsDefined() )
|
|
return nl->TwoElemList(
|
|
nl->RealAtom( point->GetX() ),
|
|
nl->RealAtom( point->GetY() ) );
|
|
else
|
|
return nl->SymbolAtom( Symbol::UNDEFINED() );
|
|
}
|
|
|
|
/*
|
|
4.4 ~In~-function
|
|
|
|
*/
|
|
Word
|
|
InPoint( const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
correct = true;
|
|
if( nl->ListLength( instance ) == 2 ) {
|
|
ListExpr first = nl->First(instance);
|
|
ListExpr second = nl->Second(instance);
|
|
|
|
correct = listutils::isNumeric(first) && listutils::isNumeric(second);
|
|
if(!correct){
|
|
return SetWord( Address(0) );
|
|
} else {
|
|
return SetWord(new Point(true, listutils::getNumValue(first),
|
|
listutils::getNumValue(second)));
|
|
}
|
|
} else if( listutils::isSymbolUndefined( instance ) ){
|
|
return SetWord(new Point(false));
|
|
}
|
|
correct = false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
/*
|
|
4.5 ~Create~-function
|
|
|
|
*/
|
|
Word
|
|
CreatePoint( const ListExpr typeInfo )
|
|
{
|
|
return SetWord( new Point( false ) );
|
|
}
|
|
|
|
/*
|
|
4.6 ~Delete~-function
|
|
|
|
*/
|
|
void
|
|
DeletePoint( const ListExpr typeInfo,
|
|
Word& w )
|
|
{
|
|
((Point *)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
4.7 ~Close~-function
|
|
|
|
*/
|
|
void
|
|
ClosePoint( const ListExpr typeInfo,
|
|
Word& w )
|
|
{
|
|
((Point *)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
4.8 ~Clone~-function
|
|
|
|
*/
|
|
Word
|
|
ClonePoint( const ListExpr typeInfo,
|
|
const Word& w )
|
|
{
|
|
return SetWord( new Point( *((Point *)w.addr) ) );
|
|
}
|
|
|
|
/*
|
|
4.8 ~SizeOf~-function
|
|
|
|
*/
|
|
int
|
|
SizeOfPoint()
|
|
{
|
|
return sizeof(Point);
|
|
}
|
|
|
|
/*
|
|
4.9 Function describing the signature of the type constructor
|
|
|
|
*/
|
|
ListExpr
|
|
PointProperty()
|
|
{
|
|
return nl->TwoElemList(
|
|
nl->FourElemList(
|
|
nl->StringAtom("Signature"),
|
|
nl->StringAtom("Example Type List"),
|
|
nl->StringAtom("List Rep"),
|
|
nl->StringAtom("Example List")),
|
|
nl->FourElemList(
|
|
nl->StringAtom("-> DATA"),
|
|
nl->StringAtom(Point::BasicType()),
|
|
nl->StringAtom("(x y)"),
|
|
nl->StringAtom("(10 5)")));
|
|
}
|
|
|
|
/*
|
|
4.10 Kind Checking Function
|
|
|
|
This function checks whether the type constructor is applied correctly. Since
|
|
type constructor ~point~ does not have arguments, this is trivial.
|
|
|
|
*/
|
|
bool
|
|
CheckPoint( ListExpr type, ListExpr& errorInfo )
|
|
{
|
|
return (listutils::isSymbol( type, Point::BasicType() ));
|
|
}
|
|
|
|
|
|
/*
|
|
4.12 Creation of the type constructor instance
|
|
|
|
*/
|
|
TypeConstructor point(
|
|
Point::BasicType(), //name
|
|
PointProperty, //property function describing signature
|
|
OutPoint, InPoint, //Out and In functions
|
|
0, 0, //SaveToList and RestoreFromList functions
|
|
CreatePoint, DeletePoint, //object creation and deletion
|
|
OpenAttribute<Point>,
|
|
SaveAttribute<Point>, // object open and save
|
|
ClosePoint, ClonePoint, //object close, and clone
|
|
Point::Cast, //cast function
|
|
SizeOfPoint, //sizeof function
|
|
CheckPoint); //kind checking function
|
|
|
|
ostream& operator<<( ostream& o, const Points& ps )
|
|
{
|
|
o << "<";
|
|
if( !ps.IsDefined() ) {
|
|
o << " undef ";
|
|
} else {
|
|
for( int i = 0; i < ps.Size(); i++ )
|
|
{
|
|
Point p(true);
|
|
ps.Get( i, p );
|
|
o << " " << p;
|
|
}
|
|
}
|
|
o << ">";
|
|
return o;
|
|
}
|
|
|
|
// use this when adding and sorting the DBArray
|
|
int PointCompare( const void *a, const void *b )
|
|
{
|
|
const Point *pa = (const Point*)a,
|
|
*pb = (const Point*)b;
|
|
assert(pa->IsDefined());
|
|
assert(pb->IsDefined());
|
|
if( *pa == *pb )
|
|
return 0;
|
|
|
|
if( *pa < *pb )
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// use this when testing for containment or removing duplicates
|
|
// in the DBArray
|
|
int PointCompareAlmost( const void *a, const void *b )
|
|
{
|
|
const Point *pa = (const Point*)a,
|
|
*pb = (const Point*)b;
|
|
|
|
assert(pa->IsDefined());
|
|
assert(pb->IsDefined());
|
|
if( AlmostEqual( *pa, *pb ) )
|
|
return 0;
|
|
|
|
if( *pa < *pb )
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// Old implementation, should be replaced by the following one
|
|
// to avoid problems when sorting and removing duplicates
|
|
int PointHalfSegmentCompare( const void *a, const void *b )
|
|
{
|
|
const Point *pa = (const Point *)a;
|
|
const HalfSegment *hsb = (const HalfSegment *)b;
|
|
assert(pa->IsDefined());
|
|
|
|
if( AlmostEqual( *pa, hsb->GetDomPoint() ) )
|
|
return 0;
|
|
|
|
if( *pa < hsb->GetDomPoint() )
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
// // use this when adding and sorting the DBArray
|
|
// int PointHalfSegmentCompare( const void *a, const void *b )
|
|
// {
|
|
// const Point *pa = (const Point *)a;
|
|
// const HalfSegment *hsb = (const HalfSegment *)b;
|
|
//
|
|
// if( *pa == hsb->GetDomPoint() )
|
|
// return 0;
|
|
//
|
|
// if( *pa < hsb->GetDomPoint() )
|
|
// return -1;
|
|
//
|
|
// return 1;
|
|
// }
|
|
|
|
// use this when testing for containment or removing duplicates
|
|
// in the DBArray
|
|
int PointHalfSegmentCompareAlmost( const void *a, const void *b )
|
|
{
|
|
const Point *pa = (const Point *)a;
|
|
const HalfSegment *hsb = (const HalfSegment *)b;
|
|
assert(pa->IsDefined());
|
|
|
|
if( AlmostEqual( *pa, hsb->GetDomPoint() ) )
|
|
return 0;
|
|
|
|
if( *pa < hsb->GetDomPoint() )
|
|
return -1;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
5.2 List Representation
|
|
|
|
The list representation of a point is
|
|
|
|
---- (x y)
|
|
----
|
|
|
|
5.3 ~Out~-function
|
|
|
|
*/
|
|
ListExpr
|
|
OutPoints( ListExpr typeInfo, Word value )
|
|
{
|
|
Points* points = (Points*)(value.addr);
|
|
if(!points->IsDefined()){
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
if( points->IsEmpty() )
|
|
return nl->TheEmptyList();
|
|
|
|
Point p(true);
|
|
assert( points->Get( 0, p ) );
|
|
ListExpr result =
|
|
nl->OneElemList( OutPoint( nl->TheEmptyList(), SetWord( (void*)&p ) ) );
|
|
ListExpr last = result;
|
|
|
|
for( int i = 1; i < points->Size(); i++ )
|
|
{
|
|
assert( points->Get( i, p ) );
|
|
last = nl->Append( last,
|
|
OutPoint( nl->TheEmptyList(), SetWord( (void*)&p ) ) );
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
5.4 ~In~-function
|
|
|
|
*/
|
|
Word
|
|
InPoints( const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
if(listutils::isSymbolUndefined(instance)) {
|
|
Points* points = new Points(0);
|
|
points->Clear();
|
|
points->SetDefined(false);
|
|
correct=true;
|
|
return SetWord( Address(points) );
|
|
}
|
|
Points* points = new Points( max(0,nl->ListLength( instance) ) );
|
|
points->SetDefined( true );
|
|
if(nl->AtomType(instance)!=NoAtom) {
|
|
points->DeleteIfAllowed();
|
|
correct = false;
|
|
cout << __PRETTY_FUNCTION__ << ": Unexpected Atom!" << endl;
|
|
return SetWord( Address(points) );
|
|
}
|
|
|
|
ListExpr rest = instance;
|
|
points->StartBulkLoad();
|
|
while( !nl->IsEmpty( rest ) ) {
|
|
ListExpr first = nl->First( rest );
|
|
rest = nl->Rest( rest );
|
|
|
|
Point *p = (Point*)InPoint( nl->TheEmptyList(),
|
|
first, 0, errorInfo, correct ).addr;
|
|
if( correct && p->IsDefined() ) {
|
|
(*points) += (*p);
|
|
delete p;
|
|
} else {
|
|
if(p) {
|
|
delete p;
|
|
}
|
|
cout << __PRETTY_FUNCTION__ << ": Incorrect or undefined point!" << endl;
|
|
points->DeleteIfAllowed();
|
|
correct = false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
}
|
|
points->EndBulkLoad();
|
|
|
|
if( points->IsValid() ) {
|
|
correct = true;
|
|
return SetWord( points );
|
|
}
|
|
points->DeleteIfAllowed();
|
|
correct = false;
|
|
cout << __PRETTY_FUNCTION__ << ": Invalid points value!" << endl;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
/*
|
|
5.5 ~Create~-function
|
|
|
|
*/
|
|
Word
|
|
CreatePoints( const ListExpr typeInfo )
|
|
{
|
|
return SetWord( new Points( 0 ) );
|
|
}
|
|
|
|
/*
|
|
5.6 ~Delete~-function
|
|
|
|
*/
|
|
void
|
|
DeletePoints( const ListExpr typeInfo, Word& w )
|
|
{
|
|
Points *ps = (Points *)w.addr;
|
|
ps->Destroy();
|
|
ps->DeleteIfAllowed(false);
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
5.7 ~Close~-function
|
|
|
|
*/
|
|
void
|
|
ClosePoints( const ListExpr typeInfo, Word& w )
|
|
{
|
|
((Points *)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
5.8 ~Clone~-function
|
|
|
|
*/
|
|
Word
|
|
ClonePoints( const ListExpr typeInfo, const Word& w )
|
|
{
|
|
return SetWord( new Points( *((Points *)w.addr) ) );
|
|
}
|
|
|
|
/*
|
|
7.8 ~Open~-function
|
|
|
|
*/
|
|
bool
|
|
OpenPoints( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Points *ps = (Points*)Attribute::Open( valueRecord, offset, typeInfo );
|
|
value = SetWord( ps );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
7.8 ~Save~-function
|
|
|
|
*/
|
|
bool
|
|
SavePoints( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Points *ps = (Points*)value.addr;
|
|
Attribute::Save( valueRecord, offset, typeInfo, ps );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
5.8 ~SizeOf~-function
|
|
|
|
*/
|
|
int
|
|
SizeOfPoints()
|
|
{
|
|
return sizeof(Points);
|
|
}
|
|
|
|
/*
|
|
5.11 Function describing the signature of the type constructor
|
|
|
|
*/
|
|
ListExpr
|
|
PointsProperty()
|
|
{
|
|
return (nl->TwoElemList(
|
|
nl->FourElemList(nl->StringAtom("Signature"),
|
|
nl->StringAtom("Example Type List"),
|
|
nl->StringAtom("List Rep"),
|
|
nl->StringAtom("Example List")),
|
|
nl->FourElemList(nl->StringAtom("-> DATA"),
|
|
nl->StringAtom(Points::BasicType()),
|
|
nl->StringAtom("(<point>*) where point is (<x><y>)"),
|
|
nl->StringAtom("( (10 1)(4 5) )"))));
|
|
}
|
|
|
|
/*
|
|
5.12 Kind checking function
|
|
|
|
This function checks whether the type constructor is applied correctly. Since
|
|
type constructor ~point~ does not have arguments, this is trivial.
|
|
|
|
*/
|
|
bool
|
|
CheckPoints( ListExpr type, ListExpr& errorInfo )
|
|
{
|
|
return (nl->IsEqual( type, Points::BasicType() ));
|
|
}
|
|
|
|
/*
|
|
5.13 ~Cast~-function
|
|
|
|
*/
|
|
void* CastPoints(void* addr)
|
|
{
|
|
return (new (addr) Points());
|
|
}
|
|
|
|
/*
|
|
5.14 Creation of the type constructor instance
|
|
|
|
*/
|
|
TypeConstructor points(
|
|
Points::BasicType(), //name
|
|
PointsProperty, //property function describing signature
|
|
OutPoints, InPoints, //Out and In functions
|
|
0, 0, //SaveTo and RestoreFrom List functions
|
|
CreatePoints, DeletePoints, //object creation and deletion
|
|
OpenPoints, SavePoints, // object open and save
|
|
ClosePoints, ClonePoints, //object close and clone
|
|
CastPoints, //cast function
|
|
SizeOfPoints, //sizeof function
|
|
CheckPoints ); //kind checking function
|
|
|
|
/*
|
|
6 Type Constructor ~halfsegment~
|
|
|
|
A ~halfsegment~ value is a pair of points, with a boolean flag indicating the
|
|
dominating point .
|
|
|
|
6.1 Implementation of the class ~halfsegment~
|
|
|
|
*/
|
|
void HalfSegment::Translate( const Coord& x, const Coord& y )
|
|
{
|
|
lp.Translate( x, y );
|
|
rp.Translate( x, y );
|
|
}
|
|
|
|
|
|
void HalfSegment::Set( bool ldp, const Point& lp, const Point& rp )
|
|
{
|
|
assert( lp.IsDefined() );
|
|
assert( rp.IsDefined() );
|
|
assert( !AlmostEqual( lp, rp ) );
|
|
|
|
this->ldp = ldp;
|
|
if( lp < rp )
|
|
{
|
|
this->lp = lp;
|
|
this->rp = rp;
|
|
}
|
|
else // rp > lp
|
|
{
|
|
this->lp = rp;
|
|
this->rp = lp;
|
|
}
|
|
}
|
|
|
|
int HalfSegment::Compare( const HalfSegment& hs ) const
|
|
{
|
|
const Point& dp = GetDomPoint(),
|
|
sp = GetSecPoint(),
|
|
DP = hs.GetDomPoint(),
|
|
SP = hs.GetSecPoint();
|
|
|
|
if( dp < DP )
|
|
return -1;
|
|
else if( dp > DP )
|
|
return 1;
|
|
|
|
if( ldp != hs.ldp )
|
|
{
|
|
if( ldp == false )
|
|
return -1;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
bool v1 = IsVertical();
|
|
bool v2 = hs.IsVertical();
|
|
if( v1 && v2 ) // both are vertical
|
|
{
|
|
if( ( (CompareDouble(sp.GetY(),dp.GetY())>0)
|
|
&& ( CompareDouble(SP.GetY(),DP.GetY())>0)
|
|
)
|
|
||
|
|
( (CompareDouble(sp.GetY(),dp.GetY())<0)
|
|
&& (CompareDouble(SP.GetY(),DP.GetY())<0) ) )
|
|
{
|
|
if( sp < SP )
|
|
return -1;
|
|
if( sp > SP )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
else if( CompareDouble(sp.GetY(),dp.GetY())>0)
|
|
{
|
|
if( ldp == true )
|
|
return 1;
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if( ldp == true )
|
|
return -1;
|
|
return 1;
|
|
}
|
|
}
|
|
else if( AlmostEqual(dp.GetX(),sp.GetX()) )
|
|
{
|
|
if( CompareDouble(sp.GetY(), dp.GetY())>0 )
|
|
{
|
|
if( ldp == true )
|
|
return 1;
|
|
return -1;
|
|
}
|
|
else if( CompareDouble(sp.GetY(),dp.GetY())<0 )
|
|
{
|
|
if( ldp == true )
|
|
return -1;
|
|
return 1;
|
|
}
|
|
}
|
|
else if( AlmostEqual(DP.GetX(), SP.GetX()) )
|
|
{
|
|
if( CompareDouble(SP.GetY() , DP.GetY())>0 )
|
|
{
|
|
if( ldp == true )
|
|
return -1;
|
|
return 1;
|
|
}
|
|
else if( CompareDouble(SP.GetY() , DP.GetY())<0 )
|
|
{
|
|
if( ldp == true )
|
|
return 1;
|
|
return -1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Coord xd = dp.GetX(), yd = dp.GetY(),
|
|
xs = sp.GetX(), ys = sp.GetY(),
|
|
Xd = DP.GetX(), Yd = DP.GetY(),
|
|
Xs = SP.GetX(), Ys = SP.GetY();
|
|
double k = (yd - ys) / (xd - xs),
|
|
K= (Yd -Ys) / (Xd - Xs);
|
|
|
|
if( CompareDouble(k , K) <0 )
|
|
return -1;
|
|
if( CompareDouble( k, K) > 0)
|
|
return 1;
|
|
|
|
if( sp < SP )
|
|
return -1;
|
|
if( sp > SP )
|
|
return 1;
|
|
return 0;
|
|
}
|
|
}
|
|
assert( true ); // This code should never be reached
|
|
return 0;
|
|
}
|
|
|
|
HalfSegment& HalfSegment::operator=( const HalfSegment& hs )
|
|
{
|
|
ldp = hs.ldp;
|
|
lp = hs.lp;
|
|
rp = hs.rp;
|
|
attr = hs.attr;
|
|
return *this;
|
|
}
|
|
|
|
bool HalfSegment::operator==( const HalfSegment& hs ) const
|
|
{
|
|
return Compare(hs) == 0;
|
|
}
|
|
|
|
bool HalfSegment::operator!=( const HalfSegment& hs ) const
|
|
{
|
|
return !( *this == hs );
|
|
}
|
|
|
|
bool HalfSegment::operator<( const HalfSegment& hs ) const
|
|
{
|
|
return Compare(hs) == -1;
|
|
}
|
|
|
|
bool HalfSegment::operator>( const HalfSegment& hs ) const
|
|
{
|
|
return Compare(hs) == 1;
|
|
}
|
|
|
|
int HalfSegment::LogicCompare( const HalfSegment& hs ) const
|
|
{
|
|
if( attr.faceno < hs.attr.faceno )
|
|
return -1;
|
|
|
|
if( attr.faceno > hs.attr.faceno )
|
|
return 1;
|
|
|
|
if( attr.cycleno < hs.attr.cycleno )
|
|
return -1;
|
|
|
|
if( attr.cycleno > hs.attr.cycleno )
|
|
return 1;
|
|
|
|
if( attr.edgeno < hs.attr.edgeno )
|
|
return -1;
|
|
|
|
if( attr.edgeno > hs.attr.edgeno )
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int HalfSegmentCompare(const void *a, const void *b)
|
|
{
|
|
const HalfSegment *hsa = (const HalfSegment *)a,
|
|
*hsb = (const HalfSegment *)b;
|
|
return hsa->Compare( *hsb );
|
|
}
|
|
|
|
int HalfSegmentLogicCompare(const void *a, const void *b)
|
|
{
|
|
const HalfSegment *hsa = (const HalfSegment *)a,
|
|
*hsb = (const HalfSegment *)b;
|
|
|
|
return hsa->LogicCompare( *hsb );
|
|
}
|
|
|
|
int LRSCompare( const void *a, const void *b )
|
|
{
|
|
const LRS *lrsa = (const LRS *)a,
|
|
*lrsb = (const LRS *)b;
|
|
|
|
if( lrsa->lrsPos < lrsb->lrsPos )
|
|
return -1;
|
|
if( lrsa->lrsPos > lrsb->lrsPos )
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
bool HalfSegment::insideLeft() const{
|
|
|
|
if(ldp){
|
|
return attr.insideAbove;
|
|
} else {
|
|
return !attr.insideAbove;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
ostream& operator<<(ostream &os, const HalfSegment& hs)
|
|
{
|
|
return os << "("
|
|
<<"F("<< hs.attr.faceno
|
|
<<") C("<< hs.attr.cycleno
|
|
<<") E(" << hs.attr.edgeno<<") DP("
|
|
<< (hs.IsLeftDomPoint()? "L":"R")
|
|
<<") IA("<< (hs.attr.insideAbove? "A":"U")
|
|
<<") Co("<<hs.attr.coverageno
|
|
<<") PNo("<<hs.attr.partnerno
|
|
<<") ("<< hs.GetLeftPoint() << " "<< hs.GetRightPoint() <<") ";
|
|
}
|
|
|
|
bool HalfSegment::Intersects( const HalfSegment& hs,
|
|
const Geoid* geoid /* = 0 */) const
|
|
{
|
|
assert(!geoid); // spherical geometry not yet implemented!
|
|
|
|
double k, a, K, A;
|
|
if( !BoundingBox().Intersects( hs.BoundingBox() ) )
|
|
return false;
|
|
|
|
Coord xl = lp.GetX(),
|
|
yl = lp.GetY(),
|
|
xr = rp.GetX(),
|
|
yr = rp.GetY(),
|
|
Xl = hs.GetLeftPoint().GetX(),
|
|
Yl = hs.GetLeftPoint().GetY(),
|
|
Xr = hs.GetRightPoint().GetX(),
|
|
Yr = hs.GetRightPoint().GetY();
|
|
|
|
if( AlmostEqual( xl, xr ) &&
|
|
AlmostEqual( Xl, Xr ) )
|
|
// both segments are vertical
|
|
{
|
|
if( AlmostEqual( xl, Xl ) &&
|
|
( AlmostEqual( yl, Yl ) || AlmostEqual( yl, Yr ) ||
|
|
AlmostEqual( yr, Yl ) || AlmostEqual( yr, Yr ) ||
|
|
( yl > Yl && yl < Yr ) || ( yr > Yl && yr < Yr ) ||
|
|
( Yl > yl && Yl < yr ) || ( Yr > yl && Yr < yr ) ) )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
if( !AlmostEqual( xl, xr ) )
|
|
// this segment is not vertical
|
|
{
|
|
k = (yr - yl) / (xr - xl);
|
|
a = yl - k * xl;
|
|
}
|
|
|
|
|
|
if( !AlmostEqual( Xl, Xr ) )
|
|
// hs is not vertical
|
|
{
|
|
K = (Yr - Yl) / (Xr - Xl);
|
|
A = Yl - K * Xl;
|
|
}
|
|
|
|
if( AlmostEqual( Xl, Xr ) )
|
|
//only hs is vertical
|
|
{
|
|
Coord y0 = k * Xl + a;
|
|
|
|
if( ( Xl > xl || AlmostEqual( Xl, xl ) ) &&
|
|
( Xl < xr || AlmostEqual( Xl, xr ) ) )
|
|
{
|
|
if( ( ( y0 > Yl || AlmostEqual( y0, Yl ) ) &&
|
|
( y0 < Yr || AlmostEqual( y0, Yr ) ) ) ||
|
|
( ( y0 > Yr || AlmostEqual( y0, Yr ) ) &&
|
|
( y0 < Yl || AlmostEqual( y0, Yl ) ) ) )
|
|
// (Xl, y0) is the intersection point
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if( AlmostEqual( xl, xr ) )
|
|
// only this segment is vertical
|
|
{
|
|
Coord Y0 = K * xl + A;
|
|
|
|
if( ( xl > Xl || AlmostEqual( xl, Xl ) ) &&
|
|
( xl < Xr || AlmostEqual( xl, Xr ) ) )
|
|
{
|
|
if( ( ( Y0 > yl || AlmostEqual( Y0, yl ) ) &&
|
|
( Y0 < yr || AlmostEqual( Y0, yr ) ) ) ||
|
|
( ( Y0 > yr || AlmostEqual( Y0, yr ) ) &&
|
|
( Y0 < yl || AlmostEqual( Y0, yl ) ) ) )
|
|
// (xl, Y0) is the intersection point
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// both segments are non-vertical
|
|
|
|
if( AlmostEqual( k, K ) )
|
|
// both segments have the same inclination
|
|
{
|
|
/* if( ( AlmostEqual( A, a ) &&
|
|
(( ( xl > Xl || AlmostEqual( xl, Xl ) ) &&
|
|
( xl < Xr || AlmostEqual( xl, Xr ) ) ))) ||
|
|
( ( Xl > xl || AlmostEqual( xl, Xl ) ) &&
|
|
( Xl < xr || AlmostEqual( xr, Xl ) ) ) )
|
|
// the segments are in the same straight line
|
|
return true;*/
|
|
if( AlmostEqual( A, a ) &&
|
|
(( ( xl > Xl || AlmostEqual( xl, Xl ) ) &&
|
|
( xl < Xr || AlmostEqual( xl, Xr ) ) ) ||
|
|
( ( Xl > xl || AlmostEqual( xl, Xl ) ) &&
|
|
( Xl < xr || AlmostEqual( xr, Xl ) ) ) ))
|
|
// the segments are in the same straight line
|
|
return true;
|
|
|
|
}
|
|
else
|
|
{
|
|
Coord x0 = (A - a) / (k - K);
|
|
// y0 = x0 * k + a;
|
|
|
|
if( ( x0 > xl || AlmostEqual( x0, xl ) ) &&
|
|
( x0 < xr || AlmostEqual( x0, xr ) ) &&
|
|
( x0 > Xl || AlmostEqual( x0, Xl ) ) &&
|
|
( x0 < Xr || AlmostEqual( x0, Xr ) ) )
|
|
// the segments intersect at (x0, y0)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool HalfSegment::InnerIntersects( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
if(geoid){
|
|
cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< endl;
|
|
assert( false ); // TODO: implement spherical geometry case
|
|
}
|
|
double k = 0.0;
|
|
double a = 0.0;
|
|
double K = 0.0;
|
|
double A = 0.0;
|
|
Coord x0; //, y0; (x0, y0) is the intersection
|
|
|
|
Coord xl = lp.GetX(), yl = lp.GetY(),
|
|
xr = rp.GetX(), yr = rp.GetY();
|
|
|
|
if( xl != xr )
|
|
{
|
|
k = (yr - yl) / (xr - xl);
|
|
a = yl - k * xl;
|
|
}
|
|
|
|
Coord Xl = hs.GetLeftPoint().GetX(),
|
|
Yl = hs.GetLeftPoint().GetY(),
|
|
Xr = hs.GetRightPoint().GetX(),
|
|
Yr = hs.GetRightPoint().GetY();
|
|
|
|
if( Xl != Xr )
|
|
{
|
|
K = (Yr - Yl) / (Xr - Xl);
|
|
A = Yl - K * Xl;
|
|
}
|
|
|
|
if( xl == xr && Xl == Xr ) //both l and L are vertical lines
|
|
{
|
|
if( xl != Xl )
|
|
return false;
|
|
|
|
Coord ylow, yup, Ylow, Yup;
|
|
if( yl < yr )
|
|
{
|
|
ylow = yl;
|
|
yup = yr;
|
|
}
|
|
else
|
|
{
|
|
ylow = yr;
|
|
yup = yl;
|
|
}
|
|
|
|
if( Yl < Yr)
|
|
{
|
|
Ylow = Yl;
|
|
Yup = Yr;
|
|
}
|
|
else
|
|
{
|
|
Ylow = Yr;
|
|
Yup = Yl;
|
|
}
|
|
|
|
if( ylow >= Yup || yup <= Ylow )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
if( Xl == Xr ) //only L is vertical
|
|
{
|
|
double y0 = k * Xl + a;
|
|
Coord yy = y0;
|
|
|
|
//(Xl, y0) is the intersection of l and L
|
|
if( Xl >= xl && Xl <= xr )
|
|
{
|
|
if( ( yy > Yl && yy < Yr ) ||
|
|
( yy > Yr && yy < Yl ) )
|
|
return true;
|
|
return false;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
if( xl == xr ) //only l is vertical
|
|
{
|
|
double Y0 = K * xl + A;
|
|
Coord YY = Y0;
|
|
|
|
//(xl, Y0) is the intersection of l and L
|
|
if( xl > Xl && xl < Xr )
|
|
{
|
|
if( ( YY >= yl && YY <= yr ) ||
|
|
( YY >= yr && YY <= yl ) )
|
|
return true;
|
|
return false;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
//otherwise: both segments are non-vertical
|
|
if( k == K )
|
|
{
|
|
if( A != a ) //Parallel lines
|
|
return false;
|
|
|
|
//they are in the same straight line
|
|
if( xr <= Xl || xl >= Xr )
|
|
return false;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
x0 = (A - a) / (k - K); // y0=x0*k+a;
|
|
Coord xx = x0;
|
|
if( xx >= xl && xx <= xr && xx > Xl && xx < Xr )
|
|
return true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
// corrected modulo function ( '%' has problems with negative values)
|
|
double fmod2(const double &y, const double &x) {
|
|
return y - x*floor(y/x);
|
|
}
|
|
|
|
bool HalfSegment::Intersection( const HalfSegment& hs, Point& resp,
|
|
const Geoid* geoid /* = 0 */ ) const
|
|
{
|
|
if(geoid){ // spherical geometry not yet implemented!
|
|
assert(false);
|
|
if(!geoid->IsDefined()){
|
|
resp.SetDefined(false);
|
|
}
|
|
// see http://williams.best.vwh.net/avform.htm#Intersection
|
|
Point res(false);//Destination point (UNDEFINED if no unique intersection)
|
|
Point p1 = this->GetLeftPoint(); //1st segment's starting point
|
|
Point p1end = this->GetRightPoint(); //1st segment's ending point
|
|
Point p2 = hs.GetLeftPoint(); //2nd segment' s starting point
|
|
Point p2end = hs.GetRightPoint(); //2nd segment's ending point
|
|
if(!p1.checkGeographicCoord() || !p2.checkGeographicCoord() ||
|
|
!p1end.checkGeographicCoord() || !p2end.checkGeographicCoord() ) {
|
|
return false;
|
|
}
|
|
double lon1 = degToRad(p1.GetX());
|
|
double lat1 = degToRad(p1.GetY());
|
|
double lon2 = degToRad(p2.GetX());
|
|
double lat2 = degToRad(p2.GetY());
|
|
|
|
double brng1 = p1.Direction(p1end,false,geoid); //Initial bearing from p1
|
|
double brng2 = p2.Direction(p2end,false,geoid); //Initial bearing from p2
|
|
if( (brng1<0) || (brng2<0) ){
|
|
return false;
|
|
}
|
|
double brng13 = degToRad(brng1);
|
|
double brng23 = degToRad(brng2);
|
|
|
|
double dLat = lat2-lat1;
|
|
double dLon = lon2-lon1;
|
|
errno = 0;
|
|
double dist12 = 2*asin( sqrt( sin(dLat/2)*sin(dLat/2) +
|
|
cos(lat1)*cos(lat2)*sin(dLon/2)*sin(dLon/2) ) );
|
|
if( (errno !=0) || (dist12 <= 0) ){
|
|
return false;
|
|
}
|
|
// initial/final bearings between points
|
|
errno = 0;
|
|
double brngA = acos( ( sin(lat2) - sin(lat1)*cos(dist12) ) /
|
|
( sin(dist12)*cos(lat1) ) );
|
|
if(errno != 0){
|
|
brngA = 0; // protect against rounding
|
|
errno = 0;
|
|
}
|
|
double brngB = acos( ( sin(lat1) - sin(lat2)*cos(dist12) ) /
|
|
( sin(dist12)*cos(lat2) ) );
|
|
double brng12 = 0;
|
|
double brng21 = 0;
|
|
if (sin(lon2-lon1) > 0) {
|
|
brng12 = brngA;
|
|
brng21 = 2*M_PI - brngB;
|
|
} else {
|
|
brng12 = 2*M_PI - brngA;
|
|
brng21 = brngB;
|
|
}
|
|
|
|
double alpha1 = fmod2((brng13 - brng12 +M_PI),(2*M_PI)) - M_PI;//angle 2-1-3
|
|
double alpha2 = fmod2((brng21 - brng23 +M_PI),(2*M_PI)) - M_PI;//angle 1-2-3
|
|
|
|
if( (sin(alpha1)==0) && (sin(alpha2)==0) ){// infinite intersections
|
|
return false;
|
|
}
|
|
if( (errno != 0) || (sin(alpha1)*sin(alpha2) < 0) ){//ambiguous intersection
|
|
return false;
|
|
}
|
|
//Ed Williams takes abs of alpha1/alpha2, but seems to break calculation?
|
|
//alpha1 = fabs(alpha1);
|
|
//alpha2 = fabs(alpha2);
|
|
|
|
double alpha3 = acos( -cos(alpha1)*cos(alpha2) +
|
|
sin(alpha1)*sin(alpha2)*cos(dist12) );
|
|
double dist13 = atan2( sin(dist12)*sin(alpha1)*sin(alpha2),
|
|
cos(alpha2)+cos(alpha1)*cos(alpha3) );
|
|
double lat3 = asin( sin(lat1)*cos(dist13) +
|
|
cos(lat1)*sin(dist13)*cos(brng13) );
|
|
double dLon13 = atan2( sin(brng13)*sin(dist13)*cos(lat1),
|
|
cos(dist13)-sin(lat1)*sin(lat3) );
|
|
double lon3 = lon1+dLon13;
|
|
lon3 = fmod2((lon3+M_PI),(2*M_PI)) - M_PI; // normalise to -180..180º
|
|
res.SetDefined( (errno == 0) );
|
|
res.Set( radToDeg(lon3), radToDeg(lat3) );
|
|
return true;
|
|
} else { // euclidean geometry
|
|
double k = 0.0;
|
|
double a = 0.0;
|
|
double K = 0.0;
|
|
double A = 0.0;
|
|
|
|
if(!BoundingBox().Intersects(hs.BoundingBox())){
|
|
resp.SetDefined(false);
|
|
return false;
|
|
}
|
|
|
|
resp.SetDefined( true );
|
|
|
|
Coord xl = lp.GetX(),
|
|
yl = lp.GetY(),
|
|
xr = rp.GetX(),
|
|
yr = rp.GetY(),
|
|
Xl = hs.GetLeftPoint().GetX(),
|
|
Yl = hs.GetLeftPoint().GetY(),
|
|
Xr = hs.GetRightPoint().GetX(),
|
|
Yr = hs.GetRightPoint().GetY();
|
|
|
|
// Check for same endpoints
|
|
if (AlmostEqual(lp,hs.GetLeftPoint())){
|
|
if (!hs.Contains(rp) && !this->Contains(hs.GetRightPoint())){
|
|
resp = lp;
|
|
return true;
|
|
} else {
|
|
return false; //overlapping halfsegments
|
|
}
|
|
} else if (AlmostEqual(rp,hs.GetLeftPoint())){
|
|
if (!hs.Contains(lp) && !this->Contains(hs.GetRightPoint())){
|
|
resp = rp;
|
|
return true;
|
|
} else {
|
|
return false; //overlapping halfsegments
|
|
}
|
|
} else if (AlmostEqual(lp,hs.GetRightPoint())){
|
|
if (!hs.Contains(rp) && !this->Contains(hs.GetLeftPoint())){
|
|
resp = lp;
|
|
return true;
|
|
} else {
|
|
return false; //overlapping halfsegments
|
|
}
|
|
} else if (AlmostEqual(rp,hs.GetRightPoint())){
|
|
if (!hs.Contains(lp) && !this->Contains(hs.GetLeftPoint())){
|
|
resp = rp;
|
|
return true;
|
|
} else {
|
|
return false; //overlapping halfsegments
|
|
}
|
|
}
|
|
|
|
if( AlmostEqual( xl, xr ) &&
|
|
AlmostEqual( Xl, Xr ) ){
|
|
// both segments are vertical
|
|
if( AlmostEqual( yr, Yl ) ){
|
|
resp.Set( xl, yr );
|
|
return true;
|
|
}
|
|
if( AlmostEqual( yl, Yr ) ) {
|
|
resp.Set( xl, yl );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if( !AlmostEqual( xl, xr ) ) { // this segment is not vertical
|
|
k = (yr - yl) / (xr - xl);
|
|
a = yl - k * xl;
|
|
}
|
|
|
|
if( !AlmostEqual( Xl, Xr ) ){ // hs is not vertical
|
|
K = (Yr - Yl) / (Xr - Xl);
|
|
A = Yl - K * Xl;
|
|
}
|
|
|
|
if( AlmostEqual( Xl, Xr ) ) {//only hs is vertical
|
|
Coord y0 = k * Xl + a;
|
|
|
|
if( ( Xl > xl || AlmostEqual( Xl, xl ) ) &&
|
|
( Xl < xr || AlmostEqual( Xl, xr ) ) ){
|
|
if( ( ( y0 > Yl || AlmostEqual( y0, Yl ) ) &&
|
|
( y0 < Yr || AlmostEqual( y0, Yr ) ) ) ||
|
|
( ( y0 > Yr || AlmostEqual( y0, Yr ) ) &&
|
|
( y0 < Yl || AlmostEqual( y0, Yl ) ) ) ){
|
|
// (Xl, y0) is the intersection point
|
|
resp.Set( Xl, y0 );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if( AlmostEqual( xl, xr ) ){
|
|
// only this segment is vertical
|
|
Coord Y0 = K * xl + A;
|
|
|
|
if( ( xl > Xl || AlmostEqual( xl, Xl ) ) &&
|
|
( xl < Xr || AlmostEqual( xl, Xr ) ) ){
|
|
if( ( ( Y0 > yl || AlmostEqual( Y0, yl ) ) &&
|
|
( Y0 < yr || AlmostEqual( Y0, yr ) ) ) ||
|
|
( ( Y0 > yr || AlmostEqual( Y0, yr ) ) &&
|
|
( Y0 < yl || AlmostEqual( Y0, yl ) ) ) ){
|
|
// (xl, Y0) is the intersection point
|
|
resp.Set( xl, Y0 );
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// both segments are non-vertical
|
|
|
|
if( AlmostEqual( k, K ) ){
|
|
// both segments have the same inclination
|
|
if( AlmostEqual( rp, hs.lp ) ){
|
|
resp = rp;
|
|
return true;
|
|
}
|
|
if( AlmostEqual( lp, hs.rp ) ){
|
|
resp = lp;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Coord x0 = (A - a) / (k - K);
|
|
Coord y0 = x0 * k + a;
|
|
|
|
if( ( x0 > xl || AlmostEqual( x0, xl ) ) &&
|
|
( x0 < xr || AlmostEqual( x0, xr ) ) &&
|
|
( x0 > Xl || AlmostEqual( x0, Xl ) ) &&
|
|
( x0 < Xr || AlmostEqual( x0, Xr ) ) ){
|
|
// the segments intersect at (x0, y0)
|
|
resp.Set( x0, y0 );
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
bool HalfSegment::Intersection( const HalfSegment& hs,
|
|
HalfSegment& reshs,
|
|
const Geoid* geoid /* = 0 */) const
|
|
{
|
|
if(geoid) {
|
|
Point p1s = GetLeftPoint();
|
|
Point p1e = GetRightPoint();
|
|
Point p2s = hs.GetLeftPoint();
|
|
Point p2e = hs.GetRightPoint();
|
|
bool ok = p1s.checkGeographicCoord() && p1e.checkGeographicCoord() &&
|
|
p2s.checkGeographicCoord() && p2e.checkGeographicCoord() &&
|
|
AlmostEqual(Distance( hs, geoid ),0.0);
|
|
if(!ok){ // error OR no intersection (distance >=0)
|
|
return false;
|
|
}
|
|
Point intersectpoint(false);
|
|
if( Intersection( hs, intersectpoint, geoid) && intersectpoint.IsDefined()){
|
|
return false; // intersection is a point;
|
|
};
|
|
if( (p1s.GetX() > 0) && (p1e.GetX() <0) ) { //crosses the +/-180° meridean!
|
|
Point tmp(p1s);
|
|
p1s=p1e;
|
|
p1e=tmp; // swap start/endpoint for *this
|
|
}
|
|
if( (p2s.GetX() >0) && (p2e.GetX() <0) ) { // crosses the +/-180° meridean!
|
|
Point tmp(p2s);
|
|
p2s=p2e;
|
|
p2e=tmp; // swap start/endpoint for hs
|
|
}
|
|
// no special case: crossing the equator (happens once at most!)
|
|
|
|
Point leftborder(true);
|
|
if(AlmostEqual(hs.Distance(p1s, geoid), 0.0)) {// LeftPoint on hs?
|
|
leftborder = p1s;
|
|
} else if(AlmostEqual(Distance(p2s, geoid), 0.0)) {// hs.LeftPoint on *this?
|
|
leftborder = p2s;
|
|
} else {
|
|
return false;
|
|
}
|
|
Point rightborder(true);
|
|
if(AlmostEqual(hs.Distance(p1e, geoid), 0.0)) {// RightPoint on hs?
|
|
leftborder = p1e;
|
|
} else if(AlmostEqual(Distance(p2e, geoid), 0.0)){// hs.RightPoint on *this?
|
|
leftborder = p2e;
|
|
} else {
|
|
return false;
|
|
}
|
|
reshs.Set(true, leftborder, rightborder);
|
|
return true;
|
|
} // else: euclidean geometry
|
|
double k, a, K, A;
|
|
|
|
if( !BoundingBox().Intersects( hs.BoundingBox() ) )
|
|
return false;
|
|
|
|
if( AlmostEqual( *this, hs ) )
|
|
{
|
|
reshs = hs;
|
|
return true;
|
|
}
|
|
|
|
Coord xl = lp.GetX(),
|
|
yl = lp.GetY(),
|
|
xr = rp.GetX(),
|
|
yr = rp.GetY(),
|
|
Xl = hs.GetLeftPoint().GetX(),
|
|
Yl = hs.GetLeftPoint().GetY(),
|
|
Xr = hs.GetRightPoint().GetX(),
|
|
Yr = hs.GetRightPoint().GetY();
|
|
|
|
if( AlmostEqual( xl, xr ) &&
|
|
AlmostEqual( Xl, Xr ) )
|
|
//both l and L are vertical lines
|
|
{
|
|
Coord ylow, yup, Ylow, Yup;
|
|
if( yl < yr )
|
|
{
|
|
ylow = yl;
|
|
yup = yr;
|
|
}
|
|
else
|
|
{
|
|
ylow = yr;
|
|
yup = yl;
|
|
}
|
|
|
|
if( Yl < Yr )
|
|
{
|
|
Ylow = Yl;
|
|
Yup = Yr;
|
|
}
|
|
else
|
|
{
|
|
Ylow = Yr;
|
|
Yup = Yl;
|
|
}
|
|
|
|
if( yup > Ylow && ylow < Yup &&
|
|
!AlmostEqual( yup, Ylow ) &&
|
|
!AlmostEqual( ylow, Yup ) )
|
|
{
|
|
Point p1(true), p2(true);
|
|
if( ylow > Ylow )
|
|
p1.Set( xl, ylow );
|
|
else
|
|
p1.Set( xl, Ylow );
|
|
|
|
if( yup < Yup )
|
|
p2.Set( xl, yup );
|
|
else
|
|
p2.Set( xl, Yup );
|
|
|
|
reshs.Set( true, p1, p2 );
|
|
return true;
|
|
}
|
|
else return false;
|
|
}
|
|
|
|
if( AlmostEqual( Xl, Xr ) ||
|
|
AlmostEqual( xl, xr ) )
|
|
//only L or l is vertical
|
|
return false;
|
|
|
|
//otherwise: both *this and *arg are non-vertical lines
|
|
|
|
k = (yr - yl) / (xr - xl);
|
|
a = yl - k * xl;
|
|
|
|
K = (Yr - Yl) / (Xr - Xl);
|
|
A = Yl - K * Xl;
|
|
|
|
if( AlmostEqual( k, K ) &&
|
|
AlmostEqual( A, a ) )
|
|
{
|
|
if( xr > Xl && xl < Xr &&
|
|
!AlmostEqual( xr, Xl ) &&
|
|
!AlmostEqual( xl, Xr ) )
|
|
{
|
|
Point p1(true), p2(true);
|
|
if( xl > Xl )
|
|
p1.Set( xl, yl );
|
|
else
|
|
p1.Set( Xl, Yl );
|
|
|
|
if( xr < Xr )
|
|
p2.Set( xr, yr );
|
|
else
|
|
p2.Set( Xr, Yr );
|
|
|
|
reshs.Set( true, p1, p2 );
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool HalfSegment::Crosses( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
if(geoid){
|
|
cerr << __PRETTY_FUNCTION__ << ": Spherial geometry not implemented."
|
|
<< endl;
|
|
assert( false ); // TODO: implement spherical case.
|
|
}
|
|
double k = 0.0;
|
|
double a = 0.0;
|
|
double K = 0.0;
|
|
double A = 0.0;
|
|
|
|
if( !BoundingBox().Intersects( hs.BoundingBox(),geoid ) ){
|
|
return false;
|
|
}
|
|
Coord xl = lp.GetX(),
|
|
yl = lp.GetY(),
|
|
xr = rp.GetX(),
|
|
yr = rp.GetY(),
|
|
Xl = hs.GetLeftPoint().GetX(),
|
|
Yl = hs.GetLeftPoint().GetY(),
|
|
Xr = hs.GetRightPoint().GetX(),
|
|
Yr = hs.GetRightPoint().GetY();
|
|
|
|
if( AlmostEqual( xl, xr ) &&
|
|
AlmostEqual( Xl, Xr ) )
|
|
// both segments are vertical
|
|
return false;
|
|
|
|
if( !AlmostEqual( xl, xr ) )
|
|
// this segment is not vertical
|
|
{
|
|
k = (yr - yl) / (xr - xl);
|
|
a = yl - k * xl;
|
|
}
|
|
|
|
if( !AlmostEqual( Xl, Xr ) )
|
|
// hs is not vertical
|
|
{
|
|
K = (Yr - Yl) / (Xr - Xl);
|
|
A = Yl - K * Xl;
|
|
}
|
|
|
|
if( AlmostEqual( Xl, Xr ) )
|
|
//only hs is vertical
|
|
{
|
|
double y0 = k * Xl + a;
|
|
|
|
if( Xl > xl && !AlmostEqual( Xl, xl ) &&
|
|
Xl < xr && !AlmostEqual( Xl, xr ) )
|
|
{
|
|
if( ( y0 > Yl && !AlmostEqual( y0, Yl ) &&
|
|
y0 < Yr && !AlmostEqual( y0, Yr ) ) ||
|
|
( y0 > Yr && !AlmostEqual( y0, Yr ) &&
|
|
y0 < Yl && !AlmostEqual( y0, Yl ) ) )
|
|
// (Xl, y0) is the intersection point
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
if( AlmostEqual( xl, xr ) )
|
|
// only this segment is vertical
|
|
{
|
|
double Y0 = K * xl + A;
|
|
|
|
if( ( xl > Xl && !AlmostEqual( xl, Xl ) ) &&
|
|
( xl < Xr && !AlmostEqual( xl, Xr ) ) )
|
|
{
|
|
if( ( Y0 > yl && !AlmostEqual( Y0, yl ) &&
|
|
Y0 < yr && !AlmostEqual( Y0, yr ) ) ||
|
|
( Y0 > yr && !AlmostEqual( Y0, yr ) &&
|
|
Y0 < yl && !AlmostEqual( Y0, yl ) ) )
|
|
// (xl, Y0) is the intersection point
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// both segments are non-vertical
|
|
|
|
if( AlmostEqual( k, K ) )
|
|
// both segments have the same inclination
|
|
return false;
|
|
|
|
double x0 = (A - a) / (k - K);
|
|
// y0 = x0 * k + a;
|
|
|
|
if( x0 > xl && !AlmostEqual( x0, xl ) &&
|
|
x0 < xr && !AlmostEqual( x0, xr ) &&
|
|
x0 > Xl && !AlmostEqual( x0, Xl ) &&
|
|
x0 < Xr && !AlmostEqual( x0, Xr ) )
|
|
// the segments intersect at (x0, y0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool HalfSegment::Inside( const HalfSegment& hs,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
return hs.Contains( GetLeftPoint(),geoid ) &&
|
|
hs.Contains( GetRightPoint(),geoid );
|
|
}
|
|
|
|
bool HalfSegment::Contains( const Point& p, const Geoid* geoid/*=0*/ ) const{
|
|
if( !p.IsDefined() || (geoid && !geoid->IsDefined()) ) {
|
|
assert( p.IsDefined() );
|
|
return false;
|
|
}
|
|
if(geoid){
|
|
cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< endl;
|
|
assert(!geoid);
|
|
}
|
|
if( AlmostEqual( p, lp ) ||
|
|
AlmostEqual( p, rp ) ){
|
|
return true;
|
|
}
|
|
Coord xl = lp.GetX(), yl = lp.GetY(),
|
|
xr = rp.GetX(), yr = rp.GetY(),
|
|
x = p.GetX(), y = p.GetY();
|
|
|
|
if( xl != xr && xl != x )
|
|
// the segment is not vertical
|
|
{
|
|
double k1 = (y - yl) / (x - xl),
|
|
k2 = (yr - yl) / (xr - xl);
|
|
|
|
if( AlmostEqual( k1, k2 ) )
|
|
{
|
|
if( ( x > xl || AlmostEqual( x, xl ) ) &&
|
|
( x < xr || AlmostEqual( x, xr ) ) )
|
|
// we check only this possibility because lp < rp and
|
|
// therefore, in this case, xl < xr
|
|
return true;
|
|
}
|
|
}
|
|
else if( AlmostEqual( xl, xr ) &&
|
|
AlmostEqual( xl, x ) )
|
|
// the segment is vertical and the point is also in the
|
|
// same x-position. In this case we just have to check
|
|
// whether the point is inside the y-interval
|
|
{
|
|
if( (( y > yl || AlmostEqual( y, yl ) ) &&
|
|
( y < yr || AlmostEqual( y, yr ) )) ||
|
|
(( y < yl || AlmostEqual( y, yl ) ) &&
|
|
( y > yr || AlmostEqual( y, yr ) ) ))
|
|
// Here we check both possibilities because we do not
|
|
// know wheter yl < yr, given that we used the
|
|
// AlmostEqual function in the previous condition
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
double HalfSegment::Distance( const Point& p,
|
|
const Geoid* geoid /* = 0 */) const
|
|
{
|
|
assert( p.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(!geoid){ // euclidean geometry
|
|
Coord xl = GetLeftPoint().GetX(),
|
|
yl = GetLeftPoint().GetY(),
|
|
xr = GetRightPoint().GetX(),
|
|
yr = GetRightPoint().GetY(),
|
|
X = p.GetX(),
|
|
Y = p.GetY();
|
|
|
|
double result, auxresult;
|
|
|
|
if( xl == xr || yl == yr )
|
|
{
|
|
if( xl == xr) //hs is vertical
|
|
{
|
|
if( (yl <= Y && Y <= yr) || (yr <= Y && Y <= yl) )
|
|
result = fabs( X - xl );
|
|
else
|
|
{
|
|
result = p.Distance( GetLeftPoint() );
|
|
auxresult = p.Distance( GetRightPoint() );
|
|
if( result > auxresult )
|
|
result = auxresult;
|
|
}
|
|
}
|
|
else //hs is horizontal line: (yl==yr)
|
|
{
|
|
if( xl <= X && X <= xr )
|
|
result = fabs( Y - yl );
|
|
else
|
|
{
|
|
result = p.Distance( GetLeftPoint() );
|
|
auxresult = p.Distance( GetRightPoint() );
|
|
if( result > auxresult )
|
|
result = auxresult;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
double k = (yr - yl) / (xr - xl),
|
|
a = yl - k * xl,
|
|
xx = (k * (Y - a) + X) / (k * k + 1),
|
|
yy = k * xx + a;
|
|
Coord XX = xx,
|
|
YY = yy;
|
|
Point PP( true, XX, YY );
|
|
if( xl <= XX && XX <= xr )
|
|
result = p.Distance( PP );
|
|
else
|
|
{
|
|
result = p.Distance( GetLeftPoint() );
|
|
auxresult = p.Distance( GetRightPoint() );
|
|
if( result > auxresult )
|
|
result = auxresult;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
Point p1 = GetLeftPoint();
|
|
Point p2 = GetRightPoint();
|
|
return geodist::getDist(p1.GetX(), p1.GetY(),
|
|
p2.GetX(), p2.GetY(),
|
|
p.GetX(), p.GetY(),
|
|
geoid);
|
|
|
|
/*
|
|
// else: spherical geometry
|
|
bool ok = true;
|
|
double d13 = p.DistanceOrthodrome(GetLeftPoint(),*geoid,ok);
|
|
// initial bearing from LeftPoint to p
|
|
double theta13 = GetLeftPoint().Direction(p,false,geoid); // in deg
|
|
// initial bearing from LeftPoint to RightPoint
|
|
double theta12 = GetLeftPoint().Direction(GetRightPoint(),false,geoid);//[deg]
|
|
double R = geoid->getR();
|
|
errno = 0;
|
|
double res = asin(sin(d13/R)*sin((theta13-theta12)*M_PI/180.0))*R;
|
|
ok = ok && (errno==0) && (theta13>=0.0) && (theta12>=0.0);
|
|
if(ok){
|
|
return res;
|
|
} // else: error
|
|
return -666.666;
|
|
*/
|
|
}
|
|
|
|
double HalfSegment::Distance( const HalfSegment& hs,
|
|
const Geoid* geoid /* = 0 */) const
|
|
{
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(!geoid){ // euclidean geometry
|
|
if( Intersects( hs ) ){
|
|
return 0.0;
|
|
}
|
|
double d1 = MIN( Distance( hs.GetLeftPoint(), geoid ),
|
|
Distance( hs.GetRightPoint(), geoid ) );
|
|
|
|
double d2 = MIN( hs.Distance(this->GetLeftPoint(), geoid),
|
|
hs.Distance(this->GetRightPoint(), geoid));
|
|
return MIN(d1,d2);
|
|
} // else: spherical geometry
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented." <<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
return -1.0;
|
|
}
|
|
|
|
|
|
double HalfSegment::Distance(const Rectangle<2>& rect,
|
|
const Geoid* geoid/*=0*/) const{
|
|
|
|
assert( rect.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
if(rect.Contains(lp.BoundingBox()) ||
|
|
rect.Contains(rp.BoundingBox()) ){
|
|
return 0.0;
|
|
}
|
|
// both endpoints are outside the rectangle
|
|
double x0(rect.MinD(0));
|
|
double y0(rect.MinD(1));
|
|
double x1(rect.MaxD(0));
|
|
double y1(rect.MaxD(1));
|
|
Point p0(true,x0,y0);
|
|
Point p1(true,x1,y0);
|
|
Point p2(true,x1,y1);
|
|
Point p3(true,x0,y1);
|
|
|
|
double dist;
|
|
HalfSegment hs;
|
|
if(AlmostEqual(p0,p1)){
|
|
dist = this->Distance(p0,geoid);
|
|
} else {
|
|
hs.Set(true,p0,p1);
|
|
dist = this->Distance(hs,geoid);
|
|
}
|
|
if(AlmostEqual(dist,0)){
|
|
return 0.0;
|
|
}
|
|
if(AlmostEqual(p1,p2)){
|
|
dist = MIN( dist, this->Distance(p1,geoid));
|
|
} else {
|
|
hs.Set(true,p1,p2);
|
|
dist = MIN( dist, this->Distance(hs,geoid));
|
|
}
|
|
if(AlmostEqual(dist,0)){
|
|
return 0.0;
|
|
}
|
|
|
|
if(AlmostEqual(p2,p3)){
|
|
dist = MIN(dist, this->Distance(p2,geoid));
|
|
} else {
|
|
hs.Set(true,p2,p3);
|
|
dist = MIN( dist, this->Distance(hs,geoid));
|
|
}
|
|
if(AlmostEqual(dist,0)){
|
|
return 0.0;
|
|
}
|
|
if(AlmostEqual(p3,p0)){
|
|
dist = MIN(dist, this->Distance(p3,geoid));
|
|
} else {
|
|
hs.Set(true,p3,p0);
|
|
dist = MIN( dist, this->Distance(hs,geoid));
|
|
}
|
|
if(AlmostEqual(dist,0)){
|
|
return 0.0;
|
|
}
|
|
return dist;
|
|
}
|
|
|
|
|
|
|
|
bool HalfSegment::Intersects(const Rectangle<2>& rect,
|
|
const Geoid* geoid/*=0*/) const{
|
|
if(!rect.IsDefined()){
|
|
return false;
|
|
}
|
|
if(rect.IsEmpty()){
|
|
return false;
|
|
}
|
|
if(lp.Intersects(rect,geoid)) return true;
|
|
if(rp.Intersects(rect,geoid)) return true;
|
|
// check for intersection of the 4
|
|
// segments of the rectangle
|
|
Point p1(true, rect.MinD(0), rect.MinD(1));
|
|
Point p2(true, rect.MaxD(0), rect.MinD(1));
|
|
Point p3(true, rect.MaxD(0), rect.MaxD(1));
|
|
Point p4(true, rect.MinD(0), rect.MaxD(1));
|
|
|
|
HalfSegment hs1(true,p1,p2);
|
|
if(Intersects(hs1)) return true;
|
|
HalfSegment hs2(true,p2,p3);
|
|
if(Intersects(hs2)) return true;
|
|
HalfSegment hs3(true,p3,p4);
|
|
if(Intersects(hs3)) return true;
|
|
HalfSegment hs4(true,p4,p1);
|
|
if(Intersects(hs4)) return true;
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
double HalfSegment::MaxDistance(const Rectangle<2>& rect,
|
|
const Geoid* geoid /*=0*/) const{
|
|
|
|
assert( rect.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
cout << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<<endl;
|
|
assert(false); // TODO: Implement spherical geometry case.
|
|
}
|
|
// both endpoints are outside the rectangle
|
|
double x0(rect.MinD(0));
|
|
double y0(rect.MinD(1));
|
|
double x1(rect.MaxD(0));
|
|
double y1(rect.MaxD(1));
|
|
Point p0(true,x0,y0);
|
|
Point p1(true,x1,y0);
|
|
Point p2(true,x1,y1);
|
|
Point p3(true,x0,y1);
|
|
double d1 = lp.Distance(p0,geoid);
|
|
double d2 = lp.Distance(p1,geoid);
|
|
double d3 = lp.Distance(p2,geoid);
|
|
double d4 = lp.Distance(p3,geoid);
|
|
double dist1 = MAX(MAX(d1,d2),MAX(d3,d4));
|
|
d1 = rp.Distance(p0,geoid);
|
|
d2 = rp.Distance(p1,geoid);
|
|
d3 = rp.Distance(p2,geoid);
|
|
d4 = rp.Distance(p3,geoid);
|
|
double dist2 = MAX(MAX(d1,d2),MAX(d3,d4));
|
|
double dist = MAX(dist1,dist2);
|
|
return dist;
|
|
}
|
|
|
|
|
|
bool HalfSegment::RayAbove( const Point& p, double &abovey0 ) const
|
|
{
|
|
assert( p.IsDefined() );
|
|
|
|
const Coord& x = p.GetX(), y = p.GetY(),
|
|
xl = GetLeftPoint().GetX(),
|
|
yl = GetLeftPoint().GetY(),
|
|
xr = GetRightPoint().GetX(),
|
|
yr = GetRightPoint().GetY();
|
|
|
|
if (xl!=xr)
|
|
{
|
|
if( x == xl && yl > y )
|
|
{
|
|
abovey0 = yl;
|
|
return true;
|
|
}
|
|
else if( xl < x && x <= xr )
|
|
{
|
|
double k = (yr - yl) / (xr - xl),
|
|
a = (yl - k * xl),
|
|
y0 = k * x + a;
|
|
Coord yy = y0;
|
|
if( yy > y )
|
|
{
|
|
abovey0 = y0;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
typedef unsigned int outcode;
|
|
enum { TOP = 0x1, BOTTOM = 0x2, RIGHT = 0x4, LEFT = 0x8 };
|
|
|
|
outcode CompOutCode( double x, double y, double xmin,
|
|
double xmax, double ymin, double ymax)
|
|
{
|
|
outcode code = 0;
|
|
if (y > ymax)
|
|
code |=TOP;
|
|
else
|
|
if (y < ymin)
|
|
code |= BOTTOM;
|
|
if ( x > xmax)
|
|
code |= RIGHT;
|
|
else
|
|
if ( x < xmin)
|
|
code |= LEFT;
|
|
return code;
|
|
}
|
|
|
|
void HalfSegment::CohenSutherlandLineClipping( const Rectangle<2> &window,
|
|
double &x0, double &y0,
|
|
double &x1, double &y1,
|
|
bool &accept,
|
|
const Geoid* geoid/*=0*/ ) const
|
|
{
|
|
assert( window.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
|
|
if(geoid){
|
|
cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< endl;
|
|
assert( false ); // TODO: implement spherical geometry case
|
|
}
|
|
|
|
// Outcodes for P0, P1, and whatever point lies outside the clip rectangle*/
|
|
outcode outcode0, outcode1, outcodeOut;
|
|
double xmin = window.MinD(0) , xmax = window.MaxD(0),
|
|
ymin = window.MinD(1), ymax = window.MaxD(1);
|
|
bool done = false;
|
|
accept = false;
|
|
|
|
outcode0 = CompOutCode( x0, y0, xmin, xmax, ymin, ymax);
|
|
outcode1 = CompOutCode( x1, y1, xmin, xmax, ymin, ymax);
|
|
|
|
do{
|
|
if ( !(outcode0 | outcode1) ) {
|
|
//"Trivial accept and exit"<<endl;
|
|
accept = true;
|
|
done = true;
|
|
} else if (outcode0 & outcode1){
|
|
done = true;
|
|
//"Logical and is true, so trivial reject and exit"<<endl;
|
|
} else {
|
|
//Failed both tests, so calculate the line segment to clip:
|
|
//from an outside point to an instersection with clip edge.
|
|
double x,y;
|
|
// At least one endpoint is outside the clip rectangle; pick it.
|
|
outcodeOut = outcode0 ? outcode0 : outcode1;
|
|
//Now find the intersection point;
|
|
//use formulas y = y0 + slope * (x - x0), x = x0 + (1 /slope) * (y-y0).
|
|
|
|
if (outcodeOut & TOP){ //Divide the line at top of clip rectangle
|
|
x = x0 + (x1 - x0) * (ymax - y0) / (y1 - y0);
|
|
y = ymax;
|
|
}else if (outcodeOut & BOTTOM){
|
|
//Divide line at bottom edge of clip rectangle
|
|
x = x0 + (x1 - x0) * (ymin - y0) / (y1 - y0);
|
|
y = ymin;
|
|
}else if (outcodeOut & RIGHT){
|
|
//Divide line at right edge of clip rectangle
|
|
y = y0 + (y1 - y0) * (xmax - x0) / (x1 - x0);
|
|
x = xmax;
|
|
} else {// divide lene at left edge of clip rectangle
|
|
y = y0 + (y1 - y0) * (xmin - x0) / (x1 - x0);
|
|
x = xmin;
|
|
}
|
|
|
|
//Now we move outside point to intersection point to clip
|
|
//and get ready for next pass
|
|
if (outcodeOut == outcode0){
|
|
x0 = x;
|
|
y0 = y;
|
|
outcode0 = CompOutCode(x0, y0, xmin, xmax, ymin, ymax);
|
|
} else {
|
|
x1 = x;
|
|
y1 = y;
|
|
outcode1 = CompOutCode(x1, y1, xmin, xmax, ymin, ymax);
|
|
}
|
|
}
|
|
}
|
|
while( done == false );
|
|
}
|
|
|
|
void HalfSegment::WindowClippingIn( const Rectangle<2> &window,
|
|
HalfSegment &hsInside, bool &inside,
|
|
bool &isIntersectionPoint,
|
|
Point &intersectionPoint,
|
|
const Geoid* geoid/*=0*/) const
|
|
{
|
|
if( !window.IsDefined() ) {
|
|
intersectionPoint.SetDefined( false );
|
|
assert( window.IsDefined() );
|
|
}
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
cerr << __PRETTY_FUNCTION__ << ": Spherical geometry not implemented."
|
|
<< endl;
|
|
assert( false ); // TODO: implement spherical geometry case
|
|
}
|
|
|
|
double x0 = GetLeftPoint().GetX(),
|
|
y0 = GetLeftPoint().GetY(),
|
|
x1 = GetRightPoint().GetX(),
|
|
y1 = GetRightPoint().GetY();
|
|
|
|
CohenSutherlandLineClipping(window, x0, y0, x1, y1, inside);
|
|
isIntersectionPoint=false;
|
|
intersectionPoint.SetDefined( false );
|
|
|
|
if (inside)
|
|
{
|
|
Point lp( true, x0, y0 ), rp(true, x1, y1 );
|
|
|
|
if (lp==rp)
|
|
{
|
|
intersectionPoint.SetDefined( true );
|
|
isIntersectionPoint = true;
|
|
intersectionPoint=lp;
|
|
}
|
|
else
|
|
{
|
|
AttrType attr=this->GetAttr();
|
|
hsInside.Set(true, rp, lp);
|
|
hsInside.SetAttr(attr);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
6.2 List Representation
|
|
|
|
The list representation of a HalfSegment is
|
|
|
|
---- ( bool (lp rp))
|
|
----
|
|
|
|
where the bool value indicate whether the dominating point is the left point.
|
|
|
|
6.3 ~In~ and ~Out~ Functions
|
|
|
|
*/
|
|
ListExpr
|
|
OutHalfSegment( ListExpr typeInfo, Word value )
|
|
{
|
|
HalfSegment* hs;
|
|
hs = (HalfSegment*)(value.addr);
|
|
|
|
Point lp = hs->GetLeftPoint(),
|
|
rp = hs->GetRightPoint();
|
|
|
|
return nl->TwoElemList(
|
|
nl-> BoolAtom(hs->IsLeftDomPoint() ),
|
|
nl->TwoElemList(
|
|
OutPoint( nl->TheEmptyList(), SetWord( &lp ) ),
|
|
OutPoint( nl->TheEmptyList(), SetWord( &rp) ) ) );
|
|
}
|
|
|
|
Word
|
|
InHalfSegment( const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
if ( nl->ListLength( instance ) == 2 )
|
|
{
|
|
ListExpr first = nl->First(instance),
|
|
second = nl->Second(instance),
|
|
firstP = nl->TheEmptyList(),
|
|
secondP = nl->TheEmptyList();
|
|
bool ldp;
|
|
|
|
if( nl->IsAtom(first) && nl->AtomType(first) == BoolType )
|
|
{
|
|
ldp = nl->BoolValue(first);
|
|
|
|
if( nl->ListLength(second) == 2 )
|
|
{
|
|
firstP = nl->First(second);
|
|
secondP = nl->Second(second);
|
|
}
|
|
|
|
correct=true;
|
|
Point *lp = (Point*)InPoint(nl->TheEmptyList(),
|
|
firstP, 0, errorInfo, correct ).addr;
|
|
if( correct && lp->IsDefined() )
|
|
{
|
|
Point *rp = (Point*)InPoint(nl->TheEmptyList(),
|
|
secondP, 0, errorInfo, correct ).addr;
|
|
if( correct && rp->IsDefined() && *lp != *rp )
|
|
{
|
|
HalfSegment *hs = new HalfSegment( ldp, *lp, *rp );
|
|
delete lp;
|
|
delete rp;
|
|
return SetWord( hs );
|
|
}
|
|
delete rp;
|
|
}
|
|
delete lp;
|
|
}
|
|
}
|
|
|
|
correct = false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
|
|
/*
|
|
~getNext~
|
|
|
|
auxiliary function for Line::Transform
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
int getNext(const Array<HalfSegment>* hss, int pos, const char* usage){
|
|
HalfSegment hs;
|
|
hss->Get(pos,hs);
|
|
pos = hs.attr.partnerno;
|
|
hss->Get(pos,hs);
|
|
Point dp = hs.GetDomPoint();
|
|
int res = -1;
|
|
// go backwards from pos, store a candidate as res
|
|
int pos1 = pos-1;
|
|
bool done = false;
|
|
// search left of pos
|
|
while( (pos1>=0) && ! done){
|
|
hss->Get(pos1,hs);
|
|
Point dp1 = hs.GetDomPoint();
|
|
if(!AlmostEqual(dp,dp1)){
|
|
done = true;
|
|
} else {
|
|
if(usage[pos1] == 3){
|
|
return pos1;
|
|
} else if(usage[pos1]==0){
|
|
res = pos1;
|
|
}
|
|
pos1--;
|
|
}
|
|
}
|
|
// search right of pos
|
|
pos1 = pos+1;
|
|
done = false;
|
|
while((pos1<hss->Size()) && ! done){
|
|
hss->Get(pos1,hs);
|
|
Point dp1 = hs.GetDomPoint();
|
|
if(!AlmostEqual(dp,dp1)){
|
|
done = true;
|
|
} else {
|
|
if(usage[pos1] == 3){
|
|
return pos1;
|
|
} else if(usage[pos1]==0){
|
|
res = pos1;
|
|
}
|
|
pos1++;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
~markUsage~
|
|
|
|
This function marks all HalfSgements of a line with its usage. This means:
|
|
0 : Part of an unique cycle
|
|
1 : not Part of a cycle
|
|
2 : Part of an ambiguous cycle
|
|
|
|
|
|
*/
|
|
template<template<typename T>class Array>
|
|
void markUsage( LineT<Array>* line, char* usage, char* critical){
|
|
markUsage( (Array<HalfSegment>*) line->GetFLOB(0),usage, critical);
|
|
}
|
|
|
|
|
|
template<template<typename T>class Array>
|
|
void markUsage(const Array<HalfSegment>* hss, char* usage, char* critical ){
|
|
// step 1: mark halfsegments not belonging to a cycle
|
|
memset(usage,0,hss->Size());
|
|
// meaning of elements of usage
|
|
// 0 : not used => part of a cycle
|
|
// 1 : not part of a cycle
|
|
// 2 : part of a cycle
|
|
// 3 : part of the current path (will be set to 1 or 2 later)
|
|
// 4 : partner of a segment in the current path
|
|
memset(critical,0,hss->Size());
|
|
// 1.1 mark critical points
|
|
int count = 0;
|
|
HalfSegment hs;
|
|
Point lastPoint;
|
|
for(int i=0;i<hss->Size();i++){
|
|
hss->Get(i,hs);
|
|
Point currentPoint = hs.GetDomPoint();
|
|
if(i==0){
|
|
lastPoint = currentPoint;
|
|
count = 1;
|
|
} else {
|
|
if(AlmostEqual(lastPoint,currentPoint)){
|
|
count++;
|
|
} else {
|
|
if(count != 2){
|
|
for(int r=1;r<=count-1;r++){
|
|
critical[i-r] = 1;
|
|
}
|
|
}
|
|
lastPoint = currentPoint;
|
|
count = 1;
|
|
}
|
|
}
|
|
}
|
|
if(count != 2){
|
|
for(int r=1;r<=count;r++){
|
|
critical[hss->Size()-r] = 1;
|
|
}
|
|
}
|
|
// 1.2 mark segments
|
|
for(int i=0;i<hss->Size();i++) {
|
|
if(usage[i]==0 ){
|
|
// not used, but critical
|
|
hss->Get(i,hs);
|
|
vector<int> path;
|
|
usage[i] = 3; // part of current path
|
|
usage[hs.attr.partnerno] = 4;
|
|
// extend path
|
|
int pos = i;
|
|
path.push_back(pos);
|
|
int next = getNext(hss,pos,usage);
|
|
while(!path.empty()) {
|
|
if(next < 0){
|
|
// no extension found => mark segments in path as non-cycle
|
|
// go back until the next critical point is reachedA
|
|
bool done = path.empty();
|
|
while(!done){
|
|
if(path.empty()){
|
|
done = true;
|
|
} else {
|
|
pos = path.back();
|
|
path.pop_back();
|
|
usage[pos] = 1;
|
|
hss->Get(pos,hs);
|
|
usage[hs.attr.partnerno] = 1;
|
|
|
|
if(critical[pos]){
|
|
done = true;
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
if(!path.empty()){
|
|
pos = path.back();
|
|
next = getNext(hss,pos,usage);
|
|
}
|
|
} else {
|
|
if(usage[next] == 3){
|
|
// (sub) path found
|
|
int p = path.back();
|
|
path.pop_back();
|
|
while(p!=next && !path.empty()){
|
|
usage[p] = 2;
|
|
hss->Get(p,hs);
|
|
usage[hs.attr.partnerno] = 2;
|
|
p = path.back();
|
|
path.pop_back();
|
|
}
|
|
usage[p] = 2;
|
|
hss->Get(p,hs);
|
|
usage[hs.attr.partnerno] = 2;
|
|
if(!path.empty()){
|
|
pos = path.back(); // try to extend first part of path
|
|
next = getNext(hss,pos,usage);
|
|
}
|
|
} else { // normal extension
|
|
pos = next;
|
|
path.push_back(pos);
|
|
usage[pos] = 3;
|
|
hss->Get(pos,hs);
|
|
usage[hs.attr.partnerno] = 4;
|
|
next = getNext(hss,pos,usage);
|
|
}
|
|
}
|
|
} // while path not empty
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
Simple function marking some elements between min and max as used to avoid
|
|
segments degenerated to single points.
|
|
|
|
*/
|
|
static double maxDist(const vector<Point>& orig, // points
|
|
const int min, const int max, // range
|
|
int& index, // resultindex
|
|
const Geoid* geoid=0){
|
|
|
|
// search the point with the largest distance to the segment between
|
|
// orig[min] and orig[max]
|
|
double maxdist = 0.0;
|
|
int maxindex = -1;
|
|
|
|
try{
|
|
if(!AlmostEqual(orig.at(min),orig.at(max))){
|
|
HalfSegment hs(true,orig.at(min),orig.at(max));
|
|
for(int i=min+1; i<max; i++){
|
|
double dist = hs.Distance(orig.at(i),geoid);
|
|
if(dist>maxdist){
|
|
maxdist = dist;
|
|
maxindex = i;
|
|
}
|
|
}
|
|
} else { // special case of a cycle
|
|
Point p = orig[min];
|
|
for(int i=min+1; i<max; i++){
|
|
double dist = p.Distance(orig.at(i),geoid);
|
|
if(dist>maxdist){
|
|
maxdist = dist;
|
|
maxindex = i;
|
|
}
|
|
}
|
|
}
|
|
index = maxindex;
|
|
return maxdist;
|
|
} catch (out_of_range&){
|
|
cerr << "min=" << min << " max=" << max << " size="
|
|
<< orig.size() << endl;
|
|
assert(false);
|
|
return -1.0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
Implementation of the Douglas Peucker algorithm.
|
|
|
|
The return indoicates whether between min and max further points was
|
|
used.
|
|
|
|
*/
|
|
|
|
bool douglas_peucker(const vector<Point>& orig, // original line
|
|
const double epsilon, // maximum derivation
|
|
bool* use, // result
|
|
const int min, const int max,
|
|
const bool force ,
|
|
const Geoid* geoid){ // current range
|
|
// always use the endpoints
|
|
use[min] = true;
|
|
use[max] = true;
|
|
if(min+1>=max){ // no inner points, nothing to do
|
|
return false;
|
|
}
|
|
int index;
|
|
double maxdist = maxDist(orig,min,max,index,geoid);
|
|
bool cycle = AlmostEqual(orig[min], orig[max]);
|
|
if( (maxdist<=epsilon) && // line closed enough
|
|
!cycle && // no degenerated segment
|
|
!force){
|
|
return false; // all ok, stop recursion
|
|
} else {
|
|
bool ins = douglas_peucker(orig,epsilon,use,min,index,cycle,geoid);
|
|
if(index>=0){
|
|
douglas_peucker(orig,epsilon,use,index,max,cycle && !ins,geoid);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void douglas_peucker(const vector<Point>& orig, // original chain of points
|
|
const double epsilon, // maximum derivation
|
|
bool* use, // result
|
|
const bool forceThrow,
|
|
const Geoid* geoid ){
|
|
for(unsigned int i=0;i<orig.size();i++){
|
|
use[i] = false;
|
|
}
|
|
// call the recursive implementation
|
|
douglas_peucker(orig,epsilon, use, 0, orig.size()-1,forceThrow,geoid);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
7.2 List Representation
|
|
|
|
The list representation of a line is
|
|
|
|
---- ((x1 y1 x2 y2) (x1 y1 x2 y2) ....)
|
|
----
|
|
|
|
or
|
|
|
|
---- undef
|
|
----
|
|
|
|
7.3 ~Out~-function
|
|
|
|
*/
|
|
ListExpr
|
|
OutLine( ListExpr typeInfo, Word value )
|
|
{
|
|
ListExpr result, last;
|
|
HalfSegment hs;
|
|
ListExpr halfseg, halfpoints, flatseg;
|
|
Line* l = (Line*)(value.addr);
|
|
|
|
if(!l->IsDefined()){
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
|
|
if( l->IsEmpty() )
|
|
return nl->TheEmptyList();
|
|
|
|
result = nl->TheEmptyList();
|
|
last = result;
|
|
bool first = true;
|
|
|
|
for( int i = 0; i < l->Size(); i++ )
|
|
{
|
|
l->Get( i, hs );
|
|
if( hs.IsLeftDomPoint() == true )
|
|
{
|
|
halfseg = OutHalfSegment( nl->TheEmptyList(), SetWord( (void*)&hs ) );
|
|
halfpoints = nl->Second( halfseg );
|
|
flatseg = nl->FourElemList(
|
|
nl->First( nl->First( halfpoints ) ),
|
|
nl->Second( nl->First( halfpoints ) ),
|
|
nl->First( nl->Second( halfpoints ) ),
|
|
nl->Second( nl->Second( halfpoints ) ) );
|
|
if( first == true )
|
|
{
|
|
result = nl->OneElemList( flatseg );
|
|
last = result;
|
|
first = false;
|
|
}
|
|
else
|
|
last = nl->Append( last, flatseg );
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
7.4 ~In~-function
|
|
|
|
*/
|
|
Word
|
|
InLine( const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
Line* l = new Line( 0 );
|
|
|
|
if(listutils::isSymbolUndefined(instance)){
|
|
l->SetDefined(false);
|
|
correct=true;
|
|
return SetWord(Address(l));
|
|
}
|
|
|
|
HalfSegment * hs;
|
|
l->StartBulkLoad();
|
|
ListExpr first, halfseg, halfpoint;
|
|
ListExpr rest = instance;
|
|
int edgeno = 0;
|
|
|
|
if( !nl->IsAtom( instance ) )
|
|
{
|
|
while( !nl->IsEmpty( rest ) )
|
|
{
|
|
first = nl->First( rest );
|
|
rest = nl->Rest( rest );
|
|
|
|
if( nl->ListLength( first ) == 4 )
|
|
{
|
|
halfpoint = nl->TwoElemList(
|
|
nl->TwoElemList(
|
|
nl->First(first),
|
|
nl->Second(first)),
|
|
nl->TwoElemList(
|
|
nl->Third(first),
|
|
nl->Fourth(first)));
|
|
} else { // wrong list representation
|
|
l->DeleteIfAllowed();
|
|
correct = false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
halfseg = nl->TwoElemList(nl->BoolAtom(true), halfpoint);
|
|
hs = (HalfSegment*)InHalfSegment( nl->TheEmptyList(), halfseg,
|
|
0, errorInfo, correct ).addr;
|
|
if( correct )
|
|
{
|
|
hs->attr.edgeno = edgeno++;
|
|
*l += *hs;
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
*l += *hs;
|
|
}
|
|
delete hs;
|
|
}
|
|
l->EndBulkLoad();
|
|
|
|
correct = true;
|
|
return SetWord( l );
|
|
}
|
|
l->DeleteIfAllowed();
|
|
correct = false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
/*
|
|
7.5 ~Create~-function
|
|
|
|
*/
|
|
Word
|
|
CreateLine( const ListExpr typeInfo )
|
|
{
|
|
return SetWord( new Line( 0 ) );
|
|
}
|
|
|
|
/*
|
|
7.6 ~Delete~-function
|
|
|
|
*/
|
|
void
|
|
DeleteLine( const ListExpr typeInfo, Word& w )
|
|
{
|
|
Line *l = (Line *)w.addr;
|
|
l->Destroy();
|
|
l->DeleteIfAllowed(false);
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
7.7 ~Close~-function
|
|
|
|
*/
|
|
void
|
|
CloseLine( const ListExpr typeInfo, Word& w )
|
|
{
|
|
((Line *)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
7.8 ~Clone~-function
|
|
|
|
*/
|
|
Word
|
|
CloneLine( const ListExpr typeInfo, const Word& w )
|
|
{
|
|
return SetWord( new Line( *((Line *)w.addr) ) );
|
|
}
|
|
|
|
/*
|
|
7.8 ~Open~-function
|
|
|
|
*/
|
|
bool
|
|
OpenLine( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Line *l = (Line*)Attribute::Open( valueRecord, offset, typeInfo );
|
|
value = SetWord( l );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
7.8 ~Save~-function
|
|
|
|
*/
|
|
bool
|
|
SaveLine( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Line *l = (Line *)value.addr;
|
|
Attribute::Save( valueRecord, offset, typeInfo, l );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
7.9 ~SizeOf~-function
|
|
|
|
*/
|
|
int SizeOfLine()
|
|
{
|
|
return sizeof(Line);
|
|
}
|
|
|
|
/*
|
|
7.11 Function describing the signature of the type constructor
|
|
|
|
*/
|
|
ListExpr
|
|
LineProperty()
|
|
{
|
|
return nl->TwoElemList(
|
|
nl->FourElemList(
|
|
nl->StringAtom("Signature"),
|
|
nl->StringAtom("Example Type List"),
|
|
nl->StringAtom("List Rep"),
|
|
nl->StringAtom("Example List")),
|
|
nl->FourElemList(
|
|
nl->StringAtom("-> DATA"),
|
|
nl->StringAtom(Line::BasicType()),
|
|
nl->StringAtom("(<segment>*) where segment is "
|
|
"(<x1><y1><x2><y2>)"),
|
|
nl->StringAtom("( (1 1 2 2)(3 3 4 4) )")));
|
|
}
|
|
|
|
/*
|
|
7.12 Kind checking function
|
|
|
|
This function checks whether the type constructor is applied correctly. Since
|
|
type constructor ~line~ does not have arguments, this is trivial.
|
|
|
|
*/
|
|
bool
|
|
CheckLine( ListExpr type, ListExpr& errorInfo )
|
|
{
|
|
return nl->IsEqual( type, Line::BasicType() );
|
|
}
|
|
|
|
/*
|
|
7.13 ~Cast~-function
|
|
|
|
*/
|
|
void* CastLine(void* addr)
|
|
{
|
|
return Line::Cast(addr);
|
|
}
|
|
|
|
/*
|
|
7.14 Creation of the type constructor instance
|
|
|
|
*/
|
|
TypeConstructor line(
|
|
Line::BasicType(), //name
|
|
LineProperty, //describing signature
|
|
OutLine, InLine, //Out and In functions
|
|
0, 0, //SaveTo and RestoreFrom List functions
|
|
CreateLine, DeleteLine, //object creation and deletion
|
|
OpenLine, SaveLine, // object open and save
|
|
CloseLine, CloneLine, //object close and clone
|
|
CastLine, //cast function
|
|
SizeOfLine, //sizeof function
|
|
CheckLine ); //kind checking function
|
|
|
|
|
|
|
|
Word
|
|
InSimpleLine( const ListExpr typeInfo, const ListExpr instance1,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct ){
|
|
|
|
ListExpr instance = instance1;
|
|
correct = true;
|
|
if(listutils::isSymbolUndefined(instance)){
|
|
SimpleLine* line = new SimpleLine( 0 );
|
|
line->SetDefined(false);
|
|
return SetWord(Address(line));
|
|
}
|
|
|
|
if(nl->AtomType(instance)!=NoAtom){
|
|
correct=false;
|
|
return SetWord(Address(0));
|
|
}
|
|
|
|
bool startSmaller = true;
|
|
if( nl->HasLength(instance,2)
|
|
&& (nl->AtomType(nl->Second(instance))==BoolType)){
|
|
startSmaller = nl->BoolValue(nl->Second(instance));
|
|
instance = nl->First(instance);
|
|
}
|
|
HalfSegment* hs;
|
|
SimpleLine* line= new SimpleLine(10);
|
|
int edgeno = 0;
|
|
ListExpr rest = instance;
|
|
line->StartBulkLoad();
|
|
while(!nl->IsEmpty(rest)){
|
|
ListExpr segment = nl->First(rest);
|
|
if(!nl->HasLength(segment,4)){
|
|
correct=false;
|
|
line->DeleteIfAllowed();
|
|
return SetWord(Address(0));
|
|
}
|
|
ListExpr halfSegment = nl->TwoElemList(
|
|
nl->BoolAtom(true),
|
|
nl->TwoElemList(
|
|
nl->TwoElemList(nl->First(segment),
|
|
nl->Second(segment)),
|
|
nl->TwoElemList(nl->Third(segment),
|
|
nl->Fourth(segment))));
|
|
hs = static_cast<HalfSegment*>(
|
|
InHalfSegment( nl->TheEmptyList(), halfSegment,
|
|
0, errorInfo, correct ).addr);
|
|
if(!correct){
|
|
delete hs;
|
|
return SetWord(Address(0));
|
|
}
|
|
hs->attr.edgeno = edgeno++;
|
|
*line += *hs;
|
|
hs->SetLeftDomPoint( !hs->IsLeftDomPoint() );
|
|
*line += *hs;
|
|
delete hs;
|
|
rest = nl->Rest(rest);
|
|
}
|
|
if(!line->EndBulkLoad()){
|
|
correct = false;
|
|
line->DeleteIfAllowed();
|
|
return SetWord(Address(0));
|
|
}else{
|
|
correct = true;
|
|
line->SetStartSmaller(startSmaller);
|
|
return SetWord(line);
|
|
}
|
|
}
|
|
|
|
|
|
ostream& operator<<(ostream& out, const LRS& lrs){
|
|
out << "lrsPos "<< lrs.lrsPos << ", hsPos " << lrs.hsPos;
|
|
return out;
|
|
}
|
|
|
|
|
|
|
|
ListExpr OutSimpleLine( ListExpr typeInfo, Word value ) {
|
|
ListExpr result, last;
|
|
HalfSegment hs;
|
|
ListExpr halfseg, halfpoints, flatseg;
|
|
SimpleLine* l = static_cast<SimpleLine*>(value.addr);
|
|
|
|
if(!l->IsDefined()){
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
if( l->IsEmpty() ){
|
|
return nl->TwoElemList( nl->TheEmptyList(),
|
|
nl->BoolAtom( l->GetStartSmaller() ) );
|
|
}
|
|
|
|
result = nl->TheEmptyList();
|
|
last = result;
|
|
bool first = true;
|
|
|
|
|
|
/*
|
|
// version using halfsegment order
|
|
|
|
for( int i = 0; i < l->Size(); i++ ) {
|
|
l->Get( i, hs );
|
|
if( hs.IsLeftDomPoint() ){
|
|
halfseg = OutHalfSegment( nl->TheEmptyList(), SetWord( (void*)&hs ) );
|
|
halfpoints = nl->Second( halfseg );
|
|
flatseg = nl->FourElemList(
|
|
nl->First( nl->First( halfpoints ) ),
|
|
nl->Second( nl->First( halfpoints ) ),
|
|
nl->First( nl->Second( halfpoints ) ),
|
|
nl->Second( nl->Second( halfpoints ) ) );
|
|
if( first == true ) {
|
|
result = nl->OneElemList( flatseg );
|
|
last = result;
|
|
first = false;
|
|
} else {
|
|
last = nl->Append( last, flatseg );
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// version using lrs order
|
|
|
|
LRS lrs;
|
|
// for some curious reason, some halfsegments occur twice
|
|
// in the lrs array
|
|
set<int> used;
|
|
|
|
|
|
for(int i=0;i<l->lrsSize(); i++){
|
|
l->Get(i,lrs);
|
|
if(used.find(lrs.hsPos)==used.end()){
|
|
used.insert(lrs.hsPos);
|
|
//cout << "LRS " << lrs << endl;
|
|
l->Get(lrs.hsPos,hs);
|
|
halfseg = OutHalfSegment( nl->TheEmptyList(), SetWord( (void*)&hs ) );
|
|
halfpoints = nl->Second( halfseg );
|
|
flatseg = nl->FourElemList(
|
|
nl->First( nl->First( halfpoints ) ),
|
|
nl->Second( nl->First( halfpoints ) ),
|
|
nl->First( nl->Second( halfpoints ) ),
|
|
nl->Second( nl->Second( halfpoints ) ) );
|
|
if( first == true ) {
|
|
result = nl->OneElemList( flatseg );
|
|
last = result;
|
|
first = false;
|
|
} else {
|
|
last = nl->Append( last, flatseg );
|
|
}
|
|
}
|
|
}
|
|
|
|
return nl->TwoElemList( result,
|
|
nl->BoolAtom( l->GetStartSmaller() ) );
|
|
}
|
|
|
|
Word CreateSimpleLine( const ListExpr typeInfo ) {
|
|
return SetWord( new SimpleLine( 0 ) );
|
|
}
|
|
|
|
void DeleteSimpleLine( const ListExpr typeInfo, Word& w ) {
|
|
SimpleLine *l = static_cast<SimpleLine*>(w.addr);
|
|
l->Destroy();
|
|
l->DeleteIfAllowed(false);
|
|
w.addr = 0;
|
|
}
|
|
|
|
|
|
void CloseSimpleLine( const ListExpr typeInfo, Word& w ) {
|
|
(static_cast<SimpleLine*>(w.addr))->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
Word CloneSimpleLine( const ListExpr typeInfo, const Word& w ) {
|
|
return SetWord( new SimpleLine( *((SimpleLine *)w.addr) ) );
|
|
}
|
|
|
|
int SizeOfSimpleLine() {
|
|
return sizeof(SimpleLine);
|
|
}
|
|
|
|
ListExpr SimpleLineProperty() {
|
|
return nl->TwoElemList(
|
|
nl->FourElemList(
|
|
nl->StringAtom("Signature"),
|
|
nl->StringAtom("Example Type List"),
|
|
nl->StringAtom("List Rep"),
|
|
nl->StringAtom("Example List")),
|
|
nl->FourElemList(
|
|
nl->StringAtom("-> DATA"),
|
|
nl->StringAtom(SimpleLine::BasicType()),
|
|
nl->TextAtom("( (<segment>*) <bool> ) where segment is "
|
|
"(<x1><y1><x2><y2>) and bool is TRUE or FALSE"),
|
|
nl->StringAtom("( ( (1 1 2 2) (2 2 1 4) ) FALSE )")));
|
|
}
|
|
|
|
bool CheckSimpleLine( ListExpr type, ListExpr& errorInfo ){
|
|
return nl->IsEqual( type, SimpleLine::BasicType() );
|
|
}
|
|
|
|
void* CastSimpleLine(void* addr) {
|
|
return SimpleLine::Cast(addr);;
|
|
}
|
|
|
|
TypeConstructor sline(
|
|
SimpleLine::BasicType(), //name
|
|
SimpleLineProperty, //describing signature
|
|
OutSimpleLine, InSimpleLine, //Out and In functions
|
|
0, 0, //SaveTo and RestoreFrom List functions
|
|
CreateSimpleLine, DeleteSimpleLine, //object creation and deletion
|
|
OpenAttribute<SimpleLine>,
|
|
SaveAttribute<SimpleLine>, // object open and save
|
|
CloseSimpleLine, CloneSimpleLine, //object close and clone
|
|
CastSimpleLine, //cast function
|
|
SizeOfSimpleLine, //sizeof function
|
|
CheckSimpleLine);
|
|
|
|
|
|
|
|
bool IsInsideAbove(const HalfSegment& hs,
|
|
const Point& thirdPoint){
|
|
if(AlmostEqual(hs.GetLeftPoint().GetX(),hs.GetRightPoint().GetX())){
|
|
// vertical segment
|
|
return
|
|
(MIN(hs.GetLeftPoint().GetX(),hs.GetRightPoint().GetX())<thirdPoint.GetX());
|
|
}
|
|
return
|
|
(MIN(hs.GetLeftPoint().GetY(),hs.GetRightPoint().GetY())<thirdPoint.GetY());
|
|
}
|
|
|
|
|
|
double VectorSize(const Point &p1, const Point &p2, const Geoid* geoid/*=0*/)
|
|
{
|
|
assert( p1.IsDefined() );
|
|
assert( p2.IsDefined() );
|
|
assert( !geoid || geoid->IsDefined() );
|
|
if(geoid){
|
|
double size = p1.Distance(p1, geoid);
|
|
assert( size > 0 );
|
|
return size;
|
|
}else {
|
|
double size = pow( (p1.GetX() - p2.GetX()),2)
|
|
+ pow( (p1.GetY() - p2.GetY()),2);
|
|
size = sqrt(size);
|
|
return size;
|
|
}
|
|
}
|
|
|
|
//The angle function returns the angle of VP1P2
|
|
// P1 is the point on the window's edge
|
|
double Angle(const Point &v, const Point &p1,const Point &p2)
|
|
{
|
|
assert( v.IsDefined() );
|
|
assert( p1.IsDefined() );
|
|
assert( p2.IsDefined() );
|
|
double coss;
|
|
|
|
//If P1P2 is vertical and the window's edge
|
|
// been tested is horizontal , then
|
|
//the angle VP1P2 is equal to 90 degrees. On the
|
|
//other hand, if P1P2 is vertical
|
|
//and the window's edge been tested is vertical, then
|
|
// the angle is 90 degrees.
|
|
//Similar tests are applied when P1P2 is horizontal.
|
|
|
|
if (p1.GetX() == p2.GetX()){ //the segment is vertical
|
|
if (v.GetY()==p1.GetY()){
|
|
return M_PI/2; //horizontal edge
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
if (p1.GetY() == p2.GetY()){ //the segment is horizontal
|
|
if (v.GetY()==p1.GetY()){
|
|
return 0; //horizontal edge
|
|
} else {
|
|
return M_PI/2;
|
|
}
|
|
}
|
|
coss = double( ( (v.GetX() - p1.GetX()) * (p2.GetX() - p1.GetX()) ) +
|
|
( (v.GetY() - p1.GetY()) * (p2.GetY() - p1.GetY()) ) ) /
|
|
(VectorSize(v,p1) * VectorSize(p2,p1));
|
|
//cout<<endl<<"Coss"<<coss;
|
|
//coss = abs(coss);
|
|
//cout<<endl<<"Coss"<<coss;
|
|
return acos(coss);
|
|
}
|
|
|
|
|
|
ostream& operator<<( ostream& o, const EdgePoint& p )
|
|
{
|
|
o << "(" << p.GetX() << ", " << p.GetY() << ")"
|
|
<<" D("<<(p.direction ? "LEFT/DOWN" : "RIGHT/UP")<<")"
|
|
<<" R("<<(p.rejected ? "Rejected" : "Accepted")<<")";
|
|
return o;
|
|
}
|
|
|
|
EdgePoint* EdgePoint::GetEdgePoint( const Point &p,
|
|
const Point &p2,
|
|
bool insideAbove,
|
|
const Point &v,
|
|
const bool reject )
|
|
{
|
|
//The point p2 must be outside the window
|
|
bool direction;
|
|
|
|
//window's vertical edge
|
|
if (v.GetX()==p.GetX())
|
|
{
|
|
if (insideAbove)
|
|
direction = false; //UP
|
|
else
|
|
direction = true; //DOWN
|
|
}
|
|
else //Horizontal edge
|
|
{
|
|
if (insideAbove)
|
|
{
|
|
if ( (p.GetX()-p2.GetX())>0 )
|
|
//p2.GetX() is located to the left of p.GetX()
|
|
direction = false; //RIGHT
|
|
else
|
|
direction = true; //LEFT
|
|
}
|
|
else
|
|
{
|
|
if ( (p.GetX()-p2.GetX())>0 )
|
|
//p2.GetX() is located to the right of p.GetX()
|
|
direction = true; //LEFT
|
|
else
|
|
direction = false; //RIGHT
|
|
}
|
|
}
|
|
return new EdgePoint(p,direction,reject);
|
|
|
|
}
|
|
|
|
void AddPointToEdgeArray( const Point &p,
|
|
const HalfSegment &hs,
|
|
const Rectangle<2> &window,
|
|
vector<EdgePoint> pointsOnEdge[4] )
|
|
{
|
|
EdgePoint *dp;
|
|
Point v(true);
|
|
AttrType attr;
|
|
attr = hs.GetAttr();
|
|
Point p2(true);
|
|
//If the left and right edges are been tested then
|
|
//it is not need to check the angle
|
|
//between the half segment and the edge. If the attribute
|
|
//inside above is true, then
|
|
//the direction is up (false), otherwise it is down (true).
|
|
if (p.GetX() == window.MinD(0))
|
|
{
|
|
dp = new EdgePoint(p,!attr.insideAbove,false);
|
|
pointsOnEdge[WLEFT].push_back(*dp);
|
|
}
|
|
else
|
|
if (p.GetX() == window.MaxD(0))
|
|
{
|
|
dp = new EdgePoint(p,!attr.insideAbove,false);
|
|
pointsOnEdge[WRIGHT].push_back(*dp);
|
|
}
|
|
if (p.GetY() == window.MinD(1))
|
|
{
|
|
v.Set(window.MinD(0), window.MinD(1));
|
|
//In this case we don't know which point is outside the window,
|
|
//so it is need to test both half segment's poinst. Moreover,
|
|
//in order to use the same comparison that is used for
|
|
//Top edge, it is need to choose the half segment point that
|
|
//is over the bottom edge.
|
|
if (hs.GetLeftPoint().GetY()>window.MinD(1))
|
|
dp = EdgePoint::GetEdgePoint(p,hs.GetLeftPoint(),
|
|
attr.insideAbove,v,false);
|
|
else
|
|
dp = EdgePoint::GetEdgePoint(p,hs.GetRightPoint(),
|
|
attr.insideAbove,v,false);
|
|
pointsOnEdge[WBOTTOM].push_back(*dp);
|
|
}
|
|
else
|
|
if (p.GetY() == window.MaxD(1))
|
|
{
|
|
v.Set(window.MinD(0), window.MaxD(1));
|
|
//In this case we don't know which point is outside the window,
|
|
//so it is need to test
|
|
if (hs.GetLeftPoint().GetY()>window.MaxD(1))
|
|
dp = EdgePoint::GetEdgePoint(p,hs.GetLeftPoint(),
|
|
attr.insideAbove,v,false);
|
|
else
|
|
dp = EdgePoint::GetEdgePoint(p,hs.GetRightPoint(),
|
|
attr.insideAbove,v,false);
|
|
pointsOnEdge[WTOP].push_back(*dp);
|
|
}
|
|
}
|
|
|
|
bool GetAcceptedPoint( vector <EdgePoint>pointsOnEdge,
|
|
int &i, const int end,
|
|
EdgePoint &ep )
|
|
{
|
|
//id is the indice of the current point in the scan
|
|
//ep is the correct edge point that will be returned.
|
|
ep = pointsOnEdge[i];
|
|
//discard all rejected points
|
|
while (ep.rejected && i<=end)
|
|
{
|
|
i++;
|
|
if (i>end)
|
|
return false;
|
|
EdgePoint epAux = pointsOnEdge[i];
|
|
//Discard all the points that was accepted but has a
|
|
// corresponding rejection point.
|
|
//In other words, point that has the same
|
|
// coordinates and direction on the edge.
|
|
if (!epAux.rejected && (epAux.direction==ep.direction) &&
|
|
(epAux.GetX() == ep.GetX()) && (epAux.GetY() == ep.GetY()) )
|
|
{
|
|
while ( (i<=end) && (epAux.direction==ep.direction) &&
|
|
(epAux.GetX() == ep.GetX()) && (epAux.GetY() == ep.GetY()) )
|
|
{
|
|
i++;
|
|
if (i>end)
|
|
return false;
|
|
epAux = pointsOnEdge[i];
|
|
}
|
|
}
|
|
ep = epAux;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool HalfSegment::RayDown( const Point& p, double &yIntersection ) const
|
|
{
|
|
if (this->IsVertical())
|
|
return false;
|
|
|
|
const Coord& x = p.GetX(), y = p.GetY(),
|
|
xl = GetLeftPoint().GetX(),
|
|
yl = GetLeftPoint().GetY(),
|
|
xr = GetRightPoint().GetX(),
|
|
yr = GetRightPoint().GetY();
|
|
|
|
// between is true, iff xl <= x <= xr.
|
|
const bool between = CompareDouble(x, xl) != -1 &&
|
|
CompareDouble(x, xr) != 1;
|
|
|
|
if (!between)
|
|
return false;
|
|
|
|
const double k = (yr - yl) / (xr - xl);
|
|
const double a = (yl - k * xl);
|
|
const double y0 = k * x + a;
|
|
|
|
if (CompareDouble(y0, y) == 1) // y0 > y: this is above p.
|
|
return false;
|
|
|
|
// y0 <= p: p is above or on this.
|
|
|
|
yIntersection = y0;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
~IsSpatialType~
|
|
|
|
This function checks whether the type given as a ListExpr is one of
|
|
~point~, ~points~, ~line~, or ~region~.
|
|
|
|
*/
|
|
|
|
bool IsSpatialType(ListExpr type){
|
|
if(!nl->IsAtom(type)){
|
|
return false;
|
|
}
|
|
if(nl->AtomType(type)!=SymbolType){
|
|
return false;
|
|
}
|
|
string t = nl->SymbolValue(type);
|
|
if(t==Point::BasicType()) return true;
|
|
if(t==Points::BasicType()) return true;
|
|
if(t==Line::BasicType()) return true;
|
|
if(t==Region::BasicType()) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
This function check whether a region value is valid after the insertion of a
|
|
new half segment. Whenever a half segment is about to be inserted, the
|
|
state of the region is checked.
|
|
A valid region must satisfy the following conditions:
|
|
|
|
1) any two cycles of the same region must be disconnect, which means that no
|
|
edges of different cycles can intersect each other;
|
|
|
|
2) edges of the same cycle can only intersect with their endpoints, but no
|
|
their middle points;
|
|
|
|
3) For a certain face, the holes must be inside the outer cycle;
|
|
|
|
4) For a certain face, any two holes can not contain each other;
|
|
|
|
5) Faces must have the outer cycle, but they can have no holes;
|
|
|
|
6) for a certain cycle, any two vertex can not be the same;
|
|
|
|
7) any cycle must be made up of at least 3 edges;
|
|
|
|
8) It is allowed that one face is inside another provided that their edges do
|
|
not intersect.
|
|
|
|
*/
|
|
|
|
vector<Point> getCycle(const bool isHole,
|
|
const vector<HalfSegment>& vhs){
|
|
|
|
// first extract the cycle
|
|
bool used[vhs.size()];
|
|
for(unsigned int i=0;i<vhs.size();i++){
|
|
used[i] = false;
|
|
}
|
|
if(vhs.size() < 3 ){
|
|
assert(false);
|
|
}
|
|
// trivial n^2 implementation
|
|
HalfSegment hs = vhs[0];
|
|
Point sp = hs.GetDomPoint();
|
|
Point cp = hs.GetSecPoint();
|
|
vector<Point> vp;
|
|
|
|
vp.push_back(sp);
|
|
vp.push_back(cp);
|
|
|
|
used[0] = true;
|
|
for(unsigned int i=1; i< vhs.size();i++){
|
|
for(unsigned int j=1; j<vhs.size(); j++){
|
|
if(!used[j]){
|
|
Point p0 = vhs[j].GetDomPoint();
|
|
Point p1 = vhs[j].GetSecPoint();
|
|
if(AlmostEqual(p0,cp)){
|
|
used[j] = true;
|
|
cp = p1;
|
|
vp.push_back(cp);
|
|
} else if(AlmostEqual(p1,cp)){
|
|
used[j] = true;
|
|
cp = p0;
|
|
vp.push_back(cp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vp.push_back(cp);
|
|
// debugging only
|
|
for(unsigned int i=0;i<vhs.size();i++){
|
|
if(!used[i]){
|
|
cerr << "Unused halfsegment found" << endl;
|
|
}
|
|
}
|
|
|
|
|
|
bool cw = getDir(vp);
|
|
|
|
if(!(( isHole && cw ) || (!isHole && !cw))){
|
|
vector<Point> vp2;
|
|
for(int i= vp.size()-1; i>=0; i--){
|
|
vp2.push_back(vp[i]);
|
|
}
|
|
return vp2;
|
|
} else {
|
|
return vp;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
Implementation of the Gauss Krueger Projection
|
|
|
|
*/
|
|
|
|
struct P3D{
|
|
double x;
|
|
double y;
|
|
double z;
|
|
};
|
|
|
|
bool WGSGK::project(const Point& src, Point& result) const{
|
|
if(!src.IsDefined()){
|
|
cerr << __PRETTY_FUNCTION__ << ": Point Argument is undefined!" << endl;
|
|
result.SetDefined(false);
|
|
return false;
|
|
}
|
|
double x = src.GetX();
|
|
double y = src.GetY();
|
|
if(x<-180 || x>180 || y<-90 || y>90){
|
|
cerr << __PRETTY_FUNCTION__ << ": Point " << src << " is not a valid "
|
|
<< "geographic coordinate!" << endl;
|
|
result.SetDefined(false);
|
|
return false;
|
|
}
|
|
|
|
double a = x*Pi/180;
|
|
double b = y*Pi/180;
|
|
if(!useWGS){
|
|
BesselBLToGaussKrueger(b, a, result);
|
|
return result.IsDefined();
|
|
}
|
|
double l1 = a;
|
|
double b1 = b;
|
|
a=awgs;
|
|
b=bwgs;
|
|
double eq=eqwgs;
|
|
double N=a/sqrt(1-eq*sin(b1)*sin(b1));
|
|
double Xq=(N+h1)*cos(b1)*cos(l1);
|
|
double Yq=(N+h1)*cos(b1)*sin(l1);
|
|
double Zq=((1-eq)*N+h1)*sin(b1);
|
|
|
|
P3D p;
|
|
HelmertTransformation(Xq, Yq, Zq, p);
|
|
double X = p.x;
|
|
double Y = p.y;
|
|
double Z = p.z;
|
|
|
|
a=abes;
|
|
b=bbes;
|
|
eq = eqbes;
|
|
|
|
BLRauenberg(X, Y, Z, p);
|
|
double b2 = p.x;
|
|
double l2 = p.y;
|
|
BesselBLToGaussKrueger(b2, l2, result);
|
|
return result.IsDefined();
|
|
}
|
|
|
|
|
|
bool WGSGK::project(const HalfSegment& src, HalfSegment& result) const{
|
|
result = src;
|
|
Point p1(true),p2(true);
|
|
if(!project(src.GetLeftPoint(),p1)) return false;
|
|
if(!project(src.GetRightPoint(),p2)) return false;
|
|
if(p2<p1){
|
|
result.attr.insideAbove = ! src.attr.insideAbove;
|
|
result.Set(src.IsLeftDomPoint(), p2, p1);
|
|
} else {
|
|
result.Set(src.IsLeftDomPoint(), p1, p2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool WGSGK::getOrig(const Point& src, Point& result) const{
|
|
if(!src.IsDefined()){
|
|
result.SetDefined(false);
|
|
return false;
|
|
}
|
|
return gk2geo(src.GetX(), src.GetY(), result);
|
|
}
|
|
|
|
void WGSGK::enableWGS(const bool enabled){
|
|
useWGS = enabled;
|
|
}
|
|
|
|
void WGSGK::setMeridian(const int m){
|
|
MDC = m;
|
|
}
|
|
|
|
void WGSGK::init(){
|
|
Pi = 3.1415926535897932384626433832795028841971693993751058209749445923078164;
|
|
rho = 180/Pi;
|
|
awgs = 6378137.0;
|
|
bwgs = 6356752.314;
|
|
abes = 6377397.155; // Bessel Semi-Major Axis = Equatorial Radius in meters
|
|
bbes = 6356078.962; // Bessel Semi-Minor Axis = Polar Radius in meters
|
|
cbes = 111120.6196; // Bessel latitude to Gauss-Krueger meters
|
|
dx = -585.7; // Translation Parameter 1
|
|
dy = -87.0; // Translation Parameter 2
|
|
dz = -409.2; // Translation Parameter 3
|
|
rotx = 2.540423689E-6; // Rotation Parameter 1
|
|
roty = 7.514612057E-7; // Rotation Parameter 2
|
|
rotz = -1.368144208E-5; // Rotation Parameter 3
|
|
sc = 0.99999122; // Scaling Factor
|
|
h1 = 0;
|
|
eqwgs = (awgs*awgs-bwgs*bwgs)/(awgs*awgs);
|
|
eqbes = (abes*abes-bbes*bbes)/(abes*abes);
|
|
MDC = 2.0; // standard in Hagena
|
|
useWGS = true; // usw coordinates in wgs ellipsoid
|
|
}
|
|
|
|
void WGSGK::HelmertTransformation(const double x, const double y,
|
|
const double z, P3D& p) const{
|
|
p.x = dx + (sc*(1*x+rotz*y-roty*z));
|
|
p.y = dy + (sc*(-rotz*x+1*y+rotx*z));
|
|
p.z = dz + (sc*(roty*x-rotx*y+1*z));
|
|
}
|
|
|
|
|
|
void WGSGK::BesselBLToGaussKrueger(const double b,
|
|
const double ll,
|
|
Point& result) const{
|
|
//double bg=180*b/Pi;
|
|
//double lng=180*ll/Pi;
|
|
double l0 = 3*MDC;
|
|
l0=Pi*l0/180;
|
|
double l=ll-l0;
|
|
double k=cos(b);
|
|
double t=sin(b)/k;
|
|
double eq=eqbes;
|
|
double Vq=1+eq*k*k;
|
|
double v=sqrt(Vq);
|
|
double Ng=abes*abes/(bbes*v);
|
|
double nk=(abes-bbes)/(abes+bbes);
|
|
double X=((Ng*t*k*k*l*l)/2)+((Ng*t*(9*Vq-t*t-4)*k*k*k*k*l*l*l*l)/24);
|
|
double gg=b+(((-3*nk/2)+(9*nk*nk*nk/16)) *
|
|
sin(2*b)+15*nk*nk*sin(4*b)/16-35*nk*nk*nk*sin(6*b)/48);
|
|
double SS=gg*180*cbes/Pi;
|
|
double Ho=(SS+X);
|
|
double Y=Ng*k*l+Ng*(Vq-t*t)*k*k*k*l*l*l/6+Ng*
|
|
(5-18*t*t+t*t*t*t)*k*k*k*k*k*l*l*l*l*l/120;
|
|
double kk=500000;
|
|
//double Pii=Pi;
|
|
double RVV = MDC;
|
|
double Re=RVV*1000000+kk+Y;
|
|
result.SetDefined(true);
|
|
result.Set(Re, Ho);
|
|
}
|
|
|
|
|
|
void WGSGK::BLRauenberg (const double x, const double y,
|
|
const double z, P3D& result) const{
|
|
|
|
double f=Pi*50/180;
|
|
double p=z/sqrt(x*x+y*y);
|
|
double f1,f2;
|
|
do
|
|
{
|
|
f1=newF(f,x,y,p);
|
|
f2=f;
|
|
f=f1;
|
|
}
|
|
while(!(abs(f2-f1)<10E-10));
|
|
|
|
result.x=f;
|
|
result.y=atan(y/x);
|
|
result.z=sqrt(x*x+y*y)/cos(f1)-abes/sqrt(1-eqbes*sin(f1)*sin(f1));
|
|
}
|
|
|
|
|
|
double WGSGK::newF(const double f, const double x,
|
|
const double y, const double p) const{
|
|
double zw;
|
|
double nnq;
|
|
zw=abes/sqrt(1-eqbes*sin(f)*sin(f));
|
|
nnq=1-eqbes*zw/(sqrt(x*x+y*y)/cos(f));
|
|
return(atan(p/nnq));
|
|
}
|
|
|
|
bool WGSGK::gk2geo(const double GKRight,
|
|
const double GKHeight,
|
|
Point& result) const{
|
|
if(GKRight<1000000 || GKHeight<1000000){
|
|
result.SetDefined(false);
|
|
return false;
|
|
}
|
|
double e2 = 0.0067192188;
|
|
double c = 6398786.849;
|
|
|
|
double bI = GKHeight/10000855.7646;
|
|
double bII = bI*bI;
|
|
double bf = 325632.08677 * bI * ((((((0.00000562025 * bII + 0.00022976983)
|
|
* bII - 0.00113566119)
|
|
* bII + 0.00424914906)
|
|
* bII - 0.00831729565)
|
|
* bII + 1));
|
|
bf /= 3600*rho;
|
|
double co = cos(bf);
|
|
double g2 = e2 *(co*co);
|
|
double g1 = c/sqrt(1+g2);
|
|
double t = tan(bf);
|
|
double fa = (GKRight - floor(GKRight/1000000)*1000000-500000)/g1;
|
|
double GeoDezRight = ((bf - fa * fa * t * (1 + g2) / 2 +
|
|
fa * fa * fa * fa * t *
|
|
(5 + 3 * t * t + 6 * g2 - 6 * g2 * t * t) / 24) *
|
|
rho);
|
|
double dl = fa - fa * fa * fa * (1 + 2 * t * t + g2) / 6 +
|
|
fa * fa * fa * fa * fa *
|
|
(1 + 28 * t * t + 24 * t * t * t * t) / 120;
|
|
|
|
double Mer = floor(GKRight/1000000);
|
|
double GeoDezHeight = dl*rho/co+Mer*3;
|
|
if(useWGS){
|
|
return bessel2WGS(GeoDezRight,GeoDezHeight,result);
|
|
}else{
|
|
result.SetDefined(true);
|
|
result.Set(GeoDezHeight,GeoDezRight);
|
|
return result.IsDefined();
|
|
}
|
|
}
|
|
|
|
bool WGSGK::bessel2WGS(const double geoDezRight1,
|
|
const double geoDezHeight1, Point& result) const{
|
|
double aBessel = abes;
|
|
double eeBessel = 0.0066743722296294277832;
|
|
double ScaleFactor = 0.00000982;
|
|
double RotXRad = -7.16069806998785E-06;
|
|
double RotYRad = 3.56822869296619E-07;
|
|
double RotZRad = 7.06858347057704E-06;
|
|
double ShiftXMeters = 591.28;
|
|
double ShiftYMeters = 81.35;
|
|
double ShiftZMeters = 396.39;
|
|
double aWGS84 = awgs;
|
|
double eeWGS84 = 0.0066943799;
|
|
double geoDezRight = (geoDezRight1/180)*Pi;
|
|
double geoDezHeight = (geoDezHeight1/180)*Pi;
|
|
double sinRight = sin(geoDezRight);
|
|
double sinRight2 = sinRight*sinRight;
|
|
double n = eeBessel*sinRight2;
|
|
n = 1-n;
|
|
n = sqrt(n);
|
|
n = aBessel/n;
|
|
double cosRight=cos(geoDezRight);
|
|
double cosHeight=cos(geoDezHeight);
|
|
double sinHeight = sin(geoDezHeight);
|
|
double CartesianXMeters = n*cosRight*cosHeight;
|
|
double CartesianYMeters = n*cosRight*sinHeight;
|
|
double CartesianZMeters = n*(1-eeBessel)*sinRight;
|
|
|
|
double CartOutputXMeters = (1 + ScaleFactor) *
|
|
CartesianXMeters + RotZRad *
|
|
CartesianYMeters -
|
|
RotYRad * CartesianZMeters + ShiftXMeters;
|
|
double CartOutputYMeters = -RotZRad * CartesianXMeters +
|
|
(1 + ScaleFactor) * CartesianYMeters +
|
|
RotXRad * CartesianZMeters + ShiftYMeters;
|
|
double CartOutputZMeters = RotYRad * CartesianXMeters -
|
|
RotXRad * CartesianYMeters +
|
|
(1 + ScaleFactor) * CartesianZMeters +
|
|
ShiftZMeters;
|
|
|
|
geoDezHeight = atan(CartOutputYMeters/CartOutputXMeters);
|
|
double Latitude = (CartOutputXMeters*CartOutputXMeters)+
|
|
(CartOutputYMeters*CartOutputYMeters);
|
|
Latitude = sqrt(Latitude);
|
|
double InitLat = Latitude;
|
|
Latitude = CartOutputZMeters/Latitude;
|
|
Latitude = atan(Latitude);
|
|
double LatitudeIt = 99999999;
|
|
do{
|
|
LatitudeIt = Latitude;
|
|
double sinLat = sin(Latitude);
|
|
n = 1-eeWGS84*sinLat*sinLat;
|
|
n = sqrt(n);
|
|
n = aWGS84/n;
|
|
Latitude = InitLat; // sqrt(CartoutputXMeters^2+CartOutputYMeters^2)
|
|
Latitude = (CartOutputZMeters+eeWGS84*n*sin(LatitudeIt))/Latitude;
|
|
Latitude = atan(Latitude);
|
|
} while(abs(Latitude-LatitudeIt)>=0.000000000000001);
|
|
|
|
result.SetDefined(true);
|
|
result.Set((geoDezHeight/Pi)*180, (Latitude/Pi)*180);
|
|
return result.IsDefined();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
8.2 List Representation
|
|
|
|
The list representation of a region is
|
|
|
|
---- (face1 face2 face3 ... )
|
|
where facei=(outercycle, holecycle1, holecycle2....)
|
|
|
|
cyclei= (vertex1, vertex2, .....)
|
|
where each vertex is a point.
|
|
|
|
or
|
|
|
|
undef
|
|
----
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8.3 ~Out~-function
|
|
|
|
*/
|
|
ListExpr
|
|
OutRegion( ListExpr typeInfo, Word value )
|
|
{
|
|
Region* cr = (Region*)(value.addr);
|
|
if(!cr->IsDefined()){
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
if( cr->IsEmpty() )
|
|
{
|
|
return (nl->TheEmptyList());
|
|
}
|
|
else
|
|
{
|
|
Region *RCopy=new Region(*cr, true); // in memory
|
|
|
|
RCopy->LogicSort();
|
|
|
|
HalfSegment hs, hsnext;
|
|
|
|
ListExpr regionNL = nl->TheEmptyList();
|
|
ListExpr regionNLLast = regionNL;
|
|
|
|
ListExpr faceNL = nl->TheEmptyList();
|
|
ListExpr faceNLLast = faceNL;
|
|
|
|
ListExpr cycleNL = nl->TheEmptyList();
|
|
ListExpr cycleNLLast = cycleNL;
|
|
|
|
ListExpr pointNL;
|
|
|
|
int currFace = -999999, currCycle= -999999; // avoid uninitialized use
|
|
Point outputP(true), leftoverP(true);
|
|
|
|
for( int i = 0; i < RCopy->Size(); i++ )
|
|
{
|
|
RCopy->Get( i, hs );
|
|
if (i==0)
|
|
{
|
|
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
|
|
{
|
|
cerr << "\n" << __PRETTY_FUNCTION__ << ": Wrong data format --- "
|
|
<< "discontiguous segments!" << endl
|
|
<< "\ths = " << hs << endl
|
|
<< "\thsnext = " << hsnext << endl;
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
pointNL = OutPoint( nl->TheEmptyList(), SetWord(&outputP) );
|
|
if (cycleNL == nl->TheEmptyList())
|
|
{
|
|
cycleNL = nl->OneElemList(pointNL);
|
|
cycleNLLast = cycleNL;
|
|
}
|
|
else
|
|
{
|
|
cycleNLLast = nl->Append( cycleNLLast, pointNL );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (hs.attr.faceno == currFace)
|
|
{
|
|
if (hs.attr.cycleno == currCycle)
|
|
{
|
|
outputP=leftoverP;
|
|
|
|
if (hs.GetLeftPoint() == leftoverP)
|
|
leftoverP = hs.GetRightPoint();
|
|
else if (hs.GetRightPoint() == leftoverP)
|
|
{
|
|
leftoverP = hs.GetLeftPoint();
|
|
}
|
|
else
|
|
{
|
|
cerr << "\n" << __PRETTY_FUNCTION__ << ": Wrong data format --- "
|
|
<< "discontiguous segment in cycle!" << endl
|
|
<< "\thh = " << hs << endl
|
|
<< "\tleftoverP = " << leftoverP << endl;
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
pointNL=OutPoint( nl->TheEmptyList(),
|
|
SetWord( &outputP) );
|
|
if (cycleNL == nl->TheEmptyList())
|
|
{
|
|
cycleNL=nl->OneElemList(pointNL);
|
|
cycleNLLast = cycleNL;
|
|
}
|
|
else
|
|
{
|
|
cycleNLLast = nl->Append(cycleNLLast, pointNL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (faceNL == nl->TheEmptyList())
|
|
{
|
|
faceNL = nl->OneElemList(cycleNL);
|
|
faceNLLast = faceNL;
|
|
}
|
|
else
|
|
{
|
|
faceNLLast = nl->Append(faceNLLast, cycleNL);
|
|
}
|
|
cycleNL = nl->TheEmptyList();
|
|
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
|
|
{
|
|
cerr << "\n" << __PRETTY_FUNCTION__ << ": Wrong data format --- "
|
|
<< "discontiguous segments in cycle!" << endl
|
|
<< "\ths = " << hs << endl
|
|
<< "\thsnext = " << hsnext << endl;
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
pointNL = OutPoint( nl->TheEmptyList(),
|
|
SetWord(&outputP) );
|
|
if (cycleNL == nl->TheEmptyList())
|
|
{
|
|
cycleNL = nl->OneElemList(pointNL);
|
|
cycleNLLast = cycleNL;
|
|
}
|
|
else
|
|
{
|
|
cycleNLLast = nl->Append(cycleNLLast, pointNL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (faceNL == nl->TheEmptyList())
|
|
{
|
|
faceNL = nl->OneElemList(cycleNL);
|
|
faceNLLast = faceNL;
|
|
}
|
|
else
|
|
{
|
|
faceNLLast = nl->Append(faceNLLast, cycleNL);
|
|
}
|
|
cycleNL = nl->TheEmptyList();
|
|
|
|
|
|
if (regionNL == nl->TheEmptyList())
|
|
{
|
|
regionNL = nl->OneElemList(faceNL);
|
|
regionNLLast = regionNL;
|
|
}
|
|
else
|
|
{
|
|
regionNLLast = nl->Append(regionNLLast, faceNL);
|
|
}
|
|
faceNL = nl->TheEmptyList();
|
|
|
|
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
|
|
{
|
|
cerr << "\n" << __PRETTY_FUNCTION__ << ": Wrong data format --- "
|
|
<< "discontiguous segments in cycle!" << endl
|
|
<< "\ths = " << hs << endl
|
|
<< "\thsnext = " << hsnext << endl;
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
|
|
pointNL = OutPoint(nl->TheEmptyList(), SetWord(&outputP));
|
|
if (cycleNL == nl->TheEmptyList())
|
|
{
|
|
cycleNL = nl->OneElemList(pointNL);
|
|
cycleNLLast = cycleNL;
|
|
}
|
|
else
|
|
{
|
|
cycleNLLast = nl->Append(cycleNLLast, pointNL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (faceNL == nl->TheEmptyList())
|
|
{
|
|
faceNL = nl->OneElemList(cycleNL);
|
|
faceNLLast = faceNL;
|
|
}
|
|
else
|
|
{
|
|
faceNLLast = nl->Append(faceNLLast, cycleNL);
|
|
}
|
|
cycleNL = nl->TheEmptyList();
|
|
|
|
|
|
if (regionNL == nl->TheEmptyList())
|
|
{
|
|
regionNL = nl->OneElemList(faceNL);
|
|
regionNLLast = regionNL;
|
|
}
|
|
else
|
|
{
|
|
regionNLLast = nl->Append(regionNLLast, faceNL);
|
|
}
|
|
faceNL = nl->TheEmptyList();
|
|
|
|
RCopy->DeleteIfAllowed();
|
|
return regionNL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
8.4 ~In~-function
|
|
|
|
*/
|
|
|
|
|
|
Word
|
|
InRegion(const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct ){
|
|
|
|
|
|
if(listutils::isSymbol(instance,Symbol::UNDEFINED())){
|
|
Region* r = new Region(0);
|
|
r->SetDefined(false);
|
|
correct = true;
|
|
return SetWord(Address(r));
|
|
}
|
|
|
|
|
|
if(nl->AtomType(instance) != NoAtom){
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
|
|
ListExpr regNL = instance;
|
|
vector<vector<Point> > cycles;
|
|
|
|
while(!nl->IsEmpty(regNL)){
|
|
ListExpr faceNL = nl->First(regNL);
|
|
regNL = nl->Rest(regNL);
|
|
if(nl->AtomType(faceNL)!=NoAtom){
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
bool firstCycle = true;
|
|
Rectangle<2> faceRect;
|
|
while(!nl->IsEmpty(faceNL)){
|
|
vector<Point> cycle;
|
|
ListExpr cycleNL = nl->First(faceNL);
|
|
faceNL = nl->Rest(faceNL);
|
|
if(nl->AtomType(cycleNL)!=NoAtom){
|
|
correct=false;
|
|
return SetWord(Address(0));
|
|
}
|
|
|
|
Point firstPoint(false,0,0);
|
|
bool fp = true;
|
|
Rectangle<2> currentBB;
|
|
while(!nl->IsEmpty(cycleNL)){
|
|
ListExpr pointNL = nl->First(cycleNL);
|
|
cycleNL = nl->Rest(cycleNL);
|
|
if(nl->ListLength(pointNL)!=2){
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
if(!listutils::isNumeric(nl->First(pointNL)) ||
|
|
!listutils::isNumeric(nl->Second(pointNL))){
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
Point p(true, listutils::getNumValue(nl->First(pointNL)),
|
|
listutils::getNumValue(nl->Second(pointNL)));
|
|
cycle.push_back(p);
|
|
if(fp){
|
|
fp = false;
|
|
firstPoint = p;
|
|
currentBB = p.BoundingBox();
|
|
} else {
|
|
currentBB = currentBB.Union(p.BoundingBox());
|
|
}
|
|
}
|
|
if(!AlmostEqual(firstPoint, cycle[cycle.size()-1])){
|
|
cycle.push_back(firstPoint);
|
|
}
|
|
if(firstCycle || !faceRect.Contains(currentBB)){
|
|
if(!getDir(cycle)){
|
|
reverseCycle(cycle);
|
|
}
|
|
firstCycle=false;
|
|
faceRect = currentBB;
|
|
} else {
|
|
if(getDir(cycle) ){
|
|
reverseCycle(cycle);
|
|
}
|
|
|
|
}
|
|
|
|
cycles.push_back(cycle);
|
|
}
|
|
}
|
|
|
|
Region* res = buildRegion(cycles);
|
|
correct = res!=0;
|
|
return SetWord(res);
|
|
}
|
|
|
|
|
|
Word
|
|
InRegion_old( const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct )
|
|
{
|
|
|
|
Region* cr = new Region( 0 );
|
|
|
|
if(listutils::isSymbolUndefined(instance)){
|
|
cr->SetDefined(0);
|
|
correct=true;
|
|
return SetWord(Address(cr));
|
|
}
|
|
|
|
cr->StartBulkLoad();
|
|
|
|
|
|
ListExpr RegionNL = instance;
|
|
ListExpr FaceNL, CycleNL;
|
|
int fcno=-1;
|
|
int ccno=-1;
|
|
int edno=-1;
|
|
int partnerno = 0;
|
|
|
|
if (!nl->IsAtom(instance))
|
|
{
|
|
while( !nl->IsEmpty( RegionNL ) )
|
|
{
|
|
FaceNL = nl->First( RegionNL );
|
|
RegionNL = nl->Rest( RegionNL);
|
|
bool isCycle = true;
|
|
|
|
//A face is composed by 1 cycle, and can have holes.
|
|
//All the holes must be inside the face. (TO BE IMPLEMENTED0)
|
|
//Region *faceCycle;
|
|
|
|
fcno++;
|
|
ccno=-1;
|
|
edno=-1;
|
|
|
|
if (nl->IsAtom( FaceNL ))
|
|
{
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
while (!nl->IsEmpty( FaceNL) )
|
|
{
|
|
CycleNL = nl->First( FaceNL );
|
|
FaceNL = nl->Rest( FaceNL );
|
|
|
|
ccno++;
|
|
edno=-1;
|
|
|
|
if (nl->IsAtom( CycleNL ))
|
|
{
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
if (nl->ListLength( CycleNL) <3)
|
|
{
|
|
cerr << __PRETTY_FUNCTION__ << ": A cycle must have at least 3 edges!"
|
|
<< endl;
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
else
|
|
{
|
|
ListExpr firstPoint = nl->First( CycleNL );
|
|
ListExpr prevPoint = nl->First( CycleNL );
|
|
ListExpr flagedSeg = nl->TheEmptyList();
|
|
ListExpr currPoint = nl->TheEmptyList();
|
|
CycleNL = nl->Rest( CycleNL );
|
|
|
|
//Starting to compute a new cycle
|
|
|
|
Points *cyclepoints= new Points( 8 ); // in memory
|
|
|
|
Point *currvertex,p1(true),p2(true),firstP(true);
|
|
|
|
//This function has the goal to store the half segments of
|
|
//the cycle that is been treated. When the cycle's computation
|
|
//is terminated the region rDir will be used to compute the
|
|
//insideAbove
|
|
//attribute of the half segments of this cycle.
|
|
Region *rDir = new Region(32);
|
|
rDir->StartBulkLoad();
|
|
|
|
|
|
currvertex = (Point*) InPoint ( nl->TheEmptyList(),
|
|
firstPoint, 0, errorInfo, correct ).addr;
|
|
if (!correct) {
|
|
// todo: delete temp objects
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
cyclepoints->StartBulkLoad();
|
|
(*cyclepoints) += (*currvertex);
|
|
p1 = *currvertex;
|
|
firstP = p1;
|
|
cyclepoints->EndBulkLoad();
|
|
delete currvertex;
|
|
|
|
while ( !nl->IsEmpty( CycleNL) )
|
|
{
|
|
// cout<<"cycle "<<endl;
|
|
currPoint = nl->First( CycleNL );
|
|
CycleNL = nl->Rest( CycleNL );
|
|
|
|
currvertex = (Point*) InPoint( nl->TheEmptyList(),
|
|
currPoint, 0, errorInfo, correct ).addr;
|
|
// cout<<"curvertex "<<*currvertex<<endl;
|
|
if (!correct) return SetWord( Address(0) );
|
|
|
|
if (cyclepoints->Contains(*currvertex))
|
|
{
|
|
cerr<< __PRETTY_FUNCTION__ << ": The same vertex: "
|
|
<<(*currvertex)
|
|
<<" appears repeatedly within the current cycle!"<<endl;
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
else
|
|
{
|
|
p2 = *currvertex;
|
|
cyclepoints->StartBulkLoad();
|
|
(*cyclepoints) += (*currvertex);
|
|
cyclepoints->EndBulkLoad(true,false,false);
|
|
}
|
|
delete currvertex;
|
|
|
|
flagedSeg = nl->TwoElemList
|
|
(nl-> BoolAtom(true),
|
|
nl->TwoElemList(prevPoint, currPoint));
|
|
prevPoint=currPoint;
|
|
edno++;
|
|
//Create left dominating half segment
|
|
HalfSegment * hs = (HalfSegment*)InHalfSegment
|
|
( nl->TheEmptyList(), flagedSeg,
|
|
0, errorInfo, correct ).addr;
|
|
if(!correct){
|
|
if(hs){
|
|
cerr << __PRETTY_FUNCTION__ << ": Creation of left dominating "
|
|
<< "half segment (1) failed!" << endl;
|
|
delete hs;
|
|
}
|
|
cr->DeleteIfAllowed();
|
|
return SetWord( Address(0) );
|
|
}
|
|
hs->attr.faceno=fcno;
|
|
hs->attr.cycleno=ccno;
|
|
hs->attr.edgeno=edno;
|
|
hs->attr.partnerno=partnerno;
|
|
partnerno++;
|
|
hs->attr.insideAbove = (hs->GetLeftPoint() == p1);
|
|
//true (L-->R ),false (R--L)
|
|
p1 = p2;
|
|
|
|
if (( correct )&&( cr->InsertOk(*hs) ))
|
|
{
|
|
(*cr) += (*hs);
|
|
// cout<<"cr+1 "<<*hs<<endl;
|
|
if( hs->IsLeftDomPoint() )
|
|
{
|
|
(*rDir) += (*hs);
|
|
// cout<<"rDr+1 "<<*hs<<endl;
|
|
hs->SetLeftDomPoint( false );
|
|
}
|
|
else
|
|
{
|
|
hs->SetLeftDomPoint( true );
|
|
// cout<<"rDr+2 "<<*hs<<endl;
|
|
(*rDir) += (*hs);
|
|
}
|
|
(*cr) += (*hs);
|
|
// cout<<"cr+2 "<<*hs<<endl;
|
|
delete hs;
|
|
}
|
|
else
|
|
{
|
|
cerr<< __PRETTY_FUNCTION__ << ": Problematic HalfSegment: "
|
|
<< endl;
|
|
if(correct)
|
|
cerr << "\nhs = " << (*hs) << " cannot be inserted." << endl;
|
|
else
|
|
cerr << "\nInvalid half segment description." << endl;
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
|
|
}
|
|
delete cyclepoints;
|
|
|
|
edno++;
|
|
flagedSeg= nl->TwoElemList
|
|
(nl-> BoolAtom(true),
|
|
nl->TwoElemList(firstPoint, currPoint));
|
|
HalfSegment * hs = (HalfSegment*)InHalfSegment
|
|
( nl->TheEmptyList(), flagedSeg,
|
|
0, errorInfo, correct ).addr;
|
|
if(!correct){
|
|
if(hs){
|
|
cerr << __PRETTY_FUNCTION__ << ": Creation of "
|
|
<< "half segment (2) failed!" << endl;
|
|
delete hs;
|
|
}
|
|
cr->DeleteIfAllowed();
|
|
return SetWord( Address(0) );
|
|
}
|
|
hs->attr.faceno=fcno;
|
|
hs->attr.cycleno=ccno;
|
|
hs->attr.edgeno=edno;
|
|
hs->attr.partnerno=partnerno;
|
|
hs->attr.insideAbove = (hs->GetRightPoint() == firstP);
|
|
//true (L-->R ),false (R--L),
|
|
//the order of typing is last point than first point.
|
|
partnerno++;
|
|
|
|
//The last half segment of the region
|
|
if (( correct )&&( cr->InsertOk(*hs) ))
|
|
{
|
|
(*cr) += (*hs);
|
|
// cout<<"cr+3 "<<*hs<<endl;
|
|
if( hs->IsLeftDomPoint() )
|
|
{
|
|
(*rDir) += (*hs);
|
|
// cout<<"rDr+3 "<<*hs<<endl;
|
|
hs->SetLeftDomPoint( false );
|
|
}
|
|
else
|
|
{
|
|
hs->SetLeftDomPoint( true );
|
|
// cout<<"rDr+4 "<<*hs<<endl;
|
|
(*rDir) += (*hs);
|
|
}
|
|
(*cr) += (*hs);
|
|
// cout<<"cr+4 "<<*hs<<endl;
|
|
delete hs;
|
|
rDir->EndBulkLoad(true, false, false, false);
|
|
|
|
|
|
//To calculate the inside above attribute
|
|
bool direction = rDir->GetCycleDirection();
|
|
|
|
int h = cr->Size() - ( rDir->Size() * 2 );
|
|
while ( h < cr->Size())
|
|
{
|
|
//after each left half segment of the region is its
|
|
//correspondig right half segment
|
|
HalfSegment hsIA;
|
|
bool insideAbove;
|
|
cr->Get(h,hsIA);
|
|
/*
|
|
The test for adjusting the inside above can be described
|
|
as above, but was implemented in a different way that
|
|
produces the same result.
|
|
if ( (direction && hsIA->attr.insideAbove) ||
|
|
(!direction && !hsIA->attr.insideAbove) )
|
|
{
|
|
//clockwise and l-->r or
|
|
//counterclockwise and r-->l
|
|
hsIA->attr.insideAbove=false;
|
|
}
|
|
else
|
|
//clockwise and r-->r or
|
|
//counterclockwise and l-->r
|
|
true;
|
|
|
|
*/
|
|
if (direction == hsIA.attr.insideAbove)
|
|
insideAbove = false;
|
|
else
|
|
insideAbove = true;
|
|
if (!isCycle)
|
|
insideAbove = !insideAbove;
|
|
HalfSegment auxhsIA( hsIA );
|
|
auxhsIA.attr.insideAbove = insideAbove;
|
|
cr->UpdateAttr(h,auxhsIA.attr);
|
|
//Get right half segment
|
|
cr->Get(h+1,hsIA);
|
|
auxhsIA = hsIA;
|
|
auxhsIA.attr.insideAbove = insideAbove;
|
|
cr->UpdateAttr(h+1,auxhsIA.attr);
|
|
h+=2;
|
|
}
|
|
|
|
//After the first face's cycle read the faceCycle variable is set.
|
|
//Afterwards
|
|
//it is tested if all the new cycles are inside the faceCycle.
|
|
/*
|
|
if (isCycle)
|
|
faceCycle = new Region(rDir,false);
|
|
else
|
|
//To implement the test
|
|
*/
|
|
rDir->DeleteIfAllowed();
|
|
//After the end of the first cycle of the face,
|
|
//all the following cycles are
|
|
//holes, then isCycle is set to false.
|
|
isCycle = false;
|
|
|
|
}
|
|
else
|
|
{
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
cr->SetNoComponents( fcno+1 );
|
|
cr->EndBulkLoad( true, true, true, false );
|
|
|
|
correct = true;
|
|
return SetWord( cr );
|
|
}
|
|
else
|
|
{
|
|
correct=false;
|
|
return SetWord( Address(0) );
|
|
}
|
|
}
|
|
|
|
/*
|
|
8.5 ~Create~-function
|
|
|
|
*/
|
|
Word
|
|
CreateRegion( const ListExpr typeInfo )
|
|
{
|
|
//cout << "CreateRegion" << endl;
|
|
|
|
return (SetWord( new Region( 0 ) ));
|
|
}
|
|
|
|
/*
|
|
8.6 ~Delete~-function
|
|
|
|
*/
|
|
void
|
|
DeleteRegion( const ListExpr typeInfo, Word& w )
|
|
{
|
|
//cout << "DeleteRegion" << endl;
|
|
|
|
Region *cr = (Region *)w.addr;
|
|
cr->Destroy();
|
|
cr->DeleteIfAllowed(false);
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
8.7 ~Close~-function
|
|
|
|
*/
|
|
void
|
|
CloseRegion( const ListExpr typeInfo, Word& w )
|
|
{
|
|
//cout << "CloseRegion" << endl;
|
|
|
|
((Region *)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
8.8 ~Clone~-function
|
|
|
|
*/
|
|
Word
|
|
CloneRegion( const ListExpr typeInfo, const Word& w )
|
|
{
|
|
//cout << "CloneRegion" << endl;
|
|
|
|
Region *cr = new Region( *((Region *)w.addr) );
|
|
return SetWord( cr );
|
|
}
|
|
|
|
/*
|
|
7.8 ~Open~-function
|
|
|
|
*/
|
|
bool
|
|
OpenRegion( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Region *r = (Region*)Attribute::Open( valueRecord, offset, typeInfo );
|
|
value = SetWord( r );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
7.8 ~Save~-function
|
|
|
|
*/
|
|
bool
|
|
SaveRegion( SmiRecord& valueRecord,
|
|
size_t& offset,
|
|
const ListExpr typeInfo,
|
|
Word& value )
|
|
{
|
|
Region *r = (Region *)value.addr;
|
|
Attribute::Save( valueRecord, offset, typeInfo, r );
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
8.9 ~SizeOf~-function
|
|
|
|
*/
|
|
int SizeOfRegion()
|
|
{
|
|
return sizeof(Region);
|
|
}
|
|
|
|
/*
|
|
8.11 Function describing the signature of the type constructor
|
|
|
|
*/
|
|
ListExpr
|
|
RegionProperty()
|
|
{
|
|
ListExpr listreplist = nl->TextAtom();
|
|
nl->AppendText(listreplist,"(<face>*) where face is"
|
|
" (<outercycle><holecycle>*); "
|
|
"<outercycle> and <holecycle> are <points>*");
|
|
ListExpr examplelist = nl->TextAtom();
|
|
nl->AppendText(examplelist,"(((3 0)(10 1)(3 1))((3.1 0.1)"
|
|
"(3.1 0.9)(6 0.8)))");
|
|
ListExpr remarkslist = nl->TextAtom();
|
|
nl->AppendText(remarkslist,"all <holecycle> must be completely within "
|
|
"<outercycle>.");
|
|
|
|
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("-> DATA"),
|
|
nl->StringAtom(Region::BasicType()),
|
|
listreplist,
|
|
examplelist,
|
|
remarkslist)));
|
|
}
|
|
|
|
/*
|
|
8.12 Kind checking function
|
|
|
|
This function checks whether the type constructor is applied correctly. Since
|
|
type constructor ~point~ does not have arguments, this is trivial.
|
|
|
|
*/
|
|
bool
|
|
CheckRegion( ListExpr type, ListExpr& errorInfo )
|
|
{
|
|
return (nl->IsEqual( type, Region::BasicType() ));
|
|
}
|
|
|
|
|
|
/*
|
|
8.14 Creation of the type constructor instance
|
|
|
|
*/
|
|
TypeConstructor region(
|
|
Region::BasicType(), //name
|
|
RegionProperty, //describing signature
|
|
OutRegion, InRegion, //Out and In functions
|
|
0, 0, //SaveTo and RestoreFrom List functions
|
|
CreateRegion, DeleteRegion, //object creation and deletion
|
|
OpenRegion, SaveRegion, // object open and save
|
|
CloseRegion, CloneRegion, //object close and clone
|
|
Region::Cast, //cast function
|
|
SizeOfRegion, //sizeof function
|
|
CheckRegion ); //kind checking function
|
|
|
|
|
|
/*
|
|
8.15 type constructor for label data type
|
|
|
|
*/
|
|
GenTC<Label> label;
|
|
|
|
/*
|
|
8.16 type contructor for disc
|
|
|
|
*/
|
|
GenTC<Disc> disc;
|
|
|
|
/*
|
|
8.17 Type constructor for Segment
|
|
|
|
*/
|
|
GenTC<Segment> segment;
|
|
|
|
/*
|
|
9.1 Implementation of type ~cpoint~
|
|
|
|
*/
|
|
|
|
/*
|
|
4 Type Constructor ~cpoint~
|
|
|
|
A cpoint represents a point in the Euclidean plane with a radius
|
|
which can be considered as a tolerance value.
|
|
|
|
4.1 Implementation of the class ~CPoint~
|
|
|
|
*/
|
|
ostream& operator<<(ostream& o, const CPoint& cp) {
|
|
ios_base::fmtflags oldOptions = o.flags();
|
|
o.setf(ios_base::fixed,ios_base::floatfield);
|
|
o.precision(8);
|
|
if(cp.IsDefined()) {
|
|
o << "(" << *((Point*)&cp) << ", " << cp.getRadius() << ")";
|
|
}
|
|
else {
|
|
o << Symbol::UNDEFINED();
|
|
}
|
|
o.flags(oldOptions);
|
|
return o;
|
|
}
|
|
|
|
const Rectangle<2> CPoint::BoundingBox(const Geoid* geoid /*=0*/) const {
|
|
assert(IsDefined());
|
|
if (IsDefined()) {
|
|
if (geoid && geoid->IsDefined()) { // spherical case
|
|
double minx = MAX(-180, x - ApplyFactor(x) - radius);
|
|
double maxx = MIN(180, x + ApplyFactor(x) + radius);
|
|
double miny = MAX(-90, y - ApplyFactor(y) - radius);
|
|
double maxy = MIN(90,y + ApplyFactor(y) + radius);
|
|
double minMax[] = {minx, maxx, miny, maxy};
|
|
return Rectangle<2>(true, minMax);
|
|
}
|
|
else if (!geoid) {
|
|
double minMax[] = {x - ApplyFactor(x) - radius,
|
|
x + ApplyFactor(x) + radius,
|
|
y - ApplyFactor(y) - radius,
|
|
y + ApplyFactor(y) + radius};
|
|
return Rectangle<2>(true, minMax);
|
|
}
|
|
}
|
|
return Rectangle<2>(false);
|
|
}
|
|
|
|
ostream& CPoint::Print(ostream &os) const {
|
|
return os << *this;
|
|
}
|
|
|
|
string CPoint::toString(const Geoid* geoid /*=0*/) const {
|
|
stringstream s;
|
|
s << "(" << ((Point*)this)->toString(geoid) << ", " << radius << ")";
|
|
return s.str();
|
|
}
|
|
|
|
|
|
bool CPoint::Inside(const Rectangle<2>& r, const Geoid* geoid /*=0*/ ) const {
|
|
assert(r.IsDefined());
|
|
if (!IsDefined() || !r.IsDefined() || (geoid && !geoid->IsDefined())) {
|
|
return false;
|
|
}
|
|
Point p = *((Point*)this);
|
|
if (!p.Inside(r)) {
|
|
return false;
|
|
}
|
|
Point right(true, radius, 0.0), top(true, 0.0, radius);
|
|
return (p + right).Inside(r) && (p - right).Inside(r)
|
|
&& (p + top).Inside(r) && (p - top).Inside(r);
|
|
}
|
|
|
|
void CPoint::ReadFromString(string value) {
|
|
ListExpr list;
|
|
if (!nl->ReadFromString(value, list)) {
|
|
if (!nl->ReadFromString("(" + value + ")", list)) {
|
|
SetDefined(false);
|
|
return;
|
|
}
|
|
}
|
|
if (listutils::isSymbolUndefined(list)) {
|
|
SetDefined(false);
|
|
return;
|
|
}
|
|
if (!nl->HasLength(list, 2)) {
|
|
SetDefined(false);
|
|
return;
|
|
}
|
|
((Point*)this)->ReadFromString(nl->ToString(nl->First(list)));
|
|
if (IsDefined() && !listutils::isNumeric(nl->Second(list))) {
|
|
radius = listutils::getNumValue(nl->Second(list));
|
|
return;
|
|
}
|
|
SetDefined(false);
|
|
}
|
|
|
|
double CPoint::Distance(const CPoint& cp, const Geoid* geoid /* = 0 */) const {
|
|
double pointDistance = ((Point*)this)->Distance(*((Point*)&cp), geoid);
|
|
return std::max(0.0, pointDistance - radius - cp.getRadius());
|
|
}
|
|
|
|
double CPoint::Distance(const Rectangle<2>& r, const Geoid* geoid/*=0*/) const {
|
|
double pointDistance = ((Point*)this)->Distance(r, geoid);
|
|
return std::max(0.0, pointDistance - radius);
|
|
}
|
|
|
|
bool CPoint::Intersects(const Rectangle<2>& r, const Geoid* geoid/*=0*/) const {
|
|
if (((Point*)this)->Intersects(r)) {
|
|
return true;
|
|
}
|
|
return Distance(r) <= radius;
|
|
}
|
|
|
|
|
|
Rectangle<2> CPoint::GeographicBBox(const CPoint &other, const Geoid &geoid)
|
|
const {
|
|
if (!checkGeographicCoord() || !other.checkGeographicCoord()
|
|
|| !geoid.IsDefined()) {
|
|
return Rectangle<2>(false);
|
|
}
|
|
double northmostLAT;
|
|
double southmostLAT;
|
|
orthodromeExtremeLatitudes(other, geoid, southmostLAT, northmostLAT);
|
|
CPoint cp1(true, MIN(GetX(), other.GetX()), southmostLAT);
|
|
CPoint cp2(true, MAX(GetX(), other.GetX()), northmostLAT);
|
|
return cp1.BoundingBox().Union(cp2.BoundingBox());
|
|
}
|
|
|
|
/*
|
|
4.2 List Representation
|
|
|
|
The list representation of a cpoint is
|
|
|
|
---- ((x y), radius)
|
|
----
|
|
|
|
4.3 ~Out~-function
|
|
|
|
*/
|
|
ListExpr OutCPoint(ListExpr typeInfo, Word value) {
|
|
CPoint* cpoint = (CPoint*)(value.addr);
|
|
if (cpoint->IsDefined()) {
|
|
return nl->TwoElemList(OutPoint(typeInfo, (Point*)cpoint),
|
|
nl->RealAtom(cpoint->getRadius()));
|
|
}
|
|
else {
|
|
return nl->SymbolAtom(Symbol::UNDEFINED());
|
|
}
|
|
}
|
|
|
|
/*
|
|
4.4 ~In~-function
|
|
|
|
*/
|
|
Word InCPoint(const ListExpr typeInfo, const ListExpr instance,
|
|
const int errorPos, ListExpr& errorInfo, bool& correct) {
|
|
correct = true;
|
|
|
|
|
|
|
|
if (nl->ListLength(instance) == 2) {
|
|
Word p = InPoint(typeInfo, nl->First(instance), errorPos, errorInfo,
|
|
correct);
|
|
correct = listutils::isNumeric(nl->Second(instance));
|
|
if (!correct) {
|
|
return SetWord(Address(0));
|
|
}
|
|
else {
|
|
return SetWord(new CPoint(*((Point*)p.addr),
|
|
listutils::getNumValue(nl->Second(instance))));
|
|
}
|
|
}
|
|
else if (listutils::isSymbolUndefined(instance)) {
|
|
return SetWord(new CPoint(false));
|
|
}
|
|
correct = false;
|
|
return SetWord(Address(0));
|
|
}
|
|
|
|
/*
|
|
4.5 ~Create~-function
|
|
|
|
*/
|
|
Word CreateCPoint(const ListExpr typeInfo) {
|
|
return SetWord(new CPoint(false));
|
|
}
|
|
|
|
/*
|
|
4.6 ~Delete~-function
|
|
|
|
*/
|
|
void DeleteCPoint(const ListExpr typeInfo, Word& w) {
|
|
((CPoint*)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
4.7 ~Close~-function
|
|
|
|
*/
|
|
void CloseCPoint(const ListExpr typeInfo, Word& w) {
|
|
((CPoint*)w.addr)->DeleteIfAllowed();
|
|
w.addr = 0;
|
|
}
|
|
|
|
/*
|
|
4.8 ~Clone~-function
|
|
|
|
*/
|
|
Word CloneCPoint(const ListExpr typeInfo, const Word& w) {
|
|
return SetWord(new CPoint(*((CPoint*)w.addr)));
|
|
}
|
|
|
|
/*
|
|
4.8 ~SizeOf~-function
|
|
|
|
*/
|
|
int SizeOfCPoint() {
|
|
return sizeof(CPoint);
|
|
}
|
|
|
|
/*
|
|
4.9 Function describing the signature of the type constructor
|
|
|
|
*/
|
|
ListExpr CPointProperty() {
|
|
return nl->TwoElemList(nl->FourElemList(
|
|
nl->StringAtom("Signature"),
|
|
nl->StringAtom("Example Type List"),
|
|
nl->StringAtom("List Rep"),
|
|
nl->StringAtom("Example List")),
|
|
nl->FourElemList(
|
|
nl->StringAtom("-> DATA"),
|
|
nl->StringAtom(CPoint::BasicType()),
|
|
nl->StringAtom("((x y), r)"),
|
|
nl->StringAtom("((0.9 1.8) 0.09)")));
|
|
}
|
|
|
|
/*
|
|
4.10 Kind Checking Function
|
|
|
|
This function checks whether the type constructor is applied correctly. Since
|
|
type constructor ~point~ does not have arguments, this is trivial.
|
|
|
|
*/
|
|
bool CheckCPoint(ListExpr type, ListExpr& errorInfo) {
|
|
return (listutils::isSymbol(type, CPoint::BasicType()));
|
|
}
|
|
|
|
|
|
/*
|
|
4.12 Creation of the type constructor instance
|
|
|
|
*/
|
|
TypeConstructor cpoint(
|
|
CPoint::BasicType(), //name
|
|
CPointProperty, //property function describing signature
|
|
OutCPoint, InCPoint, //Out and In functions
|
|
0, 0, //SaveToList and RestoreFromList functions
|
|
CreateCPoint, DeleteCPoint, //object creation and deletion
|
|
OpenAttribute<CPoint>,
|
|
SaveAttribute<CPoint>, // object open and save
|
|
CloseCPoint, CloneCPoint, //object close, and clone
|
|
CPoint::Cast, //cast function
|
|
SizeOfCPoint, //sizeof function
|
|
CheckCPoint); //kind checking function
|
|
|
|
|
|
/*
|
|
10 Operators
|
|
|
|
Definition of operators is similar to definition of type constructors. An
|
|
operator is defined by creating an instance of class ~Operator~. Again we
|
|
have to define some functions before we are able to create an ~Operator~
|
|
instance.
|
|
|
|
10.1 Type mapping functions
|
|
|
|
A type mapping function takes a nested list as argument. Its contents are
|
|
type descriptions of an operator's input parameters. A nested list describing
|
|
the output type of the operator is returned.
|
|
|
|
10.1.1 Type mapping function SpatialTypeMapBool
|
|
|
|
It is for the compare operators which have ~bool~ as resulttype, like =, !=, <,
|
|
<=, >, >=.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialTypeMapBool( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion)
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
|
|
ListExpr SpatialTypeMapCompare(ListExpr args){
|
|
string err = " st x st expected, with"
|
|
" st in {point, points, line, region, sline)";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!nl->Equal(arg1,arg2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
SpatialType st = SpatialTypeOfSymbol(arg1);
|
|
if( st == stpoint ||
|
|
st == stpoints ||
|
|
st == stline ||
|
|
st == stregion ||
|
|
st == stsline){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr SpatialTypeMapEqual(ListExpr args){
|
|
string err = "spatial x spatial expected";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err + " wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg1) || !listutils::isSymbol(arg2)){
|
|
return listutils::typeError(err + " composite type detected");
|
|
}
|
|
string s1 = nl->SymbolValue(arg1);
|
|
string s2 = nl->SymbolValue(arg2);
|
|
if(s1==s2){
|
|
if( (s1==Point::BasicType()) ||
|
|
(s1==Points::BasicType()) ||
|
|
(s1==Line::BasicType()) ||
|
|
(s1==Region::BasicType()) ||
|
|
(s1==SimpleLine::BasicType())){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
return listutils::typeError(err + " (only spatial types allowed");
|
|
}
|
|
if( (s1==Point::BasicType()) && (s2==Points::BasicType())){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
if( (s1==Points::BasicType()) && (s2==Point::BasicType())){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
return listutils::typeError(err + " (only spatial types allowed");
|
|
}
|
|
|
|
ListExpr SpatialTypeMapIsLess(ListExpr args){
|
|
string err = "point x point expected";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err + " wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg1) || !listutils::isSymbol(arg2)){
|
|
return listutils::typeError(err + " composite type detected");
|
|
}
|
|
string s1 = nl->SymbolValue(arg1);
|
|
string s2 = nl->SymbolValue(arg2);
|
|
if(s1==s2 && s1==Point::BasicType())
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
else
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
/*
|
|
10.1.2 Type mapping function GeoGeoMapBool
|
|
|
|
It is for the binary operators which have ~bool~ as result type, such as
|
|
interscets, inside, onborder, ininterior, etc.
|
|
|
|
*/
|
|
|
|
ListExpr
|
|
GeoGeoMapBool( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 ){
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if (((SpatialTypeOfSymbol( arg1 ) == stpoint) ||
|
|
(SpatialTypeOfSymbol( arg1 ) == stpoints) ||
|
|
(SpatialTypeOfSymbol( arg1 ) == stline) ||
|
|
(SpatialTypeOfSymbol( arg1 ) == stregion)) &&
|
|
((SpatialTypeOfSymbol( arg2 ) == stpoint) ||
|
|
(SpatialTypeOfSymbol( arg2 ) == stpoints) ||
|
|
(SpatialTypeOfSymbol( arg2 ) == stline) ||
|
|
(SpatialTypeOfSymbol( arg2 ) == stregion))){
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.2 Type Mapping setsetmapbool
|
|
|
|
It is for all combinations of spatial types which are represented as
|
|
sets, .i.e all types except point.
|
|
|
|
*/
|
|
|
|
ListExpr
|
|
IntersectsTM( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
SpatialType st1 = SpatialTypeOfSymbol( arg1 );
|
|
SpatialType st2 = SpatialTypeOfSymbol( arg2 );
|
|
if ( ((st1 == stpoints) || (st1 == stline) || (st1 == stregion)) &&
|
|
((st2 == stpoints) || (st2 == stline) || (st2 == stregion))) {
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
} else if(st1==stsline && st2==stsline) {
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError(" t_1 x t_2 expected,"
|
|
" with t_1, t_2 in {points,line,region}");
|
|
}
|
|
|
|
/*
|
|
10.1.2 PointsRegionMapBool
|
|
|
|
*/
|
|
ListExpr
|
|
PointsRegionMapBool( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 ){
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if (((SpatialTypeOfSymbol( arg1 ) == stpoints) ) &&
|
|
((SpatialTypeOfSymbol( arg2 ) == stregion))){
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError("Expected points x region.");
|
|
}
|
|
|
|
/*
|
|
10.1.2 RegionRegionMapBool
|
|
|
|
*/
|
|
ListExpr
|
|
RegionRegionMapBool( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 ) {
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if (((SpatialTypeOfSymbol( arg1 ) == stregion) ) &&
|
|
((SpatialTypeOfSymbol( arg2 ) == stregion))){
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError("region x region expected");
|
|
}
|
|
|
|
/*
|
|
10.1.2 PointRegionMapBool
|
|
|
|
*/
|
|
ListExpr
|
|
PointRegionMapBool( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if (((SpatialTypeOfSymbol( arg1 ) == stpoint) ) &&
|
|
((SpatialTypeOfSymbol( arg2 ) == stregion))){
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError("points x region expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.2 AdjacentTypeMap
|
|
|
|
*/
|
|
ListExpr
|
|
AdjacentTypeMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 ){
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if(
|
|
((SpatialTypeOfSymbol(arg1)==stpoints) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
((SpatialTypeOfSymbol(arg1)==stline) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
((SpatialTypeOfSymbol(arg1)==stregion) &&
|
|
(SpatialTypeOfSymbol(arg2)==stpoints)) ||
|
|
((SpatialTypeOfSymbol(arg1)==stregion) &&
|
|
(SpatialTypeOfSymbol(arg2)==stline)) ||
|
|
((SpatialTypeOfSymbol(arg1)==stregion) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion))){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
}
|
|
return listutils::typeError("points x region expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.2 InsideTypeMap
|
|
|
|
*/
|
|
ListExpr
|
|
InsideTypeMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if(
|
|
((SpatialTypeOfSymbol(arg1)==stpoint) &&
|
|
(SpatialTypeOfSymbol(arg2)==stpoints)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stpoint) &&
|
|
(SpatialTypeOfSymbol(arg2)==stline)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stpoint) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stpoints) &&
|
|
(SpatialTypeOfSymbol(arg2)==stpoints)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stpoints) &&
|
|
(SpatialTypeOfSymbol(arg2)==stline))||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stpoints) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stline) &&
|
|
(SpatialTypeOfSymbol(arg2)==stline)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stline) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)==stregion) &&
|
|
(SpatialTypeOfSymbol(arg2)==stregion)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)== stpoint) &&
|
|
(SpatialTypeOfSymbol(arg2)==stsline)) ||
|
|
|
|
((SpatialTypeOfSymbol(arg1)== stsline) &&
|
|
(SpatialTypeOfSymbol(arg2)==stsline)) ){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
}
|
|
return listutils::typeError("points x region expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.3 Type mapping function SpatialTypeMapBool1
|
|
|
|
It is for the operator ~isempty~ which have ~point~, ~points~, ~line~,
|
|
and ~region~ as input and ~bool~ resulttype.
|
|
|
|
*/
|
|
|
|
ListExpr
|
|
SpatialTypeMapBool1( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint )
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints )
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline )
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion )
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline )
|
|
return (nl->SymbolAtom( CcBool::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
ListExpr SpatialIntersectionTypeMap(ListExpr args){
|
|
string err = "t1 x t2 expected, t_i in {points, points, line, sline, region";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err + ": wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg1)){
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
}
|
|
if(!listutils::isSymbol(arg2)){
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
string a1 = nl->SymbolValue(arg1);
|
|
string a2 = nl->SymbolValue(arg2);
|
|
|
|
if(a1==Point::BasicType()){
|
|
if(a2==Point::BasicType() || a2==Points::BasicType() ||
|
|
a2==Line::BasicType() || a2==Region::BasicType() ||
|
|
a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
if(a1==Points::BasicType()){
|
|
if(a2==Point::BasicType() || a2==Points::BasicType() ||
|
|
a2==Line::BasicType() || a2==Region::BasicType() ||
|
|
a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==Line::BasicType()){
|
|
if(a2==Point::BasicType() ||a2==Points::BasicType())
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType() || a2==Region::BasicType())
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==Region::BasicType()){
|
|
if(a2==Point::BasicType() || a2==Points::BasicType())
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==SimpleLine::BasicType()){
|
|
if(a2==Point::BasicType() ||a2==Points::BasicType())
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType() || a2==Region::BasicType())
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
}
|
|
|
|
ListExpr SpatialMinusTypeMap(ListExpr args){
|
|
string err = "t1 x t2 expected, t_i in {points, points, line, sline, region";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err + ": wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg1)){
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
}
|
|
if(!listutils::isSymbol(arg2)){
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
string a1 = nl->SymbolValue(arg1);
|
|
string a2 = nl->SymbolValue(arg2);
|
|
|
|
if(a1==Point::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==Points::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==Line::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==Region::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
if(a1==SimpleLine::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
|
|
}
|
|
|
|
ListExpr SpatialUnionTypeMap(ListExpr args){
|
|
string err = "t1 x t2 expected, t_i in {points, points, line, sline, region";
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError(err + ": wrong number of arguments");
|
|
}
|
|
ListExpr arg1 = nl->First(args);
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg1)){
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
}
|
|
if(!listutils::isSymbol(arg2)){
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
string a1 = nl->SymbolValue(arg1);
|
|
string a2 = nl->SymbolValue(arg2);
|
|
|
|
if(a1==Point::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
if(a1==Points::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Points::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType())
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
if(a1==Line::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
if(a1==Region::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
if(a1==SimpleLine::BasicType()){
|
|
if(a2==Point::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==Points::BasicType()) return nl->SymbolAtom(SimpleLine::BasicType());
|
|
if(a2==Line::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
if(a2==Region::BasicType()) return nl->SymbolAtom(Region::BasicType());
|
|
if(a2==SimpleLine::BasicType()) return nl->SymbolAtom(Line::BasicType());
|
|
return listutils::typeError(err+ ": second arg not a spatial type");
|
|
}
|
|
return listutils::typeError(err+ ": first arg not a spatial type");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.7 Type mapping function for operator ~crossings~
|
|
|
|
This type mapping function is the one for ~crossings~ operator. This operator
|
|
compute the crossing point of two lines so that the result type is a set
|
|
of points.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialCrossingsTM( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stsline )
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
}
|
|
if(nl->ListLength(args==1)){ // internal crossings of a single line
|
|
arg1 = nl->First( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline){
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
}
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.8 Type mapping function for operator ~single~
|
|
|
|
This type mapping function is used for the ~single~ operator. This
|
|
operator transform a single-element points value to a point.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialSingleMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stpoints)
|
|
return (nl->SymbolAtom( Point::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.9 Type mapping function for operator ~distance~
|
|
|
|
This type mapping function is used for the ~distance~ operator. This
|
|
operator computes the distance between two spatial objects.
|
|
|
|
*/
|
|
|
|
bool isDistanceParam(ListExpr arg){
|
|
|
|
return Point::checkType(arg) ||
|
|
Points::checkType(arg) ||
|
|
Line::checkType(arg) ||
|
|
SimpleLine::checkType(arg) ||
|
|
Rectangle<2>::checkType(arg) ||
|
|
Region::checkType(arg);
|
|
}
|
|
|
|
bool sphericalDistImplemented(ListExpr arg1, ListExpr arg2){
|
|
if(Point::checkType(arg1)){
|
|
return Point::checkType(arg2) || Points::checkType(arg2)
|
|
|| Line::checkType(arg2) || SimpleLine::checkType(arg2);
|
|
}
|
|
if(Points::checkType(arg1)){
|
|
return Point::checkType(arg2) || Points::checkType(arg2)
|
|
|| Line::checkType(arg2);
|
|
}
|
|
if(Line::checkType(arg1)){
|
|
return Point::checkType(arg2) || Points::checkType(arg2);
|
|
}
|
|
if(SimpleLine::checkType(arg1)){
|
|
return Point::checkType(arg2) ;
|
|
}
|
|
if(Rectangle<2>::checkType(arg1)){
|
|
return Rectangle<2>::checkType(arg2);
|
|
}
|
|
if(Region::checkType(arg1)){
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ListExpr
|
|
SpatialDistanceMap( ListExpr args )
|
|
{
|
|
int noargs = nl->ListLength(args);
|
|
string errmsg = "Expected (T1 x T2 [x geoid]) where T1,T2 in "
|
|
"{point,points,line,sline,region,rectangle} [ x geoid].";
|
|
|
|
if( (noargs < 2) || (noargs > 3) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
|
|
ListExpr arg1, arg2;
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
|
|
if(noargs==3){
|
|
if(!Geoid::checkType(nl->Third(args))){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
}
|
|
if(!isDistanceParam(arg1) || !isDistanceParam(arg2)){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
string err2 = "spherical geometry not implemented "
|
|
"for this type combination";
|
|
|
|
// spherical geometry is not implemented for all data types
|
|
if(noargs==3 && !sphericalDistImplemented(arg1,arg2)){
|
|
return listutils::typeError(err2);
|
|
}
|
|
|
|
return listutils::basicSymbol<CcReal>();
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
10.1.10 Type Mapping for distanceSmallerThan
|
|
|
|
Signature is line x line x real x bool -> bool
|
|
|
|
*/
|
|
ListExpr distanceSmallerThanTM(ListExpr args){
|
|
string err = "line x line x real x bool expected";
|
|
if(!nl->HasLength(args,4)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(!Line::checkType(nl->First(args)) ||
|
|
!Line::checkType(nl->Second(args)) ||
|
|
!CcReal::checkType(nl->Third(args)) ||
|
|
!CcBool::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.1.10 Type mapping function for operator ~direction~ and ~heading~
|
|
|
|
This type mapping function is used for the ~direction~ and for the
|
|
~heading~ perator. This operator computes the direction from the first point
|
|
to the second point.
|
|
|
|
---- point x point [x geoid] -> real
|
|
----
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialDirectionHeadingMap( ListExpr args )
|
|
{
|
|
string err = "Expected point x point [x geoid]. ";
|
|
int len = nl->ListLength(args);
|
|
if( (len!=2) && (len!=3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!listutils::isSymbol(nl->First(args),Point::BasicType()) ||
|
|
!listutils::isSymbol(nl->Second(args),Point::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( (len==3) && !listutils::isSymbol(nl->Third(args), Geoid::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
return (nl->SymbolAtom(CcReal::BasicType() ));
|
|
}
|
|
|
|
/*
|
|
10.1.11 Type mapping function for operator ~no\_components~
|
|
|
|
This type mapping function is used for the ~no\_components~ operator. This
|
|
operator computes the number of components of a spatial object. For poins,
|
|
this function returns the number of points contained in the point set.
|
|
For regions, this function returns the faces of the region.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialNoComponentsMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
|
|
if ((SpatialTypeOfSymbol( arg1 ) == stpoints)||
|
|
(SpatialTypeOfSymbol( arg1 ) == stline)||
|
|
(SpatialTypeOfSymbol( arg1 ) == stregion))
|
|
return (nl->SymbolAtom( CcInt::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.11 Type mapping function for operator ~no\_segments~
|
|
|
|
This type mapping function is used for the ~no\_segments~ operator. This
|
|
operator computes the number of segments of a spatial object (lines and
|
|
regions only).
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialNoSegmentsMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
if (Line::checkType( arg1 ) ||
|
|
Region::checkType(arg1) ||
|
|
SimpleLine::checkType(arg1) ||
|
|
DLine::checkType(arg1))
|
|
return listutils::basicSymbol<CcInt>();
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.12 Type mapping function for operator ~size~
|
|
|
|
This type mapping function is used for the ~size~ operator. This operator
|
|
computes the size of the spatial object. For line, the size is the totle length
|
|
of the line segments.
|
|
|
|
----
|
|
{line|sline|region} --> real
|
|
{line|sline} x string --> real
|
|
----
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialSizeMap( ListExpr args )
|
|
{
|
|
string errmsg = "Expected (region) or ({line|sline} [ x geoid ]).";
|
|
int noargs = nl->ListLength( args );
|
|
if ( (noargs<1) || (noargs >2) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
ListExpr arg1 = nl->First( args );
|
|
SpatialType st = SpatialTypeOfSymbol(arg1);
|
|
if( (st!=stregion) && (st!=stline) && (st!=stsline) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( (noargs==2) &&
|
|
( (!listutils::isSymbol(nl->Second(args), Geoid::BasicType()))
|
|
|| (listutils::isSymbol(nl->First(args), Region::BasicType())) ) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
return nl->SymbolAtom(CcReal::BasicType());
|
|
}
|
|
|
|
/*
|
|
10.1.13 Type mapping function for operator ~touchpoints~
|
|
|
|
This type mapping function is used for the ~touchpoints~ operator. This operator
|
|
computes the touchpoints of a region and another region or a line.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialTouchPointsMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return (nl->SymbolAtom( Points::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.17 Type mapping function for operator ~components~
|
|
|
|
This type mapping function is used for the ~components~ operator.
|
|
|
|
*/
|
|
ListExpr SpatialComponentsMap( ListExpr args )
|
|
{
|
|
if( nl->ListLength( args ) == 1 )
|
|
{
|
|
if( SpatialTypeOfSymbol( nl->First( args ) ) == stpoints )
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(Points::BasicType()) );
|
|
|
|
if( SpatialTypeOfSymbol( nl->First( args ) ) == stregion )
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(Region::BasicType()) );
|
|
|
|
if( SpatialTypeOfSymbol( nl->First( args ) ) == stline )
|
|
return nl->TwoElemList( nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(Line::BasicType()) );
|
|
|
|
}
|
|
return listutils::typeError("point, line or region expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.18 Type Mapping function for operator ~vertices~
|
|
|
|
*/
|
|
ListExpr SpatialVerticesMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)==1)
|
|
{
|
|
if( (listutils::isSymbol(nl->First(args),Region::BasicType())) ||
|
|
(listutils::isSymbol(nl->First(args),Line::BasicType())) ){
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
}
|
|
}
|
|
return listutils::typeError("region or line required");
|
|
}
|
|
|
|
/*
|
|
10.1.18 Type Mapping function for operator ~boundary~
|
|
|
|
*/
|
|
ListExpr SpatialBoundaryMap(ListExpr args)
|
|
{
|
|
if(nl->ListLength(args)==1)
|
|
{
|
|
if( SpatialTypeOfSymbol( nl->First( args ) ) == stregion ){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
|
|
if( SpatialTypeOfSymbol( nl->First( args ) ) == stline ){
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
}
|
|
}
|
|
return listutils::typeError("region or line required");
|
|
}
|
|
|
|
/*
|
|
10.1.14 Type mapping function for operator ~commonborder~
|
|
|
|
This type mapping function is used for the ~commonborder~ operator. This
|
|
operator computes the commonborder of two regions.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialCommonBorderMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return (nl->SymbolAtom( Line::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.15 Type mapping function for operator ~bbox~
|
|
|
|
This type mapping function is used for the ~bbox~ operator. This operator
|
|
computes the bbox of a region, which is a ~rect~ (see RectangleAlgebra).
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialBBoxMap( ListExpr args )
|
|
{
|
|
int noargs = nl->ListLength( args );
|
|
string errmsg = "Expected T [x geoid], T in {region, point, line, sline, "
|
|
"points}.";
|
|
ListExpr arg1;
|
|
if ( (noargs<1) || (noargs >2) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
arg1 = nl->First( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) != stregion &&
|
|
SpatialTypeOfSymbol( arg1 ) != stpoint &&
|
|
SpatialTypeOfSymbol( arg1 ) != stline &&
|
|
SpatialTypeOfSymbol( arg1 ) != stpoints &&
|
|
SpatialTypeOfSymbol( arg1 ) != stsline &&
|
|
!Rectangle<2>::checkType(arg1) &&
|
|
!CPoint::checkType(arg1)) {
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( (noargs == 2) &&
|
|
!listutils::isSymbol(nl->Second(args),Geoid::BasicType()) ) {
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
return (nl->SymbolAtom( Rectangle<2>::BasicType() ));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
10.1.16 Type Mapping function for center.
|
|
|
|
The signature is points -> point.
|
|
|
|
*/
|
|
|
|
ListExpr SpatialCenterMap(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("One argument expected");
|
|
}
|
|
ListExpr a1 = nl->First(args);
|
|
if(Points::checkType(a1) || Rectangle<2>::checkType(a1)){
|
|
return listutils::basicSymbol<Point>();
|
|
}
|
|
return listutils::typeError("points or rect expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.17 Type Mapping function for convexhull.
|
|
|
|
The signature is points -> region.
|
|
|
|
*/
|
|
|
|
ListExpr SpatialConvexhullMap(ListExpr args){
|
|
|
|
if( (nl->ListLength(args)==1) &&
|
|
(nl->IsEqual(nl->First(args),Points::BasicType())) ){
|
|
return nl->SymbolAtom(Region::BasicType());
|
|
}
|
|
return listutils::typeError("points expected");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.17 Type mapping function for operator ~windowclipping~
|
|
|
|
This type mapping function is used for the ~windowclipping~ operators. There are
|
|
two kind of operators, one that computes the part of the object that is inside
|
|
the window (windowclippingin), and another one that computes the part that is
|
|
outside of it (windowclippingout).
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialWindowClippingMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
// arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline)
|
|
return (nl->SymbolAtom( Line::BasicType() ));
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion )
|
|
return (nl->SymbolAtom( Region::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.6 Type mapping function for operator ~atpoint~
|
|
|
|
This type mapping function is the one for ~atpoint~ operator. This operator
|
|
receives a line and a point and returns the relative position of the point
|
|
in the line as a real.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialAtPointTypeMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2, arg3;
|
|
if ( nl->ListLength( args ) == 3 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
arg3 = nl->Third( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint &&
|
|
nl->IsEqual( arg3, CcBool::BasicType() ) )
|
|
return (nl->SymbolAtom( CcReal::BasicType() ));
|
|
}
|
|
if (nl->ListLength(args) == 2)
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint)
|
|
return (nl->SymbolAtom( CcReal::BasicType() ));
|
|
}
|
|
return listutils::typeError("Expects sline, point and optional bool.");
|
|
}
|
|
|
|
/*
|
|
10.1.6 Type mapping function for operator ~atposition~
|
|
|
|
This type mapping function is the one for ~atposition~ operator. This operator
|
|
receives a line and a relative position and returns the corresponding point.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialAtPositionTypeMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2, arg3;
|
|
if ( nl->ListLength( args ) == 3 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
arg3 = nl->Third( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
nl->IsEqual( arg2, CcReal::BasicType() ) &&
|
|
nl->IsEqual( arg3, CcBool::BasicType() ) )
|
|
return (nl->SymbolAtom( Point::BasicType() ));
|
|
}
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
nl->IsEqual( arg2, CcReal::BasicType() ) )
|
|
return (nl->SymbolAtom( Point::BasicType() ));
|
|
}
|
|
return listutils::typeError("Expects sline, real and optional bool.");
|
|
}
|
|
|
|
/*
|
|
10.1.6 Type mapping function for operator ~subline~
|
|
|
|
This type mapping function is the one for ~subline~ operator. This operator
|
|
receives a line and two relative positions and returns the corresponding
|
|
sub-line.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialSubLineMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2, arg3, arg4;
|
|
if ( nl->ListLength( args ) == 4 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
arg3 = nl->Third( args );
|
|
arg4 = nl->Fourth( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
nl->IsEqual( arg2, CcReal::BasicType() ) &&
|
|
nl->IsEqual( arg3, CcReal::BasicType() ) &&
|
|
nl->IsEqual( arg4, CcBool::BasicType() ) )
|
|
return (nl->SymbolAtom( SimpleLine::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.6 Type mapping function for operator ~add~
|
|
|
|
This type mapping function is the one for the ~add~ operator.
|
|
The result type is a point.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialAddTypeMap( ListExpr args )
|
|
{
|
|
ListExpr arg1, arg2;
|
|
if ( nl->ListLength( args ) == 2 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint )
|
|
return (nl->SymbolAtom( Point::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.7 Type mapping function for the operators ~getx~ and ~gety~
|
|
|
|
This type mapping function is the one for the ~getx and ~gety operator.
|
|
The result type is a real.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialGetXYMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint )
|
|
return (nl->SymbolAtom( CcReal::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.7 Type mapping function for the operators ~line2region~
|
|
|
|
This type mapping function is the one for the ~line2region~ operator.
|
|
The result type is a region.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialLine2RegionMap( ListExpr args )
|
|
{
|
|
string err = "line expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Region>();
|
|
}
|
|
|
|
/*
|
|
10.1.7 Type mapping function for the operator ~rect2region~
|
|
|
|
This type mapping function is the one for the ~rect2region~ operator.
|
|
The result type is a region.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialRect2RegionMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stbox )
|
|
return (nl->SymbolAtom( Region::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
/*
|
|
10.1.7 Type mapping function for the operator ~create_triangle~
|
|
|
|
---- point x point x point --> region
|
|
----
|
|
|
|
*/
|
|
ListExpr SpatialCreateTriangleTM( ListExpr args ){
|
|
string errmsg = "Expected (point x point x point).";
|
|
if( nl->ListLength( args ) != 3 ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( listutils::isSymbol(nl->First(args),Point::BasicType()) &&
|
|
listutils::isSymbol(nl->Second(args),Point::BasicType()) &&
|
|
listutils::isSymbol(nl->Third(args),Point::BasicType()) ){
|
|
return (nl->SymbolAtom( Region::BasicType() ));
|
|
}
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
|
|
/*
|
|
10.1.8 Type mapping function for the operator ~area~
|
|
|
|
This type mapping function is the one for the ~area~ operator.
|
|
The result type is a real.
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialAreaMap( ListExpr args )
|
|
{
|
|
ListExpr arg1;
|
|
if ( nl->ListLength( args ) == 1 )
|
|
{
|
|
arg1 = nl->First( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion )
|
|
return (nl->SymbolAtom( CcReal::BasicType() ));
|
|
}
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.9 Type mapping for the ~polylines~ operator
|
|
|
|
The ~polylines~ operator takes a complex line and creates
|
|
a set of simple polylines from it. Thus, the signature of
|
|
this operator is line -> stream(line)
|
|
|
|
*/
|
|
ListExpr PolylinesMap(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
if( (len!=2) &&len!=3){
|
|
return listutils::typeError("line x bool [x points] expected");
|
|
}
|
|
if(!nl->IsEqual(nl->First(args),Line::BasicType()) ||
|
|
!nl->IsEqual(nl->Second(args),CcBool::BasicType())){
|
|
return listutils::typeError("line x bool expected");
|
|
}
|
|
if(len==3){
|
|
if(!nl->IsEqual(nl->Third(args),Points::BasicType())){
|
|
return listutils::typeError("line x bool [x points] expected");
|
|
}
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(Line::BasicType()));
|
|
}
|
|
|
|
|
|
/*
|
|
10.1.10 Type mapping for the ~simplify~ operator
|
|
|
|
*/
|
|
ListExpr SimplifyTypeMap(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
if((len!=2) && (len!=3) && (len!=4)){
|
|
return listutils::typeError("invalid number of"
|
|
" arguments (has to be 2,3, or 4 )");
|
|
}
|
|
if(!nl->IsEqual(nl->First(args),Line::BasicType()) ||
|
|
!nl->IsEqual(nl->Second(args),CcReal::BasicType())){
|
|
return listutils::typeError("line x real [x points] [x geoid] expected");
|
|
}
|
|
if(len==2){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
ListExpr third = nl->Third(args);
|
|
if(len==3){
|
|
if(!Points::checkType(third) && !Geoid::checkType(third)){
|
|
return listutils::typeError("line x real [x points][x geoid] expected");
|
|
} else {
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
}
|
|
// len == 4
|
|
if(!Points::checkType(third) || !Geoid::checkType(nl->Fourth(args))){
|
|
return listutils::typeError("line x real[ x points][x geoid] expected");
|
|
}
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
10.1.11 Type Mapping for the ~segments~ operator
|
|
|
|
*/
|
|
ListExpr SegmentsTypeMap(ListExpr args){
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("Invalid number of arguments");
|
|
}
|
|
if( !Line::checkType(nl->First(args))
|
|
&& !DLine::checkType(nl->First(args))){
|
|
return listutils::typeError("line expected");
|
|
}
|
|
return nl->TwoElemList(
|
|
nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->First(args)
|
|
);
|
|
}
|
|
|
|
/*
|
|
10.1.12 Type Mapping for the ~get~ operator
|
|
|
|
Signatur is points x int -> point
|
|
|
|
*/
|
|
ListExpr GetTypeMap(ListExpr args){
|
|
if( (nl->ListLength(args)==2) &&
|
|
(nl->IsEqual(nl->First(args),Points::BasicType())) &&
|
|
(nl->IsEqual(nl->Second(args),CcInt::BasicType()))){
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
return listutils::typeError("points x int expected");
|
|
}
|
|
|
|
/*
|
|
10.1.13 Type Mapping for the ~realminize~ operator
|
|
|
|
Signatur is line -> line
|
|
|
|
*/
|
|
ListExpr RealminizeTypeMap(ListExpr args){
|
|
if( (nl->ListLength(args)==1) &&
|
|
(nl->IsEqual(nl->First(args),Line::BasicType()))){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
return listutils::typeError("line expected");
|
|
}
|
|
|
|
/*
|
|
10.1.14 Type Mapping for the ~makeline~ operator
|
|
|
|
Signature is point x point -> line
|
|
|
|
*/
|
|
ListExpr MakeLineTypeMap(ListExpr args){
|
|
int len;
|
|
if((len = nl->ListLength(args))!=2){
|
|
return listutils::typeError("two arguments expected, but got " +
|
|
stringutils::int2str(len));
|
|
}
|
|
if(nl->IsEqual(nl->First(args),Point::BasicType()) &&
|
|
nl->IsEqual(nl->Second(args),Point::BasicType())){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
} else {
|
|
return listutils::typeError("point x point expected");
|
|
}
|
|
}
|
|
|
|
/*
|
|
10.1.14 Type Mapping for the ~makesline~ operator
|
|
|
|
Signature is point x point -> sline
|
|
|
|
*/
|
|
ListExpr MakeSLineTypeMap(ListExpr args){
|
|
int len;
|
|
if((len = nl->ListLength(args))!=2){
|
|
return listutils::typeError("two arguments expected, but got "
|
|
+ stringutils::int2str(len));
|
|
}
|
|
if(nl->IsEqual(nl->First(args),Point::BasicType()) &&
|
|
nl->IsEqual(nl->Second(args),Point::BasicType())){
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
} else {
|
|
return listutils::typeError("point x point expected");
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
~CommonBorder2TypeMap~
|
|
|
|
Signature: ~region~ [x] ~region~ [->] ~line~
|
|
|
|
*/
|
|
|
|
ListExpr CommonBorder2TypeMap(ListExpr args){
|
|
|
|
if(nl->ListLength(args)!=2){
|
|
return listutils::typeError("Wrong number of arguments,"
|
|
" region x region expected");
|
|
}
|
|
if(nl->IsEqual(nl->First(args),Region::BasicType()) &&
|
|
nl->IsEqual(nl->Second(args),Region::BasicType())){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
return listutils::typeError(" region x region expected");
|
|
}
|
|
|
|
/*
|
|
~toLineTypeMap~
|
|
|
|
Signature is : sline [->] line
|
|
|
|
*/
|
|
|
|
ListExpr toLineTypeMap(ListExpr args){
|
|
const string err = "sline expected";
|
|
if(nl->ListLength(args)!=1){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(nl->IsEqual(nl->First(args),SimpleLine::BasicType())){
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
ErrorReporter::ReportError(err);
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
|
|
/*
|
|
~fromLineTypeMap~
|
|
|
|
Signature is : line [->] sline
|
|
|
|
*/
|
|
|
|
ListExpr fromLineTypeMap(ListExpr args){
|
|
if ((nl->ListLength(args) == 2 &&
|
|
nl->IsEqual(nl->First(args),Line::BasicType()) &&
|
|
nl->IsEqual(nl->Second(args),CcBool::BasicType())) ||
|
|
(nl->ListLength(args) == 1 &&
|
|
nl->IsEqual(nl->First(args),Line::BasicType())))
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
else
|
|
return listutils::typeError("line or line and bool expected");
|
|
}
|
|
|
|
/*
|
|
~isCycleTypeMap~
|
|
|
|
Signature is : sline [->] bool
|
|
|
|
*/
|
|
|
|
ListExpr isCycleTypeMap(ListExpr args){
|
|
const string err = "sline expected";
|
|
if(nl->ListLength(args)!=1){
|
|
ErrorReporter::ReportError(err);
|
|
return nl->TypeError();
|
|
}
|
|
if(nl->IsEqual(nl->First(args),SimpleLine::BasicType())){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
ErrorReporter::ReportError(err);
|
|
return listutils::typeError("");
|
|
}
|
|
|
|
|
|
/*
|
|
Type Mapping for ~utm~
|
|
|
|
*/
|
|
ListExpr utmTypeMap(ListExpr args){
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
string err = "spatial type expected";
|
|
if(nl->AtomType(arg)!=SymbolType){
|
|
return listutils::typeError(err);
|
|
}
|
|
string t = nl->SymbolValue(arg);
|
|
if(t==Point::BasicType() || t==Points::BasicType() ){
|
|
// line and region not implemented yet
|
|
return nl->SymbolAtom(t);
|
|
}
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
/*
|
|
Type Mapping for ~gk~
|
|
|
|
*/
|
|
ListExpr gkTypeMap(ListExpr args){
|
|
int len = nl->ListLength(args);
|
|
if( (len < 1) || (len > 2) ){
|
|
return listutils::typeError(" 1 or 2 arguments expected");
|
|
}
|
|
string err = "point, points, (s)line, or region [ x int] expected";
|
|
ListExpr arg = nl->First(args);
|
|
if(!listutils::isSymbol(arg)){
|
|
return listutils::typeError(err);
|
|
}
|
|
string t = nl->SymbolValue(arg);
|
|
if(!( t==Point::BasicType() || t==Points::BasicType() ||
|
|
t==Line::BasicType() || t==Region::BasicType() ||
|
|
t==SimpleLine::BasicType() ||
|
|
t==Rectangle<2>::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( (len==2) && listutils::isSymbol(nl->Second(args),CcInt::BasicType()) ){
|
|
if( t==Point::BasicType() || t==Points::BasicType() ||
|
|
t==Line::BasicType() || t==Region::BasicType() ||
|
|
t==SimpleLine::BasicType()){
|
|
return nl->SymbolAtom(t); // Zone provided by user
|
|
}
|
|
} else if (len==1){
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->IntAtom(2)), // standard zone for Hagen
|
|
nl->SymbolAtom(t));
|
|
}
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
/*
|
|
Type Mapping for ~reverseGk~
|
|
|
|
*/
|
|
ListExpr reverseGkTypeMap(ListExpr args){
|
|
string err = "point, points, line, or region expected";
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!listutils::isSymbol(arg)){
|
|
return listutils::typeError(err);
|
|
}
|
|
string v = nl->SymbolValue(arg);
|
|
if( (v!=Point::BasicType()) && (v!=Points::BasicType()) &&
|
|
(v!=Line::BasicType()) && (v!=Region::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->SymbolAtom(v);
|
|
}
|
|
|
|
/*
|
|
Type Mapping for ~collect\_line~ and ~collect\_sline~
|
|
|
|
----
|
|
((stream point)) -> line
|
|
((stream sline)) -> line
|
|
((stream line)) -> line
|
|
|
|
|
|
((stream point)) -> sline
|
|
((stream sline)) -> sline
|
|
((stream line)) -> sline
|
|
----
|
|
|
|
*/
|
|
ListExpr SpatialCollectLineTypeMap(ListExpr args){
|
|
if( nl->IsEmpty(args) || nl->IsAtom(args) || nl->ListLength(args) != 2){
|
|
return listutils::typeError("Expects exactly 2 arguments.");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
if(!Stream<Attribute>::checkType(stream)){
|
|
return listutils::typeError("Expects a DATA stream.");
|
|
}
|
|
ListExpr T = nl->Second(stream);
|
|
set<string> r;
|
|
r.insert(Point::BasicType());
|
|
r.insert(SimpleLine::BasicType());
|
|
r.insert(Line::BasicType());
|
|
|
|
if(!listutils::isASymbolIn(T,r)){
|
|
return listutils::typeError("Expects stream element type to be one of "
|
|
"{point, sline, line}.");
|
|
}
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!CcBool::checkType(arg2)){
|
|
return listutils::typeError("Second argument must be bool.");
|
|
}
|
|
return nl->SymbolAtom(Line::BasicType());
|
|
}
|
|
|
|
ListExpr SpatialCollectSLineTypeMap(ListExpr args){
|
|
if( nl->IsEmpty(args) || nl->IsAtom(args) || nl->ListLength(args) != 2){
|
|
return listutils::typeError("Expects exactly 2 arguments.");
|
|
}
|
|
ListExpr stream = nl->First(args);
|
|
if(!listutils::isDATAStream(stream)){
|
|
return listutils::typeError("Expects a DATA stream.");
|
|
}
|
|
ListExpr T = nl->Second(stream);
|
|
set<string> r;
|
|
r.insert(Point::BasicType());
|
|
r.insert(SimpleLine::BasicType());
|
|
r.insert(Line::BasicType());
|
|
|
|
if(!listutils::isASymbolIn(T,r)){
|
|
return listutils::typeError("Expects stream element type to be one of "
|
|
"{point, sline, line}.");
|
|
}
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg2,CcBool::BasicType())){
|
|
return listutils::typeError("Second argument must be bool.");
|
|
}
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
}
|
|
|
|
ListExpr SpatialCollectPointsTM(ListExpr args){
|
|
string err = " {stream(point), stream(points)} x bool expected";
|
|
if(nl->ListLength(args) != 2){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr arg1 = nl->First(args);
|
|
if(nl->ListLength(arg1) != 2){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr s = nl->First(arg1);
|
|
ListExpr p = nl->Second(arg1);
|
|
if(!listutils::isSymbol(s,Symbol::STREAM())){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!listutils::isSymbol(p,Point::BasicType()) &&
|
|
!listutils::isSymbol(p,Points::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr arg2 = nl->Second(args);
|
|
if(!listutils::isSymbol(arg2,CcBool::BasicType())){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->SymbolAtom(Points::BasicType());
|
|
}
|
|
|
|
ListExpr SpatialTMSetStartSmaller(ListExpr args){
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("Expected (sline x bool).");
|
|
}
|
|
if(listutils::isSymbol(nl->First(args),SimpleLine::BasicType()) &&
|
|
listutils::isSymbol(nl->Second(args),CcBool::BasicType())){
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
}
|
|
return listutils::typeError("Expected (sline x bool).");
|
|
}
|
|
|
|
ListExpr SpatialTMGetStartSmaller(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("Expected: sline.");
|
|
}
|
|
if(listutils::isSymbol(nl->First(args),SimpleLine::BasicType())){
|
|
return nl->SymbolAtom(CcBool::BasicType());
|
|
}
|
|
return listutils::typeError("Expected: sline.");
|
|
}
|
|
|
|
ListExpr SpatialTMCreateSline(ListExpr args){
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("Expected (point x point).");
|
|
}
|
|
if(listutils::isSymbol(nl->First(args),Point::BasicType()) &&
|
|
listutils::isSymbol(nl->Second(args),Point::BasicType())){
|
|
return nl->SymbolAtom(SimpleLine::BasicType());
|
|
}
|
|
return listutils::typeError("Expected (point x point).");
|
|
}
|
|
|
|
/*
|
|
10.3 Selection functions
|
|
|
|
A selection function is quite similar to a type mapping function. The only
|
|
difference is that it doesn't return a type but the index of a value
|
|
mapping function being able to deal with the respective combination of
|
|
input parameter types.
|
|
|
|
Note that a selection function does not need to check the correctness of
|
|
argument types; it has already been checked by the type mapping function that it
|
|
is applied to correct arguments.
|
|
|
|
10.3.2 Selection function ~SpatialSelectIsEmpty~
|
|
|
|
It is used for the ~isempty~ operator
|
|
|
|
*/
|
|
int
|
|
SpatialSelectIsEmpty( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion )
|
|
return 3;
|
|
|
|
if(nl->IsEqual(arg1,SimpleLine::BasicType())){
|
|
return 4;
|
|
}
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.3 Selection function ~SpatialSelectCompare~
|
|
|
|
It is used for compare operators ($=$, $\neq$, $<$, $>$, $\geq$, $\leq$)
|
|
|
|
*/
|
|
int
|
|
SpatialSelectCompare( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoint )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 3;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stsline )
|
|
return 4;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
int SpatialSelectEqual(ListExpr args){
|
|
ListExpr a1 = nl->First(args);
|
|
ListExpr a2 = nl->Second(args);
|
|
string s1 = nl->SymbolValue(a1);
|
|
if(nl->Equal(a1,a2)){
|
|
if(s1 == Point::BasicType()) return 0;
|
|
if(s1 == Points::BasicType()) return 1;
|
|
if(s1 == Line::BasicType()) return 2;
|
|
if(s1 == Region::BasicType()) return 3;
|
|
if(s1 == SimpleLine::BasicType()) return 4;
|
|
return -1;
|
|
} else {
|
|
string s2 = nl->SymbolValue(a2);
|
|
if( (s1==Point::BasicType()) && (s2==Points::BasicType())) return 5;
|
|
if((s1==Points::BasicType()) && (s2==Point::BasicType())) return 6;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int SpatialSelectIsLess(ListExpr args){
|
|
ListExpr a1 = nl->First(args);
|
|
ListExpr a2 = nl->Second(args);
|
|
string s1 = nl->SymbolValue(a1);
|
|
if(nl->Equal(a1,a2)){
|
|
if(s1 == Point::BasicType()) return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
10.3.4 Selection function ~SpatialSelectIntersects~
|
|
|
|
It is used for the operator ~intersects~
|
|
|
|
*/
|
|
int
|
|
SpatialSelectIntersects( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 3;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 4;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 5;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 6;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 7;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 8;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stsline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stsline )
|
|
return 9;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.5 Selection function ~SpatialSelectInside~
|
|
|
|
This select function is used for the ~inside~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectInside( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoint &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 3;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 4;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 5;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 6;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 7;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 8;
|
|
|
|
if (SpatialTypeOfSymbol(arg1) == stpoint &&
|
|
SpatialTypeOfSymbol(arg2) == stsline)
|
|
return 9;
|
|
|
|
if (SpatialTypeOfSymbol(arg1) == stsline &&
|
|
SpatialTypeOfSymbol(arg2) == stsline)
|
|
return 10;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.6 Selection function ~SpatialSelectTopology~
|
|
|
|
This select function is used for the ~attached~ , and ~overlaps~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectTopology( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 3;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 4;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 5;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 6;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 7;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 8;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.6 Selection function ~SpatialSelectAdjacent~
|
|
|
|
This select function is used for the ~adjacent~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectAdjacent( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stpoints )
|
|
return 2;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 3;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 4;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
|
|
int SpatialSetOpSelect(ListExpr args){
|
|
string a1 = nl->SymbolValue(nl->First(args));
|
|
string a2 = nl->SymbolValue(nl->Second(args));
|
|
|
|
if(a1==Point::BasicType()){
|
|
if(a2==Point::BasicType()) return 0;
|
|
if(a2==Points::BasicType()) return 1;
|
|
if(a2==Line::BasicType()) return 2;
|
|
if(a2==Region::BasicType()) return 3;
|
|
if(a2==SimpleLine::BasicType()) return 4;
|
|
return -1;
|
|
}
|
|
if(a1==Points::BasicType()){
|
|
if(a2==Point::BasicType()) return 5;
|
|
if(a2==Points::BasicType()) return 6;
|
|
if(a2==Line::BasicType()) return 7;
|
|
if(a2==Region::BasicType()) return 8;
|
|
if(a2==SimpleLine::BasicType()) return 9;
|
|
return -1;
|
|
}
|
|
if(a1==Line::BasicType()){
|
|
if(a2==Point::BasicType()) return 10;
|
|
if(a2==Points::BasicType()) return 11;
|
|
if(a2==Line::BasicType()) return 12;
|
|
if(a2==Region::BasicType()) return 13;
|
|
if(a2==SimpleLine::BasicType()) return 14;
|
|
return -1;
|
|
}
|
|
|
|
if(a1==Region::BasicType()){
|
|
if(a2==Point::BasicType()) return 15;
|
|
if(a2==Points::BasicType()) return 16;
|
|
if(a2==Line::BasicType()) return 17;
|
|
if(a2==Region::BasicType()) return 18;
|
|
if(a2==SimpleLine::BasicType()) return 19;
|
|
return -1;
|
|
}
|
|
|
|
if (a1 == SimpleLine::BasicType()){
|
|
if(a2==Point::BasicType()) return 20;
|
|
if(a2==Points::BasicType()) return 21;
|
|
if(a2==Line::BasicType()) return 22;
|
|
if(a2==Region::BasicType()) return 23;
|
|
if(a2==SimpleLine::BasicType()) return 24;
|
|
return -1;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
10.3.13 Selection function ~SpatialSelectDistance~
|
|
|
|
This select function is used for the ~distance~ operator.
|
|
|
|
*/
|
|
int getDistancePos(ListExpr arg){
|
|
if(Point::checkType(arg)) return 0;
|
|
if(Points::checkType(arg)) return 1;
|
|
if(Line::checkType(arg)) return 2;
|
|
if(SimpleLine::checkType(arg)) return 3;
|
|
if(Rectangle<2>::checkType(arg)) return 4;
|
|
if(Region::checkType(arg)) return 5;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int
|
|
SpatialSelectDistance( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
int p1 = getDistancePos(arg1);
|
|
int p2 = getDistancePos(arg2);
|
|
if(p1<0 || p2 < 0 ) return -1;
|
|
return p1*6 + p2;
|
|
}
|
|
|
|
/*
|
|
10.3.15 Selection function ~SpatialSelectNoComponents~
|
|
|
|
This select function is used for the ~no\_components~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectNoComponents( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stpoints)
|
|
return 0;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stline)
|
|
return 1;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stregion)
|
|
return 2;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.16 Selection function ~SpatialSelectNoSegments~
|
|
|
|
This select function is used for the ~no\_segments~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectNoSegments( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
if(Line::checkType(arg1) ) return 0;
|
|
if(Region::checkType(arg1)) return 1;
|
|
if(SimpleLine::checkType(arg1)) return 2;
|
|
if(DLine::checkType(arg1)) return 3;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
10.3.16 Selection function ~SpatialSelectBBox~
|
|
|
|
This select function is used for the ~bbox~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectBBox( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stpoint)
|
|
return 0;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stpoints)
|
|
return 1;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stline)
|
|
return 2;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stregion)
|
|
return 3;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stsline)
|
|
return 4;
|
|
if(Rectangle<2>::checkType(arg1)){
|
|
return 5;
|
|
}
|
|
if (CPoint::checkType(arg1)) {
|
|
return 6;
|
|
}
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.17 Selection function ~SpatialSelectTouchPoints~
|
|
|
|
This select function is used for the ~touchpoints~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectTouchPoints( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stline )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion &&
|
|
SpatialTypeOfSymbol( arg2 ) == stregion )
|
|
return 2;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.17 Selection function ~SpatialComponentsSelect~
|
|
|
|
This select function is used for the ~components~ operator.
|
|
|
|
*/
|
|
int
|
|
SpatialComponentsSelect( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stpoints )
|
|
return 0;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stregion )
|
|
return 1;
|
|
|
|
if ( SpatialTypeOfSymbol( arg1 ) == stline )
|
|
return 2;
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
|
|
/*
|
|
10.3.19 Selection function ~SpatialSelectWindowClipping~
|
|
|
|
This select function is used for the ~windowclipping(in)(out)~ operators.
|
|
|
|
*/
|
|
int
|
|
SpatialSelectWindowClipping( ListExpr args )
|
|
{
|
|
ListExpr arg1 = nl->First( args );
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stline)
|
|
return 0;
|
|
|
|
if (SpatialTypeOfSymbol( arg1 ) == stregion)
|
|
return 1;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.19 Selection function ~SpatialVerticesSelect~
|
|
|
|
This select function is used for the ~vertices~ operator.
|
|
|
|
*/
|
|
int SpatialVerticesSelect( ListExpr args )
|
|
{
|
|
if( nl->IsEqual(nl->First(args), Line::BasicType()) )
|
|
return 0;
|
|
|
|
if( nl->IsEqual(nl->First(args), Region::BasicType()) )
|
|
return 1;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.19 Selection function ~SpatialBoundarySelect~
|
|
|
|
This select function is used for the ~vertices~ operator.
|
|
|
|
*/
|
|
int SpatialBoundarySelect( ListExpr args )
|
|
{
|
|
if( nl->IsEqual(nl->First(args), Region::BasicType()) )
|
|
return 0;
|
|
|
|
if( nl->IsEqual(nl->First(args), Line::BasicType()) )
|
|
return 1;
|
|
|
|
return -1; // This point should never be reached
|
|
}
|
|
|
|
/*
|
|
10.3.20 Selection function for the simplify operator
|
|
|
|
*/
|
|
int SpatialSimplifySelect(ListExpr args){
|
|
if(nl->ListLength(args)==2){ // line x real
|
|
return 0;
|
|
} else if (Points::checkType(nl->Third(args))){
|
|
return 1; // line x real x points [x geoid]
|
|
} else {
|
|
return 0; //line x real x geoid
|
|
}
|
|
|
|
}
|
|
|
|
int SpatialAtPointSelect(ListExpr args){
|
|
if (nl->ListLength(args) == 3)
|
|
return 0;
|
|
if (nl->ListLength(args) == 2)
|
|
return 1;
|
|
return -1;
|
|
}
|
|
|
|
int SpatialAtPositionSelect(ListExpr args){
|
|
if (nl->ListLength(args) == 3)
|
|
return 0;
|
|
if (nl->ListLength(args) == 2)
|
|
return 1;
|
|
return -1;
|
|
}
|
|
|
|
int SpatialSelectSize(ListExpr args){
|
|
SpatialType st = SpatialTypeOfSymbol(nl->First(args));
|
|
if(st==stline){
|
|
return 0;
|
|
}
|
|
if(st==stregion){
|
|
return 1;
|
|
}
|
|
if(st==stsline){
|
|
return 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int SpatialSelectCrossings(ListExpr args){
|
|
SpatialType st = SpatialTypeOfSymbol(nl->First(args));
|
|
if(nl->ListLength(args)==2){
|
|
if(st==stline){
|
|
return 0;
|
|
}
|
|
if(st==stsline){
|
|
return 1;
|
|
}
|
|
} else { // one singe argument = line
|
|
return 2;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
int utmSelect(ListExpr args){
|
|
string t = nl->SymbolValue(nl->First(args));
|
|
if(t==Point::BasicType()) return 0;
|
|
if(t==Points::BasicType()) return 1;
|
|
return -1;
|
|
}
|
|
|
|
int gkSelect(ListExpr args){
|
|
string t = nl->SymbolValue(nl->First(args));
|
|
if(t==Point::BasicType()) return 0;
|
|
if(t==Points::BasicType()) return 1;
|
|
if(t==Line::BasicType()) return 2;
|
|
if(t==Region::BasicType()) return 3;
|
|
if(t==SimpleLine::BasicType()) return 4;
|
|
if(t==Rectangle<2>::BasicType()) return 5;
|
|
return -1;
|
|
}
|
|
|
|
int reverseGkSelect(ListExpr args){
|
|
string t = nl->SymbolValue(nl->First(args));
|
|
if(t==Point::BasicType()) return 0;
|
|
if(t==Points::BasicType()) return 1;
|
|
if(t==Line::BasicType()) return 2;
|
|
if(t==Region::BasicType()) return 3;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int SpatialCollectLineSelect(ListExpr args){
|
|
ListExpr T = nl->Second(nl->First(args));
|
|
if(listutils::isSymbol(T, Point::BasicType())) return 0;
|
|
if(listutils::isSymbol(T, SimpleLine::BasicType())) return 1;
|
|
if(listutils::isSymbol(T, Line::BasicType())) return 2;
|
|
return -1;
|
|
}
|
|
|
|
int SpatialCollectPointsSelect(ListExpr args){
|
|
ListExpr T = nl->Second(nl->First(args));
|
|
if(listutils::isSymbol(T,Point::BasicType())) return 0;
|
|
if(listutils::isSymbol(T,Points::BasicType())) return 1;
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4 Value mapping functions
|
|
|
|
A value mapping function implements an operator's main functionality: it takes
|
|
input arguments and computes the result. Each operator consists of at least
|
|
one value mapping function. In the case of overloaded operators there are
|
|
several value mapping functions, one for each possible combination of input
|
|
parameter types.
|
|
|
|
10.4.1 Value mapping functions of operator ~isempty~
|
|
|
|
*/
|
|
template<class T>
|
|
int SpatialIsEmpty(Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage( s );
|
|
(static_cast<CcBool*>(result.addr))->Set(true,
|
|
static_cast<T*>(args[0].addr)->IsEmpty());
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.2 Value mapping functions of operator ~$=$~
|
|
|
|
*/
|
|
template<class T1, class T2>
|
|
int
|
|
SpatialEqual( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T1* a1 = static_cast<T1*>(args[0].addr);
|
|
T2* a2 = static_cast<T2*>(args[1].addr);
|
|
if(!a1->IsDefined() || !a2->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
bool e = (*a1) == (*a2);
|
|
res->Set(true,e);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.2 Value mapping functions of operator ~$<$~
|
|
|
|
*/
|
|
|
|
template<class T1, class T2>
|
|
int SpatialIsLess(Word* args, Word& result, int message, Word& local,
|
|
Supplier s)
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
T1* a1 = static_cast<T1*>(args[0].addr);
|
|
T2* a2 = static_cast<T2*>(args[1].addr);
|
|
if(!a1->IsDefined() || !a2->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
bool e = (*a1) < (*a2);
|
|
res->Set(true,e);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.3 Value mapping functions of operator ~$\neq$~
|
|
|
|
*/
|
|
template<class T>
|
|
int
|
|
SpatialNotEqual( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
T* a1 = static_cast<T*>(args[0].addr);
|
|
T* a2 = static_cast<T*>(args[1].addr);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
if(!a1->IsDefined() || !a2->IsDefined()){
|
|
res->Set(false,false);
|
|
return 0;
|
|
}
|
|
res->Set(true, (*a1) != (*a2));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.8 Value mapping functions of operator ~intersects~
|
|
|
|
*/
|
|
template<class A, class B,bool symm>
|
|
int SpatialIntersectsVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
A* a;
|
|
B* b;
|
|
if(symm){
|
|
a = static_cast<A*>(args[1].addr);
|
|
b = static_cast<B*>(args[0].addr);
|
|
} else {
|
|
a = static_cast<A*>(args[0].addr);
|
|
b = static_cast<B*>(args[1].addr);
|
|
}
|
|
|
|
if(!a->IsDefined() || !b->IsDefined()){
|
|
res->Set(false,false);
|
|
} else {
|
|
res->Set(true,a->Intersects(*b));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.9 Value mapping functions of operator ~inside~
|
|
|
|
*/
|
|
template<class First, class Second>
|
|
int SpatialInsideGeneric( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
First* arrgh1 = static_cast<First*>(args[0].addr);
|
|
Second* arrgh2 = static_cast<Second*>(args[1].addr);
|
|
if( arrgh1->IsDefined() && arrgh2->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, arrgh1->Inside( *arrgh2 ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.10 Value mapping functions of operator ~adjacent~
|
|
|
|
*/
|
|
int
|
|
SpatialAdjacent_psr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Points* ps = static_cast<Points*>(args[0].addr);
|
|
Region* r = static_cast<Region*>(args[1].addr);
|
|
if( ps->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, ps->Adjacent( *r ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAdjacent_rps( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region* r = static_cast<Region*>(args[0].addr);
|
|
Points* ps = static_cast<Points*>(args[1].addr);
|
|
if( ps->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, ps->Adjacent( *r ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAdjacent_lr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Line* l = static_cast<Line*>(args[0].addr);
|
|
Region* r = static_cast<Region*>(args[1].addr);
|
|
if( l->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, l->Adjacent( *r ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAdjacent_rl( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region* r = static_cast<Region*>(args[0].addr);
|
|
Line* l = static_cast<Line*>(args[1].addr);
|
|
if( l->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, l->Adjacent( *r ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAdjacent_rr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region* r1 = static_cast<Region*>(args[0].addr);
|
|
Region* r2 = static_cast<Region*>(args[1].addr);
|
|
if( r1->IsDefined() && r2->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, r1->Adjacent( *r2 ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.12 Value mapping functions of operator ~overlaps~
|
|
|
|
*/
|
|
int
|
|
SpatialOverlaps_rr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region* r1 = static_cast<Region*>(args[0].addr);
|
|
Region* r2 = static_cast<Region*>(args[1].addr);
|
|
if( r1->IsDefined() && r2->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true,r1->Overlaps( *r2 ) );
|
|
else
|
|
((CcBool *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.13 Value mapping functions of operator ~onborder~
|
|
|
|
*/
|
|
int
|
|
SpatialOnBorder_pr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Point* p = static_cast<Point*>(args[0].addr);
|
|
Region* r = static_cast<Region*>(args[1].addr);
|
|
if( p->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->Set( true, r->OnBorder( *p ) );
|
|
else
|
|
((CcBool *)result.addr)->Set( false, false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.14 Value mapping functions of operator ~ininterior~
|
|
|
|
*/
|
|
int
|
|
SpatialInInterior_pr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Point* p = static_cast<Point*>(args[0].addr);
|
|
Region* r = static_cast<Region*>(args[1].addr);
|
|
if( p->IsDefined() && r->IsDefined() )
|
|
((CcBool *)result.addr)->
|
|
Set( true, r->InInterior( *p ) );
|
|
else
|
|
((CcBool *)result.addr)->Set( false, false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.15 Value mapping functions of operator ~intersection~
|
|
|
|
*/
|
|
template<class A1, class A2, class R>
|
|
int SpatialIntersectionGeneric(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage( s );
|
|
A1* arg1 = static_cast<A1*>(args[0].addr);
|
|
A2* arg2 = static_cast<A2*>(args[1].addr);
|
|
R* res = static_cast<R*>(result.addr);
|
|
arg1->Intersection(*arg2, *res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.4.16 Value mapping functions of operator ~minus~
|
|
|
|
*/
|
|
|
|
template<class A1, class A2, class R>
|
|
int SpatialMinusGeneric(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage( s );
|
|
A1* arg1 = static_cast<A1*>(args[0].addr);
|
|
A2* arg2 = static_cast<A2*>(args[1].addr);
|
|
R* res = static_cast<R*>(result.addr);
|
|
assert(arg1);
|
|
assert(arg2);
|
|
assert(res);
|
|
arg1->Minus(*arg2, *res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.17 Value mapping functions of operator ~union~
|
|
|
|
*/
|
|
|
|
template<class A1, class A2, class R>
|
|
int SpatialUnionGeneric(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage( s );
|
|
A1* arg1 = static_cast<A1*>(args[0].addr);
|
|
A2* arg2 = static_cast<A2*>(args[1].addr);
|
|
R* res = static_cast<R*>(result.addr);
|
|
arg1->Union(*arg2, *res);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.18 Value mapping functions of operator ~crossings~
|
|
|
|
*/
|
|
template<class Ltype>
|
|
int
|
|
SpatialCrossings( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Ltype *cl1=((Ltype*)args[0].addr),
|
|
*cl2=((Ltype*)args[1].addr);
|
|
cl1->Crossings( *cl2, *(Points*)result.addr );
|
|
return 0;
|
|
}
|
|
|
|
|
|
int
|
|
SpatialCrossings_single( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage( s );
|
|
Line* cl=((Line*)args[0].addr);
|
|
cl->Crossings( *(Points*)result.addr );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.19 Value mapping functions of operator ~single~
|
|
|
|
*/
|
|
int
|
|
SpatialSingle_ps( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Points *ps = ((Points*)args[0].addr);
|
|
Point p(true);
|
|
if( ps->IsDefined() && (ps->Size() == 1) )
|
|
{
|
|
ps->Get( 0, p );
|
|
*(Point*)result.addr = p;
|
|
}
|
|
else
|
|
((Point *)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.20 Value mapping functions of operator ~distance~
|
|
|
|
*/
|
|
template<class A, class B, bool symm>
|
|
int SpatialDistance( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
|
|
result = qp->ResultStorage( s );
|
|
CcReal* res = static_cast<CcReal*>(result.addr);
|
|
const Geoid* geoid =
|
|
(qp->GetNoSons(s) ==3)?static_cast<Geoid*>(args[2].addr):0;
|
|
A* arg1=0;
|
|
B* arg2=0;
|
|
if(symm){
|
|
arg1 = static_cast<A*>(args[1].addr);
|
|
arg2 = static_cast<B*>(args[0].addr);
|
|
} else {
|
|
arg1 = static_cast<A*>(args[0].addr);
|
|
arg2 = static_cast<B*>(args[1].addr);
|
|
}
|
|
if(!arg1->IsDefined() || !arg2->IsDefined() ||
|
|
arg1->IsEmpty() || arg2->IsEmpty() || (geoid && !geoid->IsDefined()) ){
|
|
res->SetDefined(false);
|
|
} else {
|
|
double dist = arg1->Distance(*arg2,geoid);
|
|
res->Set(true,dist);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.21 Value Mapping of operator ~distanceSmallerThan~
|
|
|
|
Up to now, it's only emplemented for the Line type.
|
|
|
|
*/
|
|
template<class A , class B>
|
|
int distanceSmallerThanVM(Word* args,
|
|
Word& result,
|
|
int message,
|
|
Word& local,
|
|
Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
|
|
A* a1 = (A*) args[0].addr;
|
|
B* a2 = (B*) args[1].addr;
|
|
CcReal* d = (CcReal*) args[2].addr;
|
|
CcBool* b = (CcBool*) args[3].addr;
|
|
|
|
if(!a1->IsDefined() || !a2->IsDefined() ||
|
|
!d->IsDefined() || !b->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
a1->DistanceSmallerThan(*a2,d->GetValue(), b->GetValue(), *res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.4.21 Value mapping function of operator ~direction~ and ~heading~
|
|
|
|
For ~heading~ the template parameter is ~true~, for ~direction~, is is ~false~.
|
|
|
|
*/
|
|
template<bool isHeading>
|
|
int SpatialDirectionHeading_pp( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcReal* res = static_cast<CcReal*>(result.addr);
|
|
const Point *p1 = ((Point*)args[0].addr);
|
|
const Point *p2 = ((Point*)args[1].addr);
|
|
const Geoid* geoid =
|
|
(qp->GetNoSons(s)==3)?static_cast<const Geoid*>(args[2].addr):0;
|
|
try{
|
|
double d = p1->Direction(*p2, isHeading, geoid); // all saveguards included!
|
|
res->Set(d>=0.0,d);
|
|
} catch (SecondoException* e){
|
|
res->Set(false,0.0);
|
|
delete e;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.22 Value mapping functions of operator ~nocomponents~
|
|
|
|
*/
|
|
|
|
int
|
|
SpatialNoComponents_ps( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Points* ps = static_cast<const Points*>(args[0].addr);
|
|
if( ps->IsDefined() )
|
|
((CcInt *)result.addr)->Set( true, ps->Size() );
|
|
else
|
|
((CcInt *)result.addr)->Set( false, 0 );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialNoComponents_l( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Line* l = static_cast<const Line*>(args[0].addr);
|
|
if( l->IsDefined() )
|
|
((CcInt *)result.addr)->Set( true, l->NoComponents() );
|
|
else
|
|
((CcInt *)result.addr)->Set( false, 0 );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialNoComponents_r( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Region* r = static_cast<const Region*>(args[0].addr);
|
|
if( r->IsDefined() )
|
|
((CcInt *)result.addr)->Set( true, r->NoComponents() );
|
|
else
|
|
((CcInt *)result.addr)->Set( false, 0 );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.22 Value mapping functions of operator ~no\_segments~
|
|
|
|
*/
|
|
template<class T>
|
|
int
|
|
SpatialNoSegments( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const T *cl=((const T*)args[0].addr);
|
|
if( cl->IsDefined() ) {
|
|
assert( cl->Size() % 2 == 0 );
|
|
((CcInt *)result.addr)->Set( true, cl->Size() / 2 );
|
|
} else {
|
|
((CcInt *)result.addr)->Set( false, 0 );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialNoSegmentsDline( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const DLine *dl=((const DLine*)args[0].addr);
|
|
CcInt* res = (CcInt*) result.addr;
|
|
if(dl->IsDefined()){
|
|
res->Set(true,dl->getSize());
|
|
} else {
|
|
res->SetDefined(false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.22 Value mapping functions of operator ~bbox~
|
|
|
|
*/
|
|
template<class T>
|
|
int SpatialBBox(Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
|
|
result = qp->ResultStorage( s );
|
|
Rectangle<2>* box = static_cast<Rectangle<2>* >(result.addr);
|
|
const T* arg = static_cast<const T*>(args[0].addr);
|
|
const Geoid* geoid =
|
|
(qp->GetNoSons(s)==2)?static_cast<const Geoid*>(args[1].addr):0;
|
|
if(!arg->IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
box->SetDefined(false);
|
|
} else {
|
|
(*box) = arg->BoundingBox(geoid);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.23 Value mapping functions of operator ~size~
|
|
|
|
*/
|
|
template<class T>
|
|
int SpatialSize( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcReal* res = static_cast<CcReal*>(result.addr);
|
|
const T* a = static_cast<const T*>(args[0].addr);
|
|
const Geoid* g =
|
|
(qp->GetNoSons(s)==2)?static_cast<const Geoid*>(args[1].addr):0;
|
|
if(!a->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(g){ // variant using (LON,LAT)-coordinates
|
|
if(!g->IsDefined()){
|
|
res->Set(false, 0.0);
|
|
return 0;
|
|
}
|
|
bool valid = false;
|
|
res->Set(true,a->SpatialSize(*g, valid));
|
|
res->SetDefined(valid);
|
|
return 0;
|
|
} // else: variant using (X,Y)-coordinates
|
|
res->Set(true,a->SpatialSize());
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.24 Value mapping functions of operator ~touchpoints~
|
|
|
|
*/
|
|
int SpatialTouchPoints_lr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Line *l = ((const Line*)args[0].addr);
|
|
const Region *r = ((const Region*)args[1].addr);
|
|
r->TouchPoints( *l, *((Points *)result.addr) );
|
|
return 0;
|
|
}
|
|
|
|
int SpatialTouchPoints_rl( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Region *r = ((const Region*)args[0].addr);
|
|
const Line *l = ((const Line*)args[1].addr);
|
|
r->TouchPoints( *l, *((Points *)result.addr) );
|
|
return 0;
|
|
}
|
|
|
|
int SpatialTouchPoints_rr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Region *r1 = ((const Region*)args[0].addr);
|
|
const Region *r2 = ((const Region*)args[1].addr);
|
|
r1->TouchPoints( *r2, *((Points *)result.addr) );
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
ostream& operator<<(ostream& o,const SimplePoint& p) {
|
|
o << "(" << p.getX() << ", " << p.getY() << ")";
|
|
return o;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.25 Value mapping functions of operator ~commomborder~
|
|
|
|
*/
|
|
|
|
int SpatialCommonBorder_rr( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Line *l = (Line*)result.addr;
|
|
const Region *cr1 = ((const Region*)args[0].addr),
|
|
*cr2 = ((const Region*)args[1].addr);
|
|
cr1->CommonBorder( *cr2, *l );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
10.3 Operator ~translate~
|
|
|
|
10.3.1 Type mapping function for operator ~translate~
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialTranslateMap( ListExpr args )
|
|
{
|
|
string err = "SPATIAL x (real x real) expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr a1 = nl->First(args);
|
|
if( !Point::checkType(a1)
|
|
&& !Points::checkType(a1)
|
|
&& !Line::checkType(a1)
|
|
&& !Region::checkType(a1)
|
|
&& !DLine::checkType(a1)){
|
|
return listutils::typeError(err + " (first arg not supported)");
|
|
}
|
|
ListExpr a2 = nl->Second(args);
|
|
if(!nl->HasLength(a2,2)){
|
|
return listutils::typeError("invalid number od arguments");
|
|
}
|
|
if( !CcReal::checkType(nl->First(a2))
|
|
|| !CcReal::checkType(nl->Second(a2))){
|
|
return listutils::typeError("translation arguments must be of type real");
|
|
}
|
|
return a1;
|
|
}
|
|
|
|
/*
|
|
10.3.2 Value Mapping template
|
|
|
|
*/
|
|
template<class T>
|
|
int SpatialTranslateVMT( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
T* res = static_cast<T*>(result.addr);
|
|
const T* a = (const T*)args[0].addr;
|
|
|
|
Supplier son = qp->GetSupplier( args[1].addr, 0 );
|
|
Word t;
|
|
qp->Request( son, t );
|
|
const CcReal *tx = ((CcReal *)t.addr);
|
|
|
|
son = qp->GetSupplier( args[1].addr, 1 );
|
|
qp->Request( son, t );
|
|
const CcReal *ty = ((CcReal *)t.addr);
|
|
|
|
if(!a->IsDefined() || !tx->IsDefined() || !ty->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
a->Translate(tx->GetValue(), ty->GetValue(), *res);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping spatialtranslatemap[] = {
|
|
SpatialTranslateVMT<Point>,
|
|
SpatialTranslateVMT<Points>,
|
|
SpatialTranslateVMT<Line>,
|
|
SpatialTranslateVMT<Region>,
|
|
SpatialTranslateVMT<DLine> };
|
|
|
|
/*
|
|
10.3.19 Selection function ~SpatialSelectTranslate~
|
|
|
|
This select function is used for the ~translate~, rotate,
|
|
and ~scale~ operators.
|
|
|
|
*/
|
|
int SpatialSelectTranslate( ListExpr args ) {
|
|
ListExpr arg1 = nl->First( args );
|
|
if(Point::checkType(arg1)) return 0;
|
|
if(Points::checkType(arg1)) return 1;
|
|
if(Line::checkType(arg1)) return 2;
|
|
if(Region::checkType(arg1)) return 3;
|
|
if(DLine::checkType(arg1)) return 4;
|
|
return -1;
|
|
}
|
|
|
|
const string SpatialSpecTranslate =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point||points||line||region x real x real) -> "
|
|
"point||points||line||region</text--->"
|
|
"<text> _ translate[ dx, dy ]</text--->"
|
|
"<text> move the object parallely for some distance.</text--->"
|
|
"<text> query region1 translate[3.5, 15.1]</text--->"
|
|
") )";
|
|
|
|
|
|
Operator spatialtranslate (
|
|
"translate",
|
|
SpatialSpecTranslate,
|
|
5,
|
|
spatialtranslatemap,
|
|
SpatialSelectTranslate,
|
|
SpatialTranslateMap );
|
|
|
|
|
|
|
|
/*
|
|
10.4 Operator ~rotate~
|
|
|
|
10.4.1 Type mapping
|
|
|
|
*/
|
|
ListExpr
|
|
SpatialRotateMap( ListExpr args )
|
|
{
|
|
if ( nl->ListLength( args ) != 4 ) {
|
|
return listutils::typeError("wrong number of args");
|
|
}
|
|
// first arg must be of type point, points, line, region, dline
|
|
|
|
ListExpr a1 = nl->First(args);
|
|
if( !Point::checkType(a1)
|
|
&& !Points::checkType(a1)
|
|
&& !Line::checkType(a1)
|
|
&& !Region::checkType(a1)
|
|
&& !DLine::checkType(a1)){
|
|
return listutils::typeError("first arg is not a supported spatial type");
|
|
}
|
|
// the remaining arguments have to be of type real
|
|
if( (( !CcReal::checkType(nl->Second(args))
|
|
|| !CcReal::checkType(nl->Third(args)))
|
|
&& !Point::checkType(nl->Second(args)))
|
|
|| !CcReal::checkType(nl->Fourth(args))){
|
|
return listutils::typeError("rotation arguments have to be of type real");
|
|
}
|
|
return a1;
|
|
}
|
|
|
|
template<class T>
|
|
int SpatialRotate( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage(s);
|
|
T* res = static_cast<T*>(result.addr);
|
|
T* st = static_cast<T*>(args[0].addr);
|
|
CcReal* x = static_cast<CcReal*>(args[1].addr);
|
|
CcReal* y = static_cast<CcReal*>(args[2].addr);
|
|
CcReal* a = static_cast<CcReal*>(args[3].addr);
|
|
if(!st->IsDefined() || !x->IsDefined() || !y->IsDefined()
|
|
|| !a->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double angle = a->GetRealval() * M_PI / 180;
|
|
st->Rotate(x->GetRealval(),y->GetRealval(),angle,*res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
template<class T>
|
|
int SpatialRotatePt( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage(s);
|
|
T* res = static_cast<T*>(result.addr);
|
|
T* st = static_cast<T*>(args[0].addr);
|
|
Point* pt = static_cast<Point*>(args[1].addr);
|
|
CcReal* a = static_cast<CcReal*>(args[3].addr);
|
|
if(!st->IsDefined() || !pt->IsDefined() || !a->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double angle = a->GetRealval() * M_PI / 180;
|
|
st->Rotate(pt->GetX(),pt->GetY(),angle,*res);
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping spatialrotatemap[] = {
|
|
SpatialRotate<Point>,
|
|
SpatialRotate<Points>,
|
|
SpatialRotate<Line>,
|
|
SpatialRotate<Region>,
|
|
SpatialRotate<DLine>,
|
|
SpatialRotatePt<Point>,
|
|
SpatialRotatePt<Points>,
|
|
SpatialRotatePt<Line>,
|
|
SpatialRotatePt<Region>,
|
|
SpatialRotatePt<DLine>
|
|
};
|
|
|
|
|
|
int SpatialSelectRotate( ListExpr args ) {
|
|
int ret = -1;
|
|
ListExpr arg1 = nl->First( args );
|
|
ListExpr arg2 = nl->Second( args );
|
|
if(Point::checkType(arg1)) ret = 0;
|
|
else if(Points::checkType(arg1)) ret = 1;
|
|
else if(Line::checkType(arg1)) ret = 2;
|
|
else if(Region::checkType(arg1)) ret = 3;
|
|
else if(DLine::checkType(arg1)) ret = 4;
|
|
else return -1;
|
|
if (Point::checkType(arg2)) ret += 5;
|
|
return ret;
|
|
}
|
|
|
|
const string SpatialSpecRotate =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point|points|line|region|dline x real|point x real x real) -> "
|
|
"point||points||line||region|dline</text--->"
|
|
"<text> _ rotate[ x|pt, y, theta ]</text--->"
|
|
"<text> rotates the spatial object by 'theta' degrees around (x,y) or "
|
|
"point pt </text--->"
|
|
"<text> query region1 rotate[3.5, 15.1, 10.0]</text--->"
|
|
") )";
|
|
|
|
|
|
Operator spatialrotate (
|
|
"rotate",
|
|
SpatialSpecRotate,
|
|
10,
|
|
spatialrotatemap,
|
|
SpatialSelectRotate,
|
|
SpatialRotateMap );
|
|
|
|
|
|
|
|
/*
|
|
10.5 Operator ~scale~
|
|
|
|
10.5.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr SpatialScaleMap(ListExpr args) {
|
|
string err = "SPATIAL x (real x real) or SPATIAL x real expected";
|
|
if (!nl->HasLength(args, 3) && !nl->HasLength(args, 2)) {
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr a1 = nl->First(args);
|
|
if( !Point::checkType(a1)
|
|
&& !Points::checkType(a1)
|
|
&& !Line::checkType(a1)
|
|
&& !Region::checkType(a1)
|
|
&& !DLine::checkType(a1)){
|
|
return listutils::typeError(err + " (first arg not supported)");
|
|
}
|
|
if (!CcReal::checkType(nl->Second(args))) {
|
|
return listutils::typeError("translation argument(s) must have type real");
|
|
}
|
|
if (nl->HasLength(args, 3)) {
|
|
if (!CcReal::checkType(nl->Third(args))) {
|
|
return listutils::
|
|
typeError("translation argument(s) must have type real");
|
|
}
|
|
}
|
|
return a1;
|
|
}
|
|
|
|
template<class T>
|
|
int SpatialScaleVMT3( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
T* res = static_cast<T*>(result.addr);
|
|
const T* a = (const T*)args[0].addr;
|
|
const CcReal *tx = (CcReal *) args[1].addr;
|
|
const CcReal *ty = (CcReal *) args[2].addr;
|
|
if(!a->IsDefined() || !tx->IsDefined() || !ty->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
a->Scale(tx->GetValue(), ty->GetValue(), *res);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
template<class T>
|
|
int SpatialScaleVMT2(Word* args, Word& result, int message, Word& local,
|
|
Supplier s) {
|
|
result = qp->ResultStorage( s );
|
|
T* res = static_cast<T*>(result.addr);
|
|
const T* a = (const T*)args[0].addr;
|
|
const CcReal *t = (CcReal*) args[1].addr;
|
|
if(!a->IsDefined() || !t->IsDefined()){
|
|
res->SetDefined(false);
|
|
}
|
|
else {
|
|
a->Scale(t->GetValue(), t->GetValue(), *res);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping spatialscalemap[] = {
|
|
SpatialScaleVMT3<Point>,
|
|
SpatialScaleVMT3<Points>,
|
|
SpatialScaleVMT3<Line>,
|
|
SpatialScaleVMT3<Region>,
|
|
SpatialScaleVMT3<DLine>,
|
|
SpatialScaleVMT2<Point>,
|
|
SpatialScaleVMT2<Points>,
|
|
SpatialScaleVMT2<Line>,
|
|
SpatialScaleVMT2<Region>,
|
|
SpatialScaleVMT2<DLine>};
|
|
|
|
|
|
int SpatialSelectScale( ListExpr args ) {
|
|
ListExpr arg1 = nl->First(args);
|
|
if (nl->HasLength(args, 3)) {
|
|
if(Point::checkType(arg1)) return 0;
|
|
if(Points::checkType(arg1)) return 1;
|
|
if(Line::checkType(arg1)) return 2;
|
|
if(Region::checkType(arg1)) return 3;
|
|
if(DLine::checkType(arg1)) return 4;
|
|
}
|
|
else {
|
|
if(Point::checkType(arg1)) return 5;
|
|
if(Points::checkType(arg1)) return 6;
|
|
if(Line::checkType(arg1)) return 7;
|
|
if(Region::checkType(arg1)) return 8;
|
|
if(DLine::checkType(arg1)) return 9;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
const string SpatialSpecScale =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>for T in {point, points, line, region}: "
|
|
"T x real (x real) -> T</text--->"
|
|
"<text> _ scale [ _ , _ ] </text--->"
|
|
"<text> scales an object by the given factor. If the user specifies two real "
|
|
"values, each of them represents the scale factor for one dimension</text--->"
|
|
"<text> query region1 scale[1000.0]</text--->"
|
|
") )";
|
|
|
|
|
|
Operator spatialscale (
|
|
"scale",
|
|
SpatialSpecScale,
|
|
10,
|
|
spatialscalemap,
|
|
SpatialSelectScale,
|
|
SpatialScaleMap
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
int SpatialCenter_points( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage(s);
|
|
Points* ps = static_cast<Points*>(args[0].addr);
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
*res = ps->theCenter();
|
|
return 0;
|
|
}
|
|
|
|
int SpatialCenter_rect(Word* args, Word& result,
|
|
int message, Word& local, Supplier s) {
|
|
result = qp->ResultStorage( s );
|
|
Rectangle<2>* r = static_cast<Rectangle<2>*>(args[0].addr);
|
|
Point* p = static_cast<Point*>(result.addr);
|
|
if(!r->IsDefined()) {
|
|
p->SetDefined(false);
|
|
} else {
|
|
double x = r->MinD(0) + (r->MaxD(0) - r->MinD(0)) / 2;
|
|
double y = r->MinD(1) + (r->MaxD(1) - r->MinD(1)) / 2;
|
|
p->SetDefined(true);
|
|
p->Set(x, y);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SpatialCenter_Select(ListExpr args){
|
|
ListExpr a1 = nl->First(args);
|
|
if(Points::checkType(a1)) return 0;
|
|
if(Rectangle<2>::checkType(a1)) return 1;
|
|
return -1;
|
|
}
|
|
|
|
ValueMapping SpatialCenter[] = {
|
|
SpatialCenter_points,
|
|
SpatialCenter_rect
|
|
};
|
|
|
|
|
|
|
|
|
|
int SpatialConvexhull( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage(s);
|
|
Points* ps = static_cast<Points*>(args[0].addr);
|
|
Region* res = static_cast<Region*>(result.addr);
|
|
GrahamScan::convexHull(ps,res);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialLine2Region( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Line *cl = (Line *)args[0].addr;
|
|
Region *pResult = (Region *)result.addr;
|
|
pResult->Clear();
|
|
cl->Transform( *pResult );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.29 Value mapping function of operator ~rect2region~
|
|
|
|
*/
|
|
|
|
int SpatialRect2Region( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
|
|
Rectangle<2> *rect = (Rectangle<2> *)args[0].addr;
|
|
Region *res = (Region*)result.addr;
|
|
*res = Region( *rect );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.29 Value mapping function of operator ~create_triangle~
|
|
|
|
*/
|
|
|
|
int SpatialCreateTriangleVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
result = qp->ResultStorage( s );
|
|
Region* res = static_cast<Region*>(result.addr);
|
|
const Point* p1 = static_cast<const Point*>(args[0].addr);
|
|
const Point* p2 = static_cast<const Point*>(args[1].addr);
|
|
const Point* p3 = static_cast<const Point*>(args[2].addr);
|
|
*res = Region(*p1, *p2, *p3);
|
|
return 0;
|
|
}
|
|
|
|
int SpatialArea( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region *reg = (Region *)args[0].addr;
|
|
CcReal *res = (CcReal *)result.addr;
|
|
if( reg->IsDefined() )
|
|
res->Set( true, reg->Area() );
|
|
else
|
|
res->Set( false, 0.0 );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialTranslate_r( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region *cr = (Region *)args[0].addr,
|
|
*pResult = (Region *)result.addr;
|
|
pResult->Clear();
|
|
Supplier son = qp->GetSupplier( args[1].addr, 0 );
|
|
|
|
Word t;
|
|
qp->Request( son, t );
|
|
const CcReal *tx = ((CcReal *)t.addr);
|
|
|
|
son = qp->GetSupplier( args[1].addr, 1 );
|
|
qp->Request( son, t );
|
|
const CcReal *ty = ((CcReal *)t.addr);
|
|
|
|
if( cr->IsDefined() && tx->IsDefined() && ty->IsDefined() ) {
|
|
const Coord txval = (Coord)(tx->GetRealval()),
|
|
tyval = (Coord)(ty->GetRealval());
|
|
cr->Translate( txval, tyval, *pResult );
|
|
}
|
|
else
|
|
((Region*)result.addr)->SetDefined( false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.26 Value mapping functions of operator ~windowclippingin~
|
|
|
|
*/
|
|
int
|
|
SpatialWindowClippingIn_l( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Line *clippedLine = (Line*)result.addr;
|
|
Line *l = ((Line *)args[0].addr);
|
|
Rectangle<2> *window = ((Rectangle<2>*)args[1].addr);
|
|
bool inside;
|
|
l->WindowClippingIn(*window,*clippedLine,inside);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialWindowClippingIn_r( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region *clippedRegion=(Region*)result.addr;
|
|
Region *r = ((Region *)args[0].addr);
|
|
Rectangle<2> *window = ((Rectangle<2>*)args[1].addr);
|
|
r->WindowClippingIn(*window,*clippedRegion);
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
10.4.26 Value mapping functions of operator ~windowclippingout~
|
|
|
|
*/
|
|
int
|
|
SpatialWindowClippingOut_l( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Line *clippedLine = (Line*)result.addr;
|
|
Line *l = ((Line *)args[0].addr);
|
|
Rectangle<2> *window = ((Rectangle<2>*)args[1].addr);
|
|
bool outside;
|
|
l->WindowClippingOut(*window,*clippedLine,outside);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialWindowClippingOut_r( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Region *clippedRegion=(Region*)result.addr;
|
|
Region *r = ((Region *)args[0].addr);
|
|
Rectangle<2> *window = ((Rectangle<2>*)args[1].addr);
|
|
r->WindowClippingOut(*window,*clippedRegion);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.4.27 Value mapping functions of operator ~components~
|
|
|
|
*/
|
|
struct ComponentsLocalInfo
|
|
{
|
|
vector<Region*> components;
|
|
vector<Region*>::iterator iter;
|
|
};
|
|
|
|
int
|
|
SpatialComponents_r( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
ComponentsLocalInfo *localInfo;
|
|
|
|
switch( message )
|
|
{
|
|
case OPEN:
|
|
if( !((Region*)args[0].addr)->IsEmpty() ){ // IsEmpty() subsumes undef
|
|
localInfo = new ComponentsLocalInfo();
|
|
((Region*)args[0].addr)->Components( localInfo->components );
|
|
localInfo->iter = localInfo->components.begin();
|
|
local.setAddr( localInfo );
|
|
} else {
|
|
local.setAddr( 0 );
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
if( !local.addr ) {
|
|
return CANCEL;
|
|
}
|
|
localInfo = (ComponentsLocalInfo*)local.addr;
|
|
if( localInfo->iter == localInfo->components.end() )
|
|
return CANCEL;
|
|
result.setAddr( *localInfo->iter++ );
|
|
return YIELD;
|
|
|
|
case CLOSE:
|
|
|
|
if(local.addr)
|
|
{
|
|
localInfo = (ComponentsLocalInfo*)local.addr;
|
|
while( localInfo->iter != localInfo->components.end() )
|
|
{
|
|
delete *localInfo->iter++;
|
|
}
|
|
delete localInfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialComponents_ps( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
Points *localInfo;
|
|
|
|
switch( message )
|
|
{
|
|
case OPEN:{
|
|
Points* arg = static_cast<Points*>(args[0].addr);
|
|
if( !arg->IsEmpty() ){ // subsumes undef
|
|
localInfo = new Points(*arg);
|
|
localInfo->SelectFirst();
|
|
local.setAddr(localInfo);
|
|
} else {
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
if(!local.addr){
|
|
return CANCEL;
|
|
}
|
|
localInfo = (Points*)local.addr;
|
|
if(localInfo->EndOfPt() ){
|
|
return CANCEL;
|
|
}
|
|
Point p(false);
|
|
localInfo->GetPt( p );
|
|
Points* res = new Points(1);
|
|
res->StartBulkLoad();
|
|
(*res) += p;
|
|
res->EndBulkLoad();
|
|
result.addr = res;
|
|
localInfo->SelectNext();
|
|
return YIELD;
|
|
}
|
|
case CLOSE: {
|
|
if(local.addr)
|
|
{
|
|
localInfo = (Points*)local.addr;
|
|
delete localInfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
class LineComponentsLi{
|
|
public:
|
|
LineComponentsLi(Line* theLine){
|
|
this->theLine = (Line*)theLine->Copy();
|
|
size = theLine->Size();
|
|
used = new bool[size];
|
|
for(int i=0;i<size;i++){
|
|
used[i] = false;
|
|
}
|
|
pos = 0;
|
|
}
|
|
|
|
~LineComponentsLi(){
|
|
theLine->DeleteIfAllowed();
|
|
delete[] used;
|
|
}
|
|
|
|
Line* NextLine(){
|
|
// search next unused part
|
|
while(pos<size && used[pos]){
|
|
pos++;
|
|
}
|
|
// all segments are used already
|
|
if(pos>=size){
|
|
return 0;
|
|
}
|
|
Line* result = new Line(size-pos);
|
|
result->StartBulkLoad();
|
|
int edgeno = 0;
|
|
// pos points to an unused segments
|
|
stack<int> criticalPoints;
|
|
bool done=false;
|
|
HalfSegment hs;
|
|
int hspos = pos;
|
|
HalfSegment hsp;
|
|
int hsppos = -1;
|
|
criticalPoints.push(pos); // mark to search an extension here
|
|
while(!done){
|
|
theLine->Get(hspos,hs);
|
|
hsppos = hs.attr.partnerno;
|
|
theLine->Get(hsppos,hsp);
|
|
used[hspos]=true;
|
|
used[hsppos]=true;
|
|
HalfSegment hs1 = hs;
|
|
hs1.attr.edgeno = edgeno;
|
|
hs1.SetLeftDomPoint(false);
|
|
(*result) += hs1;
|
|
hs1.SetLeftDomPoint(true);
|
|
(*result) += hs1;
|
|
edgeno++;
|
|
// search an extension of result
|
|
Point p = hsp.GetDomPoint();
|
|
criticalPoints.push(hsppos);
|
|
|
|
// search within the stack
|
|
bool found = false;
|
|
while(!criticalPoints.empty() && !found){
|
|
int k = criticalPoints.top();
|
|
HalfSegment tmp;
|
|
theLine->Get(k,tmp);
|
|
Point p = tmp.GetDomPoint();
|
|
// search left of k
|
|
int m = k-1;
|
|
while(m>0 && isDomPoint(p,m) && !found){
|
|
found = !used[m];
|
|
if(!found) m--;
|
|
}
|
|
if(found){
|
|
hspos=m;
|
|
} else { // search right of k
|
|
m = k+1;
|
|
while(m<size && isDomPoint(p,m) &&!found){
|
|
found = !used[m];
|
|
if(!found) m++;
|
|
}
|
|
if(found){
|
|
hspos = m;
|
|
}
|
|
}
|
|
|
|
if(!found){
|
|
criticalPoints.pop();
|
|
}
|
|
}
|
|
done = !found; // no extension found
|
|
}
|
|
result->EndBulkLoad();
|
|
return result;
|
|
}
|
|
|
|
|
|
private:
|
|
bool isDomPoint(const Point& p,int pos){
|
|
HalfSegment hs;
|
|
theLine->Get(pos,hs);
|
|
return AlmostEqual(p,hs.GetDomPoint());
|
|
}
|
|
|
|
|
|
Line* theLine;
|
|
int pos;
|
|
int size;
|
|
bool* used;
|
|
};
|
|
|
|
|
|
int
|
|
SpatialComponents_l( Word* args, Word& result, int message,
|
|
Word& local, Supplier s ){
|
|
|
|
switch(message){
|
|
case OPEN:{
|
|
if( !((Line*)(args[0].addr))->IsEmpty() ) {
|
|
local.addr = new LineComponentsLi((Line*) args[0].addr);
|
|
} else {
|
|
local.setAddr( 0 );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST:{
|
|
if( !local.addr )
|
|
return CANCEL;
|
|
LineComponentsLi* li = (LineComponentsLi*) local.addr;
|
|
Line* res = li->NextLine();
|
|
if(res==0){
|
|
return CANCEL;
|
|
} else {
|
|
result.addr = res;
|
|
return YIELD;
|
|
}
|
|
}
|
|
case CLOSE:{
|
|
if(local.addr){
|
|
LineComponentsLi* li = (LineComponentsLi*) local.addr;
|
|
delete li;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.28 Value mapping functions of operator ~vertices~
|
|
|
|
*/
|
|
int SpatialVertices_r(Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage(s);
|
|
Region* reg = (Region*)args[0].addr;
|
|
Points* res = (Points*)result.addr;
|
|
reg->Vertices(res);
|
|
return 0;
|
|
}
|
|
|
|
int SpatialVertices_l(Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage(s);
|
|
((Line*)args[0].addr)->Vertices( (Points*) result.addr );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.28 Value mapping functions of operator ~boundary~
|
|
|
|
*/
|
|
|
|
int SpatialBoundary_r(Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage(s);
|
|
Region* reg = (Region*)args[0].addr;
|
|
Line* res = (Line*) result.addr;
|
|
reg->Boundary(res);
|
|
return 0;
|
|
}
|
|
int SpatialBoundary_l(Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage(s);
|
|
Line* line = (Line*)args[0].addr;
|
|
Points* res = (Points*) result.addr;
|
|
line->Boundary(res);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.23 Value mapping functions of operator ~atpoint~
|
|
|
|
*/
|
|
int
|
|
SpatialAtPointBool( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
SimpleLine *l = (SimpleLine*)args[0].addr;
|
|
Point *p = (Point*)args[1].addr;
|
|
CcBool *startsSmaller = (CcBool*)args[2].addr;
|
|
double res;
|
|
|
|
if( !l->IsEmpty() && // subsumes IsDefined()
|
|
p->IsDefined() &&
|
|
startsSmaller->IsDefined() &&
|
|
l->AtPoint( *p, startsSmaller->GetBoolval(), 0.0, res ) )
|
|
((CcReal*)result.addr)->Set( true, res );
|
|
else
|
|
((CcReal*)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAtPoint( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
SimpleLine *l = (SimpleLine*)args[0].addr;
|
|
Point *p = (Point*)args[1].addr;
|
|
double res;
|
|
if( l->IsDefined() && !l->IsEmpty() &&
|
|
p->IsDefined() && l->AtPoint( *p, res ) )
|
|
((CcReal*)result.addr)->Set( true, res );
|
|
else
|
|
((CcReal*)result.addr)->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.23 Value mapping functions of operator ~atposition~
|
|
|
|
*/
|
|
int
|
|
SpatialAtPositionBool( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
SimpleLine *l = (SimpleLine*)args[0].addr;
|
|
CcReal *pos = (CcReal*)args[1].addr;
|
|
CcBool *startsSmaller = (CcBool*)args[2].addr;
|
|
Point *p = (Point*)result.addr;
|
|
|
|
if( l->IsEmpty() || !pos->IsDefined() ||
|
|
!startsSmaller->IsDefined() ||
|
|
!l->AtPosition( pos->GetRealval(), startsSmaller->GetBoolval(), *p ) )
|
|
p->SetDefined( false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
SpatialAtPosition( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
SimpleLine *l = (SimpleLine*)args[0].addr;
|
|
CcReal *pos = (CcReal*)args[1].addr;
|
|
Point *p = (Point*)result.addr;
|
|
|
|
if( l->IsEmpty() || !pos->IsDefined() ||
|
|
!l->AtPosition( pos->GetRealval(), *p ) )
|
|
p->SetDefined( false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.23 Value mapping functions of operator ~subline~
|
|
|
|
*/
|
|
int
|
|
SpatialSubLine( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
SimpleLine *l = (SimpleLine*)args[0].addr;
|
|
CcReal *pos1 = (CcReal*)args[1].addr,
|
|
*pos2 = (CcReal*)args[2].addr;
|
|
CcBool *startsSmaller = (CcBool*)args[3].addr;
|
|
SimpleLine *rLine = (SimpleLine*)result.addr;
|
|
|
|
if( pos1->IsDefined() &&
|
|
pos2->IsDefined() &&
|
|
startsSmaller->IsDefined() )
|
|
l->SubLine( pos1->GetRealval(),
|
|
pos2->GetRealval(),
|
|
startsSmaller->GetBoolval(),
|
|
*rLine );
|
|
else {
|
|
rLine->Clear();
|
|
rLine->SetDefined( false );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.26 Value mapping function of operator ~add~
|
|
|
|
*/
|
|
int
|
|
SpatialAdd_p( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Point *p1= (Point*)args[0].addr;
|
|
const Point *p2= (Point*)args[1].addr;
|
|
|
|
if( p1->IsDefined() && p2->IsDefined() )
|
|
*((Point*)result.addr) = *p1 + *p2 ;
|
|
else
|
|
((Point*)result.addr)->SetDefined( false );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.27 Value mapping function of operator ~getx~
|
|
|
|
*/
|
|
int
|
|
SpatialGetX_p( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Point *p = (Point*)args[0].addr;
|
|
|
|
if( p->IsDefined() )
|
|
((CcReal*)result.addr)->Set( true, p->GetX() ) ;
|
|
else
|
|
((CcReal*)result.addr)->Set( false, 0.0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.4.28 Value mapping function of operator ~gety~
|
|
|
|
*/
|
|
int
|
|
SpatialGetY_p( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
const Point *p = (Point*)args[0].addr;
|
|
|
|
if( p->IsDefined() )
|
|
((CcReal*)result.addr)->Set( true, p->GetY() ) ;
|
|
else
|
|
((CcReal*)result.addr)->Set( false, 0.0 );
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
bool AlmostEqual2(const Point& p1, const Point& p2 ){
|
|
double z1 = abs(p1.GetX());
|
|
double z2 = abs(p1.GetY());
|
|
double z3 = abs(p2.GetX());
|
|
double z4 = abs(p2.GetY());
|
|
double Min = min(min(z1,z2) , min(z3,z4));
|
|
double eps = max(FACTOR, FACTOR*Min);
|
|
if(abs(z1-z3)>eps) return false;
|
|
if(abs(z2-z4)>eps) return false;
|
|
return true;
|
|
}
|
|
*/
|
|
|
|
|
|
template<bool allowCycles>
|
|
int SpatialPolylines(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
LineSplitter<DbArray > *localinfo;
|
|
Line *l, *res;
|
|
CcBool *b;
|
|
|
|
result = qp->ResultStorage(s);
|
|
switch (message){
|
|
case OPEN:
|
|
l = (Line*)args[0].addr;
|
|
b = (CcBool*)args[1].addr;
|
|
if(qp->GetNoSons(s)==2){
|
|
if( !l->IsEmpty() && b->IsDefined() ) {
|
|
local.setAddr(new LineSplitter<DbArray > (l,
|
|
b->GetBoolval(),
|
|
allowCycles));
|
|
} else {
|
|
local.setAddr( 0 );
|
|
}
|
|
} else if( !l->IsEmpty()
|
|
&& b->IsDefined()
|
|
&& ((Points*)args[2].addr)->IsDefined() ){
|
|
local.setAddr(new LineSplitter<DbArray >(l,
|
|
b->GetBoolval(),
|
|
allowCycles,
|
|
((Points*)args[2].addr)));
|
|
} else {
|
|
local.setAddr( 0 );
|
|
}
|
|
return 0;
|
|
case REQUEST:
|
|
if( !local.addr ){
|
|
return CANCEL;
|
|
}
|
|
localinfo = (LineSplitter<DbArray >*) local.addr;
|
|
res = localinfo->NextLine();
|
|
if(res==0){
|
|
return CANCEL;
|
|
} else {
|
|
result.setAddr(res);
|
|
return YIELD;
|
|
}
|
|
case CLOSE:
|
|
if(local.addr!=0){
|
|
localinfo = (LineSplitter<DbArray >*) local.addr;
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0; // ignore unknown message
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.4.30 Value Mapping for the ~segments~ operator
|
|
|
|
*/
|
|
template<class LT>
|
|
class SegmentsInfo{
|
|
public:
|
|
SegmentsInfo(LT* line){
|
|
this->theLine = line; // increase the ref counter of line
|
|
this->position = 0;
|
|
this->size = line->Size();
|
|
}
|
|
~SegmentsInfo(){ }
|
|
|
|
|
|
LT* NextSegment(){
|
|
return next(theLine);
|
|
}
|
|
|
|
private:
|
|
int position;
|
|
int size;
|
|
LT* theLine;
|
|
|
|
|
|
Line* next(Line* line) {
|
|
HalfSegment hs;
|
|
// search for a segment with left dominating point
|
|
bool found = false;
|
|
while((position<size) && !found){
|
|
theLine->Get(position,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
found=true;
|
|
} else {
|
|
position++;
|
|
}
|
|
}
|
|
position++; // for the next run
|
|
if(!found){ // no more segments available
|
|
return 0;
|
|
} else {
|
|
Line* res = new Line(2);
|
|
HalfSegment hs1 = hs;
|
|
res->StartBulkLoad();
|
|
hs1.attr.edgeno = 0;
|
|
(*res) += hs1;
|
|
hs1.SetLeftDomPoint(false);
|
|
(*res) += hs1;
|
|
res->EndBulkLoad();
|
|
return res;
|
|
}
|
|
}
|
|
|
|
DLine* next(DLine* line){
|
|
if(position>=size){
|
|
return 0;
|
|
}
|
|
SimpleSegment s;
|
|
line->get(position,s);
|
|
position++;
|
|
DLine* res = new DLine(1);
|
|
res->append(s);
|
|
return res;
|
|
}
|
|
|
|
};
|
|
|
|
template<class LT>
|
|
int SpatialSegmentsVMT(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
SegmentsInfo<LT>* si= (SegmentsInfo<LT>*) local.addr;;
|
|
switch(message){
|
|
case OPEN:
|
|
if(si){
|
|
delete si;
|
|
local.addr = 0;
|
|
}
|
|
if( !((LT*)args[0].addr)->IsEmpty() ){ // subsumes undef
|
|
local.addr = new SegmentsInfo<LT>((LT*)args[0].addr);
|
|
}
|
|
return 0;
|
|
|
|
case REQUEST:
|
|
result.addr = si?si->NextSegment():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
|
|
case CLOSE:
|
|
if(si)
|
|
{
|
|
delete si;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping SpatialSegmentsVM[] = {
|
|
SpatialSegmentsVMT<Line>,
|
|
SpatialSegmentsVMT<DLine>
|
|
};
|
|
|
|
int SpatialSegmentsSelect(ListExpr args){
|
|
return Line::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
10.4.31 Value Mappings for the simplify operator
|
|
|
|
*/
|
|
|
|
int SpatialSimplify_LReal(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
Line* line = static_cast<Line*>(args[0].addr);
|
|
CcReal* epsilon = static_cast<CcReal*>(args[1].addr);
|
|
Geoid* geoid = 0;
|
|
if(qp->GetNoSons(s)==3){
|
|
geoid = static_cast<Geoid*>(args[2].addr);
|
|
if(!geoid->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
}
|
|
if( line->IsDefined() && epsilon->IsDefined() ){
|
|
Points* ps= new Points(0);
|
|
line->Simplify( *res, epsilon->GetRealval(),*ps,geoid );
|
|
ps->DeleteIfAllowed();
|
|
} else {
|
|
res->SetDefined( false );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SpatialSimplify_LRealPs(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
Line* line = static_cast<Line*>(args[0].addr);
|
|
CcReal* epsilon = static_cast<CcReal*>(args[1].addr);
|
|
Points* ps = static_cast<Points*>(args[2].addr);
|
|
Geoid* geoid = 0;
|
|
if(qp->GetNoSons(s)==4){
|
|
geoid = static_cast<Geoid*>(args[3].addr);
|
|
if(!geoid->IsDefined()){
|
|
res->SetDefined(0);
|
|
return 0;
|
|
}
|
|
}
|
|
if( line->IsDefined() && epsilon->IsDefined() && ps->IsDefined() )
|
|
line->Simplify( *res, epsilon->GetRealval(), *ps,geoid);
|
|
else
|
|
res->SetDefined( false );
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.32 Value Mapping for the ~get~ operator
|
|
|
|
*/
|
|
int SpatialGet(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Points* ps = (Points*) args[0].addr;
|
|
CcInt* index = (CcInt*) args[1].addr;
|
|
if(!ps->IsDefined() || !index->IsDefined()){
|
|
((Point*)result.addr)->SetDefined(false);
|
|
return 0;
|
|
}
|
|
int i = index->GetIntval();
|
|
if(i<0 || i >= ps->Size()){
|
|
((Point*)result.addr)->SetDefined(false);
|
|
return 0;
|
|
}
|
|
|
|
Point p(true);
|
|
ps->Get(i,p);
|
|
((Point*)result.addr)->CopyFrom(&p);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
10.4.34 Value Mapping for the ~makeline~ and ~makesline~ operators
|
|
|
|
*/
|
|
void SetStartSmaller(Line* l, bool s){}
|
|
void SetStartSmaller(SimpleLine* l, bool s){
|
|
l->SetStartSmaller(s);
|
|
}
|
|
|
|
|
|
|
|
template<class LineType>
|
|
int SpatialMakeLine(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Point* p1 = static_cast<Point*>(args[0].addr);
|
|
Point* p2 = static_cast<Point*>(args[1].addr);
|
|
LineType* res = static_cast<LineType*>(result.addr);
|
|
res->Clear();
|
|
if(!p1->IsDefined() || !p2->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(AlmostEqual(*p1,*p2)){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->StartBulkLoad();
|
|
HalfSegment h(true, *p1, *p2);
|
|
h.attr.edgeno = 0;
|
|
(*res) += h;
|
|
h.SetLeftDomPoint(false);
|
|
(*res) += h;
|
|
res->EndBulkLoad();
|
|
if(p1->Compare(p2) > 0){
|
|
SetStartSmaller(res,false);
|
|
} else {
|
|
SetStartSmaller(res,true);
|
|
}
|
|
return 0;
|
|
}
|
|
/*
|
|
~Realminize~
|
|
|
|
*/
|
|
int RealminizeVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
Line* arg = static_cast<Line*>(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
Realminize2(*arg,*res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
Value Mapping for ~CommonBorder2~
|
|
|
|
*/
|
|
int CommonBorder2VM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Region* arg1 = static_cast<Region*>(args[0].addr);
|
|
Region* arg2 = static_cast<Region*>(args[1].addr);
|
|
Geoid* geoid = 0; // TODO: Add spherical geometry case
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
CommonBorder(*arg2,*arg1,*res,geoid);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Value Mapping for ~toLine~
|
|
|
|
*/
|
|
int toLineVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
SimpleLine* sline = static_cast<SimpleLine*>(args[0].addr);
|
|
Line* res = static_cast<Line*>(result.addr);
|
|
sline->toLine(*res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Value Mapping for ~fromLine~
|
|
|
|
*/
|
|
|
|
|
|
int fromLineVM1(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Line* line = static_cast<Line*>(args[0].addr);
|
|
SimpleLine* res = static_cast<SimpleLine*>(result.addr);
|
|
res->fromLine(*line);
|
|
return 0;
|
|
}
|
|
|
|
int fromLineVM2(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Line* line = static_cast<Line*>(args[0].addr);
|
|
CcBool* smaller = static_cast<CcBool*> (args[1].addr);
|
|
SimpleLine* res = static_cast<SimpleLine*>(result.addr);
|
|
res->fromLine(*line,smaller->GetBoolval());
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping fromLineVM[] = {
|
|
fromLineVM1,
|
|
fromLineVM2
|
|
};
|
|
|
|
int fromLineSelect(ListExpr args){
|
|
if(nl->ListLength(args)==1) return 0;
|
|
if(nl->ListLength(args)==2) return 1;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
Value Mapping for ~isCycle~
|
|
|
|
*/
|
|
int isCycleVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
result = qp->ResultStorage(s);
|
|
SimpleLine* sline = static_cast<SimpleLine*>(args[0].addr);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
if( sline->IsDefined() ) {
|
|
res->Set(true,sline->IsCycle());
|
|
} else {
|
|
res->SetDefined( false );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
int utmVM_p(Word* args, Word& result, int message,
|
|
Word& local, Supplier s) {
|
|
result = qp->ResultStorage(s);
|
|
Point* p = static_cast<Point*>(args[0].addr);
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
UTM utm;
|
|
utm(*p,*res);
|
|
return 0;
|
|
}
|
|
|
|
int utmVM_ps(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Points* p = static_cast<Points*>(args[0].addr);
|
|
Points* res = static_cast<Points*>(result.addr);
|
|
UTM utm;
|
|
res->Clear();
|
|
res->Resize(p->Size());
|
|
res->StartBulkLoad();
|
|
Point p1(true);
|
|
for(int i=0;i<p->Size();i++){
|
|
p->Get(i,p1);
|
|
Point p2(true);
|
|
if(! utm(p1,p2)){
|
|
res->EndBulkLoad();
|
|
res->Clear();
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
(*res) += (p2);
|
|
}
|
|
res->EndBulkLoad();
|
|
return 0;
|
|
}
|
|
|
|
int gkVM_p(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Point* p = static_cast<Point*>(args[0].addr);
|
|
CcInt* zone = static_cast<CcInt*>(args[1].addr);
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
if(!zone->IsDefined() || zone->GetValue() < 0 || zone->GetValue() > 119){
|
|
res->SetDefined(false);
|
|
}
|
|
WGSGK gk;
|
|
gk.setMeridian(zone->GetValue());
|
|
gk.project(*p,*res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
int gkVM_ps(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Points* p = static_cast<Points*>(args[0].addr);
|
|
CcInt* zone = static_cast<CcInt*>(args[1].addr);
|
|
Points* res = static_cast<Points*>(result.addr);
|
|
res->Clear();
|
|
if( !p->IsDefined() || !zone->IsDefined()
|
|
|| zone->GetValue()<0 || zone->GetValue() > 119){
|
|
res->SetDefined( false );
|
|
res->Clear();
|
|
}
|
|
WGSGK gk;
|
|
gk.setMeridian(zone->GetValue());
|
|
res->SetDefined( true );
|
|
res->Resize(p->Size());
|
|
res->StartBulkLoad();
|
|
Point p1(true);
|
|
for(int i=0;i<p->Size();i++){
|
|
p->Get(i,p1);
|
|
Point p2(true);
|
|
if(! gk.project(p1,p2)){
|
|
res->EndBulkLoad();
|
|
res->Clear();
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
(*res) += (p2);
|
|
}
|
|
res->EndBulkLoad();
|
|
return 0;
|
|
}
|
|
|
|
template<class T>
|
|
int gkVM_x(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
T* a = static_cast<T*>(args[0].addr);
|
|
CcInt* zone = static_cast<CcInt*>(args[1].addr);
|
|
T* res = static_cast<T*>(result.addr);
|
|
res->Clear();
|
|
if( !a->IsDefined() || !zone->IsDefined()
|
|
|| zone->GetValue() < 0 || zone->GetValue() > 119){
|
|
res->SetDefined( false );
|
|
return 0;
|
|
}
|
|
WGSGK gk;
|
|
gk.setMeridian(zone->GetValue());
|
|
res->SetDefined( true );
|
|
res->Resize(a->Size());
|
|
res->StartBulkLoad();
|
|
HalfSegment hs;
|
|
HalfSegment hs2;
|
|
for(int i=0;i<a->Size();i++){
|
|
a->Get(i,hs);
|
|
if(! gk.project(hs,hs2)){
|
|
res->Clear();
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
(*res) += (hs2);
|
|
}
|
|
res->EndBulkLoad();
|
|
return 0;
|
|
}
|
|
|
|
int gkVM_rect(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Rectangle<2>* r = static_cast<Rectangle<2>*>(args[0].addr);
|
|
CcInt* zone = static_cast<CcInt*>(args[1].addr);
|
|
Rectangle<2>* res = static_cast<Rectangle<2>*>(result.addr);
|
|
if( !r->IsDefined() || !zone->IsDefined()
|
|
|| zone->GetValue() < 0 || zone->GetValue() > 119){
|
|
res->SetDefined( false );
|
|
return 0;
|
|
}
|
|
WGSGK gk;
|
|
gk.setMeridian(zone->GetValue());
|
|
res->SetDefined( true );
|
|
double x1 = r->MinD(0);
|
|
double y1 = r->MinD(1);
|
|
double x2 = r->MaxD(0);
|
|
double y2 = r->MaxD(1);
|
|
|
|
Point p1(true,x1,y1);
|
|
Point p2(true,x2,y2);
|
|
Point p1p(true,0,0);
|
|
Point p2p(true,0,0);
|
|
if(!gk.project(p1,p1p) ||
|
|
!gk.project(p2,p2p)){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double mind[2];
|
|
double maxd[2];
|
|
mind[0] = min(p1p.GetX(),p2p.GetX());
|
|
mind[1] = min(p1p.GetY(),p2p.GetY());
|
|
maxd[0] = max(p1p.GetX(),p2p.GetX());
|
|
maxd[1] = max(p1p.GetY(),p2p.GetY());
|
|
res->Set(true,mind,maxd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
Reverse Gausss Krueger Projection.
|
|
|
|
|
|
*/
|
|
|
|
|
|
void reverseGK(const Point* arg, Point* result){
|
|
if(!arg->IsDefined()){
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
WGSGK gk;
|
|
bool ok = gk.getOrig(*arg, *result);
|
|
if(!ok){
|
|
result->SetDefined(false);
|
|
}
|
|
}
|
|
|
|
|
|
void reverseGK(const Points* arg, Points* result){
|
|
WGSGK gk;
|
|
result->Clear();
|
|
result->Resize(arg->Size());
|
|
result->StartBulkLoad();
|
|
for(int i=0; i< arg->Size(); i++){
|
|
Point p1(true);
|
|
arg->Get(i,p1);
|
|
Point p2(true);
|
|
bool ok = gk.getOrig(p1,p2);
|
|
if(!ok || !p2.IsDefined()){
|
|
result->Clear();
|
|
result->EndBulkLoad();
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
(*result) += p2;
|
|
}
|
|
result->EndBulkLoad();
|
|
}
|
|
|
|
|
|
|
|
bool reverseGK(HalfSegment& h, const WGSGK& gk){
|
|
Point p1 = h.GetLeftPoint();
|
|
Point p3(false,0,0);
|
|
bool ok = gk.getOrig(p1,p3);
|
|
if(!ok || !p3.IsDefined()){
|
|
return false;
|
|
}
|
|
Point p2 = h.GetRightPoint();
|
|
Point p4(false,0,0);
|
|
ok = gk.getOrig(p2,p4);
|
|
if(!ok || !p4.IsDefined()){
|
|
return false;
|
|
}
|
|
h.Set(h.IsLeftDomPoint(), p3, p4);
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
template <class T>
|
|
void reverseGK(const T* arg, T* result){
|
|
if(!arg->IsDefined()){
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
WGSGK gk;
|
|
result->Clear();
|
|
result->Resize(arg->Size());
|
|
result->StartBulkLoad();
|
|
for(int i=0;i<arg->Size();i++){
|
|
HalfSegment h;
|
|
arg->Get(i,h);
|
|
if(h.IsLeftDomPoint()){
|
|
if(!reverseGK(h,gk)){
|
|
result->EndBulkLoad();
|
|
result->Clear();
|
|
result->SetDefined(false);
|
|
return;
|
|
}
|
|
(*result) += h;
|
|
h.SetLeftDomPoint(false);
|
|
(*result) += h;
|
|
}
|
|
}
|
|
result->EndBulkLoad();
|
|
}
|
|
|
|
inline void reverseGK(const Line* arg, Line* result){
|
|
return reverseGK<Line>(arg,result);
|
|
}
|
|
|
|
inline void reverseGK(const Region* arg, Region* result){
|
|
return reverseGK<Region>(arg,result);
|
|
}
|
|
|
|
|
|
|
|
template<class T>
|
|
int reversegkVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
T* arg = static_cast<T*>(args[0].addr);
|
|
T* res = static_cast<T*>(result.addr);
|
|
reverseGK(arg,res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
Collecting line
|
|
|
|
*/
|
|
template<class ResLineType>
|
|
int SpatialCollect_lineVMPointstream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
ResLineType* L = static_cast<ResLineType*>(result.addr);
|
|
Point* P0 = 0;
|
|
Point* P1 = 0;
|
|
Word elem;
|
|
L->Clear();
|
|
L->SetDefined( true );
|
|
|
|
qp->Open(args[0].addr);
|
|
|
|
qp->Request(args[0].addr, elem);
|
|
if(!qp->Received(args[0].addr)){
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
P0 = static_cast<Point*>(elem.addr);
|
|
assert( P0 != 0 );
|
|
if(!P0->IsDefined()){ // found undefined Elem
|
|
qp->Close(args[0].addr);
|
|
L->SetDefined(false);
|
|
P0->DeleteIfAllowed();
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
L->StartBulkLoad();
|
|
qp->Request(args[0].addr, elem);
|
|
while ( qp->Received(args[0].addr) ){
|
|
P1 = static_cast<Point*>(elem.addr);
|
|
assert( P1 != 0 );
|
|
if(!P1->IsDefined()){
|
|
qp->Close(args[0].addr);
|
|
L->Clear();
|
|
L->SetDefined(false);
|
|
if(P0){ P0->DeleteIfAllowed(); P0 = 0; }
|
|
if(P1){ P1->DeleteIfAllowed(); P1 = 0; }
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
if(AlmostEqual(*P0,*P1)){
|
|
qp->Request(args[0].addr, elem);
|
|
P1->DeleteIfAllowed();
|
|
P1 = 0;
|
|
} else {
|
|
HalfSegment hs(true, *P0, *P1); // create halfsegment
|
|
(*L) += (hs);
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() ); //createcounter-halfsegment
|
|
(*L) += (hs);
|
|
P0->DeleteIfAllowed();
|
|
P0 = P1; P1 = 0;
|
|
qp->Request(args[0].addr, elem); // get next Point
|
|
}
|
|
}
|
|
L->EndBulkLoad(); // sort and realminize
|
|
|
|
qp->Close(args[0].addr);
|
|
if(P0){ P0->DeleteIfAllowed(); P0 = 0; }
|
|
return 0;
|
|
}
|
|
|
|
// helper function for SpatialCollect_line
|
|
void append(Line& l1, const Line& l2){
|
|
// l1 += l2; // runs not correctly
|
|
int size = l2.Size();
|
|
HalfSegment hs;
|
|
for(int i = 0; i < size; i++){
|
|
l2.Get( i, hs );
|
|
l1 += hs;
|
|
}
|
|
}
|
|
|
|
// helper function for SpatialCollect_line
|
|
void append(SimpleLine& l1, const SimpleLine& l2){
|
|
int size = l2.Size();
|
|
HalfSegment hs;
|
|
for(int i = 0; i < size; i++){
|
|
l2.Get( i, hs );
|
|
l1 += hs;
|
|
}
|
|
}
|
|
|
|
// helper function for SpatialCollect_line
|
|
void append(Line& l1, const SimpleLine& l2){
|
|
int size = l2.Size();
|
|
HalfSegment hs;
|
|
for(int i = 0; i < size; i++){
|
|
l2.Get( i, hs );
|
|
l1 += hs;
|
|
}
|
|
}
|
|
|
|
// helper function for SpatialCollect_line
|
|
void append(SimpleLine& l1, const Line& l2){
|
|
int size = l2.Size();
|
|
HalfSegment hs;
|
|
for(int i = 0; i < size; i++){
|
|
l2.Get( i, hs );
|
|
l1 += hs;
|
|
}
|
|
}
|
|
|
|
|
|
template <class StreamLineType, class ResLineType>
|
|
int SpatialCollect_lineVMLinestream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
ResLineType* L = static_cast<ResLineType*>(result.addr);
|
|
StreamLineType* line = 0;
|
|
L->Clear();
|
|
CcBool* ignoreUndefined = static_cast<CcBool*>(args[1].addr);
|
|
if(!ignoreUndefined->IsDefined()){
|
|
L->SetDefined(false);
|
|
return 0;
|
|
}
|
|
bool ignore = ignoreUndefined->GetValue();
|
|
|
|
Word elem;
|
|
qp->Open(args[0].addr);
|
|
|
|
L->StartBulkLoad();
|
|
qp->Request(args[0].addr, elem);
|
|
while ( qp->Received(args[0].addr) ){
|
|
line = static_cast<StreamLineType*>(elem.addr);
|
|
assert( line != 0 );
|
|
if(!ignore && !line->IsDefined()){
|
|
qp->Close(args[0].addr);
|
|
L->Clear();
|
|
L->SetDefined(false);
|
|
if(line){ line->DeleteIfAllowed(); }
|
|
return 0;
|
|
}
|
|
append(*L, *line);
|
|
line->DeleteIfAllowed();
|
|
line = 0;
|
|
qp->Request(args[0].addr, elem); // get next line
|
|
}
|
|
L->EndBulkLoad(); // sort and realminize
|
|
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
|
|
template<class StreamPointType>
|
|
int SpatialCollect_PointsVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
|
|
CcBool* ignoreUndefined = static_cast<CcBool*>(args[1].addr);
|
|
result = qp->ResultStorage(s);
|
|
Points* res = static_cast<Points*>(result.addr);
|
|
res->Clear();
|
|
if(!ignoreUndefined->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->SetDefined(true);
|
|
bool ignore = ignoreUndefined->GetValue();
|
|
Word elem;
|
|
res->StartBulkLoad();
|
|
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr,elem);
|
|
|
|
while(qp->Received(args[0].addr)){
|
|
StreamPointType* p = static_cast<StreamPointType*>(elem.addr);
|
|
if(p->IsDefined()){
|
|
(*res) += *p;
|
|
} else if(!ignore){
|
|
res->EndBulkLoad(false,false,false);
|
|
res->Clear();
|
|
res->SetDefined(false);
|
|
qp->Close(args[0].addr);
|
|
return 0;
|
|
}
|
|
p->DeleteIfAllowed();
|
|
qp->Request(args[0].addr,elem);
|
|
}
|
|
qp->Close(args[0].addr);
|
|
res->EndBulkLoad();
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*
|
|
collect sline
|
|
|
|
*/
|
|
template<class ResLineType>
|
|
int SpatialCollect_slineVMPointstream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
ResLineType* L = static_cast<ResLineType*>(result.addr);
|
|
L->Clear();
|
|
L->SetDefined( true );
|
|
CcBool* IgnoreUndef = (CcBool*) args[1].addr;
|
|
if(!IgnoreUndef->IsDefined()){
|
|
L->SetDefined(false);
|
|
return 0;
|
|
}
|
|
bool ignoreUndef = IgnoreUndef->GetValue();
|
|
|
|
Stream<Point> stream(args[0]);
|
|
Point* firstPoint = 0; // the very first point
|
|
Point* lp = 0;
|
|
stream.open();
|
|
|
|
// search for the first defined point in stream
|
|
while( (firstPoint == 0) && ((lp = stream.request())!=0) ){
|
|
if(!lp->IsDefined()){
|
|
lp->DeleteIfAllowed();
|
|
if(!ignoreUndef){
|
|
L->SetDefined(false);
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
} else {
|
|
firstPoint = (Point*) lp->Copy();
|
|
}
|
|
}
|
|
|
|
if(!firstPoint){ // no defined point found in stream
|
|
stream.close();
|
|
return 0; // an empty line
|
|
}
|
|
|
|
|
|
Point* cp;
|
|
Point* secondPoint = 0;
|
|
// iterate over remaining points
|
|
L->StartBulkLoad();
|
|
while( (cp = stream.request()) != 0){
|
|
if(!cp->IsDefined()){ // spacial case undefined point in stream
|
|
cp->DeleteIfAllowed();
|
|
if(!ignoreUndef){
|
|
firstPoint->DeleteIfAllowed();
|
|
lp->DeleteIfAllowed();
|
|
stream.close();
|
|
L->Clear();
|
|
L->SetDefined(false);
|
|
return 0;
|
|
}
|
|
} else {
|
|
if(AlmostEqual(*lp,*cp)){
|
|
cp->DeleteIfAllowed(); //ignore point that is too close to the last one
|
|
} else { // normal case: add halfsegments
|
|
if(!secondPoint){
|
|
secondPoint = (Point*) cp->Copy();
|
|
}
|
|
HalfSegment hs(true, *lp, *cp); // create halfsegment
|
|
(*L) += (hs);
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
(*L) += (hs);
|
|
lp->DeleteIfAllowed();
|
|
lp = cp;
|
|
}
|
|
}
|
|
}
|
|
stream.close();
|
|
L->EndBulkLoad(); // sort and realminize
|
|
if(lp){
|
|
if(!L->IsCycle()){
|
|
L->SetStartSmaller(*firstPoint < *lp);
|
|
} else {
|
|
L->SetStartSmaller(*firstPoint < *secondPoint);
|
|
}
|
|
lp->DeleteIfAllowed();
|
|
if(secondPoint){
|
|
secondPoint->DeleteIfAllowed();
|
|
}
|
|
}
|
|
firstPoint->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
|
|
template <class StreamLineType, class ResLineType>
|
|
int SpatialCollect_slineVMLinestream(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
ResLineType* L = static_cast<ResLineType*>(result.addr);
|
|
StreamLineType* line = 0;
|
|
L->Clear();
|
|
CcBool* ignoreUndefined = static_cast<CcBool*>(args[1].addr);
|
|
if(!ignoreUndefined->IsDefined()){
|
|
L->SetDefined(false);
|
|
return 0;
|
|
}
|
|
bool ignore = ignoreUndefined->GetValue();
|
|
Word elem;
|
|
Point* firstPoint = 0;
|
|
Point* lastPoint = 0;
|
|
bool first = true;
|
|
L->StartBulkLoad();
|
|
qp->Open(args[0].addr);
|
|
qp->Request(args[0].addr, elem);
|
|
while ( qp->Received(args[0].addr) ){
|
|
line = static_cast<StreamLineType*>(elem.addr);
|
|
assert( line != 0 );
|
|
if(!line->IsDefined())
|
|
{
|
|
if (!ignore)
|
|
{
|
|
qp->Close(args[0].addr);
|
|
L->Clear();
|
|
L->SetDefined(false);
|
|
line->DeleteIfAllowed();
|
|
line = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (first)
|
|
{
|
|
firstPoint = new Point(line->StartPoint());
|
|
lastPoint = new Point(line->EndPoint());
|
|
first = false;
|
|
append(*L, *line);
|
|
}
|
|
else
|
|
{
|
|
lastPoint->DeleteIfAllowed();
|
|
lastPoint = new Point(line->EndPoint());
|
|
append(*L, *line);
|
|
}
|
|
}
|
|
line->DeleteIfAllowed();
|
|
line = 0;
|
|
qp->Request(args[0].addr, elem); // get next line
|
|
}
|
|
qp->Close(args[0].addr);
|
|
L->EndBulkLoad(); // sort and realminize
|
|
if (L->IsDefined()){
|
|
if (firstPoint > lastPoint) L->SetStartSmaller(false);
|
|
else L->SetStartSmaller(true);
|
|
}
|
|
lastPoint->DeleteIfAllowed();
|
|
firstPoint->DeleteIfAllowed();
|
|
return 0;
|
|
}
|
|
|
|
int SpatialVMSetStartSmaller(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
SimpleLine* l = static_cast<SimpleLine*>(args[0].addr);
|
|
CcBool* b = static_cast<CcBool*>(args[1].addr);
|
|
result = qp->ResultStorage(s);
|
|
SimpleLine* res = static_cast<SimpleLine*>(result.addr);
|
|
if(!l->IsDefined() || !b->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
res->CopyFrom(l);
|
|
res->SetStartSmaller(b->GetBoolval());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SpatialVMGetStartSmaller(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
SimpleLine* l = static_cast<SimpleLine*>(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = static_cast<CcBool*>(result.addr);
|
|
if(!l->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
res->Set(true,l->GetStartSmaller());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int SpatialVMCreateSline(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
Point* p1 = static_cast<Point*>(args[0].addr);
|
|
Point* p2 = static_cast<Point*>(args[1].addr);
|
|
result = qp->ResultStorage(s);
|
|
SimpleLine* res = static_cast<SimpleLine*>(result.addr);
|
|
if(!p1->IsDefined() || !p1->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else if(AlmostEqual(*p1,*p2)){
|
|
res->Clear();
|
|
res->SetDefined(true);
|
|
} else {
|
|
res->Clear();
|
|
res->SetDefined(true);
|
|
res->StartBulkLoad();
|
|
HalfSegment hs(true, *p1, *p2);
|
|
*res += hs;
|
|
hs.SetLeftDomPoint(false);
|
|
*res += hs;
|
|
res->EndBulkLoad();
|
|
res->SetStartSmaller(*p1 < *p2);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
10.5 Definition of operators
|
|
|
|
Definition of operators is done in a way similar to definition of
|
|
type constructors: an instance of class ~Operator~ is defined.
|
|
|
|
Because almost all operators are overloaded, we have first do define an
|
|
array of value mapping functions for each operator. For nonoverloaded
|
|
operators there is also such and array defined, so it easier to make them
|
|
overloaded.
|
|
|
|
10.5.1 Definition of value mapping vectors
|
|
|
|
*/
|
|
ValueMapping spatialisemptymap[] = {
|
|
SpatialIsEmpty<Point>,
|
|
SpatialIsEmpty<Points>,
|
|
SpatialIsEmpty<Line>,
|
|
SpatialIsEmpty<Region>,
|
|
SpatialIsEmpty<SimpleLine> };
|
|
|
|
ValueMapping spatialequalmap[] = {
|
|
SpatialEqual<Point,Point>,
|
|
SpatialEqual<Points, Points>,
|
|
SpatialEqual<Line,Line>,
|
|
SpatialEqual<Region, Region>,
|
|
SpatialEqual<SimpleLine, SimpleLine>,
|
|
SpatialEqual<Point,Points>,
|
|
SpatialEqual<Points, Point>};
|
|
|
|
ValueMapping spatialislessmap[] = { SpatialIsLess<Point, Point>};
|
|
|
|
ValueMapping spatialnotequalmap[] = {
|
|
SpatialNotEqual<Point>,
|
|
SpatialNotEqual<Points>,
|
|
SpatialNotEqual<Line>,
|
|
SpatialNotEqual<Region>,
|
|
SpatialNotEqual<SimpleLine> };
|
|
|
|
ValueMapping spatialintersectsmap[] = {
|
|
SpatialIntersectsVM<Points,Points,false>,
|
|
SpatialIntersectsVM<Points,Line,false>,
|
|
SpatialIntersectsVM<Points,Region,false>,
|
|
SpatialIntersectsVM<Points,Line,true>,
|
|
SpatialIntersectsVM<Line,Line,false>,
|
|
SpatialIntersectsVM<Line,Region,false>,
|
|
SpatialIntersectsVM<Points,Region,true>,
|
|
SpatialIntersectsVM<Line,Region,true>,
|
|
SpatialIntersectsVM<Region,Region,false>,
|
|
SpatialIntersectsVM<SimpleLine,SimpleLine,false>
|
|
};
|
|
|
|
ValueMapping spatialinsidemap[] = {
|
|
SpatialInsideGeneric<Point,Points>,
|
|
SpatialInsideGeneric<Point,Line>,
|
|
SpatialInsideGeneric<Point,Region>,
|
|
SpatialInsideGeneric<Points,Points>,
|
|
SpatialInsideGeneric<Points,Line>,
|
|
SpatialInsideGeneric<Points,Region>,
|
|
SpatialInsideGeneric<Line,Line>,
|
|
SpatialInsideGeneric<Line,Region>,
|
|
SpatialInsideGeneric<Region,Region>,
|
|
SpatialInsideGeneric<Point,SimpleLine>,
|
|
SpatialInsideGeneric<SimpleLine,SimpleLine>
|
|
};
|
|
|
|
ValueMapping spatialadjacentmap[] = {
|
|
SpatialAdjacent_psr,
|
|
SpatialAdjacent_lr,
|
|
SpatialAdjacent_rps,
|
|
SpatialAdjacent_rl,
|
|
SpatialAdjacent_rr };
|
|
|
|
|
|
ValueMapping spatialintersectionVM[] = {
|
|
SpatialIntersectionGeneric<Point, Point, Points>,
|
|
SpatialIntersectionGeneric<Point, Points, Points>,
|
|
SpatialIntersectionGeneric<Point, Line, Points>,
|
|
SpatialIntersectionGeneric<Point, Region, Points>,
|
|
SpatialIntersectionGeneric<Point, SimpleLine, Points>,
|
|
|
|
SpatialIntersectionGeneric<Points, Point, Points>,
|
|
SpatialIntersectionGeneric<Points, Points, Points>,
|
|
SpatialIntersectionGeneric<Points, Line, Points>,
|
|
SpatialIntersectionGeneric<Points, Region, Points>,
|
|
SpatialIntersectionGeneric<Points, SimpleLine, Points>,
|
|
|
|
SpatialIntersectionGeneric<Line, Point, Points>,
|
|
SpatialIntersectionGeneric<Line, Points, Points>,
|
|
SpatialIntersectionGeneric<Line, Line, Line>,
|
|
SpatialIntersectionGeneric<Line, Region, Line>,
|
|
SpatialIntersectionGeneric<Line, SimpleLine, SimpleLine>,
|
|
|
|
SpatialIntersectionGeneric<Region, Point, Points>,
|
|
SpatialIntersectionGeneric<Region, Points, Points>,
|
|
SpatialIntersectionGeneric<Region, Line, Line>,
|
|
SpatialIntersectionGeneric<Region, Region, Region>,
|
|
SpatialIntersectionGeneric<Region, SimpleLine, SimpleLine>,
|
|
|
|
SpatialIntersectionGeneric<SimpleLine, Point, Points>,
|
|
SpatialIntersectionGeneric<SimpleLine, Points, Points>,
|
|
SpatialIntersectionGeneric<SimpleLine, Line, SimpleLine>,
|
|
SpatialIntersectionGeneric<SimpleLine, Region, SimpleLine>,
|
|
SpatialIntersectionGeneric<SimpleLine, SimpleLine, SimpleLine>
|
|
|
|
};
|
|
|
|
ValueMapping spatialminusVM[] = {
|
|
SpatialMinusGeneric<Point, Point, Points>,
|
|
SpatialMinusGeneric<Point, Points, Points>,
|
|
SpatialMinusGeneric<Point, Line, Points>,
|
|
SpatialMinusGeneric<Point, Region, Points>,
|
|
SpatialMinusGeneric<Point, SimpleLine, Points>,
|
|
|
|
SpatialMinusGeneric<Points, Point, Points>,
|
|
SpatialMinusGeneric<Points, Points, Points>,
|
|
SpatialMinusGeneric<Points, Line, Points>,
|
|
SpatialMinusGeneric<Points, Region, Points>,
|
|
SpatialMinusGeneric<Points, SimpleLine, Points>,
|
|
|
|
SpatialMinusGeneric<Line, Point, Line>,
|
|
SpatialMinusGeneric<Line, Points, Line>,
|
|
SpatialMinusGeneric<Line, Line, Line>,
|
|
SpatialMinusGeneric<Line, Region, Line>,
|
|
SpatialMinusGeneric<Line, SimpleLine, Line>,
|
|
|
|
SpatialMinusGeneric<Region, Point, Region>,
|
|
SpatialMinusGeneric<Region, Points, Region>,
|
|
SpatialMinusGeneric<Region, Line, Region>,
|
|
SpatialMinusGeneric<Region, Region, Region>,
|
|
SpatialMinusGeneric<Region, SimpleLine, Region>,
|
|
|
|
SpatialMinusGeneric<SimpleLine, Point, SimpleLine>,
|
|
SpatialMinusGeneric<SimpleLine, Points, SimpleLine>,
|
|
SpatialMinusGeneric<SimpleLine, Line, SimpleLine>,
|
|
SpatialMinusGeneric<SimpleLine, Region, SimpleLine>,
|
|
SpatialMinusGeneric<SimpleLine, SimpleLine, SimpleLine>
|
|
|
|
};
|
|
|
|
|
|
ValueMapping spatialunionVM[] = {
|
|
SpatialUnionGeneric<Point, Point, Points>,
|
|
SpatialUnionGeneric<Point, Points, Points>,
|
|
SpatialUnionGeneric<Point, Line, Line>,
|
|
SpatialUnionGeneric<Point, Region, Region>,
|
|
SpatialUnionGeneric<Point, SimpleLine, SimpleLine>,
|
|
|
|
SpatialUnionGeneric<Points, Point, Points>,
|
|
SpatialUnionGeneric<Points, Points, Points>,
|
|
SpatialUnionGeneric<Points, Line, Line>,
|
|
SpatialUnionGeneric<Points, Region, Region>,
|
|
SpatialUnionGeneric<Points, SimpleLine, SimpleLine>,
|
|
|
|
SpatialUnionGeneric<Line, Point, Line>,
|
|
SpatialUnionGeneric<Line, Points, Line>,
|
|
SpatialUnionGeneric<Line, Line, Line>,
|
|
SpatialUnionGeneric<Line, Region, Region>,
|
|
SpatialUnionGeneric<Line, SimpleLine, Line>,
|
|
|
|
SpatialUnionGeneric<Region, Point, Region>,
|
|
SpatialUnionGeneric<Region, Points, Region>,
|
|
SpatialUnionGeneric<Region, Line, Region>,
|
|
SpatialUnionGeneric<Region, Region, Region>,
|
|
SpatialUnionGeneric<Region, SimpleLine, Region>,
|
|
|
|
SpatialUnionGeneric<SimpleLine, Point, SimpleLine>,
|
|
SpatialUnionGeneric<SimpleLine, Points, SimpleLine>,
|
|
SpatialUnionGeneric<SimpleLine, Line, Line>,
|
|
SpatialUnionGeneric<SimpleLine, Region, Region>,
|
|
SpatialUnionGeneric<SimpleLine, SimpleLine, Line>,
|
|
|
|
};
|
|
|
|
ValueMapping spatialdistancemap[] = {
|
|
|
|
// arg1 = point
|
|
SpatialDistance<Point,Point,false>,
|
|
SpatialDistance<Points,Point,true>,
|
|
SpatialDistance<Line,Point,true>,
|
|
SpatialDistance<SimpleLine,Point,true>,
|
|
SpatialDistance<Point,Rectangle<2>,false>,
|
|
SpatialDistance<Region,Point, true>,
|
|
|
|
// arg1 = points
|
|
SpatialDistance<Points,Point,false>,
|
|
SpatialDistance<Points,Points,false>,
|
|
SpatialDistance<Line, Points, true>,
|
|
SpatialDistance<SimpleLine, Points,true>,
|
|
SpatialDistance<Points,Rectangle<2>,false>,
|
|
SpatialDistance<Region, Points, true>,
|
|
|
|
// arg1 = line
|
|
SpatialDistance<Line,Point,false>,
|
|
SpatialDistance<Line,Points,false>,
|
|
SpatialDistance<Line,Line,false>,
|
|
SpatialDistance<Line,SimpleLine, false>,
|
|
SpatialDistance<Line,Rectangle<2>,false>,
|
|
SpatialDistance<Region,Line, true>,
|
|
|
|
// arg1 = sline
|
|
SpatialDistance<SimpleLine,Point,false>,
|
|
SpatialDistance<SimpleLine,Points,false>,
|
|
SpatialDistance<Line,SimpleLine,true>,
|
|
SpatialDistance<SimpleLine,SimpleLine,false>,
|
|
SpatialDistance<SimpleLine,Rectangle<2>,false>,
|
|
SpatialDistance<Region, SimpleLine, true>,
|
|
|
|
// arg1 = rect
|
|
SpatialDistance<Point ,Rectangle<2>, true>,
|
|
SpatialDistance<Points ,Rectangle<2>, true>,
|
|
SpatialDistance<Line ,Rectangle<2>, true>,
|
|
SpatialDistance<SimpleLine,Rectangle<2>, true>,
|
|
SpatialDistance<Rectangle<2>,Rectangle<2>,false>,
|
|
SpatialDistance<Region,Rectangle<2>,true>,
|
|
|
|
// arg1 = region
|
|
SpatialDistance<Region,Point,false>,
|
|
SpatialDistance<Region,Points,false>,
|
|
SpatialDistance<Region,Line,false>,
|
|
SpatialDistance<Region,SimpleLine,false>,
|
|
SpatialDistance<Region,Rectangle<2>,false>,
|
|
SpatialDistance<Region,Region,false>,
|
|
};
|
|
|
|
ValueMapping spatialnocomponentsmap[] = {
|
|
SpatialNoComponents_ps,
|
|
SpatialNoComponents_l,
|
|
SpatialNoComponents_r };
|
|
|
|
ValueMapping spatialnosegmentsmap[] = {
|
|
SpatialNoSegments<Line>,
|
|
SpatialNoSegments<Region>,
|
|
SpatialNoSegments<SimpleLine>,
|
|
SpatialNoSegmentsDline };
|
|
|
|
ValueMapping spatialbboxmap[] = {
|
|
SpatialBBox<Point>,
|
|
SpatialBBox<Points>,
|
|
SpatialBBox<Line>,
|
|
SpatialBBox<Region>,
|
|
SpatialBBox<SimpleLine>,
|
|
SpatialBBox<Rectangle<2> >,
|
|
SpatialBBox<CPoint>};
|
|
|
|
ValueMapping spatialtouchpointsmap[] = {
|
|
SpatialTouchPoints_lr,
|
|
SpatialTouchPoints_rl,
|
|
SpatialTouchPoints_rr };
|
|
|
|
|
|
|
|
ValueMapping spatialwindowclippinginmap[] = {
|
|
SpatialWindowClippingIn_l,
|
|
SpatialWindowClippingIn_r };
|
|
|
|
ValueMapping spatialwindowclippingoutmap[] = {
|
|
SpatialWindowClippingOut_l,
|
|
SpatialWindowClippingOut_r };
|
|
|
|
|
|
ValueMapping spatialcomponentsmap[] = {
|
|
SpatialComponents_ps,
|
|
SpatialComponents_r,
|
|
SpatialComponents_l };
|
|
|
|
ValueMapping spatialverticesmap[] = {
|
|
SpatialVertices_l,
|
|
SpatialVertices_r };
|
|
|
|
ValueMapping spatialboundarymap[] = {
|
|
SpatialBoundary_r,
|
|
SpatialBoundary_l};
|
|
|
|
ValueMapping spatialaddmap[] = { SpatialAdd_p };
|
|
|
|
ValueMapping spatialgetxmap[] = { SpatialGetX_p };
|
|
|
|
ValueMapping spatialgetymap[] = { SpatialGetY_p };
|
|
|
|
ValueMapping spatialsimplifymap[] = { SpatialSimplify_LReal,
|
|
SpatialSimplify_LRealPs };
|
|
|
|
ValueMapping spatialsizemap[] = {
|
|
SpatialSize<Line>,
|
|
SpatialSize<Region>,
|
|
SpatialSize<SimpleLine>
|
|
};
|
|
|
|
ValueMapping SpatialAtPointMap[] = { SpatialAtPointBool,
|
|
SpatialAtPoint};
|
|
|
|
ValueMapping SpatialAtPositionMap[] = {SpatialAtPositionBool,
|
|
SpatialAtPosition };
|
|
|
|
ValueMapping SpatialCrossingsMap[] = {
|
|
SpatialCrossings<Line>,
|
|
SpatialCrossings<SimpleLine>,
|
|
SpatialCrossings_single
|
|
};
|
|
|
|
|
|
ValueMapping utmVM[] = {
|
|
utmVM_p,
|
|
utmVM_ps
|
|
};
|
|
|
|
ValueMapping gkVM[] = {
|
|
gkVM_p,
|
|
gkVM_ps,
|
|
gkVM_x<Line>,
|
|
gkVM_x<Region>,
|
|
gkVM_x<SimpleLine>,
|
|
gkVM_rect
|
|
};
|
|
|
|
|
|
ValueMapping reverseGKVM[] = {
|
|
reversegkVM<Point>,
|
|
reversegkVM<Points>,
|
|
reversegkVM<Line>,
|
|
reversegkVM<Region>
|
|
};
|
|
|
|
ValueMapping spatialCollectLineMap[] = {
|
|
SpatialCollect_lineVMPointstream<Line>,
|
|
SpatialCollect_lineVMLinestream<SimpleLine,Line>,
|
|
SpatialCollect_lineVMLinestream<Line,Line>
|
|
};
|
|
|
|
ValueMapping spatialCollectSLineMap[] = {
|
|
SpatialCollect_slineVMPointstream<SimpleLine>,
|
|
SpatialCollect_slineVMLinestream<SimpleLine,SimpleLine>,
|
|
SpatialCollect_lineVMLinestream<Line,SimpleLine>
|
|
};
|
|
|
|
ValueMapping spatialCollectPointsMap[] = {
|
|
SpatialCollect_PointsVM<Point>,
|
|
SpatialCollect_PointsVM<Points>
|
|
};
|
|
|
|
|
|
/*
|
|
10.5.2 Definition of specification strings
|
|
|
|
*/
|
|
const string SpatialSpecIsEmpty =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>point -> bool, points -> bool, line -> bool,"
|
|
"region -> bool, sline -> bool</text---> "
|
|
"<text>isempty ( _ )</text--->"
|
|
"<text>Returns TRUE if the value is undefined or empty. The result "
|
|
"is always defined!</text--->"
|
|
"<text>query isempty ( line1 )</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecEqual =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(point point) -> bool, (points points) -> bool, "
|
|
"(line line) -> bool, (region region) -> bool,"
|
|
" (sline sline) -> bool </text--->"
|
|
"<text>_ = _</text--->"
|
|
"<text>TRUE, iff both arguments are equal.</text--->"
|
|
"<text>query point1 = point2</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecIsLess =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(point point) -> bool </text--->"
|
|
"<text>_ < _</text--->"
|
|
"<text>TRUE, iff first argument is smaller than second argument.</text--->"
|
|
"<text>query point1 < point2</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecNotEqual =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(point point) -> bool, (points points) -> bool, "
|
|
"(line line) -> bool, (region region) -> bool,"
|
|
"(sline sline) -> bool</text--->"
|
|
"<text>_ # _</text--->"
|
|
"<text>TRUE, iff both arguments are not equal.</text--->"
|
|
"<text>query point1 # point2</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecIntersects =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(points||line||region x points||line||region) -> bool ||"
|
|
" sline x sline -> bool </text--->"
|
|
"<text>_ intersects _</text--->"
|
|
"<text>TRUE, iff both arguments intersect.</text--->"
|
|
"<text>query region1 intersects region2</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecInside =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(points||line||sline||region x points||line||sline||region) "
|
|
"-> bool</text--->"
|
|
"<text>_ inside _</text--->"
|
|
"<text>TRUE iff the first argument is inside the second.</text--->"
|
|
"<text>query point1 inside line1</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecAdjacent =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(points||line||region x points||line||region) -> bool</text--->"
|
|
"<text>_ adjacent _</text--->"
|
|
"<text>TRUE, iff both regions are adjacent.</text--->"
|
|
"<text>query r1 adjacent r2</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecOverlaps =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(region x region) -> bool</text--->"
|
|
"<text>_ overlaps _</text--->"
|
|
"<text>TRUE, iff both objects overlap each other.</text--->"
|
|
"<text>query line overlap region</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecOnBorder =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point x region) -> bool</text--->"
|
|
"<text>_ onborder _</text--->"
|
|
"<text>TRUE, iff the point is on the border of the region."
|
|
"</text--->"
|
|
"<text>query mehringdamm onborder thecenter</text--->"
|
|
") )";
|
|
|
|
|
|
const string SpatialSpecIninterior =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point x region) -> bool</text--->"
|
|
"<text>_ ininterior _</text--->"
|
|
"<text>TRUE, iff the point is within the region's interior."
|
|
"</text--->"
|
|
"<text>query mehringdamm ininterior thecenter</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecInInterior =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point x region) -> bool</text--->"
|
|
"<text>_ ininterior _</text--->"
|
|
"<text>TRUE, iff the first argument is in the interior of the second."
|
|
"</text--->"
|
|
"<text>query point ininterior region</text--->"
|
|
") )";
|
|
|
|
const string SpatialIntersectionSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{point x points, line, sline, region } x"
|
|
" {point, points, line, sline, region} -> T, "
|
|
" where T = points if any point or point type is one of the "
|
|
" arguments or the argument having the smaller dimension </text--->"
|
|
"<text>intersection(arg1, arg2)</text--->"
|
|
"<text>intersection of two spatial objects</text--->"
|
|
"<text>query intersection(tiergarten, thecenter) </text--->"
|
|
") )";
|
|
|
|
|
|
const string SpatialMinusSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{point, points, line, sline, region } x"
|
|
" {point, points, line, sline region} -> T "
|
|
" </text--->"
|
|
"<text>arg1 minus arg2</text--->"
|
|
"<text>difference of two spatial objects</text--->"
|
|
"<text>query tiergarten minus thecenter </text--->"
|
|
") )";
|
|
|
|
const string SpatialUnionSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{point , points, line, sline, region } x"
|
|
" {point, points, line, sline, region} -> T "
|
|
" </text--->"
|
|
"<text>arg1 union arg2</text--->"
|
|
"<text>union of two spatial objects</text--->"
|
|
"<text>query tiergarten union thecenter </text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCrossings =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(line x line) -> points || "
|
|
"sline x sline -> points || line -> points</text--->"
|
|
"<text>crossings( _, _ )</text--->"
|
|
"<text>crossing points of two (or one) line(s).</text--->"
|
|
"<text>query crossings(line1, line2)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSingle =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(points) -> point</text--->"
|
|
"<text> single( _ )</text--->"
|
|
"<text>transform a single-element points value to point value.</text--->"
|
|
"<text>query single(points)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecDistance =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point||points||line||sline||rect||region x "
|
|
"point||points||line||sline||rect||region) [ x geoid] -> real</text--->"
|
|
"<text>distance( _, _,[,_] )</text--->"
|
|
"<text>compute distance between two spatial objects. Note: spherical"
|
|
" distance (using geoid) is not available for all types.</text--->"
|
|
"<text>query distance(point, line)</text--->"
|
|
") )";
|
|
|
|
const string distanceSmallerThanSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line x line x real x bool -> bool "
|
|
"</text--->"
|
|
"<text>distanceSmallerThan( _, _ ,_,_)</text--->"
|
|
"<text>Checks if the distance is smaller than a given value"
|
|
" If the boolean parameter is set to be true, also an"
|
|
" distance equals to the given max Dist is allowed. .</text--->"
|
|
"<text>query distanceSmallerThan(l1,l2,50.0,TRUE)</text--->"
|
|
") )";
|
|
const string SpatialSpecDirection =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point -> real</text--->"
|
|
"<text>direction( P1, P2 [, Geoid] )</text--->"
|
|
"<text>Compute the direction DIR from point P1 to point P1 in degrees "
|
|
"(0<=DIR<360) using standard mathematical measure (counter-clockwise, "
|
|
"0.0 degrees means parallel to the X-axis). The direction of a point to "
|
|
"itself is UNDEFINED. If a Geoid is passed, P1, P2 are expected to be valid "
|
|
"geographic positions, otherwise euclidean coordinates are assumed.</text--->"
|
|
"<text>query direction(p1, p2)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecHeading =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point [x geoid] -> real</text--->"
|
|
"<text>heading( P1, P2 [, Geoid] )</text--->"
|
|
"<text>Compute the heading HEAD from point P1 to point P1 in degrees "
|
|
"(0<HEAD<=360) using standard aviation heading (clockwise, 360 degrees "
|
|
"means NORTH --- 0 is not allowed). The heading of a point to "
|
|
"itself is UNDEFINED. If a Geoid is passed, P1, P2 are expected to be valid "
|
|
"geographic positions, otherwise euclidean coordinates are assumed.</text--->"
|
|
"<text>query heading(p1, p2)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecNocomponents =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(points||line||region) -> int</text--->"
|
|
"<text> no_components( _ )</text--->"
|
|
"<text>return the number of components (points: points, line: segments, "
|
|
"region: faces) of a spatial object.</text--->"
|
|
"<text>query no_components(region)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecNoSegments =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{line, region, sline, dline} -> int</text--->"
|
|
"<text> no_segments( _ )</text--->"
|
|
"<text>return the number of half segments of a region.</text--->"
|
|
"<text>query no_segments(region)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecBbox =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point||points||line||region||sline||cpoint) [x geoid] -> rect"
|
|
"</text--->"
|
|
"<text> bbox( Obj [, Geoid ] )</text--->"
|
|
"<text>Returns the bounding box of a spatial object Obj. If Geoid is passed, "
|
|
"the geographical bounding box is computed.</text--->"
|
|
"<text>query bbox(tiergarten)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSize =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {line|sline|region} -> real \n"
|
|
" {line|sline} x geoid -> real</text--->"
|
|
"<text> size( O [, Geoid ] )</text--->"
|
|
"<text>Returns the size (line, sline: length, region: area) of a spatial "
|
|
"object O. For sline and line objects, the optional parameter Geoid allows "
|
|
"to compute line lengthes based on (LON,LAT)-coordinates instead of metric "
|
|
"(X,Y)-coordinates.\n"
|
|
"CAVEAT: For a 'line' the result is only valid if the original segments do "
|
|
"not cross each other. For 'sline' the result should always be correct."
|
|
"</text--->"
|
|
"<text> query size(line)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecTouchpoints =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(line||region x region) -> points</text--->"
|
|
"<text> touchpoints( _, _ ) </text--->"
|
|
"<text> return the touch points of a region and another "
|
|
"region or line.</text--->"
|
|
"<text> query touchpoints(line, region)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCommonborder =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(region x region) -> line</text--->"
|
|
"<text> commonborder( _, _ )</text--->"
|
|
"<text> return the common border of two regions.</text--->"
|
|
"<text> query commonborder(region1, region2)</text--->"
|
|
") )";
|
|
|
|
|
|
const string SpatialSpecCenter =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {points,rect} -> point </text--->"
|
|
"<text> center( _ ) </text--->"
|
|
"<text> computes the center of the argument</text--->"
|
|
"<text> query center(vertices(tiergarten))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecConvexhull =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> points -> region </text--->"
|
|
"<text> convexhull( _ ) </text--->"
|
|
"<text> computes the convex hull of the points value</text--->"
|
|
"<text> query convexhull(vertices(tiergarten))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecAdd =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point -> point</text--->"
|
|
"<text> _ + _</text--->"
|
|
"<text> Returns the vector sum of two points.</text--->"
|
|
"<text> query [const point value (0.0 -1.2)] + "
|
|
"[const point value (-5.0 1.2)] </text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecWindowClippingIn =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(line x rect) -> line, (region x rect) --> region</text--->"
|
|
"<text> windowclippingin( _, _ ) </text--->"
|
|
"<text> computes the part of the object that is inside the window.</text--->"
|
|
"<text> query windowclippingin(line1, window)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecWindowClippingOut =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(line x rect) -> line, (region x rect) --> region</text--->"
|
|
"<text> windowclippingout( _, _ ) </text--->"
|
|
"<text> computes the part of the object that is outside the window.</text--->"
|
|
"<text> query windowclippingout(line1, rect)</text--->"
|
|
") )";
|
|
|
|
|
|
const string SpatialSpecComponents =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>points -> stream(points), region -> stream(region), "
|
|
"line -> stream(line)</text--->"
|
|
"<text>components( _ )</text--->"
|
|
"<text>Returns the components of a points (the contained point values) or "
|
|
"region (the contained faces) object as a stream."
|
|
"Both, empty and undefined objects result in empty stream.</text--->"
|
|
"<text>query components(r1) count;</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecVertices =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(region -> points) or (line -> points)</text--->"
|
|
"<text>vertices(_)</text--->"
|
|
"<text>Returns the vertices of a region or line as a stream."
|
|
"Both, empty and undefined objects result in empty stream.</text--->"
|
|
"<text>query vertices(r1)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecBoundary =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>(region -> line) or (line -> points)</text--->"
|
|
"<text>boundary(_)</text--->"
|
|
"<text>Returns the boundary of a region or a line.</text--->"
|
|
"<text>query boundary(thecenter)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecAtPoint =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>sline x point [x bool] -> real</text--->"
|
|
"<text>atpoint(_, _, _)</text--->"
|
|
"<text>Returns the relative position of the point on the line."
|
|
"The optional boolean flag indicates where the positions start, i.e. "
|
|
"from the smaller point (TRUE) or from the bigger one (FALSE)."
|
|
"If the boolean flag is not given the orientation is taken from the sline"
|
|
"</text---><text>query atpoint(l, p, TRUE)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecAtPosition =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>sline x real [x bool] -> point</text--->"
|
|
"<text>atposition(_, _, _)</text--->"
|
|
"<text>Returns the point at a relative position in the line."
|
|
"The optional boolean flag indicates where the positions start, i.e. "
|
|
"from the smaller point (TRUE) or from the bigger one (FALSE)."
|
|
"If the boolean flag is not given the orientation is taken from the sline"
|
|
"</text---><text>query atposition(l, 0.0, TRUE)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSubLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
"( <text>sline x real x real x bool -> line</text--->"
|
|
"<text>subline(_, _, _, _)</text--->"
|
|
"<text>Returns the sub-line inside the two relative positions."
|
|
"The boolean flag indicates where the positions start, i.e. "
|
|
"from the smaller point (TRUE) or from the bigger one (FALSE)."
|
|
"</text---><text>query subline(l, 0.0, size(.l), TRUE)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecGetX =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point -> real</text--->"
|
|
"<text>getx( _ )</text--->"
|
|
"<text>Extracts the x-component of a point.</text--->"
|
|
"<text> query getx([const point value (0.0 -1.2)])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecGetY =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point -> real</text--->"
|
|
"<text>gety( _ )</text--->"
|
|
"<text>Extracts the y-component of a point.</text--->"
|
|
"<text> query gety([const point value (0.0 -1.2)])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecLine2Region =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line -> region</text--->"
|
|
"<text>_ line2region</text--->"
|
|
"<text>Converts a line object to a region object.</text--->"
|
|
"<text> query gety([const point value (0.0 -1.2)])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecRect2Region =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>rect -> region</text--->"
|
|
"<text>_ rect2region</text--->"
|
|
"<text>Converts a rect object to a region object.</text--->"
|
|
"<text> query </text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCreateTriangle =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x poin x point -> region</text--->"
|
|
"<text>create_triangle( P1, P2, P3 )</text--->"
|
|
"<text>Creates a triangular region with vertexes P1, P2, P3. If Any two "
|
|
"points are equal, the result is empty. If any point is undefined, the "
|
|
"result is undefined. Use operators such as 'convexhull', 'union', and "
|
|
"'minus' to compose more complex regions.</text--->"
|
|
"<text> query create_triangle(makepoint(0,0),makepoint(0,10),"
|
|
"makepoint(0,-10))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecArea =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>region -> real</text--->"
|
|
"<text>area( _ )</text--->"
|
|
"<text>Returns the area of a region object as a real value.</text--->"
|
|
"<text> query area( tiergarten )</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecPolylines =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line x bool [ x points] -> stream( line ) </text--->"
|
|
"<text> _ polylines [ _ , _ ] </text--->"
|
|
"<text>Returns a stream of simple line objects "
|
|
"whose union is the original line. The boolean parameter"
|
|
"indicates to ignore critical points as splitpoints.</text--->"
|
|
"<text> query trajectory(train1) polylines [TRUE] count</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecPolylinesC =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line x bool [ x points] -> stream( line ) </text--->"
|
|
"<text> _ polylinesC [ _ , _ ] </text--->"
|
|
"<text>Returns a stream of simple line objects "
|
|
" whose union is the original line. The boolean parameter"
|
|
"indicates to ignore critical points (branches) as splitpoints."
|
|
" Some of the resulting polylines may build a cycle.</text--->"
|
|
"<text> query trajectoryC(train1) polylines [TRUE] count</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSimplify =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line x real [x points x [geoid] ] -> line </text--->"
|
|
"<text> simplify( line, epsilon, [, ips ] ) </text--->"
|
|
"<text>Simplifies a line value, using a maximum error of 'epsilon'. The "
|
|
"points value 'ips' marks important points, that must be kept. </text--->"
|
|
"<text> query simplify(trajectory(train1),10.0) count</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSegments =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line -> stream( line ) </text--->"
|
|
"<text> segments( _ ) </text--->"
|
|
"<text>Returns a stream of segments of the line.</text--->"
|
|
"<text>query (segments(PotsdamLine) count) = "
|
|
"(no_segments(PotsdamLine)) </text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecGet =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>points x int -> point </text--->"
|
|
"<text> _ get _ </text--->"
|
|
"<text>Returns a point from a points value.</text--->"
|
|
"<text>query vertices(BGrenzenLine) get [1] </text--->"
|
|
") )";
|
|
|
|
|
|
const string SpatialSpecMakeLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point -> line </text--->"
|
|
"<text> makeline( _, _ ) </text--->"
|
|
"<text>Create a one-segment line from the arguments.</text--->"
|
|
"<text>query makeline([const point value (0 0)],"
|
|
" [ const point value (100 40)])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecMakeSLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point -> line </text--->"
|
|
"<text> makesline( _, _ ) </text--->"
|
|
"<text>Create a one-segment sline from the arguments. The starting point "
|
|
"is determined automatically based on the ordering of point values: the "
|
|
"'smaller' argument becomes the sline's starting point. If you want to "
|
|
"determine the starting point by yourself, use operator 'create_sline' "
|
|
"instead!</text--->"
|
|
"<text>query makesline([const point value (0 0)],"
|
|
" [ const point value (100 40)])</text--->"
|
|
") )";
|
|
|
|
const string CommonBorder2Spec =
|
|
"((\"Signature\" \"Syntax\" \"Meaning\" \"Example\" )"
|
|
" ( <text> region x region -> line </text--->"
|
|
" \" _ commonborder2 _ \" "
|
|
" <text> computes the common part of the"
|
|
" boundaries of the arguments </text---> "
|
|
" \" query r1 commonborder2 r2 \" ))";
|
|
|
|
|
|
const string SpatialSpecRealminize =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line -> line </text--->"
|
|
"<text> realminize( _ ) </text--->"
|
|
"<text>Returns the realminized argument: segments are split at each inner "
|
|
"crosspoint.</text--->"
|
|
"<text>query realminize(train7sections)</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecToLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>sline -> line </text--->"
|
|
"<text> toline( _ ) </text--->"
|
|
"<text>Converts an sline into a line</text--->"
|
|
"<text>query toline(fromline(trajectory(train7))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecFromLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line [x bool]-> sline </text--->"
|
|
"<text> fromline( _ ) fromline (_ ,_) </text--->"
|
|
"<text>Converts a line into an sline, if no bool value is defined the"
|
|
"sline starts from the smaller endpoint. Otherwise the bool value "
|
|
"tells if the sline starts from the smaller or bigger endpoint.</text--->"
|
|
"<text>query toline(fromline(trajectory(train7))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecIsCycle =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>sline -> bool </text--->"
|
|
"<text> iscycle( _ ) </text--->"
|
|
"<text>check for cycle</text--->"
|
|
"<text>query iscycle(fromline(trajectory(train7))</text--->"
|
|
") )";
|
|
|
|
const string utmSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>t -> t, t in {point, points} </text--->"
|
|
"<text> utm( _ ) </text--->"
|
|
"<text>projects the arguments using the utm projection</text--->"
|
|
"<text>query utm([const point value ( 0 0)])</text--->"
|
|
") )";
|
|
|
|
const string gkSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>t [ x int] -> t, t in {point, points, line, region} </text--->"
|
|
"<text> gk( geoobj [, zone] ) </text--->"
|
|
"<text>Projects the argument 'geoobj' using the Gauss Krueger projection "
|
|
"with center meridian 'zone'. Zone width is 3°. If 'zone' is not provided,"
|
|
"2 (center meridian = 6°E, suits the location of Hagen) will be used as a "
|
|
"default. 'geoobj' is expected to have geografic coordinates (LON,LAT) in °"
|
|
", the result's coordinates (NORTHING,EASTING) are in metres.</text--->"
|
|
"<text>query gk([const point value (0 0)])</text--->"
|
|
") )";
|
|
|
|
const string reverseGkSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>t -> t, t in {point, points, line, region} </text--->"
|
|
"<text> reverseGk( geoobj ) </text--->"
|
|
"<text> Converts a spatial objects from Gauss Krueger reference systems"
|
|
" into geographic coordinates using the WGS1984 ellipsoid. "
|
|
" The computation is iterative, thus the result is only an approximation."
|
|
" Whenever problems occur,i.e. if a coordinates is not in the Gauss"
|
|
" Krueger system,the result will be undefined."
|
|
"</text--->"
|
|
"<text>query reverseGk(gk([const point value (7 51)]))</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCollectLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(T) -> line, T in {point, line, sline} </text--->"
|
|
"<text> _ collect_line [ IgnoreUndef ]</text--->"
|
|
"<text>Collects a stream of 'line' or 'sline' values into a single 'line' "
|
|
"value. Creates a 'line' value by consecutively connecting subsequent "
|
|
"'point' values from the stream by segments. If the stream provides 0 or 1"
|
|
"Element, the result is defined, but empty.\n"
|
|
"If any of the stream elements is undefined and 'ignoreUndef' is set to "
|
|
"FALSE, so is the resulting 'line' value.</text--->"
|
|
"<text>query [const line value ()] feed collect_line [TRUE] )</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCollectSLine =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(T) -> sline, T in {point, line, sline} </text--->"
|
|
"<text> _ collect_sline [ IgnoreUndef ]</text--->"
|
|
"<text>Collects a stream of 'line' or 'sline' values into a single 'sline' "
|
|
"value. Creates a 'sline' value by consecutively connecting subsequent "
|
|
"'point' values from the stream by segments. If the stream provides 0 or 1"
|
|
"Element, the result is defined, but empty.\n"
|
|
"If any of the stream elemnts is undefined and parameter 'IgnoreUndef' is "
|
|
"set to FALSE, the resulting 'sline' value is undef. If any segements cross "
|
|
"each other or the segments do not form a single curve, the result is "
|
|
"undefined.</text--->"
|
|
"<text>query [const line value ()] feed collect_sline[TRUE])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCollectPoints =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>stream(T) x bool -> points, T in {point, points} </text--->"
|
|
"<text> _ collect_points[ IgnoreUndef ] </text--->"
|
|
"<text>Collects a stream of point or points values into a single points "
|
|
"value. If the bool parameter 'IgnoreUndef' is set to TRUE, undefined "
|
|
"points within the stream are ignored. Otherwise the result is set to be "
|
|
"undefined if an undefined value is inside the stream. </text--->"
|
|
"<text>query [const points value ()] feed collect_points[true])</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecSetStartSmaller =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>sline x bool -> sline </text--->"
|
|
"<text> set_startsmaller( l, b ) </text--->"
|
|
"<text>Sets the starting flag for a simple line 'l' to 'b'. </text--->"
|
|
"<text>query get_startsmaller(set_startsmaller(fromline(BGrenzenLine),FALSE)) "
|
|
"= FALSE</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecGetStartSmaller =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>sline -> bool </text--->"
|
|
"<text> get_startsmaller( l ) </text--->"
|
|
"<text>Retrieves the starting flag for a simple line 'l'. </text--->"
|
|
"<text>query get_startsmaller(set_startsmaller(fromline(BGrenzenLine),FALSE)) "
|
|
"= FALSE</text--->"
|
|
") )";
|
|
|
|
const string SpatialSpecCreateSline =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>point x point -> sline </text--->"
|
|
"<text> create_sline( p1, p2 ) </text--->"
|
|
"<text>Creates a simple line, directed from p1 to p2. If p1 = p2, the result is"
|
|
" empty.</text--->"
|
|
"<text>query atposition(create_sline(makepoint(10,10),makepoint(0,0)),0.0,TRUE)"
|
|
"= makepoint(10,10)</text--->"
|
|
") )";
|
|
|
|
/*
|
|
10.5.3 Definition of the operators
|
|
|
|
*/
|
|
Operator spatialisempty (
|
|
"isempty",
|
|
SpatialSpecIsEmpty,
|
|
5,
|
|
spatialisemptymap,
|
|
SpatialSelectIsEmpty,
|
|
SpatialTypeMapBool1 );
|
|
|
|
Operator spatialequal (
|
|
"=",
|
|
SpatialSpecEqual,
|
|
7,
|
|
spatialequalmap,
|
|
SpatialSelectEqual,
|
|
SpatialTypeMapEqual );
|
|
|
|
Operator spatialisless (
|
|
"<",
|
|
SpatialSpecIsLess,
|
|
1,
|
|
spatialislessmap,
|
|
SpatialSelectIsLess,
|
|
SpatialTypeMapIsLess );
|
|
|
|
Operator spatialnotequal (
|
|
"#",
|
|
SpatialSpecNotEqual,
|
|
5,
|
|
spatialnotequalmap,
|
|
SpatialSelectCompare,
|
|
SpatialTypeMapCompare );
|
|
|
|
Operator spatialintersects (
|
|
"intersects1",
|
|
SpatialSpecIntersects,
|
|
10,
|
|
spatialintersectsmap,
|
|
SpatialSelectIntersects,
|
|
IntersectsTM );
|
|
|
|
Operator spatialinside (
|
|
"inside",
|
|
SpatialSpecInside,
|
|
11,
|
|
spatialinsidemap,
|
|
SpatialSelectInside,
|
|
InsideTypeMap );
|
|
|
|
Operator spatialadjacent (
|
|
"adjacent",
|
|
SpatialSpecAdjacent,
|
|
5,
|
|
spatialadjacentmap,
|
|
SpatialSelectAdjacent,
|
|
AdjacentTypeMap );
|
|
|
|
Operator spatialoverlaps (
|
|
"overlaps",
|
|
SpatialSpecOverlaps,
|
|
SpatialOverlaps_rr,
|
|
Operator::SimpleSelect,
|
|
RegionRegionMapBool );
|
|
|
|
Operator spatialonborder (
|
|
"onborder",
|
|
SpatialSpecOnBorder,
|
|
SpatialOnBorder_pr,
|
|
Operator::SimpleSelect,
|
|
PointRegionMapBool );
|
|
|
|
Operator spatialininterior (
|
|
"ininterior",
|
|
SpatialSpecIninterior,
|
|
SpatialInInterior_pr,
|
|
Operator::SimpleSelect,
|
|
PointRegionMapBool );
|
|
|
|
|
|
Operator spatialintersection (
|
|
"intersection1",
|
|
SpatialIntersectionSpec,
|
|
25,
|
|
spatialintersectionVM,
|
|
SpatialSetOpSelect,
|
|
SpatialIntersectionTypeMap );
|
|
|
|
Operator spatialminus (
|
|
"minus1",
|
|
SpatialMinusSpec,
|
|
25,
|
|
spatialminusVM,
|
|
SpatialSetOpSelect,
|
|
SpatialMinusTypeMap );
|
|
|
|
Operator spatialunion (
|
|
"union1",
|
|
SpatialUnionSpec,
|
|
25,
|
|
spatialunionVM,
|
|
SpatialSetOpSelect,
|
|
SpatialUnionTypeMap );
|
|
|
|
Operator spatialcrossings (
|
|
"crossings1",
|
|
SpatialSpecCrossings,
|
|
3,
|
|
SpatialCrossingsMap,
|
|
SpatialSelectCrossings,
|
|
SpatialCrossingsTM );
|
|
|
|
Operator spatialsingle (
|
|
"single",
|
|
SpatialSpecSingle,
|
|
SpatialSingle_ps,
|
|
Operator::SimpleSelect,
|
|
SpatialSingleMap );
|
|
|
|
Operator spatialdistance (
|
|
"distance",
|
|
SpatialSpecDistance,
|
|
36,
|
|
spatialdistancemap,
|
|
SpatialSelectDistance,
|
|
SpatialDistanceMap );
|
|
|
|
|
|
Operator distanceSmallerThan (
|
|
"distanceSmallerThan",
|
|
distanceSmallerThanSpec,
|
|
distanceSmallerThanVM<Line,Line>,
|
|
Operator::SimpleSelect,
|
|
distanceSmallerThanTM );
|
|
|
|
Operator spatialdirection (
|
|
"direction",
|
|
SpatialSpecDirection,
|
|
SpatialDirectionHeading_pp<false>,
|
|
Operator::SimpleSelect,
|
|
SpatialDirectionHeadingMap );
|
|
|
|
Operator spatialheading (
|
|
"heading",
|
|
SpatialSpecHeading,
|
|
SpatialDirectionHeading_pp<true>,
|
|
Operator::SimpleSelect,
|
|
SpatialDirectionHeadingMap );
|
|
|
|
Operator spatialnocomponents (
|
|
"no_components",
|
|
SpatialSpecNocomponents,
|
|
3,
|
|
spatialnocomponentsmap,
|
|
SpatialSelectNoComponents,
|
|
SpatialNoComponentsMap );
|
|
|
|
Operator spatialnosegments (
|
|
"no_segments",
|
|
SpatialSpecNoSegments,
|
|
4,
|
|
spatialnosegmentsmap,
|
|
SpatialSelectNoSegments,
|
|
SpatialNoSegmentsMap );
|
|
|
|
Operator spatialbbox (
|
|
"bbox",
|
|
SpatialSpecBbox,
|
|
7,
|
|
spatialbboxmap,
|
|
SpatialSelectBBox,
|
|
SpatialBBoxMap );
|
|
|
|
Operator spatialsize (
|
|
"size",
|
|
SpatialSpecSize,
|
|
3,
|
|
spatialsizemap,
|
|
SpatialSelectSize,
|
|
SpatialSizeMap );
|
|
|
|
Operator spatialtouchpoints (
|
|
"touchpoints",
|
|
SpatialSpecTouchpoints,
|
|
3,
|
|
spatialtouchpointsmap,
|
|
SpatialSelectTouchPoints,
|
|
SpatialTouchPointsMap );
|
|
|
|
Operator spatialcommonborder (
|
|
"commonborder",
|
|
SpatialSpecCommonborder,
|
|
SpatialCommonBorder_rr,
|
|
Operator::SimpleSelect,
|
|
SpatialCommonBorderMap );
|
|
|
|
|
|
|
|
|
|
Operator spatialcenter (
|
|
"center",
|
|
SpatialSpecCenter,
|
|
2,
|
|
SpatialCenter,
|
|
SpatialCenter_Select,
|
|
SpatialCenterMap );
|
|
|
|
Operator spatialconvexhull (
|
|
"convexhull",
|
|
SpatialSpecConvexhull,
|
|
SpatialConvexhull,
|
|
Operator::SimpleSelect,
|
|
SpatialConvexhullMap );
|
|
|
|
Operator spatialadd (
|
|
"+",
|
|
SpatialSpecAdd,
|
|
1,
|
|
spatialaddmap,
|
|
Operator::SimpleSelect,
|
|
SpatialAddTypeMap );
|
|
|
|
Operator spatialwindowclippingin (
|
|
"windowclippingin",
|
|
SpatialSpecWindowClippingIn,
|
|
4,
|
|
spatialwindowclippinginmap,
|
|
SpatialSelectWindowClipping,
|
|
SpatialWindowClippingMap );
|
|
|
|
Operator spatialwindowclippingout (
|
|
"windowclippingout",
|
|
SpatialSpecWindowClippingOut,
|
|
4,
|
|
spatialwindowclippingoutmap,
|
|
SpatialSelectWindowClipping,
|
|
SpatialWindowClippingMap );
|
|
|
|
|
|
Operator spatialcomponents (
|
|
"components",
|
|
SpatialSpecComponents,
|
|
3,
|
|
spatialcomponentsmap,
|
|
SpatialComponentsSelect,
|
|
SpatialComponentsMap );
|
|
|
|
Operator spatialvertices (
|
|
"vertices",
|
|
SpatialSpecVertices,
|
|
2,
|
|
spatialverticesmap,
|
|
SpatialVerticesSelect,
|
|
SpatialVerticesMap);
|
|
|
|
Operator spatialboundary (
|
|
"boundary",
|
|
SpatialSpecBoundary,
|
|
2,
|
|
spatialboundarymap,
|
|
SpatialBoundarySelect,
|
|
SpatialBoundaryMap);
|
|
|
|
Operator spatialsimplify (
|
|
"simplify",
|
|
SpatialSpecSimplify,
|
|
2,
|
|
spatialsimplifymap,
|
|
SpatialSimplifySelect,
|
|
SimplifyTypeMap );
|
|
|
|
Operator spatialatpoint (
|
|
"atpoint",
|
|
SpatialSpecAtPoint,
|
|
2,
|
|
SpatialAtPointMap,
|
|
SpatialAtPointSelect,
|
|
SpatialAtPointTypeMap );
|
|
|
|
Operator spatialatposition (
|
|
"atposition",
|
|
SpatialSpecAtPosition,
|
|
2,
|
|
SpatialAtPositionMap,
|
|
SpatialAtPositionSelect,
|
|
SpatialAtPositionTypeMap );
|
|
|
|
Operator spatialsubline (
|
|
"subline",
|
|
SpatialSpecSubLine,
|
|
SpatialSubLine,
|
|
Operator::SimpleSelect,
|
|
SpatialSubLineMap );
|
|
|
|
Operator spatialgetx (
|
|
"getx",
|
|
SpatialSpecGetX,
|
|
1,
|
|
spatialgetxmap,
|
|
Operator::SimpleSelect,
|
|
SpatialGetXYMap );
|
|
|
|
Operator spatialgety (
|
|
"gety",
|
|
SpatialSpecGetY,
|
|
1,
|
|
spatialgetymap,
|
|
Operator::SimpleSelect,
|
|
SpatialGetXYMap );
|
|
|
|
Operator spatialline2region (
|
|
"line2region",
|
|
SpatialSpecLine2Region,
|
|
SpatialLine2Region,
|
|
Operator::SimpleSelect,
|
|
SpatialLine2RegionMap );
|
|
|
|
Operator spatialrect2region (
|
|
"rect2region",
|
|
SpatialSpecRect2Region,
|
|
SpatialRect2Region,
|
|
Operator::SimpleSelect,
|
|
SpatialRect2RegionMap );
|
|
|
|
Operator spatialCreateTriangle (
|
|
"create_triangle",
|
|
SpatialSpecCreateTriangle,
|
|
SpatialCreateTriangleVM,
|
|
Operator::SimpleSelect,
|
|
SpatialCreateTriangleTM );
|
|
|
|
Operator spatialarea (
|
|
"area",
|
|
SpatialSpecArea,
|
|
SpatialArea,
|
|
Operator::SimpleSelect,
|
|
SpatialAreaMap );
|
|
|
|
|
|
Operator spatialpolylines (
|
|
"polylines",
|
|
SpatialSpecPolylines,
|
|
SpatialPolylines<false>,
|
|
Operator::SimpleSelect,
|
|
PolylinesMap );
|
|
|
|
Operator spatialpolylinesC (
|
|
"polylinesC",
|
|
SpatialSpecPolylinesC,
|
|
SpatialPolylines<true>,
|
|
Operator::SimpleSelect,
|
|
PolylinesMap );
|
|
|
|
Operator spatialsegments (
|
|
"segments",
|
|
SpatialSpecSegments,
|
|
2,
|
|
SpatialSegmentsVM,
|
|
SpatialSegmentsSelect,
|
|
SegmentsTypeMap );
|
|
|
|
Operator spatialget (
|
|
"get",
|
|
SpatialSpecGet,
|
|
SpatialGet,
|
|
Operator::SimpleSelect,
|
|
GetTypeMap );
|
|
|
|
Operator makeline (
|
|
"makeline",
|
|
SpatialSpecMakeLine,
|
|
SpatialMakeLine<Line>,
|
|
Operator::SimpleSelect,
|
|
MakeLineTypeMap );
|
|
|
|
Operator makesline (
|
|
"makesline",
|
|
SpatialSpecMakeSLine,
|
|
SpatialMakeLine<SimpleLine>,
|
|
Operator::SimpleSelect,
|
|
MakeSLineTypeMap );
|
|
|
|
Operator realminize(
|
|
"realminize", //name
|
|
SpatialSpecRealminize, //specification
|
|
RealminizeVM, //value mapping
|
|
Operator::SimpleSelect, //trivial selection function
|
|
RealminizeTypeMap //type mapping
|
|
);
|
|
|
|
|
|
|
|
Operator commonborder2(
|
|
"commonborder2", //name
|
|
CommonBorder2Spec, //specification
|
|
CommonBorder2VM, //value mapping
|
|
Operator::SimpleSelect, //trivial selection function
|
|
CommonBorder2TypeMap //type mapping
|
|
);
|
|
|
|
|
|
Operator spatialtoline (
|
|
"toline",
|
|
SpatialSpecToLine,
|
|
toLineVM,
|
|
Operator::SimpleSelect,
|
|
toLineTypeMap );
|
|
|
|
Operator spatialfromline (
|
|
"fromline",
|
|
SpatialSpecFromLine,
|
|
2,
|
|
fromLineVM,
|
|
fromLineSelect,
|
|
fromLineTypeMap );
|
|
|
|
Operator spatialiscycle (
|
|
"iscycle",
|
|
SpatialSpecIsCycle,
|
|
isCycleVM,
|
|
Operator::SimpleSelect,
|
|
isCycleTypeMap );
|
|
|
|
|
|
|
|
Operator utmOp (
|
|
"utm",
|
|
utmSpec,
|
|
2,
|
|
utmVM,
|
|
utmSelect,
|
|
utmTypeMap );
|
|
|
|
Operator gkOp (
|
|
"gk",
|
|
gkSpec,
|
|
6,
|
|
gkVM,
|
|
gkSelect,
|
|
gkTypeMap );
|
|
|
|
Operator reverseGkOp (
|
|
"reverseGk",
|
|
reverseGkSpec,
|
|
4,
|
|
reverseGKVM,
|
|
reverseGkSelect,
|
|
reverseGkTypeMap );
|
|
|
|
/*
|
|
Creating lines and slines
|
|
|
|
*/
|
|
Operator spatialcollect_line (
|
|
"collect_line",
|
|
SpatialSpecCollectLine,
|
|
3,
|
|
spatialCollectLineMap,
|
|
SpatialCollectLineSelect,
|
|
SpatialCollectLineTypeMap);
|
|
|
|
Operator spatialcollect_sline (
|
|
"collect_sline",
|
|
SpatialSpecCollectSLine,
|
|
3,
|
|
spatialCollectSLineMap,
|
|
SpatialCollectLineSelect,
|
|
SpatialCollectSLineTypeMap);
|
|
|
|
Operator spatialcollect_points (
|
|
"collect_points",
|
|
SpatialSpecCollectPoints,
|
|
2,
|
|
spatialCollectPointsMap,
|
|
SpatialCollectPointsSelect,
|
|
SpatialCollectPointsTM);
|
|
|
|
Operator spatialsetstartsmaller (
|
|
"set_startsmaller",
|
|
SpatialSpecSetStartSmaller,
|
|
SpatialVMSetStartSmaller,
|
|
Operator::SimpleSelect,
|
|
SpatialTMSetStartSmaller);
|
|
|
|
Operator spatialgetstartsmaller (
|
|
"get_startsmaller",
|
|
SpatialSpecGetStartSmaller,
|
|
SpatialVMGetStartSmaller,
|
|
Operator::SimpleSelect,
|
|
SpatialTMGetStartSmaller);
|
|
|
|
Operator spatial_create_sline (
|
|
"create_sline",
|
|
SpatialSpecCreateSline,
|
|
SpatialVMCreateSline,
|
|
Operator::SimpleSelect,
|
|
SpatialTMCreateSline
|
|
);
|
|
|
|
/*
|
|
5.15 Operator ~makepoint~
|
|
|
|
5.15.1 Type Mapping for ~makepoint~
|
|
|
|
---- {int|real} x {int|real} --> point
|
|
----
|
|
|
|
*/
|
|
ListExpr
|
|
TypeMapMakepoint( ListExpr args )
|
|
{
|
|
if( ( nl->ListLength( args ) == 2)
|
|
&& listutils::isNumericType(nl->First(args))
|
|
&& listutils::isNumericType(nl->Second(args))){
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
return listutils::typeError("Expected ({int|real} x {int|real}).");
|
|
}
|
|
|
|
/*
|
|
5.15.2 Value Mapping for ~makepoint~
|
|
|
|
*/
|
|
template<class T1, class T2>
|
|
int MakePoint( Word* args, Word& result, int message, Word& local, Supplier s )
|
|
{
|
|
const T1* value1 = static_cast<const T1*>(args[0].addr);
|
|
const T2* value2 = static_cast<const T2*>(args[1].addr);
|
|
result = qp->ResultStorage( s );
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
|
|
if(!value1->IsDefined() || !value2->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->Set(value1->GetValue(), value2->GetValue()); // also sets to DEFINED
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping VM_MakePoint[] = {
|
|
MakePoint<CcInt,CcInt>,
|
|
MakePoint<CcInt,CcReal>,
|
|
MakePoint<CcReal,CcInt>,
|
|
MakePoint<CcReal,CcReal>
|
|
};
|
|
|
|
int SelectMakepoint( ListExpr args ){
|
|
int res = 0;
|
|
if(listutils::isSymbol(nl->First(args) ,CcReal::BasicType())) res+=2;
|
|
if(listutils::isSymbol(nl->Second(args),CcReal::BasicType())) res+=1;
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
5.15.3 Specification for operator ~makepoint~
|
|
|
|
*/
|
|
const string
|
|
SpatialSpecMakePoint =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>{real|int} x {real|int} -> point</text--->"
|
|
"<text>makepoint ( _, _ ) </text--->"
|
|
"<text>create a point from two "
|
|
"given real or integer coordinates.</text--->"
|
|
"<text>makepoint (5.0,5.0)</text---> ) )";
|
|
|
|
/*
|
|
5.15.4 Selection Function of operator ~makepoint~
|
|
|
|
Not necessary.
|
|
|
|
*/
|
|
|
|
/*
|
|
5.15.5 Definition of operator ~makepoint~
|
|
|
|
*/
|
|
Operator spatialmakepoint( "makepoint",
|
|
SpatialSpecMakePoint,
|
|
4,
|
|
VM_MakePoint,
|
|
SelectMakepoint,
|
|
TypeMapMakepoint);
|
|
|
|
|
|
/*
|
|
6.16.1 Operator halfSegments
|
|
|
|
*/
|
|
ListExpr halfSegmentsTM(ListExpr args){
|
|
if(nl->ListLength(args)!=1){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
ListExpr first = nl->First(args);
|
|
if((listutils::isSymbol(first,Line::BasicType()) ||
|
|
listutils::isSymbol(first,Region::BasicType())) ){
|
|
|
|
ListExpr attrList = nl->OneElemList( nl->TwoElemList(
|
|
nl->SymbolAtom("FaceNo"),
|
|
nl->SymbolAtom(CcInt::BasicType())));
|
|
ListExpr last = attrList;
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("CycleNo"),
|
|
nl->SymbolAtom(CcInt::BasicType())));
|
|
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("EdgeNo"),
|
|
nl->SymbolAtom(CcInt::BasicType())));
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("CoverageNo"),
|
|
nl->SymbolAtom(CcInt::BasicType())));
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("InsideAbove"),
|
|
nl->SymbolAtom(CcBool::BasicType())));
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("PartnerNo"),
|
|
nl->SymbolAtom(CcInt::BasicType())));
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Ldp"),
|
|
nl->SymbolAtom(CcBool::BasicType())));
|
|
|
|
last = nl->Append(last, nl->TwoElemList(nl->SymbolAtom("Segment"),
|
|
nl->SymbolAtom(Line::BasicType())));
|
|
|
|
return nl->TwoElemList(
|
|
nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
attrList));
|
|
|
|
} else {
|
|
return listutils::typeError("line or region expected");
|
|
}
|
|
}
|
|
|
|
|
|
template<class T>
|
|
class halfSegmentsLocalInfo{
|
|
public:
|
|
halfSegmentsLocalInfo(T* s, ListExpr tt){
|
|
if(!s->IsDefined()){
|
|
source = 0;
|
|
size = 0;
|
|
pos = 0;
|
|
tupleType = 0;
|
|
} else {
|
|
source = s;
|
|
size = s->Size();
|
|
pos = 0;
|
|
tupleType = new TupleType(tt);
|
|
}
|
|
}
|
|
|
|
~halfSegmentsLocalInfo(){
|
|
if(tupleType){
|
|
tupleType->DeleteIfAllowed();
|
|
tupleType=0;
|
|
}
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(pos>=size){
|
|
return 0;
|
|
}
|
|
HalfSegment hs;
|
|
source->Get(pos,hs);
|
|
pos++;
|
|
Tuple* res = new Tuple(tupleType);
|
|
res->PutAttribute(0, new CcInt(true,hs.attr.faceno));
|
|
res->PutAttribute(1, new CcInt(true,hs.attr.cycleno));
|
|
res->PutAttribute(2, new CcInt(true,hs.attr.edgeno));
|
|
res->PutAttribute(3, new CcInt(true,hs.attr.coverageno));
|
|
res->PutAttribute(4, new CcBool(true,hs.attr.insideAbove));
|
|
res->PutAttribute(5, new CcInt(true,hs.attr.partnerno));
|
|
res->PutAttribute(6, new CcBool(true,hs.IsLeftDomPoint()));
|
|
Line* line = new Line(2);
|
|
line->StartBulkLoad();
|
|
(*line) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*line) += hs;
|
|
line->EndBulkLoad();
|
|
res->PutAttribute(7, line);
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
T* source;
|
|
int size;
|
|
int pos;
|
|
TupleType* tupleType;
|
|
};
|
|
|
|
|
|
template<class T>
|
|
int halfSegmentsVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
halfSegmentsLocalInfo<T>* li =
|
|
static_cast<halfSegmentsLocalInfo<T>*>(local.addr);
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
ListExpr resultType = GetTupleResultType( s );
|
|
li = new halfSegmentsLocalInfo<T>( static_cast<T*>(args[0].addr),
|
|
nl->Second(resultType)
|
|
);
|
|
local.addr=li;
|
|
return 0;
|
|
}
|
|
case REQUEST:{
|
|
result.addr = li->next();
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE:{
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
ValueMapping halfSegmentsvm[] = { halfSegmentsVM<Line>,
|
|
halfSegmentsVM<Region> };
|
|
|
|
|
|
int halfSegmentsSelect(ListExpr args){
|
|
return listutils::isSymbol(nl->First(args),Line::BasicType())?0:1;
|
|
}
|
|
|
|
|
|
const string
|
|
halfSegmentsSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> {line,region} -> stream(tuple(faceno, cycleno,...))</text--->"
|
|
"<text>halfSegments(_) </text--->"
|
|
"<text>Gives the values of the halfsegment into a tuple stream "
|
|
"</text--->"
|
|
"<text>query halfSegments(zoogarten) count</text---> ) )";
|
|
|
|
|
|
Operator halfSegmentsOp (
|
|
"halfSegments",
|
|
halfSegmentsSpec,
|
|
2,
|
|
halfSegmentsvm,
|
|
halfSegmentsSelect,
|
|
halfSegmentsTM );
|
|
|
|
|
|
ListExpr distanceOrthodromeTM(ListExpr args){
|
|
int noargs = nl->ListLength(args);
|
|
string errmsg = "Expected (point x point) or (point x point x geoid).";
|
|
if(noargs < 2){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( !listutils::isSymbol(nl->First(args),Point::BasicType())
|
|
|| !listutils::isSymbol(nl->Second(args),Point::BasicType()) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(noargs==2){
|
|
return nl->SymbolAtom(CcReal::BasicType());
|
|
}
|
|
if(noargs != 3){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(listutils::isSymbol(nl->Third(args),Geoid::BasicType())){
|
|
return nl->SymbolAtom(CcReal::BasicType());
|
|
}
|
|
return listutils::typeError(errmsg);
|
|
};
|
|
|
|
int distanceOrthodromeVM( Word* args, Word& result, int message,
|
|
Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
CcReal* res = static_cast<CcReal*>(result.addr);
|
|
Point* p1 = static_cast<Point*>(args[0].addr);
|
|
Point* p2 = static_cast<Point*>(args[1].addr);
|
|
if(!p1->IsDefined() || !p2->IsDefined()){
|
|
res->Set(false, 0.0);
|
|
return 0;
|
|
}
|
|
Geoid* g = 0;
|
|
if(qp->GetNoSons(s)==3){
|
|
g = static_cast<Geoid*>(args[2].addr);
|
|
if(!g->IsDefined()){
|
|
res->Set(false, 0.0);
|
|
return 0;
|
|
}
|
|
} else { // use the WGS1984 as the default
|
|
g = new Geoid(Geoid::WGS1984);
|
|
}
|
|
bool valid = true;
|
|
res->Set(true,p1->DistanceOrthodrome(*p2,*g,valid));
|
|
res->SetDefined(valid);
|
|
if( (qp->GetNoSons(s)!=3) && g) {
|
|
delete g; g = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const string distanceOrthodromeSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> point x point [x geoid] -> real</text--->"
|
|
"<text>distanceOrthodrome( p1, p2 [, g]) </text--->"
|
|
"<text>Returns the length of the orthodrome between point p1 and p2 "
|
|
"according to the specified geoid g. Both points must represent geographic "
|
|
"coordinates (LON,LAT), where -180<=LON<=180 and -90<=LAT<=90."
|
|
"If any argument is invalid or undefined, the result is UNDEF. If no geoid "
|
|
"is specified, WGS1984 is used as the default geoid.</text--->"
|
|
"<text>query distanceOrthodrome(makepoint(7.494968217,51.376125146),"
|
|
"makepoint(-73.984633618, 40.728925452 ),create_geoid(\"WGS1984\"))"
|
|
"</text---> ) )";
|
|
|
|
Operator spatial_distanceOrthodrome (
|
|
"distanceOrthodrome",
|
|
distanceOrthodromeSpec,
|
|
distanceOrthodromeVM,
|
|
Operator::SimpleSelect,
|
|
distanceOrthodromeTM );
|
|
|
|
/*
|
|
6.16.2 Operator ~point2string~
|
|
|
|
---- point [ x geoid ] --> string
|
|
----
|
|
|
|
*/
|
|
|
|
ListExpr point2stringTM(ListExpr args){
|
|
int noargs = nl->ListLength(args);
|
|
string errmsg = "Expected point [x geoid].";
|
|
if( (noargs<1) || (noargs>2) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(!listutils::isSymbol(nl->First(args),Point::BasicType())){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( (noargs == 2) &&
|
|
!listutils::isSymbol(nl->Second(args),Geoid::BasicType())){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
return nl->SymbolAtom(CcString::BasicType());
|
|
}
|
|
|
|
int point2stringVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
const Point* p = static_cast<const Point*>(args[0].addr);
|
|
const Geoid* geoid =
|
|
(qp->GetNoSons(s)==2)?static_cast<const Geoid*>(args[1].addr):0;
|
|
result = qp->ResultStorage(s);
|
|
CcString* res = static_cast<CcString*>(result.addr);
|
|
|
|
if(!p->IsDefined() || (geoid && !geoid->IsDefined()) ){
|
|
res->SetDefined(false);
|
|
} else {
|
|
res->Set(true,p->toString(geoid));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const string point2stringSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> point [x geoid] -> string</text--->"
|
|
"<text>point2string( P, [, Geoid] ) </text--->"
|
|
"<text>Returns a textual representation of the point P. If Geoid is not "
|
|
"provided, the result is a pair of euclidean coordinates, otherwise, it is "
|
|
"a textual representation of geographic coordinates.</text--->"
|
|
"<text>query point2string(makepoint(7.494968217,51.376125146), "
|
|
"create_geoid(\"WGS1984\"))"
|
|
"</text---> ) )";
|
|
|
|
Operator point2string (
|
|
"point2string",
|
|
point2stringSpec,
|
|
point2stringVM,
|
|
Operator::SimpleSelect,
|
|
point2stringTM );
|
|
|
|
/*
|
|
6.16.2 Operator ~midpointBetween~
|
|
|
|
---- point x point [x geoid] [x real] --> point
|
|
----
|
|
|
|
If the optional real parameter is not present, a real default parameter with
|
|
value 0.5 is appended.
|
|
|
|
*/
|
|
ListExpr PointPointOptGeoidOptReal2PointTM(ListExpr args){
|
|
int noargs = nl->ListLength(args);
|
|
string errmsg = "Expected point x point [x geoid] [x real].";
|
|
if( (noargs<2) || (noargs>4) ){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(!listutils::isSymbol(nl->First(args),Point::BasicType())){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if(!listutils::isSymbol(nl->Second(args),Point::BasicType())){
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
if( noargs == 2 ) {
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->RealAtom(0.5)),
|
|
nl->SymbolAtom(Point::BasicType()));
|
|
}
|
|
if( noargs == 3 ){
|
|
if( listutils::isSymbol(nl->Third(args),Geoid::BasicType())) {
|
|
// special case: append default real parameter of 0.5
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->RealAtom(0.5)),
|
|
nl->SymbolAtom(Point::BasicType()));
|
|
}
|
|
if( listutils::isSymbol(nl->Third(args),CcReal::BasicType()) ){
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
}
|
|
if( noargs == 4 ){
|
|
if( listutils::isSymbol(nl->Third(args),Geoid::BasicType()) &&
|
|
listutils::isSymbol(nl->Fourth(args),CcReal::BasicType())) {
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
}
|
|
return listutils::typeError(errmsg);
|
|
}
|
|
|
|
|
|
int midpointBetweenVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
int noargs = qp->GetNoSons(s);
|
|
const Point* p1 = static_cast<const Point*>(args[0].addr);
|
|
const Point* p2 = static_cast<const Point*>(args[1].addr);
|
|
const Geoid* geoid = (noargs==4)?static_cast<const Geoid*>(args[2].addr):0;
|
|
const CcReal* f = static_cast<const CcReal*>(args[noargs-1].addr);
|
|
result = qp->ResultStorage(s);
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
|
|
if(!p1->IsDefined() || !p2->IsDefined() || !f->IsDefined() ||
|
|
(geoid && !geoid->IsDefined()) ){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(geoid){
|
|
if( !p1->checkGeographicCoord() || !p2->checkGeographicCoord() ||
|
|
(AlmostEqual(p1->GetY()+p2->GetY(), 0.0) &&
|
|
AlmostEqual(fabs(p1->GetX() - p2->GetX()), 180.0) ) ) {
|
|
// points may not be antipodal! points must represent valid geo-coords!
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
}
|
|
res->SetDefined(true);
|
|
*res = p1->MidpointTo(*p2,f->GetValue(),geoid);
|
|
return 0;
|
|
}
|
|
|
|
const string midpointBetweenSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> point x point [x geoid] [x real] -> point</text--->"
|
|
"<text>midpointbetween( P1, P2 [, Geoid] [, RelDost] ) </text--->"
|
|
"<text>Returns the point with the rlative distance 0.0<=RelDist<=1.0 on the "
|
|
"shortest path from point P1 to point P2. If RelDist is not given, it defaults "
|
|
"to 0.5.If Geoid is passed, the midpoint is with respect to the orthodrome "
|
|
"P1->P2 (the points being geographic coordinates), otherwise the midpoint "
|
|
"in euclidean space. If a Geoid is passed, and P1, P2 are antipodal, the "
|
|
"result is UNDEF.</text--->"
|
|
"<text>query midpointBetween(makepoint(7.494968217,51.376125146), "
|
|
"makepoint(7.0,51.0), create_geoid(\"WGS1984\"), 0.7)</text---> ) )";
|
|
|
|
Operator spatialmidpointbetween (
|
|
"midpointBetween",
|
|
midpointBetweenSpec,
|
|
midpointBetweenVM,
|
|
Operator::SimpleSelect,
|
|
PointPointOptGeoidOptReal2PointTM );
|
|
|
|
/*
|
|
6.16.3 Operators ~direction2heading~ and ~heading2direction~
|
|
|
|
---- real --> real
|
|
----
|
|
|
|
*/
|
|
ListExpr Real2RealTM(ListExpr args){
|
|
int noargs = nl->ListLength(args);
|
|
if (noargs!=1 || !listutils::isSymbol(nl->First(args),CcReal::BasicType()) ) {
|
|
return listutils::typeError("Expected (real).");
|
|
}
|
|
return nl->SymbolAtom(CcReal::BasicType());
|
|
}
|
|
|
|
template<bool toDirection>
|
|
int ConvertDirHeadVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
const CcReal* c = static_cast<const CcReal*>(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
CcReal* res = static_cast<CcReal*>(result.addr);
|
|
|
|
if(!c->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
double out = (toDirection)?headingToDirection(c->GetValue())
|
|
:directionToHeading(c->GetValue());
|
|
res->Set(true, out );
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const string DirectionToHeadingSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> real -> real</text--->"
|
|
"<text>direction2heading( D ) </text--->"
|
|
"<text>Converts a direction D (mathematical angle notation, counter-clockwise, "
|
|
"0<=d<360, 0 = directed as the positive X-half-axis) to the navigational "
|
|
"heading value (0<H<=360, clockwise, 360 = NORTH).</text--->"
|
|
"<text>query direction2heading(135.5)</text---> ) )";
|
|
|
|
const string HeadingToDirectionSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> real -> real</text--->"
|
|
"<text>heading2direction( D ) </text--->"
|
|
"<text>Converts a navigational heading value (0<H<=360, clockwise, 360 = NORTH)"
|
|
"to a direction (mathematical angle notation, counter-clockwise, 0<=d<360, "
|
|
"0 = directed as the positive X-half-axis).</text--->"
|
|
"<text>query heading2direction(135.5)</text---> ) )";
|
|
|
|
Operator spatialDirectionToHeading (
|
|
"direction2heading",
|
|
DirectionToHeadingSpec,
|
|
ConvertDirHeadVM<false>,
|
|
Operator::SimpleSelect,
|
|
Real2RealTM
|
|
);
|
|
|
|
Operator spatialHeadingToDirection (
|
|
"heading2direction",
|
|
HeadingToDirectionSpec,
|
|
ConvertDirHeadVM<true>,
|
|
Operator::SimpleSelect,
|
|
Real2RealTM
|
|
);
|
|
|
|
/*
|
|
6.17 Operator ~spatialgetstartpoint~
|
|
|
|
---- sline --> point
|
|
|
|
Returns the start point of the simple line.
|
|
|
|
*/
|
|
|
|
ListExpr SLine2PointTM(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("Expected: sline.");
|
|
}
|
|
if(listutils::isSymbol(nl->First(args),SimpleLine::BasicType())){
|
|
return nl->SymbolAtom(Point::BasicType());
|
|
}
|
|
return listutils::typeError("Expected: sline.");
|
|
}
|
|
|
|
template<bool start>
|
|
int SpatialGetPointVM(Word* args, Word& result, int message,
|
|
Word& local, Supplier s){
|
|
SimpleLine* l = static_cast<SimpleLine*>(args[0].addr);
|
|
result = qp->ResultStorage(s);
|
|
Point* res = static_cast<Point*>(result.addr);
|
|
if(!l->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
if (start)
|
|
res->Set(l->StartPoint());
|
|
else
|
|
res->Set(l->EndPoint());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
const string SpatialGetStartPointSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> sline -> point</text--->"
|
|
"<text>getstartpoint( _ ) </text--->"
|
|
"<text>Returns the starting point of a simple line.</text--->"
|
|
"<text>query getstartpoint(fromline(BGrenzenLinie))</text---> ) )";
|
|
|
|
const string SpatialGetEndPointSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text> sline -> point</text--->"
|
|
"<text>getendpoint( _ ) </text--->"
|
|
"<text>Returns the end point of a simple line.</text--->"
|
|
"<text>query getendpoint(fromline(BGrenzenLinie))</text---> ) )";
|
|
|
|
Operator spatialgetstartpoint (
|
|
"getstartpoint",
|
|
SpatialGetStartPointSpec,
|
|
SpatialGetPointVM<true>,
|
|
Operator::SimpleSelect,
|
|
SLine2PointTM
|
|
);
|
|
|
|
Operator spatialgetendpoint (
|
|
"getendpoint",
|
|
SpatialGetEndPointSpec,
|
|
SpatialGetPointVM<false>,
|
|
Operator::SimpleSelect,
|
|
SLine2PointTM
|
|
);
|
|
|
|
|
|
/*
|
|
5.14 Operator ~circle~
|
|
|
|
5.14.1 Type Mapping for ~circle~
|
|
|
|
*/
|
|
ListExpr
|
|
circleTM( ListExpr args )
|
|
{
|
|
string err = "point x real x int expected";
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Point::checkType(nl->First(args)) ||
|
|
!CcReal::checkType(nl->Second(args)) ||
|
|
!CcInt::checkType(nl->Third(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->SymbolAtom(Region::BasicType());
|
|
}
|
|
|
|
void generateCircle(Point* p, double radius, int n , Region* res){
|
|
res->Clear(); // clear the result region
|
|
if (!p->IsDefined() ) { // Nothing to do
|
|
res->SetDefined( false );
|
|
return;
|
|
}
|
|
double x, y;
|
|
x = p->GetX();
|
|
y = p->GetY();
|
|
if(n<3 || n>100 || radius <=0){
|
|
res->SetDefined(false);
|
|
return;
|
|
}
|
|
|
|
double valueX, valueY;
|
|
double angle;
|
|
int partnerno = 0;
|
|
HalfSegment hs;
|
|
|
|
res->SetDefined( true );
|
|
res->StartBulkLoad();
|
|
|
|
// Calculate a polygon with (n) vertices and (n) edges.
|
|
// To get the vertices, divide 360 degree in n parts using
|
|
// a standardised circle around p with circumference U = 2 * PI * r.
|
|
|
|
for( int i = 0; i < n; i++ ) {
|
|
// The first point/vertex of the segment
|
|
angle = i * 2 * M_PI/n; // angle to starting vertex
|
|
valueX = x + radius * cos(angle);
|
|
valueY = y + radius * sin(angle);
|
|
Point v1(true, valueX ,valueY);
|
|
|
|
// The second point/vertex of the segment
|
|
if ((i+1) >= n){ // angle to end vertex
|
|
angle = 0 * 2 * M_PI/n; // for inner vertex
|
|
} else {
|
|
angle = (i+1) * 2 * M_PI/n;// for ending = starting vertex
|
|
}
|
|
valueX = x + radius * cos(angle);
|
|
valueY = y + radius * sin(angle);
|
|
Point v2(true, valueX ,valueY);
|
|
|
|
// Create a halfsegment for this segment
|
|
hs.Set(true, v1, v2);
|
|
hs.attr.faceno = 0; // only one face
|
|
hs.attr.cycleno = 0; // only one cycle
|
|
hs.attr.edgeno = partnerno;
|
|
hs.attr.partnerno = partnerno++;
|
|
hs.attr.insideAbove = (hs.GetLeftPoint() == v1);
|
|
|
|
// Add halfsegments 2 times with opposite LeftDomPoints
|
|
*res += hs;
|
|
hs.SetLeftDomPoint( !hs.IsLeftDomPoint() );
|
|
*res += hs;
|
|
}
|
|
res->EndBulkLoad();
|
|
}
|
|
|
|
|
|
/*
|
|
5.14.2 Value Mapping for ~circle~
|
|
|
|
*/
|
|
int circleVM( Word* args, Word& result, int message, Word& local, Supplier s )
|
|
{
|
|
result = qp->ResultStorage( s );
|
|
Point* p = (Point*)args[0].addr; // Centre of the circle
|
|
CcReal* r = (CcReal*)args[1].addr; // Radius of the circle.
|
|
CcInt* narg = (CcInt*)args[2].addr; // number of edges
|
|
Region *res = (Region*)result.addr;
|
|
|
|
if(!p->IsDefined() || !r->IsDefined() || !narg->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
generateCircle(p, r->GetValue(), narg->GetValue(), res);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
5.14.3 Specification for operator ~circle~
|
|
|
|
*/
|
|
const string
|
|
circleSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>(point real int) -> region</text--->"
|
|
"<text>circle ( p, r, n ) </text--->"
|
|
"<text>Creates a region with a shape approximating a circle "
|
|
"with a given a given center point p and radius r>0.0 by a "
|
|
"regular polygon with 2<n<101 edges.\n"
|
|
"Parameters out of the given perimeters result in an "
|
|
"empty region, undef values in an undef one .</text--->"
|
|
"<text>circle (p,10.0,10)</text---> ) )";
|
|
|
|
/*
|
|
5.14.4 Selection Function of operator ~circle~
|
|
|
|
Not necessary.
|
|
|
|
*/
|
|
|
|
/*
|
|
5.14.5 Definition of operator ~circle~
|
|
|
|
*/
|
|
Operator spatialcircle( "circle",
|
|
circleSpec,
|
|
circleVM,
|
|
Operator::SimpleSelect,
|
|
circleTM);
|
|
|
|
/*
|
|
1.1 Operator ~longlines~
|
|
|
|
The operator gets a stream of ~sline~ values and returns a stream of ~sline~
|
|
values, where the input values are connected to as long as possible line parts.
|
|
|
|
1.1.1 TypeMapping
|
|
|
|
*/
|
|
|
|
ListExpr SpatialLongLinesTM(ListExpr args){
|
|
string err = "line expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
if(!nl->IsEqual(nl->First(args),Line::BasicType())) {
|
|
return listutils::typeError("Argument of type " + Line::BasicType() +
|
|
" expected.");
|
|
}
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(SimpleLine::BasicType()));
|
|
}
|
|
|
|
/*
|
|
1.1.1 Value Mapping
|
|
|
|
1.1.1.1 class LongLinesLocalInfo
|
|
|
|
Stores informations of current situation between request calls. And delivers
|
|
result of request using ~GetNextResult~ function.
|
|
|
|
*/
|
|
|
|
|
|
class LongLinesLocalInfo{
|
|
|
|
public:
|
|
|
|
/*
|
|
Constructor initializes values
|
|
|
|
*/
|
|
LongLinesLocalInfo(Line* li) : firstPoint(false, 0.0,0.0){
|
|
assert(li->IsDefined() && !li->IsEmpty());
|
|
this->theLine = li;
|
|
size = li->Size();
|
|
used = new vector<bool>(size, false);
|
|
pointset = new set<Point>();
|
|
pointset->clear();
|
|
intermediate = new vector<SimpleLine> ();
|
|
intermediate->clear();
|
|
};
|
|
|
|
/*
|
|
Deconstructor deletes internal array
|
|
|
|
*/
|
|
~LongLinesLocalInfo(){
|
|
pointset->clear();
|
|
delete pointset;
|
|
used->clear();
|
|
delete used;
|
|
intermediate->clear();
|
|
delete intermediate;
|
|
};
|
|
|
|
/*
|
|
Splits the ~Line~ into ~SimpleLine~s which are each long as possible.
|
|
|
|
*/
|
|
SimpleLine* GetNextResult(){
|
|
// Find valid start Segment of a line (part)
|
|
if (!intermediate->empty()){
|
|
SimpleLine* res = new SimpleLine(intermediate->back());
|
|
intermediate->pop_back();
|
|
return res;
|
|
}
|
|
int curPos = 0;
|
|
while (used->at(curPos) && curPos < size-1){
|
|
curPos++;
|
|
}
|
|
if (used->at(curPos)) {
|
|
return 0;
|
|
}
|
|
HalfSegment curHS, test;
|
|
theLine->Get(curPos, curHS);
|
|
Point curStartPoint = curHS.GetDomPoint();
|
|
if (curPos + 1 < size && !used->at(curPos+1)) {
|
|
theLine->Get(curPos+1,test);
|
|
if (test.GetDomPoint() != curStartPoint) {
|
|
return GetLongestFrom(curPos);
|
|
}
|
|
}
|
|
Point curEndPoint = curHS.GetSecPoint();
|
|
int partnerpos = curHS.GetAttr().partnerno;
|
|
int testPos = partnerpos;
|
|
bool done = false;
|
|
vector<bool>* visited = new vector<bool>(*used);
|
|
visited->at(curPos) = true;
|
|
visited->at(partnerpos) = true;
|
|
while (!done){
|
|
if (partnerpos > 0) testPos = partnerpos -1;
|
|
theLine->Get(testPos,test);
|
|
if (!visited->at(testPos) &&
|
|
AlmostEqual(test.GetDomPoint(),curEndPoint)){
|
|
curPos = testPos;
|
|
curEndPoint = test.GetSecPoint();
|
|
partnerpos = test.GetAttr().partnerno;
|
|
visited->at(curPos) = true;
|
|
visited->at(partnerpos) = true;
|
|
} else {
|
|
if (partnerpos < size - 1) testPos = partnerpos + 1;
|
|
theLine->Get(testPos,test);
|
|
if (!visited->at(testPos) &&
|
|
AlmostEqual(test.GetDomPoint(),curEndPoint)){
|
|
curPos = testPos;
|
|
curEndPoint = test.GetSecPoint();
|
|
partnerpos = test.GetAttr().partnerno;
|
|
visited->at(curPos) = true;
|
|
visited->at(partnerpos) = true;
|
|
} else {
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
visited->clear();
|
|
delete visited;
|
|
return GetLongestFrom(partnerpos);
|
|
};
|
|
|
|
private:
|
|
|
|
SimpleLine* SearchPartner(vector<double>* lengths,
|
|
vector<SimpleLine>* foLines){
|
|
SimpleLine* res = 0;
|
|
int pos1 = -1;
|
|
int pos2 = -1;
|
|
double maxLength = 0.0;
|
|
do{
|
|
maxLength = 0.0;
|
|
for (unsigned int i = 0 ; i < lengths->size()-1; i++){
|
|
for (unsigned int j = i+1; j < lengths->size(); j++){
|
|
double curLength = lengths->at(i)+lengths->at(j);
|
|
if (curLength > maxLength){
|
|
maxLength = curLength;
|
|
pos1 = i;
|
|
pos2 = j;
|
|
}
|
|
}
|
|
}
|
|
if (maxLength > 0.0){
|
|
if (pos1 == 0) {
|
|
if (res == 0 && lengths->at(pos2) > 0.0){
|
|
res = new SimpleLine(foLines->at(pos2));
|
|
lengths->at(pos1) = 0.0;
|
|
lengths->at(pos2) = 0.0;
|
|
} else {
|
|
if (res != 0 && lengths->at(pos2) > 0.0){
|
|
intermediate->push_back(foLines->at(pos2));
|
|
lengths->at(pos1) = 0.0;
|
|
lengths->at(pos2) = 0.0;
|
|
} else {
|
|
lengths->at(pos1) = 0.0;
|
|
}
|
|
}
|
|
} else {
|
|
SimpleLine l1 = foLines->at(pos1);
|
|
SimpleLine l2 = foLines->at(pos2);
|
|
int edgeno = 0;
|
|
SimpleLine* inres = new SimpleLine(true);
|
|
inres->Clear();
|
|
inres->StartBulkLoad();
|
|
HalfSegment hs;
|
|
if (lengths->at(pos1) > 0.0){
|
|
for (int i = 0 ; i < l1.Size(); i++) {
|
|
l1.Get(i,hs);
|
|
AttrType attr = hs.GetAttr();
|
|
attr.edgeno = edgeno;
|
|
hs.SetAttr(attr);
|
|
inres->operator+=(hs);
|
|
if (hs.IsLeftDomPoint()) hs.SetLeftDomPoint(false);
|
|
else hs.SetLeftDomPoint(true);
|
|
inres->operator+=(hs);
|
|
edgeno++;
|
|
}
|
|
}
|
|
if (lengths->at(pos2) > 0.0){
|
|
for (int i = 0 ; i < l2.Size(); i++) {
|
|
l2.Get(i,hs);
|
|
AttrType attr = hs.GetAttr();
|
|
attr.edgeno = edgeno;
|
|
hs.SetAttr(attr);
|
|
inres->operator+=(hs);
|
|
if (hs.IsLeftDomPoint()) hs.SetLeftDomPoint(false);
|
|
else hs.SetLeftDomPoint(true);
|
|
inres->operator+=(hs);
|
|
edgeno++;
|
|
}
|
|
}
|
|
inres->EndBulkLoad();
|
|
intermediate->push_back(*inres);
|
|
inres->Clear();
|
|
delete inres;
|
|
lengths->at(pos1) = 0.0;
|
|
lengths->at(pos2) = 0.0;
|
|
}
|
|
pos1 = -1;
|
|
pos2 = -1;
|
|
}
|
|
} while (maxLength > 0.0);
|
|
return res;
|
|
};
|
|
|
|
void AddHalfSegmentToResult(SimpleLine* result, int index,
|
|
Point& curEndPoint, Point& firstPoint,
|
|
double& length, int& partnerpos,
|
|
bool& done, int& edgeno, bool first){
|
|
HalfSegment curHs;
|
|
theLine->Get(index, curHs);
|
|
AttrType attr = curHs.GetAttr();
|
|
curEndPoint = curHs.GetSecPoint();
|
|
partnerpos = attr.partnerno;
|
|
attr.edgeno = edgeno;
|
|
curHs.SetAttr(attr);
|
|
if (pointset->find(curEndPoint) != pointset->end() && !first) {
|
|
done = true;
|
|
if (AlmostEqual(curEndPoint,firstPoint)) {
|
|
used->at(index) = true;
|
|
used->at(partnerpos) = true;
|
|
length += curHs.Length();
|
|
result->operator+=(curHs);
|
|
pointset->insert(curEndPoint);
|
|
if (curHs.IsLeftDomPoint()) curHs.SetLeftDomPoint(false);
|
|
else curHs.SetLeftDomPoint(true);
|
|
result->operator+=(curHs);
|
|
edgeno++;
|
|
}
|
|
} else {
|
|
used->at(index) = true;
|
|
used->at(partnerpos) = true;
|
|
result->operator+=(curHs);
|
|
pointset->insert(curEndPoint);
|
|
if (curHs.IsLeftDomPoint()) curHs.SetLeftDomPoint(false);
|
|
else curHs.SetLeftDomPoint(true);
|
|
result->operator+=(curHs);
|
|
length += curHs.Length();
|
|
edgeno++;
|
|
}
|
|
};
|
|
|
|
SimpleLine* GetLongestFrom(int indStart){
|
|
// Initalize result computation
|
|
SimpleLine* result = new SimpleLine(true);
|
|
result->Clear();
|
|
result->StartBulkLoad();
|
|
double length = 0.0;
|
|
HalfSegment curHs;
|
|
theLine->Get(indStart, curHs);
|
|
Point curStartPoint = curHs.GetDomPoint();
|
|
firstPoint = curStartPoint;
|
|
Point curEndPoint = curHs.GetSecPoint();
|
|
pointset->insert(curStartPoint);
|
|
int partnerpos = curHs.GetAttr().partnerno;
|
|
bool done = false;
|
|
int edgeno = 0;
|
|
AddHalfSegmentToResult(result, indStart, curEndPoint, firstPoint,
|
|
length, partnerpos, done, edgeno, true);
|
|
// The dominating point of the next segment in the line is equal to the
|
|
// endpoint of the currentSegment. The next halfsegment therefore is stored
|
|
// near partnerpos in ~line~.
|
|
// Start search for HalfSegments starting in curEndPoint at left of
|
|
// partnerpos
|
|
while (!done){
|
|
int firstPos = partnerpos;
|
|
int lastPos = partnerpos;
|
|
int countLeft = 0;
|
|
int countRight = 0;
|
|
int curPos = partnerpos -1;
|
|
HalfSegment test;
|
|
if (curPos >= 0 && curPos < size)
|
|
{
|
|
theLine->Get(curPos,test);
|
|
while (AlmostEqual(test.GetDomPoint(),curEndPoint) && curPos >= 0){
|
|
if (!used->at(curPos)) countLeft++;
|
|
curPos--;
|
|
if (curPos >= 0)theLine->Get(curPos, test);
|
|
}
|
|
}
|
|
if (countLeft > 0) firstPos = curPos + 1;
|
|
if (firstPos < 0) firstPos = 0;
|
|
// Continue search at right of partnerpos
|
|
curPos = partnerpos + 1;
|
|
if (curPos >= 0 && curPos < size) {
|
|
theLine->Get(curPos,test);
|
|
while (AlmostEqual(test.GetDomPoint(), curEndPoint) && curPos < size){
|
|
if (!used->at(curPos)) countRight++;
|
|
curPos++;
|
|
if (curPos < size)theLine->Get(curPos,test);
|
|
}
|
|
}
|
|
|
|
if (countRight > 0) lastPos = curPos - 1;
|
|
if (lastPos >= size) lastPos = size-1;
|
|
int count = countLeft + countRight;
|
|
if (count == 1) {
|
|
int i = firstPos;
|
|
bool ok = false;
|
|
while (!ok && i <= lastPos){
|
|
if(i != partnerpos && !used->at(i)){
|
|
AddHalfSegmentToResult(result, i, curEndPoint,firstPoint,
|
|
length, partnerpos, done, edgeno, false);
|
|
ok = true;
|
|
}
|
|
i++;
|
|
}
|
|
} else {
|
|
if (count > 1) {
|
|
vector<double>* followLength = new vector<double>();
|
|
vector<int>* followPos = new vector<int>();
|
|
vector<SimpleLine>* followLine = new vector<SimpleLine>();
|
|
followLength->push_back(length);
|
|
followPos->push_back(-1);
|
|
followLine->push_back(SimpleLine(false));
|
|
int helpCount = 1;
|
|
for (int i = firstPos; i <= lastPos; i++){
|
|
if (i != partnerpos && !used->at(i)){
|
|
followPos->push_back(i);
|
|
helpCount++;
|
|
used->at(i) = true;
|
|
theLine->Get(i,test);
|
|
int partnerpos = test.GetAttr().partnerno;
|
|
if (partnerpos >= 0 && partnerpos < size)
|
|
used->at(partnerpos) = true;
|
|
}
|
|
}
|
|
for (int i = 1; i < helpCount; i++){
|
|
SimpleLine* helpLine = GetLongestFrom(followPos->at(i));
|
|
followLine->push_back(*helpLine);
|
|
followLength->push_back(helpLine->Length());
|
|
delete helpLine;
|
|
}
|
|
SimpleLine* ind = SearchPartner(followLength,followLine);
|
|
if (ind == 0) {
|
|
done = true;
|
|
} else {
|
|
for (int i = 0; i < ind->Size(); i++){
|
|
ind->Get(i,test);
|
|
AttrType attr = test.GetAttr();
|
|
attr.edgeno = edgeno;
|
|
test.SetAttr(attr);
|
|
result->operator+=(test);
|
|
if (test.IsLeftDomPoint()) test.SetLeftDomPoint(false);
|
|
else test.SetLeftDomPoint(true);
|
|
result->operator+=(test);
|
|
edgeno++;
|
|
}
|
|
delete ind;
|
|
ind = 0;
|
|
done = true;
|
|
}
|
|
followLength->clear();
|
|
delete followLength;
|
|
followPos->clear();
|
|
delete followPos;
|
|
followLine->clear();
|
|
delete followLine;
|
|
} else { // count < 1 ,
|
|
done = true;
|
|
}
|
|
}
|
|
}
|
|
//cleanup and return result
|
|
result->EndBulkLoad();
|
|
return result;
|
|
};
|
|
|
|
/*
|
|
First point of current computed line for check of valid cycle
|
|
|
|
*/
|
|
Point firstPoint;
|
|
|
|
/*
|
|
Pointer to a boolean array telling if a HalfSegement has been used or not.
|
|
|
|
*/
|
|
vector<bool>* used;
|
|
|
|
/*
|
|
Source line
|
|
|
|
*/
|
|
Line* theLine;
|
|
|
|
/*
|
|
Number of HalfSegments of source line.
|
|
|
|
*/
|
|
int size;
|
|
|
|
/*
|
|
Already touched line points.
|
|
|
|
*/
|
|
|
|
set<Point>* pointset;
|
|
|
|
/*
|
|
Intermediate results
|
|
|
|
*/
|
|
|
|
vector<SimpleLine>* intermediate;
|
|
};
|
|
|
|
int SpatialLongLinesVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
switch(message){
|
|
|
|
case OPEN:{
|
|
|
|
Line* li = static_cast<Line*> (args[0].addr);
|
|
if (!li->IsDefined() || li->IsEmpty()) {
|
|
local.setAddr(0);
|
|
} else {
|
|
LongLinesLocalInfo* localinfo = new LongLinesLocalInfo(li);
|
|
local.setAddr(localinfo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST: {
|
|
result = qp->ResultStorage(s);
|
|
if(!local.addr) return CANCEL;
|
|
LongLinesLocalInfo* localinfo = (LongLinesLocalInfo*) local.addr;
|
|
SimpleLine* res = localinfo->GetNextResult();
|
|
|
|
if(res == 0 || !res->IsDefined()){
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
} else {
|
|
result.setAddr(res);
|
|
return YIELD;
|
|
}
|
|
}
|
|
|
|
case CLOSE: {
|
|
|
|
if(local.addr != 0){
|
|
LongLinesLocalInfo* localinfo = (LongLinesLocalInfo*) local.addr;
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
1.1.1 Specification
|
|
|
|
*/
|
|
|
|
const string SpatialLongLinesSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>line -> (stream sline)</text--->"
|
|
"<text> _ longlines </text--->"
|
|
"<text>Converts a line into an stream of longest possible sline "
|
|
"values.</text--->"
|
|
"<text> _ longlines </text---> ) )";
|
|
|
|
/*
|
|
1.1.1 Operatordefiniton
|
|
|
|
*/
|
|
|
|
Operator spatiallonglines(
|
|
"longlines",
|
|
SpatialLongLinesSpec,
|
|
SpatialLongLinesVM,
|
|
Operator::SimpleSelect,
|
|
SpatialLongLinesTM
|
|
);
|
|
|
|
/*
|
|
1.1 Operator ~splitslineatpoints~
|
|
|
|
The operator gets an ~sline~ value and an ~points~ value defining a set of
|
|
split points. The operator splits the input value at the given points and
|
|
returns the result as an stream of ~sline~ values.
|
|
|
|
1.1.1 TypeMapping
|
|
|
|
*/
|
|
|
|
ListExpr SplitSLineAtPointsTM(ListExpr args){
|
|
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError("two arguments expected");
|
|
}
|
|
|
|
if(!nl->IsEqual(nl->First(args),SimpleLine::BasicType()))
|
|
return listutils::typeError("First argument must be an " +
|
|
SimpleLine::BasicType());
|
|
|
|
if (!nl->IsEqual(nl->Second(args), Points::BasicType()))
|
|
return listutils::typeError("Second Argument must be an " +
|
|
Points::BasicType());
|
|
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->SymbolAtom(SimpleLine::BasicType()));
|
|
}
|
|
|
|
/*
|
|
1.1.1 Value Mapping
|
|
|
|
1.1.1.1 class SplitSLineAtPointsLocalInfo
|
|
|
|
Stores informations of current situation between request calls. And delivers
|
|
result of request using ~GetNextResult~ function.
|
|
|
|
*/
|
|
|
|
|
|
struct SplitSLineAtPointsLocalInfo{
|
|
|
|
SplitSLineAtPointsLocalInfo(SimpleLine* li, Points* pts)
|
|
{
|
|
assert(li->IsDefined() && !li->IsEmpty() &&
|
|
pts->IsDefined());
|
|
|
|
if (pts->IsEmpty())
|
|
{
|
|
intermediate = new vector<SimpleLine> ();
|
|
intermediate->clear();
|
|
intermediate->push_back(*li);
|
|
}
|
|
else
|
|
{
|
|
intermediate = li->SplitAtPPoints(pts);
|
|
}
|
|
};
|
|
|
|
~SplitSLineAtPointsLocalInfo(){
|
|
intermediate->clear();
|
|
delete intermediate;
|
|
};
|
|
|
|
SimpleLine* GetNextResult(){
|
|
if (!intermediate->empty())
|
|
{
|
|
SimpleLine* res = new SimpleLine(intermediate->back());
|
|
intermediate->pop_back();
|
|
return res;
|
|
} else
|
|
return 0;
|
|
};
|
|
|
|
vector<SimpleLine>* intermediate;
|
|
|
|
|
|
};
|
|
|
|
int SplitSLineAtPointsVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s )
|
|
{
|
|
switch(message){
|
|
|
|
case OPEN:{
|
|
|
|
SimpleLine* li = static_cast<SimpleLine*> (args[0].addr);
|
|
Points* pts = static_cast<Points*> (args[1].addr);
|
|
if (!li->IsDefined() || li->IsEmpty() || !pts->IsDefined()) {
|
|
local.setAddr(0);
|
|
} else {
|
|
SplitSLineAtPointsLocalInfo* localinfo =
|
|
new SplitSLineAtPointsLocalInfo(li,pts);
|
|
local.setAddr(localinfo);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
case REQUEST: {
|
|
result = qp->ResultStorage(s);
|
|
if(!local.addr) return CANCEL;
|
|
SplitSLineAtPointsLocalInfo* localinfo =
|
|
(SplitSLineAtPointsLocalInfo*) local.addr;
|
|
SimpleLine* res = localinfo->GetNextResult();
|
|
if(res == 0 || !res->IsDefined()){
|
|
result.setAddr(0);
|
|
return CANCEL;
|
|
} else {
|
|
result.setAddr(res);
|
|
return YIELD;
|
|
}
|
|
}
|
|
|
|
case CLOSE: {
|
|
if(local.addr != 0)
|
|
{
|
|
SplitSLineAtPointsLocalInfo* localinfo =
|
|
(SplitSLineAtPointsLocalInfo*) local.addr;
|
|
delete localinfo;
|
|
local.setAddr(0);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.1.1 Specification
|
|
|
|
*/
|
|
const string SplitSLineAtPointsSpec =
|
|
"( ( \"Signature\" \"Syntax\" \"Meaning\" \"Example\" ) "
|
|
"( <text>sline X points -> (stream sline)</text--->"
|
|
"<text> splitslineatpoints (_, _) </text--->"
|
|
"<text>Splits the sline value into sublines defined by the points.</text--->"
|
|
"<text> splitslineatpoints(sline, points) </text---> ) )";
|
|
|
|
/*
|
|
1.1.1 Operatordefiniton
|
|
|
|
*/
|
|
|
|
Operator spatialsplitslineatpoints(
|
|
"splitslineatpoints",
|
|
SplitSLineAtPointsSpec,
|
|
SplitSLineAtPointsVM,
|
|
Operator::SimpleSelect,
|
|
SplitSLineAtPointsTM);
|
|
|
|
|
|
|
|
/*
|
|
10.38 ~findCycles~
|
|
|
|
This operator finds cycles in a line value. The returned cycles build a
|
|
partition of the space divided by the line. Each halfsegment is used
|
|
once, if it belongs to the outer cycle of the line, twice, if it separates
|
|
the inner region or is not used, if it leads to a dead end or connects a "hole"
|
|
with another cycle.
|
|
|
|
10.38.1 Type Mapping
|
|
|
|
The Signature is: line [->] stream(line)
|
|
|
|
*/
|
|
ListExpr findCyclesTM(ListExpr args){
|
|
string err = "line [x bool] expected";
|
|
int len = nl->ListLength(args);
|
|
if((len != 2) && (len!=1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(len==2){
|
|
if(!CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
}
|
|
return nl->TwoElemList( listutils::basicSymbol<Stream<Line> >(),
|
|
listutils::basicSymbol<Line>());
|
|
|
|
}
|
|
|
|
/*
|
|
10.38.2 LocalInfo
|
|
|
|
*/
|
|
class FindCyclesInfo{
|
|
public:
|
|
|
|
/*
|
|
~Constructor~
|
|
|
|
*/
|
|
FindCyclesInfo(Line* _line, const bool extractH):line(_line), globalPos(0){
|
|
extractHoles = extractH;
|
|
assert(line->IsDefined());
|
|
max = line->Size();
|
|
usage = new char[max];
|
|
memset(usage,0,max); // mark all halfssegments to be unused
|
|
isCritical = new char[max];
|
|
memset(isCritical,0,max);
|
|
DbArray<HalfSegment>* hss = (DbArray<HalfSegment>*) line->GetFLOB(0);
|
|
RegionCreator<DbArray >::findCritical(hss,isCritical);
|
|
}
|
|
|
|
/*
|
|
~Destructor~
|
|
|
|
*/
|
|
~FindCyclesInfo(){
|
|
delete[] usage;
|
|
delete[] isCritical;
|
|
}
|
|
|
|
|
|
/*
|
|
~nextLine~
|
|
|
|
Returns the next cycles within this line or 0
|
|
if no further cycle can be found.
|
|
|
|
*/
|
|
|
|
Line* nextLine(){
|
|
if(extractHoles){
|
|
return nextLineExtractHoles();
|
|
} else {
|
|
return nextLineSimpleCycles();
|
|
}
|
|
}
|
|
|
|
Line* nextLineSimpleCycles(){
|
|
Line* res = 0;
|
|
if(!cycles.empty()){
|
|
res = constructLine(cycles.back());
|
|
cycles.pop_back();
|
|
return res;
|
|
}
|
|
|
|
while(globalPos < max){
|
|
computeCycles(globalPos);
|
|
if(!cycles.empty()){
|
|
res = constructLine(cycles.back());
|
|
cycles.pop_back();
|
|
return res;
|
|
} else {
|
|
usage[globalPos] = 1;
|
|
globalPos++;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
Line* nextLineExtractHoles(){ // extended version including holes detection
|
|
Line* res = 0;
|
|
if(!cycles.empty()){ // cycles are computed
|
|
if(globalPos < (int)cycles.size()){
|
|
res = constructLine(cycles[globalPos]);
|
|
globalPos++;
|
|
return res;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// compute cycles
|
|
// step 1: compute all simple cycles
|
|
int pos = 0;
|
|
while(pos < max){
|
|
computeCycles(pos);
|
|
pos++;
|
|
}
|
|
|
|
if(cycles.empty()){
|
|
return 0;
|
|
}
|
|
if(cycles.size() < 2){ // no hole possible
|
|
res = constructLine(cycles[0]);
|
|
globalPos = 1;
|
|
return res;
|
|
}
|
|
|
|
|
|
// step 3 store Bounding boxes for each cycle ( acceleration of step 3)
|
|
vector<Rectangle<2> > boxes;
|
|
for(size_t i=0; i < cycles.size();i++){
|
|
boxes.push_back(computeBox(cycles[i]));
|
|
}
|
|
|
|
vector<int> outers;
|
|
|
|
for(size_t c1 = 0; c1<cycles.size();c1++){
|
|
int outer = -1;
|
|
double dist = 0;
|
|
for(size_t c2 = 0; c2<cycles.size(); c2++){
|
|
if(c1!=c2){
|
|
if(boxes[c2].Contains(boxes[c1])){
|
|
pair<bool,double> r = isHole(cycles[c1], cycles[c2]);
|
|
if(r.first){
|
|
if((outer < 0) || (dist > r.second)){
|
|
outer = c2;
|
|
dist = r.second;
|
|
}
|
|
}
|
|
} else {
|
|
}
|
|
}
|
|
}
|
|
outers.push_back(outer);
|
|
}
|
|
|
|
|
|
// step 4 append hole cycles to the outer cycles
|
|
// it may be, that halfsegments belongs to more than one hole
|
|
// such halfsegments should not be inserted into a result lineA
|
|
|
|
memset(usage,0,max);
|
|
for(size_t i=0;i<cycles.size();i++){
|
|
if(outers[i]>=0){
|
|
append(cycles[outers[i]],cycles[i],usage,1);
|
|
}
|
|
}
|
|
res = constructLine(cycles[0]);
|
|
globalPos = 1;
|
|
return res;
|
|
}
|
|
|
|
static bool odd(const int i){
|
|
int mask = 1;
|
|
return (i & mask);
|
|
}
|
|
|
|
|
|
private:
|
|
Line* line;
|
|
int globalPos;
|
|
int max;
|
|
char* usage;
|
|
char* isCritical;
|
|
vector<int> currentPath;
|
|
vector<vector<int> > cycles;
|
|
bool extractHoles;
|
|
|
|
|
|
void printCycle(const vector<int>& cycle){
|
|
vector<int>::const_iterator it;
|
|
for(it=cycle.begin();it!=cycle.end();it++){
|
|
HalfSegment hs;
|
|
line->Get(*it,hs);
|
|
cout << *it << " : " << hs.SimpleString() << endl;
|
|
}
|
|
}
|
|
|
|
void printCycleAsLine(const vector<int>& cycle, ostream& o = cout){
|
|
vector<int>::const_iterator it;
|
|
o << setprecision(16);
|
|
o << "(line (";
|
|
for(it=cycle.begin();it!=cycle.end();it++){
|
|
HalfSegment hs;
|
|
line->Get(*it,hs);
|
|
o << "(" << hs.GetDomPoint().GetX() << " " << hs.GetDomPoint().GetY()
|
|
<< " " << hs.GetSecPoint().GetX() << " " << hs.GetSecPoint().GetY()
|
|
<< ")";
|
|
}
|
|
o << "))";
|
|
}
|
|
|
|
void printAsRel(const vector<int>& cycle, ostream& o = cout){
|
|
o << setprecision(16);
|
|
o << "( (rel(tuple((No int)(Partner int) (Seg line)))) (" << endl;
|
|
vector<int>::const_iterator it;
|
|
HalfSegment hs;
|
|
for(it=cycle.begin();it!=cycle.end();it++){
|
|
line->Get(*it,hs);
|
|
o << "(" << *it << " " << hs.attr.partnerno << " "
|
|
<< "((" << hs.GetDomPoint().GetX() << " " << hs.GetDomPoint().GetY()
|
|
<< " " << hs.GetSecPoint().GetX() << " " << hs.GetSecPoint().GetY()
|
|
<< ")) )" << endl;
|
|
}
|
|
o << "))";
|
|
|
|
}
|
|
|
|
|
|
|
|
// axuxiliary function (for debugging
|
|
// returns 0 if (x,y) is outside the cycle
|
|
// returns 2 if (x,y) is on the cycle
|
|
// returns 1 if (x,y) is inside the cycle
|
|
int cycleContains(const vector<int>& cycle, const double& x,
|
|
const double& y) const{
|
|
int hits = 0;
|
|
HalfSegment hs;
|
|
for(size_t i=0;i<cycle.size();i++){
|
|
line->Get(cycle[i],hs);
|
|
if(hs.Contains(Point(true,x,y))){
|
|
return 2; //
|
|
}
|
|
double dist = RegionCreator<DbArray >::getLeftDist(hs,x,y,true);
|
|
if(dist>=0){
|
|
hits++;
|
|
}
|
|
}
|
|
int mask = 1;
|
|
return (hits&mask) > 0?1:0;
|
|
}
|
|
|
|
|
|
pair<bool, double> isHole(const vector<int>& hole, const vector<int>& outer){
|
|
|
|
|
|
HalfSegment hs;
|
|
int hi = hole[0];
|
|
line->Get(hi,hs);
|
|
Point dp = hs.GetDomPoint();
|
|
Point sp = hs.GetSecPoint();
|
|
double x = (dp.GetX() + sp.GetX()) /2;
|
|
double y = (dp.GetY() + sp.GetY()) /2;
|
|
int count = 0;
|
|
double dist = 0;
|
|
|
|
for(size_t i=0;i<outer.size();i++){
|
|
int ho = outer[i];
|
|
if(ho == hi){
|
|
return pair<bool,double>(false,0);
|
|
}
|
|
line->Get(ho, hs);
|
|
if(hs.attr.partnerno==hi) {
|
|
return pair<bool,double>(false,0);
|
|
}
|
|
double dist1 = RegionCreator<DbArray >::getLeftDist(hs,x,y, true);
|
|
|
|
if(dist1>=0){
|
|
count++;
|
|
if((count==1) || (dist1<dist)){
|
|
dist = dist1;
|
|
}
|
|
}
|
|
}
|
|
if(odd(count)){
|
|
return pair<bool,double>(true,dist);
|
|
} else {
|
|
return pair<bool,double>(false,0);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
int find(const vector<int>& v, int value){
|
|
for(size_t i=0;i<v.size();i++){
|
|
if(v[i]==value){
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void erase(vector<int>& v, int value){
|
|
int pos = find(v,value);
|
|
if(pos>=0){
|
|
v[pos] = v.back();
|
|
v.pop_back();
|
|
}
|
|
}
|
|
|
|
void append(vector<int>& v1, vector<int>& v2, char* usage, char Umark){
|
|
// v1 is the outer
|
|
// v2 is some hole
|
|
// usage is 0 => unused
|
|
// usage == Umark => halfsegment is already used
|
|
|
|
HalfSegment hs;
|
|
for(size_t i=0;i<v2.size();i++){
|
|
int n = v2[i];
|
|
line->Get(n,hs);
|
|
int p = hs.attr.partnerno;
|
|
if( (usage[n]==Umark) || (usage[p]==Umark)){
|
|
erase(v1,n);
|
|
erase(v1,p);
|
|
} else {
|
|
usage[n] = Umark;
|
|
usage[p] = Umark;
|
|
v1.push_back(n);
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle<2> computeBox(const vector<int>& cycle){
|
|
Rectangle<2> res(false);
|
|
HalfSegment hs;
|
|
Point p(true);
|
|
for(size_t i=0;i<cycle.size();i++){
|
|
line->Get(cycle[i],hs);
|
|
p = hs.GetDomPoint();
|
|
if(i==0){
|
|
res = p.BoundingBox();
|
|
} else {
|
|
res.Extend(p.BoundingBox());
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
~findCycle~
|
|
|
|
Tries to find a cycle starting at the dominating point of the Halfsegment
|
|
at position pos. Note, the cycle can also start with an halfsegment at
|
|
another location (having the same dominating point). It's assumed the
|
|
dominating point is the left most one within this cycle.
|
|
|
|
Only non-used halfsegments are used for finding the cycle.
|
|
For dead ends, all halfssegments are set to be used. This avoids the
|
|
inspection of dead ends more than once.
|
|
|
|
*/
|
|
Line* findCycle(int pos){
|
|
int pos1 = findStartPos(pos);
|
|
if(pos1 < 0){ // no start found
|
|
usage[pos] = 1;
|
|
return 0;
|
|
}
|
|
pos = pos1;
|
|
usage[pos] = 3;
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
if(usage[hs.attr.partnerno]==0){
|
|
usage[hs.attr.partnerno]=4;
|
|
}
|
|
currentPath.push_back(pos);
|
|
Line* line = findCycle(currentPath);
|
|
return line;
|
|
}
|
|
|
|
void computeCycles( int pos ){
|
|
int pos1 = findStartPos(pos);
|
|
if(pos1 < 0){
|
|
return; // keep cycles empty
|
|
}
|
|
pos = pos1;
|
|
usage[pos] = 3;
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
if(usage[hs.attr.partnerno]==0){
|
|
usage[hs.attr.partnerno] = 4;
|
|
}
|
|
vector<int> path;
|
|
path.push_back(pos);
|
|
computeCycles(path);
|
|
}
|
|
|
|
void computeCycles(vector<int>& path){
|
|
HalfSegment hs;
|
|
while(!path.empty()){
|
|
int pos = path.back();
|
|
int next = nextPos(pos);
|
|
if(next<0){ // no extension found
|
|
reducePath(path);
|
|
} else {
|
|
if(usage[next]==3) { // found cycle
|
|
vector<int> cycle;
|
|
int s = path.back();
|
|
while(s!=next){
|
|
path.pop_back();
|
|
cycle.push_back(s);
|
|
usage[s] = 1;
|
|
line->Get(s,hs);
|
|
if(usage[hs.attr.partnerno]==4){
|
|
usage[hs.attr.partnerno] = 0;
|
|
}
|
|
s = path.back();
|
|
}
|
|
// process last element in path
|
|
path.pop_back();
|
|
cycle.push_back(s);
|
|
usage[s] = 1;
|
|
line->Get(s,hs);
|
|
if(usage[hs.attr.partnerno]==4){
|
|
usage[hs.attr.partnerno] = 0;
|
|
}
|
|
if(isClockwise(cycle)){
|
|
cycles.push_back(cycle);
|
|
}
|
|
} else { // normal extension
|
|
usage[next] = 3;
|
|
line->Get(next,hs);
|
|
if(!usage[hs.attr.partnerno]){
|
|
usage[hs.attr.partnerno] = 4;
|
|
}
|
|
path.push_back(next);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool isClockwise(vector<int>& path){
|
|
assert(!path.empty());
|
|
// find the smallest dom point in path
|
|
int index = 0;
|
|
HalfSegment hs;
|
|
line->Get(path[0],hs);
|
|
Point dp = hs.GetDomPoint();
|
|
for(size_t i=1;i<path.size();i++){
|
|
line->Get(path[i],hs);
|
|
Point dp1 = hs.GetDomPoint();
|
|
if(dp1<dp){
|
|
index = i;
|
|
dp = dp1;
|
|
}
|
|
}
|
|
|
|
line->Get(path[index],hs);
|
|
Point p2 = hs.GetDomPoint();
|
|
Point p3 = hs.GetSecPoint();
|
|
line->Get(path[ ( index +1 )%path.size()],hs);
|
|
Point p1 = hs.GetDomPoint();
|
|
return Region::GetCycleDirection(p1,p2,p3);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void print(const vector<int>& v)const{
|
|
cout << "<";
|
|
for(size_t i =0;i<v.size();i++){
|
|
if(i>0) cout << ", ";
|
|
cout << v[i];
|
|
}
|
|
cout << ">";
|
|
}
|
|
|
|
Line* findCycle(vector<int>& currentPath){
|
|
int pos = currentPath.back();
|
|
//cout << "start a new path with " << pos << endl;
|
|
int next = nextPos(pos);
|
|
vector<int> resPath; // path to construct
|
|
vector<int> cycleStarts; // stores the positions of subcycles
|
|
while(!currentPath.empty()){
|
|
while((next>=0) && !currentPath.empty()){
|
|
if(usage[next] == 3){ // found sub path
|
|
cycleStarts.push_back(resPath.size());
|
|
while(currentPath.back() != next){
|
|
int last = currentPath.back();
|
|
resPath.push_back(last);
|
|
currentPath.pop_back();
|
|
usage[last] = 1;
|
|
//cout << "put " << last << " from path into result" << endl;
|
|
}
|
|
// process first elem of path
|
|
int last = currentPath.back();
|
|
resPath.push_back(last);
|
|
currentPath.pop_back();
|
|
usage[last] = 1;
|
|
//cout << "put " << last << " from path into result" << endl;
|
|
if(!currentPath.empty()){
|
|
pos = currentPath.back();
|
|
next = nextPos(pos);
|
|
}
|
|
} else { // normal extension
|
|
usage[next] = 3;
|
|
HalfSegment hs;
|
|
line->Get(next,hs);
|
|
if(usage[hs.attr.partnerno]==0){
|
|
usage[hs.attr.partnerno]= 4;
|
|
}
|
|
currentPath.push_back(next);
|
|
//cout << "extend path " << next << endl;
|
|
pos = next;
|
|
next = nextPos(pos);
|
|
}
|
|
}
|
|
if(!currentPath.empty()){
|
|
reducePath(currentPath);
|
|
if(!currentPath.empty()){
|
|
pos = currentPath.back();
|
|
next = nextPos(pos);
|
|
}
|
|
}
|
|
} // currentPath not emptyA
|
|
|
|
|
|
if(resPath.empty()){
|
|
return 0;
|
|
} else {
|
|
HalfSegment hs;
|
|
// mark the locked halfsegments as usuable
|
|
// find out the index of the leftmost point within the path
|
|
size_t outerPathStarts = cycleStarts.back();
|
|
int smallestIndex = cycleStarts.back();
|
|
Point smallestDomPoint(true);
|
|
for(size_t i=0;i<resPath.size();i++){
|
|
line->Get(resPath[i],hs);
|
|
if(usage[hs.attr.partnerno]==4){
|
|
usage[hs.attr.partnerno] = 0;
|
|
}
|
|
if(i==outerPathStarts){
|
|
smallestDomPoint = hs.GetDomPoint();
|
|
} else if(i>outerPathStarts){
|
|
Point p = hs.GetDomPoint();
|
|
if(smallestDomPoint.GetX() > p.GetX()){
|
|
smallestIndex = i;
|
|
smallestDomPoint = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
line->Get( resPath[smallestIndex],hs);
|
|
Point p2 = hs.GetDomPoint();
|
|
Point p3 = hs.GetSecPoint();
|
|
size_t i3 = smallestIndex +1;
|
|
if(i3==resPath.size()){
|
|
i3 = outerPathStarts;
|
|
}
|
|
line->Get( resPath[i3],hs);
|
|
Point p1 = hs.GetDomPoint();
|
|
|
|
if(!Region::GetCycleDirection(p1,p2,p3)){
|
|
//cout << "ignore Path of length " << resPath.size() << endl;
|
|
return 0;
|
|
} else {
|
|
//cout << "path directed clockwise" << endl;
|
|
//cout << "Path has " << resPath.size() << " edges" << endl;
|
|
return constructLine(resPath);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void reducePath(vector<int>& path){
|
|
//size_t oldsize = path.size();
|
|
if(path.empty()){
|
|
return;
|
|
}
|
|
int last = path.back();
|
|
path.pop_back();
|
|
usage[last] = 1;
|
|
HalfSegment hs;
|
|
line->Get(last,hs);
|
|
if(usage[hs.attr.partnerno]==4){
|
|
usage[hs.attr.partnerno] = 0;
|
|
}
|
|
//cout << "remove " << last << " from path " << endl;
|
|
while(!path.empty()){
|
|
if(isCritical[last]){ // possible a new extension
|
|
return;
|
|
} else {
|
|
//cout << last << " is not critical remove next "<< endl;
|
|
}
|
|
|
|
last = path.back();
|
|
path.pop_back();
|
|
usage[last] = 1;
|
|
line->Get(last,hs);
|
|
if(usage[hs.attr.partnerno]==4){
|
|
usage[hs.attr.partnerno] = 0;
|
|
}
|
|
//cout << "remove " << last << " from path " << endl;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
Returns the position of the halfsegment having the same domination point as the
|
|
halfsegment at pos with the highest slope.
|
|
If only one unused halfsegment is available, -1 is returned. This can be the
|
|
case if 1) a dead end 2) only the halfsegment with the smallest slope is
|
|
available (this halfsegment cannot contribute to a cycle in clockwise order).
|
|
|
|
*/
|
|
int findStartPos(int pos) const{
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
// collect all unused halfsegments with the same dominating point as hs
|
|
// into a vector
|
|
vector<pair<HalfSegment,int> > candidates;
|
|
if(!usage[pos]){
|
|
candidates.push_back(pair<HalfSegment,int>(hs,pos));
|
|
}
|
|
Point dp = hs.GetDomPoint();
|
|
int pos1 = pos-1;
|
|
bool done = false;
|
|
while( (pos1>=0) && ! done) {
|
|
line->Get(pos1,hs);
|
|
if(!AlmostEqual(hs.GetDomPoint(),dp)){
|
|
done = true;
|
|
} else if(!usage[pos1]){
|
|
candidates.push_back( pair<HalfSegment,int>(hs,pos1));
|
|
}
|
|
pos1--;
|
|
}
|
|
pos1=pos + 1;
|
|
done = false;
|
|
while( (pos1<line->Size()) && ! done) {
|
|
line->Get(pos1,hs);
|
|
if(!AlmostEqual(hs.GetDomPoint(),dp)){
|
|
done = true;
|
|
} else if(!usage[pos1]){
|
|
candidates.push_back(pair<HalfSegment,int>(hs,pos1));
|
|
}
|
|
pos1++;
|
|
}
|
|
|
|
|
|
if(candidates.size() < 1){
|
|
return -1;
|
|
}
|
|
// search hs with minimum slope
|
|
|
|
// search for the unused halfsegment with he maximum slope
|
|
int index = -1;
|
|
double slope = 0;
|
|
for(size_t i=0; i< candidates.size(); i++){
|
|
if(!usage[candidates[i].second]){
|
|
pair<HalfSegment,int> cand = candidates[i];
|
|
double slope1=0;
|
|
bool up;
|
|
if(getSlope(cand.first,slope1,up)){
|
|
if(index < 0 ){
|
|
index = i;
|
|
slope = slope1;
|
|
} else if(slope1>slope){
|
|
index = i;
|
|
slope = slope1;
|
|
}
|
|
} else { // vertical segment
|
|
if(up){ // maximum slope found immediately
|
|
return candidates[i].second;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(index < 0){ // no unused element found
|
|
return -1;
|
|
}
|
|
return candidates[index].second;
|
|
|
|
}
|
|
|
|
/*
|
|
Computes the slope of an HalfSegment from dompoint to secpoint.
|
|
If the segment is vertical, false is returned and up is set to be
|
|
true if the segment goes up.
|
|
|
|
*/
|
|
static bool getSlope(const HalfSegment& hs, double& slope, bool& up){
|
|
Point p1 = hs.GetDomPoint();
|
|
Point p2 = hs.GetSecPoint();
|
|
double x1 = p1.GetX();
|
|
double x2 = p2.GetX();
|
|
double y1 = p1.GetY();
|
|
double y2 = p2.GetY();
|
|
up = y1 < y2;
|
|
if(AlmostEqual(x1,x2)){
|
|
return false;
|
|
}
|
|
slope = (y2-y1)/(x2-x1);
|
|
return true;
|
|
}
|
|
|
|
Line* constructLine(vector<int>& path){
|
|
if(path.empty()){
|
|
return 0;
|
|
}
|
|
Line* res = new Line(path.size()*2);
|
|
res->StartBulkLoad();
|
|
vector<int>::iterator it;
|
|
int c = -1;
|
|
for(it=path.begin(); it!=path.end();it++){
|
|
c++;
|
|
HalfSegment hs;
|
|
line->Get(*it,hs);
|
|
hs.attr.edgeno = c;
|
|
(*res) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*res) +=hs;
|
|
}
|
|
res->EndBulkLoad(true,false);
|
|
return res;
|
|
}
|
|
|
|
|
|
/*
|
|
~nextPos~
|
|
|
|
extends a pth ending at the halfsegment at pos by the halfsegment having the
|
|
most right direction. If this halfsegment is not present (dead end) or
|
|
already used, (-1, false) is returned.
|
|
|
|
*/
|
|
|
|
int nextPos(int pos) const{
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
Point dp = hs.GetSecPoint(); // dompoint of the partner
|
|
int ppos = hs.attr.partnerno;
|
|
vector<pair<HalfSegment,int> > candidates;
|
|
int ppos1 = ppos-1;
|
|
bool done = false;
|
|
HalfSegment hs1;
|
|
while((ppos1>=0)&&!done){
|
|
line->Get(ppos1,hs1);
|
|
if(!AlmostEqual(dp,hs1.GetDomPoint())){
|
|
done = true;
|
|
} else {
|
|
if(usage[ppos1]==3){
|
|
return ppos1;
|
|
}
|
|
if(!usage[ppos1]){
|
|
candidates.push_back(pair<HalfSegment,int>(hs1,ppos1));
|
|
}
|
|
}
|
|
ppos1--;
|
|
}
|
|
ppos1 = ppos+1;
|
|
done = false;
|
|
while((ppos1<line->Size())&&!done){
|
|
line->Get(ppos1,hs1);
|
|
if(!AlmostEqual(dp,hs1.GetDomPoint())){
|
|
done = true;
|
|
} else {
|
|
if(usage[ppos1]==3){
|
|
return ppos1;
|
|
}
|
|
if(!usage[ppos1]){
|
|
candidates.push_back(pair<HalfSegment,int>(hs1,ppos1));
|
|
}
|
|
}
|
|
ppos1++;
|
|
}
|
|
|
|
if(candidates.empty()){ // no extension possible
|
|
return -1;
|
|
}
|
|
if(candidates.size()==1){ // simple extension, no branch
|
|
return candidates[0].second;
|
|
}
|
|
|
|
//partition candidates to those ones left of hs and those ones right of hs
|
|
vector<pair<HalfSegment,int> > candidates_left;
|
|
vector<pair<HalfSegment,int> > candidates_right;
|
|
dp = hs.GetDomPoint();
|
|
Point sp = hs.GetSecPoint();
|
|
for(unsigned int i=0;i<candidates.size();i++){
|
|
Point p = candidates[i].first.GetSecPoint();
|
|
if(isRight(dp,sp,p)){
|
|
candidates_right.push_back(candidates[i]);
|
|
} else {
|
|
candidates_left.push_back(candidates[i]);
|
|
}
|
|
}
|
|
|
|
|
|
candidates = candidates_right.size()>0?candidates_right:candidates_left;
|
|
|
|
|
|
// search for the right most extension
|
|
int index = 0;
|
|
for(unsigned int i=1;i<candidates.size() ; i++){
|
|
if(moreRight(candidates[index].first, candidates[i].first)){
|
|
index = i;
|
|
}
|
|
}
|
|
|
|
return candidates[index].second;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
~notLeft~
|
|
|
|
Returns true if __hs__ is vertical or directed to right.
|
|
|
|
*/
|
|
|
|
bool notLeft(HalfSegment& hs) const{
|
|
double dx = hs.GetDomPoint().GetX();
|
|
double sx = hs.GetSecPoint().GetX();
|
|
return (AlmostEqual(dx,sx) || (sx>dx));
|
|
}
|
|
|
|
/*
|
|
~moreRight~
|
|
|
|
Returns true if hs2 closes a smaller clockwise cycle than hs1.
|
|
Both halfsegments must
|
|
have the same dominating point.
|
|
|
|
*/
|
|
bool moreRight(HalfSegment& hs1, HalfSegment& hs2) const{
|
|
Point dp1 = hs1.GetDomPoint();
|
|
Point dp2 = hs2.GetDomPoint();
|
|
assert(AlmostEqual(dp1,dp2));
|
|
Point sp1 = hs1.GetSecPoint();
|
|
Point sp2 = hs2.GetSecPoint();
|
|
return isRight(dp1,sp1,sp2);
|
|
}
|
|
|
|
|
|
/*
|
|
~isRight~
|
|
|
|
Returns true if r is on the right side of the line defined by p and q
|
|
|
|
*/
|
|
bool isRight(const Point& p, const Point& q, const Point& r) const{
|
|
double rx=r.GetX();
|
|
double ry=r.GetY();
|
|
double px=p.GetX();
|
|
double py=p.GetY();
|
|
double qx=q.GetX();
|
|
double qy=q.GetY();
|
|
|
|
double A2 = px*qy + py*rx + qx*ry - (rx*qy + ry*px +qx*py);
|
|
return A2 < 0;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
/*
|
|
10.38.3 Value Mapping
|
|
|
|
*/
|
|
|
|
int findCyclesVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s )
|
|
{
|
|
FindCyclesInfo* li = (FindCyclesInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr=0;
|
|
}
|
|
Line* arg = (Line*) args[0].addr;
|
|
bool extractHoles = false;
|
|
if(qp->GetNoSons(s)==2){
|
|
CcBool* b = (CcBool*) args[1].addr;
|
|
if(!b->IsDefined()){
|
|
return 0;
|
|
}
|
|
extractHoles = b->GetValue();
|
|
}
|
|
if(arg->IsDefined()){
|
|
local.addr = new FindCyclesInfo(arg, extractHoles);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->nextLine():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
default: return -1;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
10.38.4 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec findCyclesSpec (
|
|
"line [x bool] -> stream(line)",
|
|
"findCycles(_,_)",
|
|
"Finds minimum cycles within a line value. If the boolean argument is"
|
|
" present and TRUE, holes are also part of the resulting lines",
|
|
"query findCycles(BGrenzenLine) count"
|
|
);
|
|
|
|
/*
|
|
10.38.5 Operator Instance
|
|
|
|
*/
|
|
Operator findCycles(
|
|
"findCycles",
|
|
findCyclesSpec.getStr(),
|
|
findCyclesVM,
|
|
Operator::SimpleSelect,
|
|
findCyclesTM
|
|
);
|
|
|
|
|
|
/*
|
|
10.39 Debug-Operator markUsage
|
|
|
|
10.39.1 Type Mapping: line -> Stream(tuple(L:line)(U : int))
|
|
|
|
*/
|
|
ListExpr markUsageTM(ListExpr args){
|
|
string err = "line expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr attrList = nl->TwoElemList(
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("L"),
|
|
listutils::basicSymbol<Line>()),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("Usage"),
|
|
listutils::basicSymbol<CcInt>()));
|
|
return nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Tuple> >(),
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Tuple>(),
|
|
attrList));
|
|
}
|
|
|
|
/*
|
|
10.30.2 Value Mapping
|
|
|
|
*/
|
|
|
|
class MarkUsageInfo{
|
|
public:
|
|
MarkUsageInfo(Line* line, ListExpr tupleType){
|
|
this->line = line;
|
|
if(line->IsDefined() && (line->Size()>0)){
|
|
usage = new char[line->Size()];
|
|
critical = new char[line->Size()];
|
|
pos = 0;
|
|
markUsage(line,usage, critical);
|
|
tt = new TupleType(tupleType);
|
|
} else {
|
|
usage = 0;
|
|
pos = -1;
|
|
tt = 0;
|
|
}
|
|
}
|
|
|
|
~MarkUsageInfo(){
|
|
if(usage){
|
|
delete[] usage;
|
|
}
|
|
if( critical){
|
|
delete[] critical;
|
|
}
|
|
if(tt){
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
Tuple* next(){
|
|
if(!usage){
|
|
return 0;
|
|
}
|
|
if(pos>=line->Size()){
|
|
return 0;
|
|
}
|
|
Tuple* res = new Tuple(tt);
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
Line* l = new Line(2);
|
|
hs.attr.edgeno = 0;
|
|
l->StartBulkLoad();
|
|
(*l) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*l) += hs;
|
|
l->EndBulkLoad();
|
|
res->PutAttribute(0,l);
|
|
res->PutAttribute(1,new CcInt(true,usage[pos]));
|
|
pos++;
|
|
return res;
|
|
}
|
|
|
|
private:
|
|
Line* line;
|
|
char* usage;
|
|
char* critical;
|
|
int pos;
|
|
TupleType* tt;
|
|
};
|
|
|
|
int markUsageVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s )
|
|
{
|
|
MarkUsageInfo* li = (MarkUsageInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new MarkUsageInfo((Line*) args[0].addr,
|
|
nl->Second(GetTupleResultType(s)));
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec markUsageSpec (
|
|
"line -> stream(tuple(L line)(U int))",
|
|
"markUsage(_)",
|
|
"Rteurns the usage of halfsegments. (for debugging only)",
|
|
"query markUsage(GrenzenLine) "
|
|
);
|
|
|
|
/*
|
|
10.38.5 Operator Instance
|
|
|
|
*/
|
|
Operator markUsageOp(
|
|
"markUsage",
|
|
markUsageSpec.getStr(),
|
|
markUsageVM,
|
|
Operator::SimpleSelect,
|
|
markUsageTM
|
|
);
|
|
|
|
/*
|
|
10.40 Operator getCriticalPoints
|
|
|
|
|
|
10.40.1 Signature
|
|
|
|
|
|
*/
|
|
|
|
ListExpr criticalPointsTM(ListExpr args){
|
|
string err = "line expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Points>();
|
|
}
|
|
|
|
|
|
|
|
int criticalPointsVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s )
|
|
{
|
|
Line* arg = (Line*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
Points* res = (Points*) result.addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
} else {
|
|
res->SetDefined(true);
|
|
res->Clear();
|
|
if(arg->Size()==0){
|
|
return 0;
|
|
}
|
|
char* usage = new char[arg->Size()];
|
|
char* crit = new char[arg->Size()];
|
|
markUsage(arg,usage,crit);
|
|
res->StartBulkLoad();
|
|
for(int i=0;i<arg->Size();i++){
|
|
if(crit[i]){
|
|
HalfSegment hs;
|
|
arg->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
(*res) += hs.GetDomPoint();
|
|
}
|
|
}
|
|
}
|
|
res->EndBulkLoad();
|
|
delete[] usage;
|
|
delete[] crit;
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
OperatorSpec criticalPointsSpec (
|
|
"line -> points",
|
|
"criticalPoints(_)",
|
|
"Returns the critical points of a line value",
|
|
"query criticalPoints(GrenzenLine) "
|
|
);
|
|
|
|
Operator criticalPoints(
|
|
"criticalPoints",
|
|
criticalPointsSpec.getStr(),
|
|
criticalPointsVM,
|
|
Operator::SimpleSelect,
|
|
criticalPointsTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
Operator testRegionCreator
|
|
|
|
For debugging only, should be removed after tests
|
|
|
|
Signature : line -> region
|
|
|
|
*/
|
|
ListExpr testRegionCreatorTM(ListExpr args){
|
|
string err = "line expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Region>();
|
|
}
|
|
|
|
|
|
int testRegionCreatorVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s )
|
|
{
|
|
Line* line = (Line*) args[0].addr;
|
|
result=qp->ResultStorage(s);
|
|
Region* res = (Region*) result.addr;
|
|
RegionCreator<DbArray>::createRegion((DbArray<HalfSegment>*)
|
|
line->GetFLOB(0),res);
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec testRegionCreatorSpec (
|
|
"line -> region",
|
|
"testRegionCreator(_)",
|
|
"for debugging only",
|
|
"query testRegionCreator(GrenzenLine) "
|
|
);
|
|
|
|
Operator testRegionCreator(
|
|
"testRegionCreator",
|
|
testRegionCreatorSpec.getStr(),
|
|
testRegionCreatorVM,
|
|
Operator::SimpleSelect,
|
|
testRegionCreatorTM
|
|
);
|
|
|
|
|
|
/*
|
|
1.40 Collect Box
|
|
|
|
1.40.1 TypeMapping
|
|
|
|
Signature is Stream<SPATIAL> x bool -> rectangle
|
|
|
|
*/
|
|
ListExpr collect_boxTM(ListExpr args){
|
|
string err = "stream(spatial) x bool expected ";
|
|
if (!nl->HasLength(args, 2)) {
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if (!Stream<Attribute>::checkType(nl->First(args))) {
|
|
return listutils::typeError(err +" (first arg is not an attribute stream)");
|
|
}
|
|
if (!CcBool::checkType(nl->Second(args))) {
|
|
return listutils::typeError(err + " (second arg is not a bool)");
|
|
}
|
|
ListExpr attr = nl->Second(nl->First(args));
|
|
if (listutils::isKind(attr, Kind::SPATIAL1D())) {
|
|
return listutils::basicSymbol<Rectangle<1> >();
|
|
}
|
|
if (listutils::isKind(attr, Kind::SPATIAL2D())) {
|
|
return listutils::basicSymbol<Rectangle<2> >();
|
|
}
|
|
if (listutils::isKind(attr, Kind::SPATIAL3D())) {
|
|
return listutils::basicSymbol<Rectangle<3> >();
|
|
}
|
|
if (listutils::isKind(attr, Kind::SPATIAL4D())) {
|
|
return listutils::basicSymbol<Rectangle<4> >();
|
|
}
|
|
if (listutils::isKind(attr, Kind::SPATIAL8D())) {
|
|
return listutils::basicSymbol<Rectangle<8> >();
|
|
}
|
|
return listutils::typeError(err + " (attribute not in kind SPATIALxD)");
|
|
}
|
|
|
|
int collect_boxSelect(ListExpr args) {
|
|
ListExpr attr = nl->Second(nl->First(args));
|
|
if (listutils::isKind(attr, Kind::SPATIAL1D())) return 0;
|
|
if (listutils::isKind(attr, Kind::SPATIAL2D())) return 1;
|
|
if (listutils::isKind(attr, Kind::SPATIAL3D())) return 2;
|
|
if (listutils::isKind(attr, Kind::SPATIAL4D())) return 3;
|
|
if (listutils::isKind(attr, Kind::SPATIAL8D())) return 4;
|
|
return -1;
|
|
}
|
|
|
|
template<int dim>
|
|
int collect_boxVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s) {
|
|
Stream<StandardSpatialAttribute<dim> > stream(args[0]);
|
|
CcBool* ignoreUndefined = (CcBool*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
Rectangle<dim>* res = (Rectangle<dim>*) result.addr;
|
|
res->SetDefined(false);
|
|
if(!ignoreUndefined->IsDefined()){
|
|
return 0;
|
|
}
|
|
stream.open();
|
|
bool first = true;
|
|
bool useundef = !ignoreUndefined->GetValue();
|
|
StandardSpatialAttribute<dim>* a = stream.request();
|
|
while(a){
|
|
if(a->IsDefined()){
|
|
Rectangle<dim> box = a->BoundingBox();
|
|
if(box.IsDefined()){
|
|
if(first){
|
|
res->SetDefined(true);
|
|
(*res) = box;
|
|
first = false;
|
|
} else {
|
|
res->Extend(box);
|
|
}
|
|
}
|
|
} else {
|
|
if(!useundef){
|
|
res->SetDefined(false);
|
|
a->DeleteIfAllowed();
|
|
a=0;
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
}
|
|
a->DeleteIfAllowed();
|
|
a = stream.request();
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping collect_boxVMs[] = {collect_boxVM<1>, collect_boxVM<2>,
|
|
collect_boxVM<3>, collect_boxVM<4>, collect_boxVM<8>};
|
|
|
|
OperatorSpec collect_boxSpec (
|
|
"stream<SPATIAL> x bool -> rectangle",
|
|
" _ collect_box[_]",
|
|
"Computes the bounding box from a stream of spatial attributes"
|
|
"If the second parameter is set to be false, undefined elements"
|
|
" within the stream are ignored. Otherwise an undefined element"
|
|
" will lead to an undefined result.",
|
|
"query strassen feed projecttransformstream[GeoData] collect_box[TRUE] "
|
|
);
|
|
|
|
Operator collect_box(
|
|
"collect_box",
|
|
collect_boxSpec.getStr(),
|
|
5,
|
|
collect_boxVMs,
|
|
collect_boxSelect,
|
|
collect_boxTM
|
|
);
|
|
|
|
/*
|
|
1.38 Operator intersection_rob
|
|
|
|
To test the robust implementation
|
|
|
|
1.38.1 Type Mapping
|
|
|
|
*/
|
|
|
|
ListExpr intersection_robTM(ListExpr args){
|
|
string err = "region x line expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr a1 = nl->First(args);
|
|
ListExpr a2 = nl->Second(args);
|
|
if(Region::checkType(a1) && // region x line
|
|
Line::checkType(a2)){
|
|
return listutils::basicSymbol<Line>();
|
|
}
|
|
if(Line::checkType(a1) && // line x region
|
|
Region::checkType(a2)){
|
|
return listutils::basicSymbol<Line>();
|
|
}
|
|
|
|
if(Line::checkType(a1) && // line x line
|
|
Line::checkType(a2)){
|
|
return listutils::basicSymbol<Line>();
|
|
}
|
|
|
|
return listutils::typeError();
|
|
}
|
|
|
|
/*
|
|
1.38.2 Value Mapping
|
|
|
|
Implemented as template for later support of other types.
|
|
|
|
*/
|
|
template<class A, class B, class R>
|
|
int intersection_robVM1(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
A* arg1 = (A*) args[0].addr;
|
|
B* arg2 = (B*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
R* res = (R*) result.addr;
|
|
if(!arg1->IsDefined() || !arg2->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
robust::intersection(*arg1,*arg2,*res);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.38.3 ValueMappinmg Array and SelectionFunction
|
|
|
|
*/
|
|
ValueMapping intersection_robVM[] = {
|
|
intersection_robVM1<Region,Line,Line>,
|
|
intersection_robVM1<Line,Region,Line>,
|
|
intersection_robVM1<Line,Line,Line>
|
|
};
|
|
|
|
int intersection_robSelect(ListExpr args){
|
|
ListExpr a1 = nl->First(args);
|
|
ListExpr a2 = nl->Second(args);
|
|
if(Region::checkType(a1) && Line::checkType(a2)){
|
|
return 0;
|
|
}
|
|
if(Line::checkType(a1) && Region::checkType(a2)){
|
|
return 0;
|
|
}
|
|
if(Line::checkType(a1) && Line::checkType(a2)){
|
|
return 0;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
1.38.4 Specification
|
|
|
|
*/
|
|
OperatorSpec intersection_robSpec (
|
|
"region x line -> line | line x region -> line | line x line -> line",
|
|
" intersection_rob(_,_)",
|
|
"Computes the intersection of the arguments"
|
|
"using a (hopefully) robust implementation.",
|
|
"query intersection_rob(thecenter, boundary(thecenter) "
|
|
);
|
|
|
|
/*
|
|
1.38.5 Operator instance
|
|
|
|
*/
|
|
Operator spatialintersection_rob( "intersection_rob",
|
|
intersection_robSpec.getStr(),
|
|
3,
|
|
intersection_robVM,
|
|
intersection_robSelect,
|
|
intersection_robTM);
|
|
|
|
|
|
|
|
/*
|
|
1.39 Operator contains_rob
|
|
|
|
1.39.1 Type Mapping
|
|
|
|
Signature is region x point [x bool] -> bool
|
|
|
|
*/
|
|
ListExpr contains_robTM(ListExpr args){
|
|
|
|
string err = " region x point [ x bool] expected";
|
|
int len = nl->ListLength(args);
|
|
if( (len!=2) && (len!=3) ){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Region::checkType(nl->First(args)) ||
|
|
!Point::checkType(nl->Second(args)) ){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(len==3){
|
|
if(!CcBool::checkType(nl->Third(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
return nl->ThreeElemList( nl->SymbolAtom(Symbol::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<CcBool>());
|
|
|
|
}
|
|
/*
|
|
1.39.2 Value Mapping
|
|
|
|
*/
|
|
int contains_robVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
Region* reg = (Region*) args[0].addr;
|
|
Point* p = (Point*) args[1].addr;
|
|
CcBool* b = (CcBool*) args[2].addr;
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
if(!reg->IsDefined() || !p->IsDefined() || !b->IsDefined()){
|
|
res->SetDefined(false);
|
|
}
|
|
int r = robust::contains(*reg,*p);
|
|
if(r==0){
|
|
res->Set(true,false);
|
|
} else {
|
|
if(r==1 || b->GetValue()){
|
|
res->Set(true,true);
|
|
} else {
|
|
res->Set(true,false);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.39.3 Specification
|
|
|
|
*/
|
|
OperatorSpec contains_robSpec (
|
|
"region x point [x bool] -> line",
|
|
" contains_rob(_,_)",
|
|
"Checks whether the point is inside the region"
|
|
"if the booolean argument is present and has value true"
|
|
" the function returns also true, if the point is "
|
|
" on the border of the region",
|
|
"query contains_rob(thecenter, mehringdamm "
|
|
);
|
|
|
|
/*
|
|
1.39.4 Operator instance
|
|
|
|
*/
|
|
Operator contains_rob(
|
|
"contains_rob",
|
|
contains_robSpec.getStr(),
|
|
contains_robVM,
|
|
Operator::SimpleSelect,
|
|
contains_robTM
|
|
);
|
|
|
|
|
|
/*
|
|
1.40 Operator ~holes~
|
|
|
|
1.40.1 Type Mapping : region -> region
|
|
|
|
*/
|
|
|
|
ListExpr getHolesTM(ListExpr args){
|
|
string err = "region expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + "( wrong number of args");
|
|
}
|
|
if(!Region::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Region>();
|
|
}
|
|
|
|
/*
|
|
1.40.2 Value Mapping
|
|
|
|
*/
|
|
int getHolesVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Region* arg = (Region*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
Region* res = (Region*) result.addr;
|
|
arg->getHoles(*res);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.40.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec getHolesSpec (
|
|
"region -> region ",
|
|
" getHoles(_) ",
|
|
"Returns the holes of a region.",
|
|
"query isempty(getHoles(thecenter)) "
|
|
);
|
|
|
|
/*
|
|
1.40.4 Operator instance
|
|
|
|
*/
|
|
Operator getHoles(
|
|
"getHoles",
|
|
getHolesSpec.getStr(),
|
|
getHolesVM,
|
|
Operator::SimpleSelect,
|
|
getHolesTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
1.41 Opegartor collect_line2
|
|
|
|
This operatotr is for checking the robust realminize implementation.
|
|
|
|
1.41.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr collect_line2TM(ListExpr args){
|
|
string err =" stream(line) expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + " (wrong number of args)");
|
|
}
|
|
if(!Stream<Line>::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Line>();
|
|
}
|
|
|
|
|
|
int collect_line2VM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Line* res = (Line*) result.addr;
|
|
Stream<Line> stream(args[0]);
|
|
stream.open();
|
|
Line* line;
|
|
res->Clear();
|
|
res->SetDefined(true);
|
|
DbArray<HalfSegment>* dba= new DbArray<HalfSegment>(2000);
|
|
HalfSegment hs;
|
|
while( (line = stream.request()) != 0){
|
|
if(line->IsDefined()){
|
|
for(int i=0;i<line->Size();i++){
|
|
line->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
dba->Append(hs);
|
|
}
|
|
}
|
|
}
|
|
line->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
|
|
// get the DbArray from result;
|
|
res->StartBulkLoad();
|
|
DbArray<HalfSegment>* dbl = (DbArray<HalfSegment>*) res->GetFLOB(0);
|
|
robust::realminize(*dba, *dbl);
|
|
dba->destroy();
|
|
delete dba;
|
|
|
|
res->EndBulkLoad(true,false); // sort, no realminize
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
1.41.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec collect_line2Spec (
|
|
" stream(line) -> line ",
|
|
" _ collect_line2 ",
|
|
" Builds the union of all lines within the "
|
|
"stream iggnoring undefined lines.",
|
|
"query strassen feed projecttransformstream[GeoData] collect_line2 "
|
|
);
|
|
|
|
/*
|
|
1.41.4 Operator instance
|
|
|
|
*/
|
|
Operator collect_line2(
|
|
"collect_line2",
|
|
collect_line2Spec.getStr(),
|
|
collect_line2VM,
|
|
Operator::SimpleSelect,
|
|
collect_line2TM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
1.41 Operator ~getInnerPoint~
|
|
|
|
Returns some point from the inetrior of a region.
|
|
|
|
1.41.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr getInnerPointTM(ListExpr args){
|
|
string err = "region expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + " (wrong number of arguments");
|
|
}
|
|
if(!Region::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Point>();
|
|
}
|
|
|
|
/*
|
|
1.41.2 Value Mapping
|
|
|
|
*/
|
|
int getInnerPointVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Region* arg = (Region*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
Point* res = (Point*) result.addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(arg->IsEmpty()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
HalfSegment hs;
|
|
arg->Get(0,hs);
|
|
double dx = abs(hs.GetDomPoint().GetX() - hs.GetSecPoint().GetX());
|
|
double dy = abs(hs.GetDomPoint().GetY() - hs.GetSecPoint().GetY());
|
|
double dist = -1;
|
|
Point mp = hs.middlePoint();
|
|
double x = mp.GetX();
|
|
double y = mp.GetY();
|
|
|
|
double (*distComp)(const HalfSegment& hs, const double x, const double y);
|
|
|
|
int dir;
|
|
|
|
if(dx>dy){
|
|
if(hs.attr.insideAbove){
|
|
distComp = &PRegionCreator::getUpDist;
|
|
dir = 0;
|
|
} else {
|
|
distComp = &PRegionCreator::getDownDist;
|
|
dir = 1;
|
|
}
|
|
} else {
|
|
double y2 = hs.GetSecPoint().GetY();
|
|
double y1 = hs.GetDomPoint().GetY();
|
|
if(hs.attr.insideAbove == (y2>y1)){
|
|
distComp = &PRegionCreator::getLeftDist;
|
|
dir = 2;
|
|
} else{
|
|
distComp = &PRegionCreator::getRightDist;
|
|
dir = 3;
|
|
}
|
|
}
|
|
double d;
|
|
|
|
|
|
for(int i=1;i<arg->Size();i++){
|
|
arg->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
d = distComp(hs,x,y);
|
|
if(d>0){
|
|
if((dist<0) || d<dist){
|
|
dist = d;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
assert(dist>0);
|
|
switch(dir){
|
|
case 0: y += dist/2.0; break;
|
|
case 1: y -= dist/2.0; break;
|
|
case 2: x -= dist/2.0; break;
|
|
case 3: x += dist/2.0; break;
|
|
default: assert(false);
|
|
}
|
|
res->Set(x,y);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
1.42.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec getInnerPointSpec (
|
|
" region -> point ",
|
|
" getInnerPoint(_) ",
|
|
" Returns some point within the interior of a region.",
|
|
" query WFaechen feed extend[IP : getInnerPoint(.GeoData)] "
|
|
"filter[ inInterior(IP,.GeoData)] count = WFlaechen count "
|
|
);
|
|
|
|
|
|
/*
|
|
1.42.4 Operator instance
|
|
|
|
*/
|
|
Operator getInnerPoint(
|
|
"getInnerPoint",
|
|
getInnerPointSpec.getStr(),
|
|
getInnerPointVM,
|
|
Operator::SimpleSelect,
|
|
getInnerPointTM
|
|
);
|
|
|
|
|
|
/*
|
|
1.43 Operator ~checkRealm~
|
|
|
|
Debugging operator. Checks whether the halfsegments of a line or a regaion
|
|
are realminized.
|
|
|
|
1.43.1 Type Map
|
|
|
|
*/
|
|
ListExpr checkRealmTM(ListExpr args){
|
|
string err = "line or region expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!Line::checkType(arg) && !Region::checkType(arg)){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
/*
|
|
1.43.2 Value Mapping
|
|
|
|
*/
|
|
int checkRealmVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
if(message==INIT){
|
|
ListExpr ttl = robust::RealmChecker<DbArray>::getTupleType();
|
|
ListExpr numTupleType = SecondoSystem::GetCatalog()->NumericType(ttl);
|
|
TupleType* tt = new TupleType(numTupleType);
|
|
qp->GetLocal2(s).addr=tt;
|
|
return 0;
|
|
}
|
|
if(message==FINISH){
|
|
( (TupleType*)qp->GetLocal2(s).addr)->DeleteIfAllowed();
|
|
qp->GetLocal2(s).addr=0;
|
|
return 0;
|
|
}
|
|
|
|
TupleType* tt = (TupleType*)qp->GetLocal2(s).addr;
|
|
Attribute* arg = (Attribute*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
} else {
|
|
DbArray<HalfSegment>* hss = (DbArray<HalfSegment>*) arg->GetFLOB(0);
|
|
robust::RealmChecker<DbArray> rc(hss,tt);
|
|
res->Set(true,rc.checkRealm());
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
1.42.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec checkRealmSpec (
|
|
" {region,line} -> bool ",
|
|
" checkRealm(_) ",
|
|
" Checks the argumet for correct realminization (debug operator).",
|
|
" query WFaechen feed filter[ checkRealm(.GeoData)(] count "
|
|
);
|
|
|
|
|
|
/*
|
|
1.42.4 Operator instance
|
|
|
|
*/
|
|
Operator checkRealmOp(
|
|
"checkRealm",
|
|
checkRealmSpec.getStr(),
|
|
checkRealmVM,
|
|
Operator::SimpleSelect,
|
|
checkRealmTM
|
|
);
|
|
|
|
|
|
/*
|
|
1.43 Operator badRealm
|
|
|
|
This operator returns the pairs of halfsegments within a
|
|
line or a region violating the realm properties.
|
|
|
|
*/
|
|
|
|
ListExpr badRealmTM(ListExpr args){
|
|
string err = "line or region expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err + " ( wrong number of args");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(!Line::checkType(arg) && !Region::checkType(arg)){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr tt = robust::RealmChecker<DbArray>::getTupleType();
|
|
return nl->TwoElemList( listutils::basicSymbol<Stream<Tuple> >(),
|
|
tt);
|
|
}
|
|
|
|
/*
|
|
1.43.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int badRealmVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
PRealmChecker* li = (PRealmChecker*) local.addr;
|
|
switch(message){
|
|
case OPEN : {
|
|
if(li){
|
|
delete li;
|
|
li = 0;
|
|
}
|
|
Attribute* a = (Attribute*) args[0].addr;
|
|
if(a->IsDefined()){
|
|
DbArray<HalfSegment>* hss =
|
|
(DbArray<HalfSegment>*) a->GetFLOB(0);
|
|
local.addr = new PRealmChecker(hss);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->nextTuple():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
/*
|
|
1.43.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec badRealmSpec (
|
|
" {region,line} -> stream(tuple( (No1 int)(Partner1 int)"
|
|
"(Segment1 line)(No2 int)(Partner2 int)(Segment2 line))) ",
|
|
" badRealm(_) ",
|
|
" Returns pairs of halfsegments of a line or a region "
|
|
"violating the realm properties. (For debugging purposes).",
|
|
" query badRealm(BGrenzenLine) count "
|
|
);
|
|
|
|
|
|
/*
|
|
1.43.4 Operator instance
|
|
|
|
*/
|
|
Operator badRealmOp(
|
|
"badRealm",
|
|
badRealmSpec.getStr(),
|
|
badRealmVM,
|
|
Operator::SimpleSelect,
|
|
badRealmTM
|
|
);
|
|
|
|
|
|
/*
|
|
1.44 Operator crossings_rob
|
|
|
|
|
|
1.44.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr crossings_robTM(ListExpr args){
|
|
|
|
string err = "line x line expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Line::checkType(nl->First(args)) ||
|
|
!Line::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Points>();
|
|
}
|
|
|
|
/*
|
|
1.44.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int crossings_robVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Line* l1 = (Line*) args[0].addr;
|
|
Line* l2 = (Line*) args[1].addr;
|
|
Points* res = (Points*) result.addr;
|
|
robust::crossings(*l1,*l2,*res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
1.44.3 Specification
|
|
|
|
*/
|
|
|
|
OperatorSpec crossings_robSpec (
|
|
" line x line -> points",
|
|
" crossings_rob(l1,l2) ",
|
|
" Returns common points of two lines which are not part "
|
|
" of a common segment",
|
|
" query crossings_rob(BGrenzenLine,BGrenzenLine) "
|
|
);
|
|
|
|
|
|
/*
|
|
1.44.4 Operator instance
|
|
|
|
*/
|
|
Operator crossings_rob(
|
|
"crossings_rob",
|
|
crossings_robSpec.getStr(),
|
|
crossings_robVM,
|
|
Operator::SimpleSelect,
|
|
crossings_robTM
|
|
);
|
|
|
|
|
|
|
|
|
|
class LineSplitIterator{
|
|
|
|
public:
|
|
|
|
LineSplitIterator(Line* line){
|
|
theLine=line;
|
|
if(!line->IsDefined() || (line->Size() == 0)){
|
|
theLine = 0;
|
|
used = 0;
|
|
return;
|
|
}
|
|
size = line->Size();
|
|
theLine = line;
|
|
used = new bool[size];
|
|
memset(used,0,size*sizeof(bool));
|
|
position = 0;
|
|
start = 0;
|
|
searchStart();
|
|
}
|
|
|
|
~LineSplitIterator(){
|
|
if(used){
|
|
delete[] used;
|
|
}
|
|
}
|
|
|
|
|
|
bool next(HalfSegment& nextHs, bool& newStart){
|
|
if(position>=size){
|
|
return false;
|
|
}
|
|
theLine->Get(position,nextHs); // get HalfSegment
|
|
newStart = (position==start);
|
|
gotoNext();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
Line* theLine;
|
|
bool* used;
|
|
uint32_t position;
|
|
uint32_t start;
|
|
uint32_t size;
|
|
|
|
|
|
void gotoNext(){
|
|
HalfSegment hs;
|
|
theLine->Get(position,hs);
|
|
used[position] = true;
|
|
// goto Partnerhs
|
|
uint32_t partnerNo = hs.attr.partnerno;
|
|
used[partnerNo] = true;
|
|
int x = getExtension(partnerNo);
|
|
if(x!=0){
|
|
position = partnerNo+x;
|
|
return;
|
|
}
|
|
searchStart();
|
|
}
|
|
|
|
// returns 0 if there is no extension
|
|
// returns x != 0 if (pos+x) is an unused extension
|
|
int getExtension(int pos){
|
|
HalfSegment hs;
|
|
theLine->Get(pos,hs);
|
|
Point dp = hs.GetDomPoint();
|
|
HalfSegment cand;
|
|
// search left
|
|
int pos2 = pos - 1;
|
|
while(pos2>0){
|
|
if(used[pos2]){
|
|
pos2--;
|
|
} else {
|
|
theLine->Get(pos2,cand);
|
|
if(AlmostEqual(dp,cand.GetDomPoint())){
|
|
return pos2-pos;
|
|
} else {
|
|
pos2 = -1;
|
|
}
|
|
}
|
|
}
|
|
// search right
|
|
pos2 = pos+1;
|
|
while(pos2<(int)size){
|
|
if(used[pos2]){
|
|
pos2++;
|
|
} else {
|
|
theLine->Get(pos2,cand);
|
|
if(AlmostEqual(dp,cand.GetDomPoint())){
|
|
return pos2-pos;
|
|
} else {
|
|
pos2 = size;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
void searchStart(){
|
|
|
|
start = 0;
|
|
while( (start<size) && used[start]){
|
|
start++;
|
|
}
|
|
if(start>=size){
|
|
position = start;
|
|
return;
|
|
}
|
|
int start2 = start;
|
|
int x;
|
|
x = getExtension(start2);
|
|
HalfSegment n;
|
|
bool* used2 = new bool[size];
|
|
memset(used2,0,size*sizeof(bool));
|
|
used2[start] = true;
|
|
while(x){
|
|
theLine->Get(start2 + x,n);
|
|
|
|
start2 = n.attr.partnerno;
|
|
x = getExtension(start2);
|
|
if(used2[start2]){ // cycle
|
|
position = start2;
|
|
start = start2;
|
|
delete[] used2;
|
|
return;
|
|
}
|
|
used2[start2] = true;
|
|
}
|
|
delete[] used2;
|
|
start = start2;
|
|
position = start;
|
|
}
|
|
};
|
|
|
|
|
|
/*
|
|
10.9.81 Operator ~linesplit~
|
|
|
|
This operator splits a line into pieces of equal length
|
|
|
|
*/
|
|
|
|
ListExpr splitlineTM(ListExpr args){
|
|
|
|
int len = nl->ListLength(args);
|
|
if((len<2) || (len>4) ){
|
|
return listutils::typeError("2, 3, or 4 args required");
|
|
}
|
|
string err = "line x real [x geoid [xreal]] expected";
|
|
if(!Line::checkType(nl->First(args)) ||
|
|
!CcReal::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if((len>2) &&
|
|
!Geoid::checkType(nl->Third(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( (len>3 ) &&
|
|
!CcReal::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
|
|
ListExpr res = nl->TwoElemList(
|
|
listutils::basicSymbol<Stream<Line> >(),
|
|
listutils::basicSymbol<Line>() );
|
|
if(len==4){ // all arguments come form user
|
|
return res;
|
|
}
|
|
ListExpr gl = nl->TheEmptyList();
|
|
if(len == 3) { // scaleFactor not present
|
|
gl = nl->OneElemList(nl->RealAtom(1.0));
|
|
} else {
|
|
assert(len==2);
|
|
gl = nl->TwoElemList(
|
|
nl->TwoElemList(
|
|
listutils::basicSymbol<Geoid>(),
|
|
nl->SymbolAtom(Symbol::UNDEFINED())),
|
|
nl->RealAtom(1.0));
|
|
}
|
|
return nl->ThreeElemList(nl->SymbolAtom(Symbol::APPEND()),
|
|
gl,
|
|
res);
|
|
|
|
}
|
|
|
|
|
|
|
|
class SplitLineInfo{
|
|
|
|
|
|
public:
|
|
SplitLineInfo(Line* _line, CcReal* _dist, Geoid* _geoid,
|
|
CcReal* _scale){
|
|
if(!_line->IsDefined() || !_dist->IsDefined() ){
|
|
it=0;
|
|
return;
|
|
}
|
|
dist = _dist->GetValue();
|
|
if((_line->Size()==0) || dist<0 || AlmostEqual(dist,0)){
|
|
it = 0;
|
|
return;
|
|
}
|
|
if(!_scale->IsDefined()){
|
|
scale = 1.0;
|
|
} else {
|
|
scale = _scale->GetValue();
|
|
}
|
|
if(AlmostEqual(scale,0) || scale < 0){
|
|
it = 0;
|
|
return;
|
|
}
|
|
it = new LineSplitIterator(_line);
|
|
hasLastHs = false;
|
|
geoid = _geoid->IsDefined()?_geoid:0;
|
|
}
|
|
|
|
~SplitLineInfo(){
|
|
if(it){
|
|
delete it;
|
|
}
|
|
}
|
|
|
|
|
|
// debug code
|
|
Line* next1(){
|
|
static uint32_t count = 0;
|
|
|
|
count++;
|
|
HalfSegment hs;
|
|
bool newStart;
|
|
bool hn = it->next(hs, newStart);
|
|
if(!hn){
|
|
return 0;
|
|
}
|
|
Line* res = new Line(2);
|
|
res->StartBulkLoad() ;
|
|
(*res) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*res) += hs;
|
|
res->EndBulkLoad(true,true,true);
|
|
return res;
|
|
|
|
}
|
|
// debug code
|
|
|
|
Line* next(){
|
|
Line* res = new Line(10);
|
|
res->StartBulkLoad();
|
|
double length = 0;
|
|
bool done = false;
|
|
if(hasLastHs){
|
|
done = appendHs(res,lastHs, length);
|
|
}
|
|
|
|
HalfSegment hs;
|
|
bool newStart;
|
|
while(!done){
|
|
bool nextHs = it->next(hs , newStart);
|
|
if(!nextHs){
|
|
done = true;
|
|
} else {
|
|
if(newStart){
|
|
if(res->Size()==0){
|
|
done = appendHs(res,hs,length);
|
|
} else {
|
|
done = true;
|
|
lastHs = hs;
|
|
hasLastHs = true;
|
|
}
|
|
} else {
|
|
done = appendHs(res,hs,length);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
res->EndBulkLoad(true,true,true);
|
|
if(res->Size()==0){
|
|
delete res;
|
|
res = 0;
|
|
}
|
|
return res;
|
|
|
|
}
|
|
|
|
private:
|
|
LineSplitIterator* it;
|
|
HalfSegment lastHs;
|
|
bool hasLastHs;
|
|
double dist;
|
|
Geoid* geoid;
|
|
double scale;
|
|
|
|
|
|
bool appendHs(Line*& res, HalfSegment& hs, double& length){
|
|
|
|
Point p1 = hs.GetDomPoint();
|
|
Point p2 = hs.GetSecPoint();
|
|
p1.Scale(1.0/scale);
|
|
p2.Scale(1.0/scale);
|
|
|
|
//cout << "compute distance between " << p1 << " and " << p2 << endl;
|
|
|
|
double hsl = p1.Distance(p2, geoid);
|
|
|
|
if(((length + hsl) <= dist) || AlmostEqual(length+hsl,dist)){
|
|
// append complete hs
|
|
(*res) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*res) += hs;
|
|
hasLastHs = false;
|
|
length += hsl;
|
|
return AlmostEqual(dist,length+hsl);
|
|
}
|
|
// append only a part of hs
|
|
double rest = dist - length;
|
|
assert(rest>0);
|
|
double delta = rest/hsl;
|
|
|
|
assert(delta > 0);
|
|
assert(delta < 1);
|
|
Point dp = hs.GetDomPoint();
|
|
Point sp = hs.GetSecPoint();
|
|
double x1 = dp.GetX();
|
|
double x2 = sp.GetX();
|
|
double xs = x1 + delta*(x2-x1);
|
|
double y1 = dp.GetY();
|
|
double y2 = sp.GetY();
|
|
double ys = y1 + delta*(y2-y1);
|
|
Point ps(true,xs,ys);
|
|
|
|
if(AlmostEqual(dp,ps)){
|
|
// nothing to append
|
|
lastHs.Set(true,dp,sp);
|
|
hasLastHs = true;
|
|
return true;
|
|
}
|
|
if(AlmostEqual(ps,sp)){
|
|
// append nearly all
|
|
(*res) += hs;
|
|
hs.SetLeftDomPoint(!hs.IsLeftDomPoint());
|
|
(*res) += hs;
|
|
hasLastHs = false;
|
|
length += hsl;
|
|
return true;
|
|
}
|
|
// real split
|
|
HalfSegment hs1(true, dp, ps);
|
|
HalfSegment hs2(true, ps, sp);
|
|
|
|
lastHs = hs2;
|
|
hasLastHs = true;
|
|
(*res) += hs1;
|
|
hs1.SetLeftDomPoint(false);
|
|
(*res) += hs1;
|
|
length += rest;
|
|
return true;
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
int splitlineVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
|
|
SplitLineInfo* li = (SplitLineInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
}
|
|
local.addr = new SplitLineInfo((Line*) args[0].addr,
|
|
(CcReal*) args[1].addr,
|
|
(Geoid*) args[2].addr,
|
|
(CcReal*) args[3].addr);
|
|
return 0;
|
|
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
}
|
|
case CLOSE : {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
}
|
|
|
|
}
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
OperatorSpec splitlineSpec (
|
|
" line x real [x geoid [x real]] -> points",
|
|
" linesplit(line, length, geoid, scalefactor)",
|
|
" Splits a line into pieces of a given length. "
|
|
" if geoid is given, the distance computations are"
|
|
" done using this geoid. For scaled up geodata, the "
|
|
"optional argument scalefactor can be used. All coordinates"
|
|
" are divided by this factor befor the distance is computed",
|
|
" query splitline(BGrenzenLine) count "
|
|
);
|
|
|
|
|
|
/*
|
|
1.44.4 Operator instance
|
|
|
|
*/
|
|
Operator splitline(
|
|
"splitline",
|
|
splitlineSpec.getStr(),
|
|
splitlineVM,
|
|
Operator::SimpleSelect,
|
|
splitlineTM
|
|
);
|
|
|
|
|
|
/*
|
|
2 Type Constructor dline
|
|
|
|
*/
|
|
|
|
GenTC<DLine> dline;
|
|
|
|
/*
|
|
Type Constructor DRM
|
|
|
|
*/
|
|
GenTC<DRM> drm;
|
|
|
|
GenTC<OIM> oim;
|
|
|
|
|
|
/*
|
|
Operator ~computeDRM~
|
|
|
|
Computes the direction relation matrix for 2 elements in Spatial2D.
|
|
|
|
*/
|
|
|
|
ListExpr computeDRMTM(ListExpr args){
|
|
string err="SPATIAL2D x SPATIAL2D expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!listutils::isKind(nl->First(args),Kind::SPATIAL2D()) ||
|
|
!listutils::isKind(nl->Second(args),Kind::SPATIAL2D() )){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<DRM>();
|
|
}
|
|
|
|
|
|
/*
|
|
Value Mapping
|
|
|
|
*/
|
|
|
|
int computeDRMVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
StandardSpatialAttribute<2>* a =
|
|
(StandardSpatialAttribute<2>*) args[0].addr;
|
|
StandardSpatialAttribute<2>* b =
|
|
(StandardSpatialAttribute<2>*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
DRM* res = (DRM*) result.addr;
|
|
res->computeFrom(*a,*b);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
|
|
Specification
|
|
|
|
*/
|
|
OperatorSpec computeDRMSpec (
|
|
" SPATIAL2D x SPATIAL2D -> drm",
|
|
" computeDRM(_,_)",
|
|
" Computes the direction relation matrix for two "
|
|
" spatial objects.",
|
|
" query computeDRM(BGrenzenline, mehringdamm) "
|
|
);
|
|
|
|
/*
|
|
Operator instance
|
|
|
|
*/
|
|
Operator computeDRM(
|
|
"computeDRM",
|
|
computeDRMSpec.getStr(),
|
|
computeDRMVM,
|
|
Operator::SimpleSelect,
|
|
computeDRMTM
|
|
);
|
|
|
|
|
|
/*
|
|
Operator ~computeOIM~
|
|
|
|
Computes the objects interaction matrix for 2 elements in Spatial2D.
|
|
|
|
*/
|
|
|
|
ListExpr computeOIMTM(ListExpr args){
|
|
string err="SPATIAL2D x SPATIAL2D expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!listutils::isKind(nl->First(args),Kind::SPATIAL2D()) ||
|
|
!listutils::isKind(nl->Second(args),Kind::SPATIAL2D() )){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<OIM>();
|
|
}
|
|
|
|
|
|
/*
|
|
Value Mapping
|
|
|
|
*/
|
|
|
|
int computeOIMVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
StandardSpatialAttribute<2>* a =
|
|
(StandardSpatialAttribute<2>*) args[0].addr;
|
|
StandardSpatialAttribute<2>* b =
|
|
(StandardSpatialAttribute<2>*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
OIM* res = (OIM*) result.addr;
|
|
res->computeFrom(*a,*b);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
|
|
Specification
|
|
|
|
*/
|
|
OperatorSpec computeOIMSpec (
|
|
" SPATIAL2D x SPATIAL2D -> oim",
|
|
" computeOIM(_,_)",
|
|
" Computes the objects interaction matrix for two "
|
|
" spatial objects.",
|
|
" query computeOIM(BGrenzenline, mehringdamm) "
|
|
);
|
|
|
|
/*
|
|
Operator instance
|
|
|
|
*/
|
|
Operator computeOIM(
|
|
"computeOIM",
|
|
computeOIMSpec.getStr(),
|
|
computeOIMVM,
|
|
Operator::SimpleSelect,
|
|
computeOIMTM
|
|
);
|
|
|
|
/*
|
|
Operator collectDline
|
|
|
|
|
|
Type Mapping
|
|
|
|
Signature: stream(point) x bool -> dline
|
|
stream(line) x bool -> dline
|
|
stream(dline) x bool -> dline
|
|
|
|
*/
|
|
ListExpr collectDlineTM(ListExpr args){
|
|
string err = " stream(X) [x bool] expected, x in {point,line,dline) ";
|
|
if(!nl->HasLength(args,1) && !nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(nl->HasLength(args,2) && !CcBool::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr st = nl->First(args);
|
|
if( !Stream<Point>::checkType(st)
|
|
&& !Stream<Line>::checkType(st)
|
|
&& !Stream<DLine>::checkType(st)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(nl->HasLength(args,2)){
|
|
return listutils::basicSymbol<DLine>();
|
|
} else {
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(true)),
|
|
listutils::basicSymbol<DLine>());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
int collectDlineVMpoint(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Stream<Point> stream(args[0]);
|
|
bool stopUndef = false;
|
|
CcBool* ignoreUndef= (CcBool*) args[1].addr;
|
|
stopUndef = ignoreUndef->IsDefined() && !ignoreUndef->GetBoolval();
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
res->clear();
|
|
res->SetDefined(true);
|
|
|
|
stream.open();
|
|
|
|
Point* p = 0;
|
|
Point* lastP = 0;
|
|
|
|
while( (p = stream.request()) ){
|
|
if(!p->IsDefined()){
|
|
if(stopUndef){
|
|
if(lastP){
|
|
lastP->DeleteIfAllowed();
|
|
lastP=0;
|
|
}
|
|
}
|
|
p->DeleteIfAllowed();
|
|
p=0;
|
|
|
|
} else {
|
|
if(lastP){
|
|
SimpleSegment s(lastP->GetX(), lastP->GetY(),
|
|
p->GetX(), p->GetY());
|
|
res->append(s);
|
|
lastP->DeleteIfAllowed();
|
|
}
|
|
lastP = p;
|
|
}
|
|
}
|
|
if(lastP){
|
|
lastP->DeleteIfAllowed();
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
|
|
int collectDlineVMline(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Stream<Line> stream(args[0]);
|
|
bool stopUndef = false;
|
|
CcBool* ignoreUndef= (CcBool*) args[1].addr;
|
|
stopUndef = ignoreUndef->IsDefined() && !ignoreUndef->GetBoolval();
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
res->clear();
|
|
res->SetDefined(true);
|
|
|
|
stream.open();
|
|
Line* line;
|
|
while((line=stream.request())){
|
|
if(!line->IsDefined()){
|
|
if(stopUndef){
|
|
res->clear();
|
|
res->SetDefined(false);
|
|
line->DeleteIfAllowed();
|
|
stream.close();
|
|
return 0;
|
|
} else {
|
|
line->DeleteIfAllowed();
|
|
}
|
|
} else {
|
|
HalfSegment hs;
|
|
for(int i=0;i<line->Size();i++){
|
|
line->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
SimpleSegment s(hs);
|
|
res->append(s);
|
|
}
|
|
}
|
|
line->DeleteIfAllowed();
|
|
}
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
int collectDlineVMdline(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Stream<DLine> stream(args[0]);
|
|
bool stopUndef = false;
|
|
CcBool* ignoreUndef= (CcBool*) args[1].addr;
|
|
stopUndef = ignoreUndef->IsDefined() && !ignoreUndef->GetBoolval();
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
res->clear();
|
|
res->SetDefined(true);
|
|
|
|
stream.open();
|
|
DLine* line;
|
|
while((line=stream.request())){
|
|
if(!line->IsDefined()){
|
|
if(stopUndef){
|
|
res->clear();
|
|
res->SetDefined(false);
|
|
line->DeleteIfAllowed();
|
|
stream.close();
|
|
return 0;
|
|
} else {
|
|
line->DeleteIfAllowed();
|
|
}
|
|
} else {
|
|
SimpleSegment s;
|
|
for(int i=0;i<line->getSize();i++){
|
|
line->get(i,s);
|
|
res->append(s);
|
|
}
|
|
line->DeleteIfAllowed();
|
|
}
|
|
}
|
|
stream.close();
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
ValueMapping Array and Select function
|
|
|
|
*/
|
|
|
|
ValueMapping collectDlineVM[] ={
|
|
collectDlineVMpoint,
|
|
collectDlineVMline,
|
|
collectDlineVMdline,
|
|
};
|
|
|
|
int collectDlineSelect(ListExpr args){
|
|
ListExpr sa = nl->Second(nl->First(args));
|
|
if(Point::checkType(sa)) return 0;
|
|
if(Line::checkType(sa)) return 1;
|
|
if(DLine::checkType(sa)) return 2;
|
|
return -1;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
Specification
|
|
|
|
*/
|
|
OperatorSpec collectDlineSpec (
|
|
" stream(X) x bool -> dline, X in {point,line,dline)",
|
|
" _ collectDline[_]",
|
|
" Collects the stream elements into a single dline value"
|
|
" If the boolean argument is true, undefined value in"
|
|
" the stream are ignored, otherwise the result is undefined if"
|
|
" there is any undefined stream element",
|
|
" query strassen feed projecttransformstream[Geodata] collectDline[TRUE] "
|
|
);
|
|
|
|
/*
|
|
Operator instance
|
|
|
|
*/
|
|
|
|
Operator collectDline(
|
|
"collectDline",
|
|
collectDlineSpec.getStr(),
|
|
3,
|
|
collectDlineVM,
|
|
collectDlineSelect,
|
|
collectDlineTM);
|
|
|
|
|
|
/*
|
|
40 Operator ~computeLabel~
|
|
|
|
40.1 Type Mapping
|
|
|
|
*/
|
|
ListExpr computeLabelTM(ListExpr args){
|
|
string err = "line x string expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( !Line::checkType(nl->First(args))
|
|
|| !CcString::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Label>();
|
|
}
|
|
|
|
|
|
/*
|
|
40.2 Value Mapping
|
|
|
|
*/
|
|
|
|
int computeLabelVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
Line* line = (Line*) args[0].addr;
|
|
CcString* lab = (CcString*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
Label* res = (Label*) result.addr;
|
|
if(!line->IsDefined() || !lab->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
if(line->Size()==0){
|
|
res->set(lab->GetValue(),0,0,0);
|
|
}
|
|
Rectangle<2> r = line->BoundingBox();
|
|
double x = (r.MinD(0) + r.MaxD(0)) / 2;
|
|
double y = (r.MinD(1) + r.MaxD(1)) / 2;
|
|
Point p(true,x,y);
|
|
|
|
HalfSegment hs;
|
|
HalfSegment minHs;
|
|
double distance;
|
|
line->Get(0,hs);
|
|
minHs = hs;
|
|
distance = hs.Distance(p);
|
|
// determine the halfsegment located near of the bbox center
|
|
for(int i=1;i<line->Size();i++){
|
|
line->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
double dist = hs.Distance(p);
|
|
if(dist < distance){
|
|
distance = dist;
|
|
minHs = hs;
|
|
}
|
|
}
|
|
}
|
|
p = minHs.GetLeftPoint();
|
|
Point p2 = minHs.GetRightPoint();
|
|
double dx = p2.GetX() - p.GetX();
|
|
double dy = p2.GetY() - p.GetY();
|
|
double ang;
|
|
if(AlmostEqual(dy,0)){
|
|
ang = 0;
|
|
} else if(AlmostEqual(dx,0)){
|
|
ang = 90;
|
|
} else {
|
|
double len = sqrt(dx*dx+dy*dy);
|
|
ang = acos(dy/len) * 180 / M_PI;
|
|
cout << "Name " << lab->GetValue() << endl;
|
|
cout << "Angle 1 = " << ang << endl;
|
|
ang = ang -90;
|
|
if(ang<-90){
|
|
ang = ang + 180;
|
|
}
|
|
}
|
|
res->set(lab->GetValue(), p.GetX() + dx/3, p.GetY()+dy/3, ang);
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
40.3 Specification
|
|
|
|
*/
|
|
OperatorSpec computeLabelSpec (
|
|
" line x string -> spatiallabel",
|
|
" computeLabel(line, name)",
|
|
" Computes a label for a line",
|
|
" query computeLabel(BGrenzenLine,\"boundary\") "
|
|
);
|
|
|
|
/*
|
|
40.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator computeLabelOP(
|
|
"computeLabel",
|
|
computeLabelSpec.getStr(),
|
|
computeLabelVM,
|
|
Operator::SimpleSelect,
|
|
computeLabelTM
|
|
);
|
|
|
|
/*
|
|
41 Operator centroidDisc
|
|
|
|
*/
|
|
ListExpr centroidDiscTM(ListExpr args){
|
|
string err = "points [x geoid]expected";
|
|
if(!nl->HasLength(args,1) && !nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Points::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(nl->HasLength(args,2) && !Geoid::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Disc>();
|
|
}
|
|
|
|
|
|
int centroidDiscVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Points* arg = (Points*) args[0].addr;
|
|
Disc* res = (Disc*) result.addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
Geoid* geoid = 0;
|
|
if(qp->GetNoSons(s) ==2){
|
|
geoid = (Geoid*) args[1].addr;
|
|
if(!geoid->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
}
|
|
int size = arg->Size();
|
|
if(size==0){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
Point center = arg->theCenter();
|
|
double rad =0;
|
|
Point p(true);
|
|
for(int i=0;i<size;i++){
|
|
arg->Get(i,p);
|
|
double d = center.Distance(p,geoid);
|
|
if(d>rad){
|
|
rad = d;
|
|
}
|
|
}
|
|
res->set(center.GetX(),center.GetY(),rad);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
|
|
41.3 Specification
|
|
|
|
*/
|
|
OperatorSpec centroidDiscSpec (
|
|
" points [x geoid] -> disc",
|
|
" centroidDisc(_,_)",
|
|
" Computes a disc enclosing all points from the argument "
|
|
"with the centroid of the points as center",
|
|
" query centroidDisc(train7stations) "
|
|
);
|
|
|
|
/*
|
|
40.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator centroidDiscOP(
|
|
"centroidDisc",
|
|
centroidDiscSpec.getStr(),
|
|
centroidDiscVM,
|
|
Operator::SimpleSelect,
|
|
centroidDiscTM
|
|
);
|
|
|
|
|
|
/*
|
|
Operator calcDisc
|
|
|
|
*/
|
|
ListExpr calcDiscTM(ListExpr args){
|
|
string err = "points expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!Points::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Disc>();
|
|
}
|
|
|
|
|
|
/*
|
|
Implementation from:
|
|
|
|
Smallest enclosing disks (balls and ellipsoids), Emo Welzl, 1991
|
|
|
|
*/
|
|
Disc getDisc(const vector<Point>& R){
|
|
switch(R.size()){
|
|
case 0 : {Disc d(false); return d;}
|
|
case 1 : {Disc d(R[0]); return d;}
|
|
case 2 : {Disc d(R[0],R[1]);
|
|
return d;
|
|
}
|
|
|
|
case 3 : { double d1 = R[0].Distance(R[1]);
|
|
double d2 = R[0].Distance(R[2]);
|
|
double d3 = R[1].Distance(R[2]);
|
|
Disc d (false);
|
|
if(d1 > d2 && d1 > d3){
|
|
d = Disc(R[0],R[1]);
|
|
} else if(d2>d3){
|
|
d = Disc(R[0],R[2]);
|
|
} else {
|
|
d = Disc(R[1],R[2]);
|
|
}
|
|
if(!d.contains(R[0]) ||
|
|
!d.contains(R[1]) ||
|
|
!d.contains(R[2])){
|
|
d = Disc(R[0],R[1],R[2]);
|
|
}
|
|
return d;
|
|
}
|
|
default: assert(false);
|
|
}
|
|
Disc d(false);
|
|
return d;
|
|
}
|
|
|
|
|
|
Disc getDisc(const Point* R, const int Rsize){
|
|
switch(Rsize){
|
|
case 0 : {Disc d(false); return d;}
|
|
case 1 : {Disc d(R[0]); return d;}
|
|
case 2 : {Disc d(R[0],R[1]);
|
|
return d;
|
|
}
|
|
|
|
case 3 : { double d1 = R[0].Distance(R[1]);
|
|
double d2 = R[0].Distance(R[2]);
|
|
double d3 = R[1].Distance(R[2]);
|
|
Disc d (false);
|
|
if(d1 > d2 && d1 > d3){
|
|
d = Disc(R[0],R[1]);
|
|
} else if(d2>d3){
|
|
d = Disc(R[0],R[2]);
|
|
} else {
|
|
d = Disc(R[1],R[2]);
|
|
}
|
|
if(!d.contains(R[0]) ||
|
|
!d.contains(R[1]) ||
|
|
!d.contains(R[2])){
|
|
d = Disc(R[0],R[1],R[2]);
|
|
}
|
|
return d;
|
|
}
|
|
default: assert(false);
|
|
}
|
|
Disc d(false);
|
|
return d;
|
|
}
|
|
|
|
/** originally, recursive method to compute the smallest enclosing disc **/
|
|
Disc sed(Points& P, int pos, vector<Point> R){
|
|
if(pos==P.Size() || R.size()==3){
|
|
return getDisc(R);
|
|
}
|
|
Point p(true);
|
|
P.Get(pos,p);
|
|
Disc d = sed(P,pos+1,R);
|
|
if(!d.contains(p)){
|
|
R.push_back(p);
|
|
d = sed(P,pos+1,R);
|
|
}
|
|
return d;
|
|
}
|
|
|
|
struct sedEntry{
|
|
|
|
sedEntry(const int _pos,
|
|
const Point _R[3],
|
|
int _Rsize,
|
|
const bool _cond):
|
|
pos(_pos),cond(_cond),Rsize(_Rsize),
|
|
R{_R[0],_R[1],_R[2]}{
|
|
for(int i=0;i<Rsize;i++){
|
|
R[i] = _R[i];
|
|
}
|
|
}
|
|
|
|
void append(const Point& p){
|
|
R[Rsize] = p;
|
|
Rsize++;
|
|
}
|
|
|
|
int pos;
|
|
bool cond;
|
|
int Rsize;
|
|
Point R[3];
|
|
};
|
|
|
|
ostream& operator<<( ostream& o,const sedEntry& e){
|
|
o << e.pos << ", " << e.Rsize;
|
|
return o;
|
|
}
|
|
|
|
|
|
// stack based variant of sed
|
|
Disc sedSt(Points& P){
|
|
int size = P.Size();
|
|
if(size==0){
|
|
return Disc(false);
|
|
}
|
|
|
|
Disc d(false);
|
|
|
|
//stack<sedEntry> st;
|
|
Stack<sedEntry> st;
|
|
Point initial[] = {Point(false),Point(false),Point(false)};
|
|
sedEntry f1(0,initial,3,true);
|
|
f1.Rsize=0;
|
|
st.push(f1);
|
|
sedEntry f2(0,initial,3,false);
|
|
f2.Rsize = 0;
|
|
st.push(f2);
|
|
|
|
int* shuffle = new int[size];
|
|
for(int i=0;i<size;i++){
|
|
shuffle[i] = i;
|
|
}
|
|
for(int i=0;i<size;i++){
|
|
int tmp = shuffle[i];
|
|
int p = rand() % size;
|
|
shuffle[i] = shuffle[p];
|
|
shuffle[p] = tmp;
|
|
}
|
|
|
|
while(!st.isEmpty()){
|
|
const sedEntry e = st.pop();
|
|
if(e.pos==size || e.Rsize==3){
|
|
d = getDisc(e.R,e.Rsize);
|
|
} else {
|
|
if(!e.cond){
|
|
sedEntry e1(e.pos+1,e.R,e.Rsize,true);
|
|
sedEntry e2(e.pos+1,e.R,e.Rsize,false);
|
|
st.push(e1);
|
|
st.push(e2);
|
|
} else {
|
|
int pos = e.pos;
|
|
Point p(true);
|
|
P.Get(shuffle[pos],p);
|
|
if(!d.contains(p)){
|
|
sedEntry e1(e.pos+1,e.R,e.Rsize,true);
|
|
sedEntry e2(e.pos+1,e.R,e.Rsize,false);
|
|
e1.append(p);
|
|
e2.append(p);
|
|
st.push(e1);
|
|
st.push(e2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
delete[] shuffle;
|
|
return d;
|
|
}
|
|
|
|
|
|
|
|
int calcDiscVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
Points* ps = (Points*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
Disc* res = (Disc*) result.addr;
|
|
if(!ps->IsDefined() || ps->IsEmpty()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
Disc d = sedSt(*ps);
|
|
*res = d;
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec calcDiscSpec (
|
|
" points -> disc",
|
|
" calcDisc(_)",
|
|
" Computes a the minimum disc enclosing all points from the argument ",
|
|
" query caclcDisc(train7stations) "
|
|
);
|
|
|
|
/*
|
|
40.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator calcDiscOP(
|
|
"calcDisc",
|
|
calcDiscSpec.getStr(),
|
|
calcDiscVM,
|
|
Operator::SimpleSelect,
|
|
calcDiscTM
|
|
);
|
|
|
|
|
|
|
|
|
|
/*
|
|
42 Operator createDisc
|
|
|
|
*/
|
|
ListExpr createDiscTM(ListExpr args){
|
|
string err = "point [x point [x point]] expected";
|
|
if( !nl->HasLength(args,1) && !nl->HasLength(args,2)
|
|
&& !nl->HasLength(args,3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
while(!nl->IsEmpty(args)){
|
|
if(!Point::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
args = nl->Rest(args);
|
|
}
|
|
return listutils::basicSymbol<Disc>();
|
|
}
|
|
|
|
|
|
int createDiscVM(Word* args, Word& result, int message, Word& local,
|
|
Supplier s ){
|
|
|
|
result = qp->ResultStorage(s);
|
|
Disc* res = (Disc*) result.addr;
|
|
switch(qp->GetNoSons(s)){
|
|
case 1: { Disc d(*((Point*)args[0].addr));
|
|
*res = d;
|
|
return 0;
|
|
}
|
|
case 2: { Disc d( *((Point*)args[0].addr),
|
|
*((Point*)args[1].addr));
|
|
*res = d;
|
|
return 0;
|
|
}
|
|
case 3: { Disc d(*((Point*)args[0].addr),
|
|
*((Point*)args[1].addr),
|
|
*((Point*)args[2].addr));
|
|
*res = d;
|
|
return 0;
|
|
}
|
|
default: assert(false);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec createDiscSpec (
|
|
" point [x point [ x point]] -> disc",
|
|
" createDisc(_,_,_)",
|
|
" Computes a the minimum disc enclosing the arguments ",
|
|
" query createDisc( [const point value (8 7)]) "
|
|
);
|
|
|
|
/*
|
|
40.4 Operator instance
|
|
|
|
*/
|
|
|
|
Operator createDiscOP(
|
|
"createDisc",
|
|
createDiscSpec.getStr(),
|
|
createDiscVM,
|
|
Operator::SimpleSelect,
|
|
createDiscTM
|
|
);
|
|
|
|
/*
|
|
\section{Operator berlin2wgs}
|
|
|
|
Converts a pair of coordinates from bbbike / BerlinMOD format into WGS84
|
|
|
|
\subsection{Type Mapping}
|
|
|
|
*/
|
|
ListExpr berlin2wgsTM(ListExpr args) {
|
|
if (!nl->HasLength(args, 1)) {
|
|
return listutils::typeError("Exactly one argument expected.");
|
|
}
|
|
ListExpr arg = nl->First(args);
|
|
if(Point::checkType(arg) || Points::checkType(arg) ||
|
|
Line::checkType(arg) || Region::checkType(arg) ||
|
|
Rectangle<2>::checkType(arg)){
|
|
return arg;
|
|
}
|
|
return listutils::typeError("Type point, points, line, or region expected.");
|
|
}
|
|
|
|
int berlin2wgsSelect(ListExpr args) {
|
|
ListExpr a1 = nl->First(args);
|
|
if (Point::checkType(a1)) return 0;
|
|
if (Points::checkType(a1)) return 1;
|
|
if (Line::checkType(a1)) return 2;
|
|
if (Region::checkType(a1)) return 3;
|
|
if (Rectangle<2>::checkType(a1)) return 4;
|
|
return -1;
|
|
}
|
|
|
|
template<class T>
|
|
int berlin2wgsVM(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
T* src = (T*)args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
T* res = (T*)result.addr;
|
|
if (src->IsDefined()) {
|
|
Berlin2WGS converter;
|
|
converter.convert(src, res);
|
|
}
|
|
else {
|
|
res->SetDefined(false);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec berlin2wgsSpec(
|
|
" T -> T, where T in {point, points, line, region, rect}",
|
|
" berlin2wgs( _ )",
|
|
" Converts coordinates from bbbike/BerlinMOD format into WGS84 coordinates.",
|
|
" query berlin2wgs([const point value (13132, 10876)])"
|
|
);
|
|
|
|
ValueMapping berlin2wgsVMs[] = {
|
|
berlin2wgsVM<Point>,
|
|
berlin2wgsVM<Points>,
|
|
berlin2wgsVM<Line>,
|
|
berlin2wgsVM<Region>,
|
|
berlin2wgsVM<Rectangle<2> >
|
|
};
|
|
|
|
/*
|
|
\subsection{Operator instance}
|
|
|
|
*/
|
|
Operator berlin2wgs(
|
|
"berlin2wgs",
|
|
berlin2wgsSpec.getStr(),
|
|
4,
|
|
berlin2wgsVMs,
|
|
berlin2wgsSelect,
|
|
berlin2wgsTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
10.132 Operator ~elements~
|
|
|
|
*/
|
|
ListExpr elementsTM(ListExpr args){
|
|
string err="points expected";
|
|
if( !nl->HasLength(args,1) ||
|
|
!Points::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return nl->TwoElemList( listutils::basicSymbol<Stream<Point> >(),
|
|
listutils::basicSymbol<Point>());
|
|
}
|
|
|
|
class elementsInfo{
|
|
public:
|
|
elementsInfo(Points* _ps): ps(_ps), pos(0),max(0),tmp(0,0){
|
|
max = ps->Size();
|
|
}
|
|
|
|
Point* next(){
|
|
if(pos>=max){
|
|
return 0;
|
|
}
|
|
ps->Get(pos,tmp);
|
|
pos++;
|
|
return new Point(tmp);
|
|
}
|
|
|
|
private:
|
|
Points* ps;
|
|
size_t pos;
|
|
size_t max;
|
|
Point tmp;
|
|
};
|
|
|
|
int elementsVM(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
elementsInfo* li = (elementsInfo*) local.addr;
|
|
switch(message){
|
|
case OPEN: {
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
Points* ps = (Points*) args[0].addr;
|
|
if(ps->IsDefined()){
|
|
local.addr = new elementsInfo(ps);
|
|
}
|
|
return 0;
|
|
}
|
|
case REQUEST:
|
|
result.addr=li?li->next():0;
|
|
return result.addr?YIELD:CANCEL;
|
|
case CLOSE :
|
|
if(li){
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
OperatorSpec elementsSpec(
|
|
" points -> stream(point)",
|
|
" elements( _ )",
|
|
" Puts the elements of a points object into a stream.",
|
|
" query elements(train7stations) count"
|
|
);
|
|
|
|
|
|
Operator elementsOP(
|
|
"elements",
|
|
elementsSpec.getStr(),
|
|
elementsVM,
|
|
Operator::SimpleSelect,
|
|
elementsTM
|
|
);
|
|
|
|
|
|
/*
|
|
Operator twist
|
|
|
|
Creates a dline.
|
|
|
|
*/
|
|
ListExpr twistTM(ListExpr args){
|
|
string err = "{real,int} x int x int expected";
|
|
if(!nl->HasLength(args,3)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( ( !CcReal::checkType(nl->First(args))
|
|
&&!CcInt::checkType(nl->First(args)))
|
|
|| !CcInt::checkType(nl->Second(args))
|
|
|| !CcInt::checkType(nl->Third(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<DLine>();
|
|
}
|
|
|
|
template<class S>
|
|
int twistVMT(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
S* size = (S*) args[0].addr;
|
|
CcInt* corners = (CcInt*) args[1].addr;
|
|
CcInt* offset = (CcInt*) args[2].addr;
|
|
|
|
if(!size->IsDefined() || !corners->IsDefined() || !offset->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double si = size->GetValue();
|
|
int c = corners->GetValue();
|
|
int o = offset->GetValue();
|
|
if(si<=0 || c<1 || o % c == 0){
|
|
res->SetDefined(false);
|
|
}
|
|
res->clear();
|
|
int i = 0;
|
|
double a = 360.0 / c;
|
|
a = (a * 3.14159265359) / 180;
|
|
double x1 = si * sin((((i*o)%c)*a));
|
|
double y1 = si * cos((((i*o)%c)*a));
|
|
i++;
|
|
while(i <= c){
|
|
double x2 = si * sin((((i*o)%c)*a));
|
|
double y2 = si * cos((((i*o)%c*a)));
|
|
res->append(SimpleSegment(x1,y1,x2,y2));
|
|
x1 = x2;
|
|
y1 = y2;
|
|
i++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping twistVM[] = {
|
|
twistVMT<CcInt>,
|
|
twistVMT<CcReal>
|
|
};
|
|
|
|
int twistSelect(ListExpr args){
|
|
return CcInt::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
|
|
OperatorSpec twistSpec(
|
|
" {real,int} x int x int -> dline",
|
|
" twist(size, corners, offset)",
|
|
" Creates a nice dline. ",
|
|
" query twist(40.0,9,4)"
|
|
);
|
|
|
|
Operator twistOp(
|
|
"twist",
|
|
twistSpec.getStr(),
|
|
2,
|
|
twistVM,
|
|
twistSelect,
|
|
twistTM
|
|
);
|
|
|
|
|
|
/*
|
|
Operator creating the contour of a dline value.
|
|
|
|
|
|
*/
|
|
|
|
enum join_style{join_none,join_bevel, join_miter};
|
|
|
|
class Contour2{
|
|
|
|
public:
|
|
Contour2(DLine* line, double w, join_style _join, bool _closed ){
|
|
plpos = 0;
|
|
clpos = 0;
|
|
closed = _closed;
|
|
width=w;
|
|
join = _join;
|
|
maxMiter = 3*w;
|
|
fillPolylines(line);
|
|
}
|
|
|
|
|
|
Contour2(Line* line, double w, join_style _join, bool _closed ){
|
|
plpos = 0;
|
|
clpos = 0;
|
|
closed = _closed;
|
|
width=w;
|
|
join = _join;
|
|
maxMiter = 3*w;
|
|
fillPolylines(line);
|
|
}
|
|
|
|
// by contruction, the interior of the region
|
|
// is always left to the directed segments
|
|
vector<SimplePoint>* next(){
|
|
while(plpos<polylines.size()){
|
|
vector<SimplePoint>& pl = polylines[plpos];
|
|
size_t end = pl.size()-1;
|
|
if(clpos==end){ // start new polyline
|
|
plpos++;
|
|
clpos=0;
|
|
} else {
|
|
bool left_end = !closed && (clpos==0);
|
|
bool right_end = !closed && (clpos==end-1);
|
|
int o = closed?(clpos==end-1?1:0):0;
|
|
vector<SimplePoint>* res = getContour(pl[clpos],
|
|
pl[(clpos+1) % pl.size()],
|
|
pl[(clpos+2+o) % pl.size()],
|
|
left_end, right_end);
|
|
clpos++;
|
|
if(res){
|
|
return res;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
vector<vector<SimplePoint> > polylines;
|
|
size_t plpos; // position in polylines
|
|
size_t clpos; // position in current polyline
|
|
bool closed;
|
|
double width;
|
|
join_style join;
|
|
double maxMiter;
|
|
|
|
enum direction{left, right, forward};
|
|
|
|
|
|
void fillPolylines(DLine* line){
|
|
SimpleSegment s;
|
|
for(int i=0;i<line->Size();i++){
|
|
line->get(i,s);
|
|
addSegment(s);
|
|
}
|
|
if( (polylines.size()>0) && closed
|
|
&& (polylines.back()[0] != polylines.back().back())){
|
|
polylines.back().push_back(polylines.back()[0]);
|
|
}
|
|
}
|
|
|
|
|
|
void fillPolylines(const Line* line){
|
|
int size = line->Size();
|
|
bool* used = new bool[size];
|
|
memset(used,0, size*sizeof(bool));
|
|
for(int i=0;i< size;i++){
|
|
if(!used[i]){
|
|
HalfSegment hs;
|
|
line->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
addPolyLine(line, i, used);
|
|
}
|
|
}
|
|
}
|
|
delete[] used;
|
|
}
|
|
|
|
void addPolyLine(const Line* line, int pos, bool*& used){
|
|
assert(!used[pos]);
|
|
HalfSegment hs;
|
|
line->Get(pos,hs);
|
|
assert(hs.IsLeftDomPoint());
|
|
used[pos] = true;
|
|
vector<SimplePoint> pl;
|
|
SimplePoint p1(hs.GetDomPoint().GetX(), hs.GetDomPoint().GetY());
|
|
SimplePoint p2(hs.GetSecPoint().GetX(), hs.GetSecPoint().GetY());
|
|
pl.push_back(p1);
|
|
pl.push_back(p2);
|
|
// extend polyline so far as possible
|
|
int pn = hs.attr.partnerno;
|
|
used[pn] = true;
|
|
line->Get(pn,hs);
|
|
int next;
|
|
while( (next = getExtension(line, pn, used, hs)) >=0){
|
|
SimplePoint p(hs.GetSecPoint().GetX(), hs.GetSecPoint().GetY());
|
|
pl.push_back(p);
|
|
used[pn] = true;
|
|
pn = hs.attr.partnerno;
|
|
used[pn] = true;
|
|
line->Get(pn,hs);
|
|
}
|
|
|
|
if(closed && pl[0] != pl.back() && pl.size()>1){
|
|
pl.push_back(pl[0]);
|
|
}
|
|
if(pl.size()>1){
|
|
polylines.push_back(pl);
|
|
}
|
|
|
|
}
|
|
|
|
int getExtension(const Line* line, int &partner,
|
|
bool* used, HalfSegment& hs){
|
|
// extract the halfsegment
|
|
line->Get(partner, hs);
|
|
Point p(hs.GetDomPoint());
|
|
int po = partner;
|
|
// search at the left side for extension
|
|
partner--;
|
|
while( partner >= 0){
|
|
line->Get(partner,hs);
|
|
Point p2(hs.GetDomPoint());
|
|
if(!AlmostEqual(p,p2)){
|
|
partner = -1; // break
|
|
} else {
|
|
if(!used[partner]){ // extension foundA
|
|
return partner;
|
|
}
|
|
}
|
|
partner--;
|
|
}
|
|
// nothing found left, search right
|
|
partner = po;
|
|
partner++;
|
|
while(partner < line->Size()){
|
|
line->Get(partner,hs);
|
|
Point p2(hs.GetDomPoint());
|
|
if(!AlmostEqual(p,p2)){
|
|
partner = line->Size(); // break
|
|
} else {
|
|
if(!used[partner]){ // extension found
|
|
return partner;
|
|
}
|
|
}
|
|
partner ++;
|
|
}
|
|
return -1; // no extension found
|
|
}
|
|
|
|
void addSegment(SimpleSegment& s){
|
|
if(polylines.empty()){
|
|
addFreshVector(s);
|
|
} else {
|
|
if(SimplePoint(s.x1,s.y1) != getLastPoint()){
|
|
if(closed&&(polylines.back()[0] != polylines.back().back())){
|
|
polylines.back().push_back(polylines.back()[0]);
|
|
}
|
|
addFreshVector(s);
|
|
} else {
|
|
polylines.back().push_back(SimplePoint(s.x2,s.y2));
|
|
}
|
|
}
|
|
}
|
|
|
|
void addFreshVector(SimpleSegment& s){
|
|
vector<SimplePoint> v;
|
|
v.push_back(SimplePoint(s.x1,s.y1));
|
|
v.push_back(SimplePoint(s.x2,s.y2));
|
|
polylines.push_back(v);
|
|
}
|
|
|
|
SimplePoint& getLastPoint(){
|
|
return polylines.back().back();
|
|
}
|
|
|
|
vector<SimplePoint>* getContour(SimplePoint& p1, SimplePoint& p2,
|
|
SimplePoint& p3, bool left_end, bool right_end){
|
|
|
|
if(p1==p2){
|
|
return 0;
|
|
}
|
|
|
|
vector<SimplePoint>* r = getRectangle(p1,p2);
|
|
if((join==join_none) || right_end || (p2==p3) ){
|
|
return r;
|
|
}
|
|
direction dir = getDirection(p1,p2,p3);
|
|
if(dir==forward){
|
|
return r;
|
|
}
|
|
vector<SimplePoint>* r2 = getRectangle(p2,p3);
|
|
|
|
if(join==join_bevel){
|
|
if(dir==right){
|
|
vector<SimplePoint>* res = new vector<SimplePoint>();
|
|
res->push_back(r->at(0));
|
|
res->push_back(r->at(1));
|
|
res->push_back(p2);
|
|
res->push_back(r2->at(3));
|
|
res->push_back(r->at(2));
|
|
res->push_back(r->at(3));
|
|
delete r;
|
|
delete r2;
|
|
return res;
|
|
} else {
|
|
vector<SimplePoint>* res = new vector<SimplePoint>();
|
|
res->push_back(r->at(0));
|
|
res->push_back(r->at(1));
|
|
res->push_back(r2->at(0));
|
|
res->push_back(p2);
|
|
res->push_back(r->at(2));
|
|
res->push_back(r->at(3));
|
|
delete r;
|
|
delete r2;
|
|
return res;
|
|
}
|
|
}
|
|
if(join == join_miter){
|
|
if(dir==right){
|
|
SimplePoint ip = intersectionPoint(r->at(2), r->at(3),
|
|
r2->at(2), r2->at(3));
|
|
if(dist(p2,ip) > maxMiter){
|
|
setDist(p2,ip,maxMiter);
|
|
}
|
|
vector<SimplePoint>* res = new vector<SimplePoint>();
|
|
res->push_back(r->at(0));
|
|
res->push_back(r->at(1));
|
|
res->push_back(p2);
|
|
res->push_back(r2->at(3));
|
|
res->push_back(ip);
|
|
res->push_back(r->at(2));
|
|
res->push_back(r->at(3));
|
|
delete r;
|
|
delete r2;
|
|
return res;
|
|
} else {
|
|
SimplePoint ip = intersectionPoint(r->at(0), r->at(1),
|
|
r2->at(0), r2->at(1));
|
|
if(dist(p2,ip) > maxMiter){
|
|
setDist(p2,ip,maxMiter);
|
|
}
|
|
vector<SimplePoint>* res = new vector<SimplePoint>();
|
|
res->push_back(r->at(0));
|
|
res->push_back(r->at(1));
|
|
res->push_back(ip);
|
|
res->push_back(r2->at(0));
|
|
res->push_back(p2);
|
|
res->push_back(r->at(2));
|
|
res->push_back(r->at(3));
|
|
delete r;
|
|
delete r2;
|
|
return res;
|
|
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
vector<SimplePoint>* getRectangle(SimplePoint& p1,SimplePoint& p2){
|
|
double x1 = p1.getX();
|
|
double y1 = p1.getY();
|
|
double x2 = p2.getX();
|
|
double y2 = p2.getY();
|
|
double dx = x2 - x1;
|
|
double dy = y2 - y1;
|
|
double length = sqrt(dx*dx + dy*dy);
|
|
double f = width / (2*length);
|
|
dx = dx*f;
|
|
dy = dy*f;
|
|
vector<SimplePoint>* res = new vector<SimplePoint>();
|
|
res->push_back(SimplePoint(x1 + dy, y1 - dx));
|
|
res->push_back(SimplePoint(x2 + dy, y2 - dx));
|
|
res->push_back(SimplePoint(x2 - dy, y2 + dx));
|
|
res->push_back(SimplePoint(x1 - dy, y1 + dx));
|
|
return res;
|
|
}
|
|
|
|
static direction getDirection(SimplePoint& point1,
|
|
SimplePoint& point2,
|
|
SimplePoint& point3){
|
|
double p1 = point1.getX();
|
|
double p2 = point1.getY();
|
|
double q1 = point2.getX();
|
|
double q2 = point2.getY();
|
|
double r1 = point3.getX();
|
|
double r2 = point3.getY();
|
|
// directed triangle area
|
|
double A = (r1-p1)*(r2+p2) + (q1-r1)*(q2+r2) + (p1-q1)*(p2+q2);
|
|
if(AlmostEqual(A,0)){
|
|
return forward;
|
|
}
|
|
return A>0?left: right;
|
|
}
|
|
|
|
static SimplePoint intersectionPoint(SimplePoint& p1, SimplePoint& p2,
|
|
SimplePoint& p3, SimplePoint& p4){
|
|
double u = p2.getX()- p1.getX();
|
|
double v = p3.getX() - p4.getX();
|
|
double w = p1.getX() - p3.getX();
|
|
double x = p2.getY() - p1.getY();
|
|
double y = p3.getY() - p4.getY();
|
|
double z = p1.getY() - p3.getY();
|
|
double k = y*u-v*x;
|
|
if(k==0){ // segments are parallel
|
|
assert(false);
|
|
}
|
|
double delta2 = (w*x-z*u) / k;
|
|
double delta1;
|
|
if(abs(u) > abs(x)){
|
|
delta1 = -1*((w+delta2*v)/u);
|
|
} else {
|
|
delta1 = -1*((z+delta2*y)/x);
|
|
}
|
|
double xp = p1.getX() + delta1*(p2.getX()-p1.getX());
|
|
double yp = p1.getY() + delta1*(p2.getY() - p1.getY());
|
|
// just for check
|
|
return SimplePoint(xp,yp);
|
|
}
|
|
|
|
void setDist(const SimplePoint& p1, SimplePoint& p2, double dist){
|
|
double dx = p2.getX() - p1.getX();
|
|
double dy = p2.getY() - p1.getY();
|
|
double l = sqrt(dx*dx + dy*dy);
|
|
double delta = dist/l;
|
|
p2.setX(p1.getX() + delta*dx);
|
|
p2.setY(p1.getY() + delta*dy);
|
|
}
|
|
double dist(const SimplePoint& p1, const SimplePoint& p2){
|
|
double dx = p2.getX() - p1.getX();
|
|
double dy = p2.getY() - p2.getY();
|
|
return sqrt(dx*dx + dy*dy);
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
ListExpr contour2TM(ListExpr le){
|
|
// to be extended by further arguments
|
|
// currently only dline and width
|
|
string err = "dline x real x int x bool expected";
|
|
if(!nl->HasLength(le,4)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( (!DLine::checkType(nl->First(le)) && !Line::checkType(nl->First(le)))
|
|
||( !CcReal::checkType(nl->Second(le))
|
|
&& !CcInt::checkType(nl->Second(le)))
|
|
||!CcInt::checkType(nl->Third(le))
|
|
||!CcBool::checkType(nl->Fourth(le))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<Region>();
|
|
}
|
|
|
|
Region* buildRegion(vector<SimplePoint>& pl){
|
|
Region* res = new Region(pl.size()*2);
|
|
res->SetDefined(true);
|
|
if(pl.size()<3){
|
|
assert(false);
|
|
}
|
|
res->StartBulkLoad();
|
|
for(size_t i= 0; i<pl.size();i++){
|
|
Point p1 = pl[i].getPoint();
|
|
Point p2 = pl[(i+1) % pl.size()].getPoint();
|
|
HalfSegment hs(true, p1,p2);
|
|
hs.attr.faceno =0;
|
|
hs.attr.cycleno=0;
|
|
hs.attr.edgeno = i;
|
|
hs.attr.coverageno = 0;
|
|
hs.attr.partnerno = i;
|
|
hs.attr.insideAbove = p1 < p2;
|
|
(*res) += hs;
|
|
hs.SetLeftDomPoint(false);
|
|
(*res) += hs;
|
|
}
|
|
res->EndBulkLoad(true,true,true,false);
|
|
return res;
|
|
}
|
|
|
|
|
|
template<class Li, class Wi>
|
|
int contour2VMT(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
Region* res = (Region*) result.addr;
|
|
res->Clear();
|
|
Li* L = (Li*) args[0].addr;
|
|
Wi* W = (Wi*) args[1].addr;
|
|
CcInt* J = (CcInt*) args[2].addr;
|
|
CcBool* C = (CcBool*) args[3].addr;
|
|
|
|
if(!L->IsDefined() || !W->IsDefined() || !J->IsDefined() || !C->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double w = W->GetValue();
|
|
if(w<=0){
|
|
res->Clear();
|
|
return 0;
|
|
}
|
|
vector<SimplePoint>* poly;
|
|
join_style join = join_none;
|
|
switch(J->GetValue()){
|
|
case 0 : join = join_none; break;
|
|
case 1 : join = join_bevel; break;
|
|
case 2 : join = join_miter; break;
|
|
}
|
|
|
|
Contour2 Co(L,w, join, C->GetValue());
|
|
|
|
res->SetDefined(true);
|
|
|
|
stack<pair<Region*,int> > st;
|
|
while( (poly= Co.next())){
|
|
Region* reg = buildRegion(*poly);
|
|
delete poly;
|
|
pair<Region*, int> p(reg,1);
|
|
while(!st.empty() &&st.top().second == p.second){
|
|
pair<Region*,int> p2 = st.top();
|
|
st.pop();
|
|
Region* res = new Region(0);
|
|
p.first->Union(*p2.first, *res);
|
|
delete p.first;
|
|
delete p2.first;
|
|
p.first=res;
|
|
p.second++;
|
|
}
|
|
st.push(p);
|
|
}
|
|
// now, we have to build the union of the stack elements
|
|
// in a linear way
|
|
Region* reg1=0;
|
|
Region* reg2=0;
|
|
while(!st.empty()){
|
|
pair<Region*, int> t = st.top();
|
|
st.pop();
|
|
if(reg1==0){
|
|
reg1 = t.first;
|
|
} else {
|
|
if(!reg2){
|
|
reg2 = new Region(0);
|
|
}
|
|
reg1->Union(*t.first, *reg2);
|
|
delete t.first;
|
|
swap(reg1,reg2);
|
|
}
|
|
}
|
|
if(reg1){
|
|
res->CopyFrom(reg1);
|
|
delete reg1;
|
|
}
|
|
if(reg2){
|
|
delete reg2;
|
|
}
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
OperatorSpec contour2Spec(
|
|
"{dline,line} x {real,int} x int x bool -> region",
|
|
"_ contour2 [_,_,_] ",
|
|
"Adds a buffer around the segments of a dline."
|
|
"The second argument specifies the half width of the buffer."
|
|
"The third argument specifies the join style, currently implemented "
|
|
"styles are: 1 : bevel join, 1 : miter join. All other number will "
|
|
"lead to no join. The boolean argument specifies the closeness of the "
|
|
"result." ,
|
|
" query sl contour2[3.0]"
|
|
);
|
|
|
|
|
|
ValueMapping contour2VM[] = {
|
|
contour2VMT<DLine, CcInt>,
|
|
contour2VMT<DLine, CcReal>,
|
|
contour2VMT<Line, CcInt>,
|
|
contour2VMT<Line, CcReal>
|
|
};
|
|
|
|
int contour2Select(ListExpr args){
|
|
int n1 = DLine::checkType(nl->First(args))?0:2;
|
|
int n2 = CcInt::checkType(nl->Second(args))?0:1;
|
|
return n1+n2;
|
|
}
|
|
|
|
|
|
Operator contour2Op(
|
|
"contour2",
|
|
contour2Spec.getStr(),
|
|
4,
|
|
contour2VM,
|
|
contour2Select,
|
|
contour2TM
|
|
);
|
|
|
|
|
|
/*
|
|
|
|
Operator twist2
|
|
|
|
*/
|
|
ListExpr twist2TM(ListExpr args){
|
|
// 1st arg: dline providing the segments
|
|
// 2nd arg: int, how often each segment is to divide
|
|
// 3th arg: bool, close, connect the last with the first segment
|
|
string err="dline x int x bool [x bool] expected";
|
|
if(!nl->HasLength(args,3) && !nl->HasLength(args,4)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( !DLine::checkType(nl->First(args))
|
|
|| !CcInt::checkType(nl->Second(args))
|
|
|| !CcBool::checkType(nl->Third(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(nl->HasLength(args,4)){
|
|
if(!CcBool::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<DLine>();
|
|
}
|
|
|
|
return nl->ThreeElemList(
|
|
nl->SymbolAtom(Symbols::APPEND()),
|
|
nl->OneElemList(nl->BoolAtom(false)),
|
|
listutils::basicSymbol<DLine>());
|
|
}
|
|
|
|
|
|
void insertTwist(const SimpleSegment& s1, const SimpleSegment& s2,
|
|
int num, DLine* res){
|
|
|
|
double s1_x = s1.x1;
|
|
double s1_y = s1.y1;
|
|
|
|
double dx1 = s1.x2 - s1_x;
|
|
double dy1 = s1.y2 - s1_y;
|
|
|
|
double s2_x = s2.x1;
|
|
double s2_y = s2.y1;
|
|
|
|
double dx2 = s2.x2 - s2_x;
|
|
double dy2 = s2.y2 - s2_y;
|
|
|
|
for(int i=0; i<=num;i++){
|
|
SimplePoint p1( s1_x + (i*dx1) / num, s1_y + (i*dy1)/num);
|
|
SimplePoint p2( s2_x + (i*dx2) / num, s2_y + (i*dy2)/num);
|
|
if(p1!=p2){
|
|
res->append(SimpleSegment(p1.getX(), p1.getY(),p2.getX(), p2.getY()));
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
vector<SimplePoint> computeSLTwist(vector<SimpleSegment>& segments, int num){
|
|
|
|
vector<SimplePoint> res;
|
|
for(int i=0; i<=num; i++){
|
|
for(size_t sn=0; sn<segments.size();sn++){
|
|
SimpleSegment s = segments[sn];
|
|
double x = s.x1 + i*(s.x2-s.x1)/num;
|
|
double y = s.y1 + i*(s.y2-s.y1)/num;
|
|
res.push_back(SimplePoint(x,y));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
int twist2VM(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
DLine* line = (DLine*) args[0].addr;
|
|
CcInt* num = (CcInt*) args[1].addr;
|
|
CcBool* closed = (CcBool*) args[2].addr;
|
|
CcBool* singleLine = (CcBool*) args[3].addr;
|
|
res->clear();
|
|
// undefined argument
|
|
if( !line->IsDefined() || !num->IsDefined() || !closed->IsDefined()
|
|
|| !singleLine->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
// invalid sized
|
|
if(line->getSize()<2 || num->GetValue()<2){
|
|
res->clear();
|
|
return 0;
|
|
}
|
|
|
|
if(!singleLine->GetValue()){
|
|
// simple lines
|
|
int e = closed->GetValue()?line->getSize():line->getSize()-1;
|
|
res->resize(num->GetValue()*e);
|
|
SimpleSegment s1;
|
|
SimpleSegment s2;
|
|
for(int i=0;i<e;i++){
|
|
line->get(i,s1);
|
|
line->get((i+1)%line->Size(), s2);
|
|
insertTwist(s1,s2,num->GetValue(),res);
|
|
}
|
|
return 0;
|
|
} else {
|
|
vector<SimpleSegment> v;
|
|
for(int i=0;i<line->getSize(); i++){
|
|
SimpleSegment s;
|
|
line->get(i,s);
|
|
v.push_back(s);
|
|
}
|
|
vector<SimplePoint> pl = computeSLTwist(v,num->GetValue());
|
|
res->clear();
|
|
if(pl.size()<2){
|
|
return 0;
|
|
}
|
|
for(size_t i=0;i<pl.size()-1; i++){
|
|
res->append(SimpleSegment(pl[i].getX(), pl[i].getY(),
|
|
pl[i+1].getX(), pl[i+1].getY()));
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
OperatorSpec twist2Spec(
|
|
"dline x int x bool [x bool]",
|
|
"twist2(line,num,closed[,singleLine)) ",
|
|
"Connects consecutive segments in the dline by a set of segments."
|
|
"Each segment of the dline is divided into num parts."
|
|
"If the closed argument is set to true, also the last segment of "
|
|
"the dline is connected with the first one. If the singleLine argument "
|
|
"is true, the resulting dline will consist of a single polyline. This "
|
|
"implies also the closed shape independly of the given value. ",
|
|
"query twist2(dl, 10, TRUE)"
|
|
);
|
|
|
|
|
|
Operator twist2Op(
|
|
"twist2",
|
|
twist2Spec.getStr(),
|
|
twist2VM,
|
|
Operator::SimpleSelect,
|
|
twist2TM
|
|
);
|
|
|
|
|
|
/*
|
|
twist 3
|
|
|
|
*/
|
|
ListExpr twist3TM(ListExpr args){
|
|
string err= "dline x double expected";
|
|
if(!nl->HasLength(args,2)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!DLine::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
if( !CcReal::checkType(nl->Second(args))
|
|
&& !CcInt::checkType(nl->Second(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<DLine>();
|
|
|
|
}
|
|
|
|
template<class D>
|
|
int twist3VMT(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
DLine* line = (DLine*) args[0].addr;
|
|
D* dist = (D*) args[1].addr;
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
|
|
if(!line->IsDefined() || !dist->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->clear();
|
|
queue<SimpleSegment> q;
|
|
for(int i=0;i<line->Size(); i++){
|
|
SimpleSegment s;
|
|
line->get(i,s);
|
|
res->append(s);
|
|
q.push(s);
|
|
}
|
|
double d = dist->GetValue();
|
|
if(q.size()<2 || d<=0){
|
|
return 0;
|
|
}
|
|
SimpleSegment first = q.front();
|
|
SimplePoint cp(first.x1,first.y1);
|
|
q.pop();
|
|
first = q.front();
|
|
|
|
double length;
|
|
while( (length=first.length()) > d){
|
|
double delta = d / length;
|
|
SimplePoint np ( first.x1 + delta * (first.x2-first.x1),
|
|
first.y1 + delta * (first.y2-first.y1));
|
|
SimpleSegment s( cp.getX(), cp.getY(), np.getX(), np.getY());
|
|
res->append(s);
|
|
q.push(s);
|
|
cp = np;
|
|
q.pop();
|
|
first = q.front();
|
|
}
|
|
while(!q.empty()){
|
|
res->append(q.front());
|
|
q.pop();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping twist3VM[] = {
|
|
twist3VMT<CcInt>,
|
|
twist3VMT<CcReal>
|
|
};
|
|
|
|
int twist3Select(ListExpr args){
|
|
return CcInt::checkType(nl->Second(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec twist3Spec(
|
|
"dline x double -> dline",
|
|
"twist3(line,offset) ",
|
|
"produces interesting patterns",
|
|
"query twist2(dl, 10.0)"
|
|
);
|
|
|
|
Operator twist3Op(
|
|
"twist3",
|
|
twist3Spec.getStr(),
|
|
2,
|
|
twist3VM,
|
|
twist3Select,
|
|
twist3TM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
Operator toSVG
|
|
|
|
*/
|
|
ListExpr toSVGTM(ListExpr args){
|
|
string err="dline expected";
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError(err);
|
|
}
|
|
if(!DLine::checkType(nl->First(args))){
|
|
return listutils::typeError(err);
|
|
}
|
|
return listutils::basicSymbol<FText>();
|
|
}
|
|
|
|
vector<vector<SimplePoint> > findPolylines(vector<SimpleSegment>& segments){
|
|
// todo find non-simple extensions
|
|
vector<vector<SimplePoint> > res;
|
|
for(size_t i=0;i<segments.size(); i++){
|
|
if(i==0 || res.back().back()!=SimplePoint(segments[i].x1, segments[i].y1)){
|
|
vector<SimplePoint> v;
|
|
v.push_back(SimplePoint(segments[i].x1, segments[i].y1));
|
|
v.push_back(SimplePoint(segments[i].x2, segments[i].y2));
|
|
res.push_back(v);
|
|
} else {
|
|
res.back().push_back(SimplePoint(segments[i].x2, segments[i].y2));
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
string toSVG(vector<SimpleSegment>& segments, double w, double h){
|
|
vector<vector<SimplePoint> > pls = findPolylines(segments);
|
|
stringstream ss;
|
|
ss << "<?xml version=\"1.0\"?>" << endl;
|
|
ss << "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" "
|
|
<< "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" << endl;
|
|
ss << "<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" ";
|
|
ss << "width=\""<< w <<"\" height=\""<<h<<"\"";
|
|
ss << ">" << endl;
|
|
for(size_t i=0;i<pls.size();i++){
|
|
ss << "<path style=\"fill:none;stroke:black;stroke-linejoin=bevel\" d=\"";
|
|
for(size_t p=0;p<pls[i].size();p++){
|
|
ss << (p==0?"M":"L");
|
|
ss << pls[i][p].getX() << " " << pls[i][p].getY() << " ";
|
|
}
|
|
ss << "\" />" << endl;
|
|
}
|
|
ss << "</svg>" << endl;
|
|
return ss.str();
|
|
}
|
|
|
|
vector<SimpleSegment> getSVGSegments(DLine* line,double&w, double& h ){
|
|
|
|
vector<SimpleSegment> res;
|
|
double x1=0;
|
|
double x2=0;
|
|
double y1=0;
|
|
double y2=0;
|
|
for(int i=0;i<line->Size();i++){
|
|
SimpleSegment s;
|
|
line->get(i,s);
|
|
if(i==0){
|
|
x1 = min(s.x1,s.x2);
|
|
x2 = max(s.x1,s.x2);
|
|
y1 = min(s.y1,s.y2);
|
|
y2 = max(s.y1,s.y2);
|
|
}else {
|
|
x1 = min(x1,min(s.x1,s.x2));
|
|
x2 = max(x2,max(s.x1,s.x2));
|
|
y1 = min(y1,min(s.y1,s.y2));
|
|
y2 = max(y2,max(s.y1,s.y2));
|
|
}
|
|
res.push_back(s);
|
|
}
|
|
// move box to (0,0) and mirror the y-direction
|
|
w = x2-x1;
|
|
h = y2-y1;
|
|
if(x1!=0 || y1!=0){
|
|
for(size_t i=0;i<res.size();i++){
|
|
SimpleSegment& s = res[i];
|
|
s.x1 = s.x1 - x1;
|
|
s.x2 = s.x2 - x1;
|
|
s.y1 = h - (s.y1 - y1);
|
|
s.y2 = h - (s.y2 - y1);
|
|
}
|
|
}
|
|
if(w==0) w=1;
|
|
if(h==0) h=1;
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
int toSVGVM(Word* args, Word& result, int message, Word& local,Supplier s){
|
|
DLine* line = (DLine*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
FText* res = (FText*) result.addr;
|
|
if(!line->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
double w;
|
|
double h;
|
|
vector<SimpleSegment> segs = getSVGSegments(line,w , h);
|
|
res->Set(true, toSVG(segs,w,h));
|
|
return 0;
|
|
}
|
|
|
|
|
|
OperatorSpec toSVGSpec(
|
|
"dline -> text",
|
|
"toSVG(_)",
|
|
"converts a dline into an svg representation",
|
|
"query toSVG([const dline value ((0 0 10 10))] )"
|
|
);
|
|
|
|
|
|
Operator toSVGOp(
|
|
"toSVG",
|
|
toSVGSpec.getStr(),
|
|
toSVGVM,
|
|
Operator::SimpleSelect,
|
|
toSVGTM
|
|
);
|
|
|
|
|
|
/*
|
|
|
|
Operator ~simpleProject~
|
|
|
|
This operator projects spatial objects using a very simple
|
|
projection method. Its just to test whether it works.
|
|
|
|
*/
|
|
ListExpr simpleProjectTM(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("wrong number of arguments (expected one)");
|
|
}
|
|
ListExpr a = nl->First(args);
|
|
|
|
if(Point::checkType(a)) { return a;}
|
|
if(Points::checkType(a)) { return a;}
|
|
if(DLine::checkType(a)) { return a;}
|
|
|
|
return listutils::typeError("unsupprted Type");
|
|
|
|
|
|
}
|
|
static double earthradius = 6378137.0; // eath radius in meters
|
|
static double earthperimeter = 2.0*M_PI*earthradius;
|
|
static double earthfactor = earthperimeter/360;
|
|
|
|
|
|
bool isGeo(Rectangle<2>& r){
|
|
if(r.MinD(0) < -180) return false;
|
|
if(r.MaxD(0) > 180) return false;
|
|
if(r.MinD(1) < -90) return false;
|
|
if(r.MaxD(1) > 90) return false;
|
|
return true;
|
|
}
|
|
|
|
|
|
void simpleProjectFun( double& x, double& y){
|
|
x = x * cos((M_PI*y)/180) * earthfactor;
|
|
y = y * earthfactor;
|
|
}
|
|
|
|
void simpleProjectFun(Point* a, Point* r){
|
|
double x = a->GetX();
|
|
double y = a->GetY();
|
|
simpleProjectFun(x,y);
|
|
r->Set(true,x,y);
|
|
}
|
|
|
|
|
|
void simpleProjectFun(Points* a, Points* r){
|
|
Point pa(true,0,0);
|
|
Point pr(true,0,0);
|
|
r->StartBulkLoad();
|
|
for(int i=0;i<a->Size();i++){
|
|
a->Get(i,pa);
|
|
simpleProjectFun(&pa,&pr);
|
|
(*r) += pr;
|
|
}
|
|
r->EndBulkLoad(true,false,true);
|
|
}
|
|
|
|
|
|
void simpleProjectFun(SimpleSegment* sa, SimpleSegment* sr){
|
|
|
|
*sr = *sa;
|
|
simpleProjectFun(sr->x1,sr->y1);
|
|
simpleProjectFun(sr->x2,sr->y2);
|
|
}
|
|
|
|
void simpleProjectFun(DLine* a, DLine* r){
|
|
SimpleSegment sa;
|
|
SimpleSegment sr;
|
|
for(int i=0;i<a->Size();i++){
|
|
a->get(i,sa);
|
|
simpleProjectFun(&sa,&sr);
|
|
r->append(sr);
|
|
}
|
|
}
|
|
|
|
template<class A>
|
|
int simpleProjectVMT(Word* args, Word& result, int message,
|
|
Word& local,Supplier s){
|
|
A* arg = (A*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
A* res = (A*) result.addr;
|
|
if(!arg->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->Clear();
|
|
Rectangle<2> bbox = arg->BoundingBox();
|
|
if(!bbox.IsDefined()){
|
|
// original object was empty
|
|
return 0;
|
|
}
|
|
if(!isGeo(bbox)){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
simpleProjectFun(arg,res);
|
|
return 0;
|
|
}
|
|
|
|
|
|
ValueMapping simpleProjectVM[] = {
|
|
simpleProjectVMT<Point>,
|
|
simpleProjectVMT<Points>,
|
|
simpleProjectVMT<DLine>
|
|
};
|
|
|
|
int simpleProjectSelect(ListExpr args){
|
|
ListExpr a = nl->First(args);
|
|
if(Point::checkType(a)) return 0;
|
|
if(Points::checkType(a)) return 1;
|
|
if(DLine::checkType(a)) return 2;
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec simpleProjectSpec(
|
|
"SPATIAL -> SPATIAL",
|
|
"simpleProject(_)",
|
|
"Performs a simple projection to spatial objects",
|
|
" query simpleProjection([const point value (7.475 ,51.359444)])"
|
|
);
|
|
|
|
Operator simpleProject(
|
|
"simpleProject",
|
|
simpleProjectSpec.getStr(),
|
|
3,
|
|
simpleProjectVM,
|
|
simpleProjectSelect,
|
|
simpleProjectTM
|
|
);
|
|
|
|
|
|
/*
|
|
Operator ~todline~
|
|
|
|
Converts a line or a region value into a dline.
|
|
|
|
*/
|
|
|
|
ListExpr todlineTM(ListExpr args){
|
|
if(!nl->HasLength(args,1)){
|
|
return listutils::typeError("one argument expected");
|
|
}
|
|
if( !Region::checkType(nl->First(args))
|
|
&&!Line::checkType(nl->First(args))){
|
|
return listutils::typeError("line or region expected");
|
|
}
|
|
return listutils::basicSymbol<DLine>();
|
|
}
|
|
|
|
template<class A>
|
|
int todlineVMT(Word* args, Word& result, int message,
|
|
Word& local,Supplier s){
|
|
A* a = (A*) args[0].addr;
|
|
result = qp->ResultStorage(s);
|
|
DLine* res = (DLine*) result.addr;
|
|
res->clear();
|
|
if(!a->IsDefined()){
|
|
res->SetDefined(false);
|
|
return 0;
|
|
}
|
|
res->SetDefined(true);
|
|
HalfSegment hs;
|
|
for(int i=0;i<a->Size();i++){
|
|
a->Get(i,hs);
|
|
if(hs.IsLeftDomPoint()){
|
|
SimpleSegment ss(hs);
|
|
res->append(ss);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
ValueMapping todlineVM[] = {
|
|
todlineVMT<Line>,
|
|
todlineVMT<Region>
|
|
};
|
|
|
|
int todlineSelect(ListExpr args){
|
|
return Line::checkType(nl->First(args))?0:1;
|
|
}
|
|
|
|
OperatorSpec todlineSpec(
|
|
"{line,region} -> dline",
|
|
" _ todline",
|
|
" converts a line or the boundary of a region into"
|
|
" a dline value",
|
|
" query thecenter todline"
|
|
);
|
|
|
|
Operator todlineOp(
|
|
"todline",
|
|
todlineSpec.getStr(),
|
|
2,
|
|
todlineVM,
|
|
todlineSelect,
|
|
todlineTM
|
|
);
|
|
|
|
|
|
/*
|
|
52 Operator ~distanceWithin~
|
|
|
|
Checks whether the distance between two objects is smaller than a
|
|
given value.
|
|
|
|
*/
|
|
ListExpr distanceWithinTM(ListExpr args){
|
|
string err = "SPATIAL x SPATIAL x {int, double} [x geoid] expected";
|
|
if(!nl->HasLength(args,3) && !nl->HasLength(args,4)){
|
|
return listutils::typeError(err+ " (wrong number of args)");
|
|
}
|
|
if(nl->HasLength(args,4) && !Geoid::checkType(nl->Fourth(args))){
|
|
return listutils::typeError(err + " (4th arg not of type geoid)");
|
|
}
|
|
if( !CcInt::checkType(nl->Third(args))
|
|
&&!CcReal::checkType(nl->Third(args))){
|
|
return listutils::typeError(err + " (third arg not of type int or real)");
|
|
}
|
|
ListExpr a = nl->First(args);
|
|
if( !Rectangle<2>::checkType(a)
|
|
&& !Point::checkType(a)
|
|
&& !Points::checkType(a)
|
|
&& !Line::checkType(a)
|
|
&& !Region::checkType(a)
|
|
&& !DLine::checkType(a)){
|
|
return listutils::typeError(err + "(first arg not supported)");
|
|
}
|
|
a = nl->Second(args);
|
|
if( !Rectangle<2>::checkType(a)
|
|
&& !Point::checkType(a)
|
|
&& !Points::checkType(a)
|
|
&& !Line::checkType(a)
|
|
&& !Region::checkType(a)
|
|
&& !DLine::checkType(a)){
|
|
return listutils::typeError(err + "(first arg not supported)");
|
|
}
|
|
return listutils::basicSymbol<CcBool>();
|
|
}
|
|
|
|
|
|
// generic solution
|
|
template<class A1, class A2>
|
|
bool distWithin(A1* a1, A2* a2, double dist, Geoid* geoid){
|
|
double d = a1->Distance(*a2,geoid);
|
|
return d <= dist;
|
|
}
|
|
|
|
|
|
template<class A1, class A2, class D, bool swap>
|
|
int distanceWithinVMT(Word* args, Word& result, int message,
|
|
Word& local,Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
A1* a1 = 0;
|
|
A2* a2 = 0;
|
|
if(swap){
|
|
a1 = (A1*) args[1].addr;
|
|
a2 = (A2*) args[0].addr;
|
|
} else {
|
|
a1 = (A1*) args[0].addr;
|
|
a2 = (A2*) args[1].addr;
|
|
}
|
|
D* d = (D*) args[2].addr;
|
|
Geoid* geoid = qp->GetNoSons(s)==4?(Geoid*)args[3].addr:0;
|
|
if(!a1->IsDefined() || !a2->IsDefined() || !d->IsDefined()
|
|
|| (geoid && !geoid->IsDefined())){
|
|
res->SetDefined(0);
|
|
}
|
|
double dist = d->GetValue();
|
|
if(dist<0){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
Rectangle<2> b1 = a1->BoundingBox();
|
|
Rectangle<2> b2 = a2->BoundingBox();
|
|
if(geoid){
|
|
if(!isGeo(b1) || !isGeo(b2)){
|
|
res->SetDefined(false);
|
|
}
|
|
}
|
|
double bd = b1.Distance(b2, geoid);
|
|
if(bd > dist){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
res->Set(true, distWithin(a1,a2,dist,geoid));
|
|
return 0;
|
|
}
|
|
|
|
// overloaded for point
|
|
template<class D>
|
|
int distanceWithinVMT(Word* args, Word& result, int message,
|
|
Word& local,Supplier s){
|
|
|
|
result = qp->ResultStorage(s);
|
|
CcBool* res = (CcBool*) result.addr;
|
|
Point* a1 = (Point*) args[0].addr;
|
|
Point* a2 = (Point*) args[1].addr;;
|
|
D* d = (D*) args[2].addr;
|
|
Geoid* geoid = qp->GetNoSons(s)==4?(Geoid*)args[3].addr:0;
|
|
if(!a1->IsDefined() || !a2->IsDefined() || !d->IsDefined()
|
|
|| (geoid && !geoid->IsDefined())){
|
|
res->SetDefined(0);
|
|
}
|
|
double dist = d->GetValue();
|
|
if(dist<0){
|
|
res->Set(true,false);
|
|
return 0;
|
|
}
|
|
double bd = a1->Distance(*a2, geoid);
|
|
res->Set(true, bd<= dist);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
ValueMapping distanceWithinVM[] = {
|
|
distanceWithinVMT<CcInt>,
|
|
distanceWithinVMT<CcReal>,
|
|
distanceWithinVMT<Line,Point, CcInt, false>,
|
|
distanceWithinVMT<Line,Point, CcReal, false>,
|
|
distanceWithinVMT<Line,Point, CcInt, true>,
|
|
distanceWithinVMT<Line,Point, CcReal, true>,
|
|
};
|
|
|
|
|
|
int distanceWithinSelect(ListExpr args){
|
|
ListExpr a1 = nl->First(args);
|
|
ListExpr a2 = nl->Second(args);
|
|
ListExpr a3 = nl->Third(args);
|
|
if(Point::checkType(a1) && Point::checkType(a2) && CcInt::checkType(a3)){
|
|
return 0;
|
|
}
|
|
if(Point::checkType(a1) && Point::checkType(a2) && CcReal::checkType(a3)){
|
|
return 1;
|
|
}
|
|
if(Line::checkType(a1) && Point::checkType(a2) && CcInt::checkType(a3)){
|
|
return 2;
|
|
}
|
|
if(Line::checkType(a1) && Point::checkType(a2) && CcReal::checkType(a3)){
|
|
return 3;
|
|
}
|
|
if(Point::checkType(a1) && Line::checkType(a2) && CcInt::checkType(a3)){
|
|
return 4;
|
|
}
|
|
if(Point::checkType(a1) && Line::checkType(a2) && CcReal::checkType(a3)){
|
|
return 5;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
OperatorSpec distanceWithinSpec(
|
|
"SPATIAL x SPATIAL x {int, real} [x geoid] -> bool",
|
|
"distanceWithin(_,_,_[,_])",
|
|
"Checks whether the distance between two Objects is smaller "
|
|
"than a given value",
|
|
"query distanceWithin(alexanderplatz, mehringdamm, 2000)"
|
|
);
|
|
|
|
Operator distanceWithinOp(
|
|
"distanceWithin",
|
|
distanceWithinSpec.getStr(),
|
|
6,
|
|
distanceWithinVM,
|
|
distanceWithinSelect,
|
|
distanceWithinTM
|
|
);
|
|
|
|
|
|
/*
|
|
53 Operator ~orderLine~
|
|
|
|
Orders the segments of a simple line so that the segments are connected.
|
|
|
|
\subsection{Type Mapping}
|
|
*/
|
|
ListExpr orderLineTM(ListExpr args) {
|
|
string err = "one argument of the type sline expected";
|
|
if (!nl->HasLength(args, 1)) {
|
|
return listutils::typeError(err);
|
|
}
|
|
if (!SimpleLine::checkType(nl->First(args))) {
|
|
return listutils::typeError(err);
|
|
}
|
|
ListExpr attrList = nl->TwoElemList(nl->TwoElemList(
|
|
nl->SymbolAtom("Start"),
|
|
nl->SymbolAtom(Point::BasicType())),
|
|
nl->TwoElemList(
|
|
nl->SymbolAtom("End"),
|
|
nl->SymbolAtom(Point::BasicType())));
|
|
return nl->TwoElemList(nl->SymbolAtom(Symbol::STREAM()),
|
|
nl->TwoElemList(nl->SymbolAtom(Tuple::BasicType()),
|
|
attrList));
|
|
}
|
|
|
|
/*
|
|
\subsection{Class ~OrderLineLI~}
|
|
|
|
*/
|
|
class OrderSLineLI {
|
|
|
|
public:
|
|
OrderSLineLI(const SimpleLine& src) {
|
|
tt = 0;
|
|
pos = 0;
|
|
if (!src.IsDefined()) {
|
|
return;
|
|
}
|
|
tt = getTupleType();
|
|
multimap<Point, int> point2seg;
|
|
vector<HalfSegment> halfSegments;
|
|
Point pt1(true, 0.0, 0.0);
|
|
Point pt2(true, 1.0, 1.0);
|
|
HalfSegment hs(true, pt1, pt2);
|
|
for (int i = 0; i < src.Size(); i++) {
|
|
halfSegments.push_back(hs);
|
|
}
|
|
vector<bool> isActive;
|
|
isActive.resize(src.Size());
|
|
int noActiveSegs = 0;
|
|
for (int i = 0; i < src.Size(); i++) {
|
|
src.Get(i, hs);
|
|
if (hs.IsLeftDomPoint()) {
|
|
point2seg.insert(make_pair(hs.GetLeftPoint(), i));
|
|
point2seg.insert(make_pair(hs.GetRightPoint(), i));
|
|
HalfSegment tempHs(hs);
|
|
halfSegments[i] = tempHs;
|
|
isActive[i] = true;
|
|
noActiveSegs++;
|
|
}
|
|
}
|
|
// cout << point2seg.size() << " pts and " << noActiveSegs
|
|
// << " hs inserted" << endl;
|
|
int segNo = 0;
|
|
hs = halfSegments[segNo];
|
|
pt1 = hs.GetLeftPoint();
|
|
seq.push_back(pt1);
|
|
pt2 = pt1;
|
|
bool successorFound = true;
|
|
pair<multimap<Point, int>::iterator, multimap<Point, int>::iterator> it;
|
|
while (successorFound && noActiveSegs > 0) {
|
|
isActive[segNo] = false;
|
|
noActiveSegs--;
|
|
// cout << "segment " << segNo << " deactivated; " << noActiveSegs
|
|
// << " active" << endl << "search successor of " << pt1 << endl;
|
|
it = point2seg.equal_range(pt1);
|
|
multimap<Point, int>::iterator it1 = it.first;
|
|
while (it1 != it.second && !isActive[it1->second]) {
|
|
it1++;
|
|
}
|
|
if (it1 == it.second) { // no successor
|
|
successorFound = false;
|
|
}
|
|
else {
|
|
segNo = it1->second;
|
|
// cout << "segment " << segNo << " is active" << endl;
|
|
hs = halfSegments[segNo];
|
|
if (hs.GetLeftPoint() == pt1) { // last added
|
|
pt1 = hs.GetRightPoint();
|
|
}
|
|
else {
|
|
pt1 = hs.GetLeftPoint();
|
|
}
|
|
seq.push_back(pt1);
|
|
}
|
|
}
|
|
}
|
|
|
|
~OrderSLineLI() {
|
|
if (tt) {
|
|
tt->DeleteIfAllowed();
|
|
}
|
|
}
|
|
|
|
TupleType *getTupleType() {
|
|
SecondoCatalog* sc = SecondoSystem::GetCatalog();
|
|
ListExpr resultTupleType = nl->TwoElemList(
|
|
nl->SymbolAtom(Tuple::BasicType()),
|
|
nl->TwoElemList(nl->TwoElemList(nl->SymbolAtom("Start"),
|
|
nl->SymbolAtom(Point::BasicType())),
|
|
nl->TwoElemList(nl->SymbolAtom("End"),
|
|
nl->SymbolAtom(Point::BasicType()))));
|
|
ListExpr numResultTupleType = sc->NumericType(resultTupleType);
|
|
return new TupleType(numResultTupleType);
|
|
}
|
|
|
|
Tuple *nextTuple() {
|
|
if (pos == seq.size()) {
|
|
return 0;
|
|
}
|
|
if (!tt) {
|
|
tt = getTupleType();
|
|
}
|
|
Point *p2 = 0;
|
|
if (pos == seq.size() - 1) {
|
|
p2 = new Point(seq[0]);
|
|
}
|
|
else {
|
|
p2 = new Point(seq[pos + 1]);
|
|
}
|
|
Point *p1 = new Point(seq[pos]);
|
|
pos++;
|
|
Tuple *result = new Tuple(tt);
|
|
result->PutAttribute(0, p1);
|
|
result->PutAttribute(1, p2);
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
TupleType *tt;
|
|
vector<Point> seq;
|
|
unsigned int pos;
|
|
};
|
|
|
|
/*
|
|
|
|
\subsection{Value Mapping}
|
|
*/
|
|
int orderLineVM(Word* args, Word& result, int message, Word& local,Supplier s) {
|
|
SimpleLine *src = static_cast<SimpleLine*>(args[0].addr);
|
|
OrderSLineLI *li = static_cast<OrderSLineLI*>(local.addr);
|
|
switch (message) {
|
|
case OPEN: {
|
|
if (li) {
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
li = new OrderSLineLI(*src);
|
|
local.addr = li;
|
|
return 0;
|
|
}
|
|
case REQUEST: {
|
|
result.addr = li ? li->nextTuple() : 0;
|
|
return result.addr ? YIELD : CANCEL;
|
|
}
|
|
case CLOSE: {
|
|
if (local.addr) {
|
|
li = (OrderSLineLI*)local.addr;
|
|
delete li;
|
|
local.addr = 0;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
OperatorSpec orderLineSpec(
|
|
"sline -> stream(Start: point, End: point)",
|
|
"orderLine( _ )",
|
|
"Orders the segments of a simple line so that they are connected.",
|
|
"query orderLine(fromline(BGrenzenLine))"
|
|
);
|
|
|
|
Operator orderLine(
|
|
"orderLine",
|
|
orderLineSpec.getStr(),
|
|
orderLineVM,
|
|
Operator::SimpleSelect,
|
|
orderLineTM
|
|
);
|
|
|
|
|
|
|
|
/*
|
|
11 Creating the Algebra
|
|
|
|
*/
|
|
|
|
class SpatialAlgebra : public Algebra
|
|
{
|
|
public:
|
|
SpatialAlgebra() : Algebra()
|
|
{
|
|
AddTypeConstructor( &point );
|
|
AddTypeConstructor( &points );
|
|
AddTypeConstructor( &line );
|
|
AddTypeConstructor( ®ion );
|
|
|
|
AddTypeConstructor( &sline);
|
|
AddTypeConstructor( &dline);
|
|
|
|
AddTypeConstructor( &drm);
|
|
AddTypeConstructor( &oim);
|
|
|
|
AddTypeConstructor( & label);
|
|
|
|
AddTypeConstructor( &cpoint);
|
|
|
|
|
|
point.AssociateKind(Kind::DATA());
|
|
point.AssociateKind(Kind::CSVEXPORTABLE());
|
|
point.AssociateKind(Kind::CSVIMPORTABLE());
|
|
points.AssociateKind(Kind::DATA());
|
|
line.AssociateKind(Kind::DATA());
|
|
region.AssociateKind(Kind::DATA());
|
|
sline.AssociateKind(Kind::DATA());
|
|
dline.AssociateKind(Kind::DATA());
|
|
drm.AssociateKind(Kind::DATA());
|
|
oim.AssociateKind(Kind::DATA());
|
|
label.AssociateKind(Kind::DATA());
|
|
cpoint.AssociateKind(Kind::DATA());
|
|
|
|
|
|
point.AssociateKind(Kind::SPATIAL2D());
|
|
points.AssociateKind(Kind::SPATIAL2D());
|
|
line.AssociateKind(Kind::SPATIAL2D());
|
|
region.AssociateKind(Kind::SPATIAL2D());
|
|
sline.AssociateKind(Kind::SPATIAL2D());
|
|
dline.AssociateKind(Kind::SPATIAL2D());
|
|
cpoint.AssociateKind(Kind::SPATIAL2D());
|
|
|
|
|
|
point.AssociateKind(Kind::SHPEXPORTABLE());
|
|
points.AssociateKind(Kind::SHPEXPORTABLE());
|
|
line.AssociateKind(Kind::SHPEXPORTABLE());
|
|
region.AssociateKind(Kind::SHPEXPORTABLE());
|
|
|
|
|
|
point.AssociateKind(Kind::SQLEXPORTABLE());
|
|
points.AssociateKind(Kind::SQLEXPORTABLE());
|
|
|
|
AddTypeConstructor(&disc);
|
|
disc.AssociateKind(Kind::DATA());
|
|
|
|
AddTypeConstructor(&segment);
|
|
segment.AssociateKind(Kind::DATA());
|
|
|
|
|
|
AddOperator( &spatialisempty );
|
|
AddOperator( &spatialequal );
|
|
AddOperator( &spatialisless);
|
|
AddOperator( &spatialnotequal );
|
|
AddOperator( &spatialintersects );
|
|
AddOperator( &spatialinside );
|
|
AddOperator( &spatialadjacent );
|
|
AddOperator( &spatialoverlaps );
|
|
AddOperator( &spatialonborder );
|
|
AddOperator( &spatialininterior );
|
|
AddOperator( &spatialintersection);
|
|
AddOperator( &spatialintersection_rob);
|
|
AddOperator( &spatialminus );
|
|
AddOperator( &spatialunion );
|
|
AddOperator( &spatialcrossings );
|
|
AddOperator( &spatialtouchpoints);
|
|
AddOperator( &spatialcommonborder);
|
|
AddOperator( &spatialsingle );
|
|
AddOperator( &spatialdistance );
|
|
AddOperator( &distanceSmallerThan );
|
|
AddOperator( &spatialdirection );
|
|
AddOperator( &spatialheading );
|
|
AddOperator( &spatialnocomponents );
|
|
AddOperator( &spatialnosegments );
|
|
AddOperator( &spatialsize );
|
|
AddOperator( &spatialbbox);
|
|
AddOperator( &spatialtranslate );
|
|
AddOperator( &spatialrotate );
|
|
AddOperator( &spatialcenter );
|
|
AddOperator( &spatialconvexhull );
|
|
AddOperator( &spatialwindowclippingin );
|
|
AddOperator( &spatialwindowclippingout );
|
|
AddOperator( &spatialcomponents );
|
|
AddOperator( &spatialvertices );
|
|
AddOperator( &spatialboundary );
|
|
AddOperator( &spatialscale );
|
|
AddOperator( &spatialatpoint );
|
|
AddOperator( &spatialatposition );
|
|
AddOperator( &spatialsubline );
|
|
AddOperator( &spatialadd );
|
|
AddOperator( &spatialgetx );
|
|
AddOperator( &spatialgety );
|
|
AddOperator( &spatialline2region );
|
|
AddOperator( &spatialrect2region );
|
|
AddOperator( &spatialarea );
|
|
AddOperator( &spatialpolylines);
|
|
AddOperator( &spatialpolylinesC);
|
|
AddOperator( &spatialsegments );
|
|
AddOperator( &spatialget );
|
|
AddOperator( &spatialsimplify);
|
|
AddOperator( &realminize);
|
|
AddOperator( &makeline);
|
|
AddOperator( &makesline);
|
|
AddOperator( &commonborder2);
|
|
AddOperator( &spatialtoline);
|
|
AddOperator( &spatialfromline);
|
|
AddOperator( &spatialiscycle);
|
|
AddOperator( &utmOp);
|
|
AddOperator( &gkOp);
|
|
AddOperator( &reverseGkOp);
|
|
AddOperator( &spatialcollect_line);
|
|
AddOperator( &spatialcollect_sline);
|
|
AddOperator( &spatialcollect_points);
|
|
AddOperator( &spatialmakepoint );
|
|
AddOperator( &halfSegmentsOp );
|
|
AddOperator( &spatialgetstartpoint);
|
|
AddOperator( &spatialgetendpoint);
|
|
AddOperator( &spatialsetstartsmaller ) ;
|
|
AddOperator( &spatialgetstartsmaller ) ;
|
|
AddOperator( &spatial_create_sline ) ;
|
|
AddOperator( &spatial_distanceOrthodrome );
|
|
AddOperator( &point2string );
|
|
AddOperator( &spatialmidpointbetween );
|
|
AddOperator( &spatialDirectionToHeading );
|
|
AddOperator( &spatialHeadingToDirection );
|
|
AddOperator( &spatialCreateTriangle );
|
|
|
|
AddOperator(&spatialcircle);
|
|
AddOperator(&spatiallonglines);
|
|
AddOperator(&spatialsplitslineatpoints);
|
|
|
|
AddOperator(&findCycles);
|
|
|
|
AddOperator(&markUsageOp);
|
|
AddOperator(&criticalPoints);
|
|
AddOperator(&testRegionCreator);
|
|
AddOperator(&collect_box);
|
|
AddOperator(&contains_rob);
|
|
AddOperator(&getHoles);
|
|
|
|
AddOperator(&collect_line2);
|
|
AddOperator(&getInnerPoint);
|
|
AddOperator(&checkRealmOp);
|
|
checkRealmOp.enableInitFinishSupport();
|
|
AddOperator(&badRealmOp);
|
|
AddOperator(&crossings_rob);
|
|
|
|
AddOperator(&splitline);
|
|
AddOperator(&computeDRM);
|
|
AddOperator(&computeOIM);
|
|
AddOperator(&collectDline);
|
|
AddOperator(&computeLabelOP);
|
|
AddOperator(¢roidDiscOP);
|
|
AddOperator(&calcDiscOP);
|
|
AddOperator(&createDiscOP);
|
|
AddOperator(&berlin2wgs);
|
|
AddOperator(&elementsOP);
|
|
AddOperator(&twistOp);
|
|
AddOperator(&contour2Op);
|
|
AddOperator(&twist2Op);
|
|
AddOperator(&twist3Op);
|
|
AddOperator(&toSVGOp);
|
|
AddOperator(&simpleProject);
|
|
AddOperator(&todlineOp);
|
|
AddOperator(&distanceWithinOp);
|
|
AddOperator(&orderLine);
|
|
}
|
|
~SpatialAlgebra() {};
|
|
};
|
|
|
|
/*
|
|
12 Initialization
|
|
|
|
Each algebra module needs an initialization function. The algebra manager
|
|
has a reference to this function if this algebra is included in the list
|
|
of required algebras, thus forcing the linker to include this module.
|
|
|
|
The algebra manager invokes this function to get a reference to the instance
|
|
of the algebra class and to provide references to the global nested list
|
|
container (used to store constructor, type, operator and object information)
|
|
and to the query processor.
|
|
|
|
The function has a C interface to make it possible to load the algebra
|
|
dynamically at runtime.
|
|
|
|
*/
|
|
|
|
extern "C"
|
|
Algebra*
|
|
InitializeSpatialAlgebra( NestedList* nlRef, QueryProcessor* qpRef )
|
|
{
|
|
nl = nlRef;
|
|
qp = qpRef;
|
|
return (new SpatialAlgebra());
|
|
}
|