#include <sys/types.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>
#if defined(_WIN32) && !defined(__MINGW32__)
#include "config-msvc.h"
#else
#include "config.h"
#endif
#include <spatialite/sqlite.h>
#include <spatialite/spatialite.h>
#include <spatialite/gaiaaux.h>
#include <spatialite/gaiageo.h>
static struct sqlite3_module my_net_module;
#define VNET_DIJKSTRA_ALGORITHM 1
#define VNET_A_STAR_ALGORITHM 2
#define VNET_ROUTING_SOLUTION 0xdd
#define VNET_RANGE_SOLUTION 0xbb
#define VNET_INVALID_SRID -1234
#ifdef _WIN32
#define strcasecmp _stricmp
#endif
typedef struct NetworkArcStruct
{
const struct NetworkNodeStruct *NodeFrom;
const struct NetworkNodeStruct *NodeTo;
sqlite3_int64 ArcRowid;
double Cost;
} NetworkArc;
typedef NetworkArc *NetworkArcPtr;
typedef struct NetworkNodeStruct
{
int InternalIndex;
sqlite3_int64 Id;
char *Code;
double CoordX;
double CoordY;
int NumArcs;
NetworkArcPtr Arcs;
} NetworkNode;
typedef NetworkNode *NetworkNodePtr;
typedef struct NetworkStruct
{
int Net64;
int AStar;
int EndianArch;
int MaxCodeLength;
int CurrentIndex;
int NodeCode;
int NumNodes;
char *TableName;
char *FromColumn;
char *ToColumn;
char *GeometryColumn;
char *NameColumn;
double AStarHeuristicCoeff;
NetworkNodePtr Nodes;
} Network;
typedef Network *NetworkPtr;
typedef struct ArcSolutionStruct
{
sqlite3_int64 ArcRowid;
char *FromCode;
char *ToCode;
sqlite3_int64 FromId;
sqlite3_int64 ToId;
int Points;
double *Coords;
int Srid;
char *Name;
struct ArcSolutionStruct *Next;
} ArcSolution;
typedef ArcSolution *ArcSolutionPtr;
typedef struct RowSolutionStruct
{
NetworkArcPtr Arc;
char *Name;
struct RowSolutionStruct *Next;
} RowSolution;
typedef RowSolution *RowSolutionPtr;
typedef struct RowNodeSolutionStruct
{
NetworkNodePtr Node;
double Cost;
int Srid;
struct RowNodeSolutionStruct *Next;
} RowNodeSolution;
typedef RowNodeSolution *RowNodeSolutionPtr;
typedef struct SolutionStruct
{
unsigned char Mode;
ArcSolutionPtr FirstArc;
ArcSolutionPtr LastArc;
NetworkNodePtr From;
NetworkNodePtr To;
double MaxCost;
RowSolutionPtr First;
RowSolutionPtr Last;
RowNodeSolutionPtr FirstNode;
RowNodeSolutionPtr LastNode;
RowSolutionPtr CurrentRow;
RowNodeSolutionPtr CurrentNodeRow;
sqlite3_int64 CurrentRowId;
double TotalCost;
gaiaGeomCollPtr Geometry;
} Solution;
typedef Solution *SolutionPtr;
typedef struct RoutingNode
{
int Id;
struct RoutingNode **To;
NetworkArcPtr *Link;
int DimTo;
struct RoutingNode *PreviousNode;
NetworkNodePtr Node;
NetworkArcPtr Arc;
double Distance;
double HeuristicDistance;
int Inspected;
} RoutingNode;
typedef RoutingNode *RoutingNodePtr;
typedef struct RoutingNodes
{
RoutingNodePtr Nodes;
NetworkArcPtr *ArcsBuffer;
RoutingNodePtr *NodesBuffer;
int Dim;
int DimLink;
} RoutingNodes;
typedef RoutingNodes *RoutingNodesPtr;
typedef struct HeapNode
{
RoutingNodePtr Node;
double Distance;
} HeapNode;
typedef HeapNode *HeapNodePtr;
typedef struct RoutingHeapStruct
{
HeapNodePtr Nodes;
int Count;
} RoutingHeap;
typedef RoutingHeap *RoutingHeapPtr;
typedef struct VirtualNetworkStruct
{
const sqlite3_module *pModule;
int nRef;
char *zErrMsg;
sqlite3 *db;
NetworkPtr graph;
RoutingNodesPtr routing;
int currentAlgorithm;
} VirtualNetwork;
typedef VirtualNetwork *VirtualNetworkPtr;
typedef struct VirtualNetworkCursortStruct
{
VirtualNetworkPtr pVtab;
SolutionPtr solution;
int eof;
} VirtualNetworkCursor;
typedef VirtualNetworkCursor *VirtualNetworkCursorPtr;
static RoutingNodesPtr
routing_init (NetworkPtr graph)
{
int i;
int j;
int cnt = 0;
RoutingNodesPtr nd;
RoutingNodePtr ndn;
NetworkNodePtr nn;
nd = malloc (sizeof (RoutingNodes));
nd->Nodes = malloc (sizeof (RoutingNode) * graph->NumNodes);
nd->Dim = graph->NumNodes;
nd->DimLink = 0;
for (i = 0; i < graph->NumNodes; cnt += graph->Nodes[i].NumArcs, i++);
nd->NodesBuffer = malloc (sizeof (RoutingNodePtr) * cnt);
nd->ArcsBuffer = malloc (sizeof (NetworkArcPtr) * cnt);
cnt = 0;
for (i = 0; i < graph->NumNodes; i++)
{
nn = graph->Nodes + i;
ndn = nd->Nodes + i;
ndn->Id = nn->InternalIndex;
ndn->DimTo = nn->NumArcs;
ndn->Node = nn;
ndn->To = &(nd->NodesBuffer[cnt]);
ndn->Link = &(nd->ArcsBuffer[cnt]);
cnt += nn->NumArcs;
for (j = 0; j < nn->NumArcs; j++)
{
nd->DimLink++;
ndn->To[j] = nd->Nodes + nn->Arcs[j].NodeTo->InternalIndex;
ndn->Link[j] = nn->Arcs + j;
}
}
return (nd);
}
static void
routing_free (RoutingNodes * e)
{
free (e->ArcsBuffer);
free (e->NodesBuffer);
free (e->Nodes);
free (e);
}
static RoutingHeapPtr
routing_heap_init (int n)
{
RoutingHeapPtr heap = malloc (sizeof (RoutingHeap));
heap->Count = 0;
heap->Nodes = malloc (sizeof (HeapNode) * (n + 1));
return heap;
}
static void
routing_heap_free (RoutingHeapPtr heap)
{
if (heap->Nodes != NULL)
free (heap->Nodes);
free (heap);
}
static void
dijkstra_insert (RoutingNodePtr node, HeapNodePtr heap, int size)
{
int i;
HeapNode tmp;
i = size + 1;
heap[i].Node = node;
heap[i].Distance = node->Distance;
if (i / 2 < 1)
return;
while (heap[i].Distance < heap[i / 2].Distance)
{
tmp = heap[i];
heap[i] = heap[i / 2];
heap[i / 2] = tmp;
i /= 2;
if (i / 2 < 1)
break;
}
}
static void
dijkstra_enqueue (RoutingHeapPtr heap, RoutingNodePtr node)
{
dijkstra_insert (node, heap->Nodes, heap->Count);
heap->Count += 1;
}
static void
dijkstra_shiftdown (HeapNodePtr heap, int size, int i)
{
int c;
HeapNode tmp;
for (;;)
{
c = i * 2;
if (c > size)
break;
if (c < size)
{
if (heap[c].Distance > heap[c + 1].Distance)
++c;
}
if (heap[c].Distance < heap[i].Distance)
{
tmp = heap[c];
heap[c] = heap[i];
heap[i] = tmp;
i = c;
}
else
break;
}
}
static RoutingNodePtr
dijkstra_remove_min (HeapNodePtr heap, int size)
{
RoutingNodePtr node = heap[1].Node;
heap[1] = heap[size];
--size;
dijkstra_shiftdown (heap, size, 1);
return node;
}
static RoutingNodePtr
routing_dequeue (RoutingHeapPtr heap)
{
RoutingNodePtr node = dijkstra_remove_min (heap->Nodes, heap->Count);
heap->Count -= 1;
return node;
}
static NetworkArcPtr *
dijkstra_shortest_path (RoutingNodesPtr e, NetworkNodePtr pfrom,
NetworkNodePtr pto, int *ll)
{
int from;
int to;
int i;
int k;
RoutingNodePtr n;
RoutingNodePtr p_to;
NetworkArcPtr p_link;
int cnt;
NetworkArcPtr *result;
RoutingHeapPtr heap;
from = pfrom->InternalIndex;
to = pto->InternalIndex;
heap = routing_heap_init (e->DimLink);
for (i = 0; i < e->Dim; i++)
{
n = e->Nodes + i;
n->PreviousNode = NULL;
n->Arc = NULL;
n->Inspected = 0;
n->Distance = DBL_MAX;
}
e->Nodes[from].Distance = 0.0;
dijkstra_enqueue (heap, e->Nodes + from);
while (heap->Count > 0)
{
n = routing_dequeue (heap);
if (n->Id == to)
{
break;
}
n->Inspected = 1;
for (i = 0; i < n->DimTo; i++)
{
p_to = *(n->To + i);
p_link = *(n->Link + i);
if (p_to->Inspected == 0)
{
if (p_to->Distance == DBL_MAX)
{
p_to->Distance = n->Distance + p_link->Cost;
p_to->PreviousNode = n;
p_to->Arc = p_link;
dijkstra_enqueue (heap, p_to);
}
else if (p_to->Distance > n->Distance + p_link->Cost)
{
p_to->Distance = n->Distance + p_link->Cost;
p_to->PreviousNode = n;
p_to->Arc = p_link;
}
}
}
}
routing_heap_free (heap);
cnt = 0;
n = e->Nodes + to;
while (n->PreviousNode != NULL)
{
cnt++;
n = n->PreviousNode;
}
result = malloc (sizeof (NetworkArcPtr) * cnt);
k = cnt - 1;
n = e->Nodes + to;
while (n->PreviousNode != NULL)
{
result[k] = n->Arc;
n = n->PreviousNode;
k--;
}
*ll = cnt;
return (result);
}
static RoutingNodePtr *
dijkstra_range_analysis (RoutingNodesPtr e, NetworkNodePtr pfrom,
double max_cost, int *ll)
{
int from;
int i;
RoutingNodePtr p_to;
RoutingNodePtr n;
NetworkArcPtr p_link;
int cnt;
RoutingNodePtr *result;
RoutingHeapPtr heap;
from = pfrom->InternalIndex;
heap = routing_heap_init (e->DimLink);
for (i = 0; i < e->Dim; i++)
{
n = e->Nodes + i;
n->PreviousNode = NULL;
n->Arc = NULL;
n->Inspected = 0;
n->Distance = DBL_MAX;
}
e->Nodes[from].Distance = 0.0;
dijkstra_enqueue (heap, e->Nodes + from);
while (heap->Count > 0)
{
n = routing_dequeue (heap);
n->Inspected = 1;
for (i = 0; i < n->DimTo; i++)
{
p_to = *(n->To + i);
p_link = *(n->Link + i);
if (p_to->Inspected == 0)
{
if (p_to->Distance == DBL_MAX)
{
if (n->Distance + p_link->Cost <= max_cost)
{
p_to->Distance = n->Distance + p_link->Cost;
p_to->PreviousNode = n;
p_to->Arc = p_link;
dijkstra_enqueue (heap, p_to);
}
}
else if (p_to->Distance > n->Distance + p_link->Cost)
{
p_to->Distance = n->Distance + p_link->Cost;
p_to->PreviousNode = n;
p_to->Arc = p_link;
}
}
}
}
routing_heap_free (heap);
cnt = 0;
for (i = 0; i < e->Dim; i++)
{
n = e->Nodes + i;
if (n->Inspected)
cnt++;
}
result = malloc (sizeof (NetworkNodePtr) * cnt);
cnt = 0;
for (i = 0; i < e->Dim; i++)
{
n = e->Nodes + i;
if (n->Inspected)
result[cnt++] = n;
}
*ll = cnt;
return (result);
}
static void
astar_insert (RoutingNodePtr node, HeapNodePtr heap, int size)
{
int i;
HeapNode tmp;
i = size + 1;
heap[i].Node = node;
heap[i].Distance = node->HeuristicDistance;
if (i / 2 < 1)
return;
while (heap[i].Distance < heap[i / 2].Distance)
{
tmp = heap[i];
heap[i] = heap[i / 2];
heap[i / 2] = tmp;
i /= 2;
if (i / 2 < 1)
break;
}
}
static void
astar_enqueue (RoutingHeapPtr heap, RoutingNodePtr node)
{
astar_insert (node, heap->Nodes, heap->Count);
heap->Count += 1;
}
static double
astar_heuristic_distance (NetworkNodePtr n1, NetworkNodePtr n2, double coeff)
{
double dx = n1->CoordX - n2->CoordX;
double dy = n1->CoordY - n2->CoordY;
double dist = sqrt ((dx * dx) + (dy * dy)) * coeff;
return dist;
}
static NetworkArcPtr *
astar_shortest_path (RoutingNodesPtr e, NetworkNodePtr nodes,
NetworkNodePtr pfrom, NetworkNodePtr pto,
double heuristic_coeff, int *ll)
{
int from;
int to;
int i;
int k;
RoutingNodePtr pAux;
RoutingNodePtr n;
RoutingNodePtr p_to;
NetworkNodePtr pOrg;
NetworkNodePtr pDest;
NetworkArcPtr p_link;
int cnt;
NetworkArcPtr *result;
RoutingHeapPtr heap;
from = pfrom->InternalIndex;
to = pto->InternalIndex;
pAux = e->Nodes + from;
pOrg = nodes + pAux->Id;
pAux = e->Nodes + to;
pDest = nodes + pAux->Id;
heap = routing_heap_init (e->DimLink);
for (i = 0; i < e->Dim; i++)
{
n = e->Nodes + i;
n->PreviousNode = NULL;
n->Arc = NULL;
n->Inspected = 0;
n->Distance = DBL_MAX;
n->HeuristicDistance = DBL_MAX;
}
e->Nodes[from].Distance = 0.0;
e->Nodes[from].HeuristicDistance =
astar_heuristic_distance (pOrg, pDest, heuristic_coeff);
astar_enqueue (heap, e->Nodes + from);
while (heap->Count > 0)
{
n = routing_dequeue (heap);
if (n->Id == to)
{
break;
}
n->Inspected = 1;
for (i = 0; i < n->DimTo; i++)
{
p_to = *(n->To + i);
p_link = *(n->Link + i);
if (p_to->Inspected == 0)
{
if (p_to->Distance == DBL_MAX)
{
p_to->Distance = n->Distance + p_link->Cost;
pOrg = nodes + p_to->Id;
p_to->HeuristicDistance =
p_to->Distance +
astar_heuristic_distance (pOrg, pDest,
heuristic_coeff);
p_to->PreviousNode = n;
p_to->Arc = p_link;
astar_enqueue (heap, p_to);
}
else if (p_to->Distance > n->Distance + p_link->Cost)
{
p_to->Distance = n->Distance + p_link->Cost;
pOrg = nodes + p_to->Id;
p_to->HeuristicDistance =
p_to->Distance +
astar_heuristic_distance (pOrg, pDest,
heuristic_coeff);
p_to->PreviousNode = n;
p_to->Arc = p_link;
}
}
}
}
routing_heap_free (heap);
cnt = 0;
n = e->Nodes + to;
while (n->PreviousNode != NULL)
{
cnt++;
n = n->PreviousNode;
}
result = malloc (sizeof (NetworkArcPtr) * cnt);
k = cnt - 1;
n = e->Nodes + to;
while (n->PreviousNode != NULL)
{
result[k] = n->Arc;
n = n->PreviousNode;
k--;
}
*ll = cnt;
return (result);
}
static int
cmp_nodes_code (const void *p1, const void *p2)
{
NetworkNodePtr pN1 = (NetworkNodePtr) p1;
NetworkNodePtr pN2 = (NetworkNodePtr) p2;
return strcmp (pN1->Code, pN2->Code);
}
static int
cmp_nodes_id (const void *p1, const void *p2)
{
NetworkNodePtr pN1 = (NetworkNodePtr) p1;
NetworkNodePtr pN2 = (NetworkNodePtr) p2;
if (pN1->Id == pN2->Id)
return 0;
if (pN1->Id > pN2->Id)
return 1;
return -1;
}
static NetworkNodePtr
find_node_by_code (NetworkPtr graph, const char *code)
{
NetworkNodePtr ret;
NetworkNode pN;
pN.Code = (char *) code;
ret =
bsearch (&pN, graph->Nodes, graph->NumNodes, sizeof (NetworkNode),
cmp_nodes_code);
return ret;
}
static NetworkNodePtr
find_node_by_id (NetworkPtr graph, const sqlite3_int64 id)
{
NetworkNodePtr ret;
NetworkNode pN;
pN.Id = id;
ret =
bsearch (&pN, graph->Nodes, graph->NumNodes, sizeof (NetworkNode),
cmp_nodes_id);
return ret;
}
static void
delete_solution (SolutionPtr solution)
{
ArcSolutionPtr pA;
ArcSolutionPtr pAn;
RowSolutionPtr pR;
RowSolutionPtr pRn;
RowNodeSolutionPtr pN;
RowNodeSolutionPtr pNn;
if (!solution)
return;
pA = solution->FirstArc;
while (pA)
{
pAn = pA->Next;
if (pA->FromCode)
free (pA->FromCode);
if (pA->ToCode)
free (pA->ToCode);
if (pA->Coords)
free (pA->Coords);
if (pA->Name)
free (pA->Name);
free (pA);
pA = pAn;
}
pR = solution->First;
while (pR)
{
pRn = pR->Next;
if (pR->Name)
free (pR->Name);
free (pR);
pR = pRn;
}
pN = solution->FirstNode;
while (pN)
{
pNn = pN->Next;
free (pN);
pN = pNn;
}
if (solution->Geometry)
gaiaFreeGeomColl (solution->Geometry);
free (solution);
}
static void
reset_solution (SolutionPtr solution)
{
ArcSolutionPtr pA;
ArcSolutionPtr pAn;
RowSolutionPtr pR;
RowSolutionPtr pRn;
RowNodeSolutionPtr pN;
RowNodeSolutionPtr pNn;
if (!solution)
return;
pA = solution->FirstArc;
while (pA)
{
pAn = pA->Next;
if (pA->FromCode)
free (pA->FromCode);
if (pA->ToCode)
free (pA->ToCode);
if (pA->Coords)
free (pA->Coords);
free (pA);
pA = pAn;
}
pR = solution->First;
while (pR)
{
pRn = pR->Next;
if (pR->Name)
free (pR->Name);
free (pR);
pR = pRn;
}
pN = solution->FirstNode;
while (pN)
{
pNn = pN->Next;
free (pN);
pN = pNn;
}
if (solution->Geometry)
gaiaFreeGeomColl (solution->Geometry);
solution->FirstArc = NULL;
solution->LastArc = NULL;
solution->From = NULL;
solution->To = NULL;
solution->MaxCost = 0.0;
solution->First = NULL;
solution->Last = NULL;
solution->CurrentRow = NULL;
solution->CurrentNodeRow = NULL;
solution->CurrentRowId = 0;
solution->TotalCost = 0.0;
solution->Geometry = NULL;
}
static SolutionPtr
alloc_solution (void)
{
SolutionPtr p = malloc (sizeof (Solution));
p->FirstArc = NULL;
p->LastArc = NULL;
p->From = NULL;
p->To = NULL;
p->MaxCost = 0.0;
p->First = NULL;
p->Last = NULL;
p->FirstNode = NULL;
p->LastNode = NULL;
p->CurrentRow = NULL;
p->CurrentNodeRow = NULL;
p->CurrentRowId = 0;
p->TotalCost = 0.0;
p->Geometry = NULL;
return p;
}
static void
add_arc_to_solution (SolutionPtr solution, NetworkArcPtr arc)
{
RowSolutionPtr p = malloc (sizeof (RowSolution));
p->Arc = arc;
p->Name = NULL;
p->Next = NULL;
solution->TotalCost += arc->Cost;
if (!(solution->First))
solution->First = p;
if (solution->Last)
solution->Last->Next = p;
solution->Last = p;
}
static void
add_node_to_solution (SolutionPtr solution, RoutingNodePtr node, int srid)
{
RowNodeSolutionPtr p = malloc (sizeof (RowNodeSolution));
p->Node = node->Node;
p->Cost = node->Distance;
p->Srid = srid;
p->Next = NULL;
if (!(solution->FirstNode))
solution->FirstNode = p;
if (solution->LastNode)
solution->LastNode->Next = p;
solution->LastNode = p;
}
static void
set_arc_name_into_solution (SolutionPtr solution, sqlite3_int64 arc_id,
const char *name)
{
RowSolutionPtr row = solution->First;
while (row != NULL)
{
if (row->Arc->ArcRowid == arc_id)
{
int len = strlen (name);
if (row->Name != NULL)
free (row->Name);
row->Name = malloc (len + 1);
strcpy (row->Name, name);
return;
}
row = row->Next;
}
}
static void
add_arc_geometry_to_solution (SolutionPtr solution, sqlite3_int64 arc_id,
const char *from_code, const char *to_code,
sqlite3_int64 from_id, sqlite3_int64 to_id,
int points, double *coords, int srid,
const char *name)
{
int len;
ArcSolutionPtr p = malloc (sizeof (ArcSolution));
p->ArcRowid = arc_id;
p->FromCode = NULL;
len = strlen (from_code);
if (len > 0)
{
p->FromCode = malloc (len + 1);
strcpy (p->FromCode, from_code);
}
p->ToCode = NULL;
len = strlen (to_code);
if (len > 0)
{
p->ToCode = malloc (len + 1);
strcpy (p->ToCode, to_code);
}
p->FromId = from_id;
p->ToId = to_id;
p->Points = points;
p->Coords = coords;
p->Srid = srid;
if (name == NULL)
p->Name = NULL;
else
{
len = strlen (name);
p->Name = malloc (len + 1);
strcpy (p->Name, name);
}
p->Next = NULL;
if (!(solution->FirstArc))
solution->FirstArc = p;
if (solution->LastArc)
solution->LastArc->Next = p;
solution->LastArc = p;
}
static void
build_solution (sqlite3 * handle, NetworkPtr graph, SolutionPtr solution,
NetworkArcPtr * shortest_path, int cnt)
{
int i;
char *sql;
int err;
int error = 0;
int ret;
sqlite3_int64 arc_id;
const unsigned char *blob;
int size;
sqlite3_int64 from_id;
sqlite3_int64 to_id;
char *from_code;
char *to_code;
char *name;
int tbd;
int ind;
int base = 0;
int block = 128;
int how_many;
sqlite3_stmt *stmt;
char *xfrom;
char *xto;
char *xgeom;
char *xname;
char *xtable;
gaiaOutBuffer sql_statement;
if (cnt > 0)
{
for (i = 0; i < cnt; i++)
{
add_arc_to_solution (solution, shortest_path[i]);
}
}
if (graph->GeometryColumn == NULL && graph->NameColumn == NULL)
{
return;
}
tbd = cnt;
while (tbd > 0)
{
if (tbd < block)
how_many = tbd;
else
how_many = block;
gaiaOutBufferInitialize (&sql_statement);
if (graph->NameColumn)
{
if (graph->GeometryColumn == NULL)
{
xfrom = gaiaDoubleQuotedSql (graph->FromColumn);
xto = gaiaDoubleQuotedSql (graph->ToColumn);
xname = gaiaDoubleQuotedSql (graph->NameColumn);
xtable = gaiaDoubleQuotedSql (graph->TableName);
sql =
sqlite3_mprintf
("SELECT ROWID, \"%s\", \"%s\", NULL, \"%s\" FROM \"%s\" WHERE ROWID IN (",
xfrom, xto, xname, xtable);
free (xfrom);
free (xto);
free (xname);
free (xtable);
}
else
{
xfrom = gaiaDoubleQuotedSql (graph->FromColumn);
xto = gaiaDoubleQuotedSql (graph->ToColumn);
xgeom = gaiaDoubleQuotedSql (graph->GeometryColumn);
xname = gaiaDoubleQuotedSql (graph->NameColumn);
xtable = gaiaDoubleQuotedSql (graph->TableName);
sql =
sqlite3_mprintf
("SELECT ROWID, \"%s\", \"%s\", \"%s\", \"%s\" FROM \"%s\" WHERE ROWID IN (",
xfrom, xto, xgeom, xname, xtable);
free (xfrom);
free (xto);
free (xgeom);
free (xname);
free (xtable);
}
gaiaAppendToOutBuffer (&sql_statement, sql);
sqlite3_free (sql);
}
else
{
xfrom = gaiaDoubleQuotedSql (graph->FromColumn);
xto = gaiaDoubleQuotedSql (graph->ToColumn);
xgeom = gaiaDoubleQuotedSql (graph->GeometryColumn);
xtable = gaiaDoubleQuotedSql (graph->TableName);
sql =
sqlite3_mprintf
("SELECT ROWID, \"%s\", \"%s\", \"%s\" FROM \"%s\" WHERE ROWID IN (",
xfrom, xto, xgeom, xtable);
free (xfrom);
free (xto);
free (xgeom);
free (xtable);
gaiaAppendToOutBuffer (&sql_statement, sql);
sqlite3_free (sql);
}
for (i = 0; i < how_many; i++)
{
if (i == 0)
gaiaAppendToOutBuffer (&sql_statement, "?");
else
gaiaAppendToOutBuffer (&sql_statement, ",?");
}
gaiaAppendToOutBuffer (&sql_statement, ")");
if (sql_statement.Error == 0 && sql_statement.Buffer != NULL)
ret =
sqlite3_prepare_v2 (handle, sql_statement.Buffer,
strlen (sql_statement.Buffer), &stmt,
NULL);
else
ret = SQLITE_ERROR;
gaiaOutBufferReset (&sql_statement);
if (ret != SQLITE_OK)
{
error = 1;
goto abort;
}
sqlite3_reset (stmt);
sqlite3_clear_bindings (stmt);
ind = 1;
for (i = 0; i < cnt; i++)
{
if (i < base)
continue;
if (i >= (base + how_many))
break;
sqlite3_bind_int64 (stmt, ind, shortest_path[i]->ArcRowid);
ind++;
}
while (1)
{
ret = sqlite3_step (stmt);
if (ret == SQLITE_DONE)
break;
if (ret == SQLITE_ROW)
{
arc_id = -1;
from_id = -1;
to_id = -1;
from_code = NULL;
to_code = NULL;
blob = NULL;
size = 0;
name = NULL;
err = 0;
if (sqlite3_column_type (stmt, 0) == SQLITE_INTEGER)
arc_id = sqlite3_column_int64 (stmt, 0);
else
err = 1;
if (graph->NodeCode)
{
if (sqlite3_column_type (stmt, 1) == SQLITE_TEXT)
from_code =
(char *) sqlite3_column_text (stmt, 1);
else
err = 1;
if (sqlite3_column_type (stmt, 2) == SQLITE_TEXT)
to_code =
(char *) sqlite3_column_text (stmt, 2);
else
err = 1;
}
else
{
if (sqlite3_column_type (stmt, 1) == SQLITE_INTEGER)
from_id = sqlite3_column_int64 (stmt, 1);
else
err = 1;
if (sqlite3_column_type (stmt, 2) == SQLITE_INTEGER)
to_id = sqlite3_column_int64 (stmt, 2);
else
err = 1;
}
if (graph->GeometryColumn != NULL)
{
if (sqlite3_column_type (stmt, 3) == SQLITE_BLOB)
{
blob =
(const unsigned char *)
sqlite3_column_blob (stmt, 3);
size = sqlite3_column_bytes (stmt, 3);
}
else
err = 1;
}
if (graph->NameColumn)
{
if (sqlite3_column_type (stmt, 4) == SQLITE_TEXT)
name = (char *) sqlite3_column_text (stmt, 4);
}
if (err)
error = 1;
else if (graph->GeometryColumn != NULL)
{
gaiaGeomCollPtr geom =
gaiaFromSpatiaLiteBlobWkb (blob, size);
if (geom)
{
if (geom->FirstPoint == NULL
&& geom->FirstPolygon == NULL
&& geom->FirstLinestring != NULL
&& geom->FirstLinestring ==
geom->LastLinestring)
{
int iv;
int points =
geom->FirstLinestring->Points;
double *coords =
malloc (sizeof (double) *
(points * 2));
for (iv = 0; iv < points; iv++)
{
double x;
double y;
gaiaGetPoint
(geom->
FirstLinestring->Coords, iv,
&x, &y);
*(coords + ((iv * 2) + 0)) = x;
*(coords + ((iv * 2) + 1)) = y;
}
if (from_code == NULL)
from_code = "";
if (to_code == NULL)
to_code = "";
add_arc_geometry_to_solution (solution,
arc_id,
from_code,
to_code,
from_id,
to_id,
points,
coords,
geom->Srid,
name);
}
else
error = 1;
gaiaFreeGeomColl (geom);
}
else
error = 1;
}
else if (name != NULL)
set_arc_name_into_solution (solution, arc_id, name);
}
}
sqlite3_finalize (stmt);
tbd -= how_many;
base += how_many;
}
abort:
if (shortest_path)
free (shortest_path);
if (!error && graph->GeometryColumn != NULL)
{
gaiaLinestringPtr ln;
int tot_pts = 0;
RowSolutionPtr pR;
ArcSolutionPtr pA;
int srid = -1;
if (solution->FirstArc)
srid = (solution->FirstArc)->Srid;
pR = solution->First;
while (pR)
{
pA = solution->FirstArc;
while (pA)
{
if (pR->Arc->ArcRowid == pA->ArcRowid)
{
if (pR == solution->First)
tot_pts += pA->Points;
else
tot_pts += (pA->Points - 1);
if (pA->Srid != srid)
srid = -1;
}
pA = pA->Next;
}
pR = pR->Next;
}
ln = gaiaAllocLinestring (tot_pts);
solution->Geometry = gaiaAllocGeomColl ();
solution->Geometry->Srid = srid;
gaiaInsertLinestringInGeomColl (solution->Geometry, ln);
tot_pts = 0;
pR = solution->First;
while (pR)
{
int skip;
if (pR == solution->First)
skip = 0;
else
skip = 1;
pA = solution->FirstArc;
while (pA)
{
if (pR->Arc->ArcRowid == pA->ArcRowid)
{
int ini;
int iv;
int rev;
double x;
double y;
if (graph->NodeCode)
{
if (strcmp
(pR->Arc->NodeFrom->Code,
pA->ToCode) == 0)
rev = 1;
else
rev = 0;
}
else
{
if (pR->Arc->NodeFrom->Id == pA->ToId)
rev = 1;
else
rev = 0;
}
if (rev)
{
if (skip)
ini = pA->Points - 2;
else
ini = pA->Points - 1;
for (iv = ini; iv >= 0; iv--)
{
x = *(pA->Coords + ((iv * 2) + 0));
y = *(pA->Coords + ((iv * 2) + 1));
gaiaSetPoint (ln->Coords, tot_pts, x,
y);
tot_pts++;
}
}
else
{
if (skip)
ini = 1;
else
ini = 0;
for (iv = ini; iv < pA->Points; iv++)
{
x = *(pA->Coords + ((iv * 2) + 0));
y = *(pA->Coords + ((iv * 2) + 1));
gaiaSetPoint (ln->Coords, tot_pts, x,
y);
tot_pts++;
}
}
if (pA->Name)
{
int len = strlen (pA->Name);
pR->Name = malloc (len + 1);
strcpy (pR->Name, pA->Name);
}
break;
}
pA = pA->Next;
}
pR = pR->Next;
}
}
}
static int
find_srid (sqlite3 * handle, NetworkPtr graph)
{
sqlite3_stmt *stmt;
int ret;
int srid = VNET_INVALID_SRID;
char *sql;
if (graph->GeometryColumn == NULL)
return srid;
sql = sqlite3_mprintf ("SELECT srid FROM geometry_columns WHERE "
"Lower(f_table_name) = Lower(%Q) AND Lower(f_geometry_column) = Lower(%Q)",
graph->TableName, graph->GeometryColumn);
ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
if (ret != SQLITE_OK)
return srid;
while (1)
{
ret = sqlite3_step (stmt);
if (ret == SQLITE_DONE)
break;
if (ret == SQLITE_ROW)
srid = sqlite3_column_int (stmt, 0);
}
sqlite3_finalize (stmt);
return srid;
}
static void
build_range_solution (SolutionPtr solution,
RoutingNodePtr * range_nodes, int cnt, int srid)
{
int i;
if (cnt > 0)
{
for (i = 0; i < cnt; i++)
{
add_node_to_solution (solution, range_nodes[i], srid);
}
}
if (range_nodes)
free (range_nodes);
}
static void
dijkstra_solve (sqlite3 * handle, NetworkPtr graph, RoutingNodesPtr routing,
SolutionPtr solution)
{
int cnt;
NetworkArcPtr *shortest_path =
dijkstra_shortest_path (routing, solution->From, solution->To, &cnt);
build_solution (handle, graph, solution, shortest_path, cnt);
}
static void
astar_solve (sqlite3 * handle, NetworkPtr graph, RoutingNodesPtr routing,
SolutionPtr solution)
{
int cnt;
NetworkArcPtr *shortest_path =
astar_shortest_path (routing, graph->Nodes, solution->From,
solution->To, graph->AStarHeuristicCoeff, &cnt);
build_solution (handle, graph, solution, shortest_path, cnt);
}
static void
dijkstra_within_cost_range (RoutingNodesPtr routing, SolutionPtr solution,
int srid)
{
int cnt;
RoutingNodePtr *range_nodes =
dijkstra_range_analysis (routing, solution->From, solution->MaxCost,
&cnt);
build_range_solution (solution, range_nodes, cnt, srid);
}
static void
network_free (NetworkPtr p)
{
NetworkNodePtr pN;
int i;
if (!p)
return;
for (i = 0; i < p->NumNodes; i++)
{
pN = p->Nodes + i;
if (pN->Code)
free (pN->Code);
if (pN->Arcs)
free (pN->Arcs);
}
if (p->Nodes)
free (p->Nodes);
if (p->TableName)
free (p->TableName);
if (p->FromColumn)
free (p->FromColumn);
if (p->ToColumn)
free (p->ToColumn);
if (p->GeometryColumn)
free (p->GeometryColumn);
if (p->NameColumn)
free (p->NameColumn);
free (p);
}
static NetworkPtr
network_init (const unsigned char *blob, int size)
{
NetworkPtr graph;
int net64;
int aStar = 0;
int nodes;
int node_code;
int max_code_length;
int endian_arch = gaiaEndianArch ();
const char *table;
const char *from;
const char *to;
const char *geom;
const char *name = NULL;
double a_star_coeff = 1.0;
int len;
const unsigned char *ptr;
if (size < 9)
return NULL;
if (*(blob + 0) == GAIA_NET_START)
net64 = 0;
else if (*(blob + 0) == GAIA_NET64_START)
net64 = 1;
else if (*(blob + 0) == GAIA_NET64_A_STAR_START)
{
net64 = 1;
aStar = 1;
}
else
return NULL;
if (*(blob + 1) != GAIA_NET_HEADER)
return NULL;
nodes = gaiaImport32 (blob + 2, 1, endian_arch);
if (nodes <= 0)
return NULL;
if (*(blob + 6) == GAIA_NET_CODE)
node_code = 1;
else if (*(blob + 6) == GAIA_NET_ID)
node_code = 0;
else
return NULL;
max_code_length = *(blob + 7);
if (*(blob + 8) != GAIA_NET_TABLE)
return NULL;
ptr = blob + 9;
len = gaiaImport16 (ptr, 1, endian_arch);
ptr += 2;
table = (char *) ptr;
ptr += len;
if (*ptr != GAIA_NET_FROM)
return NULL;
ptr++;
len = gaiaImport16 (ptr, 1, endian_arch);
ptr += 2;
from = (char *) ptr;
ptr += len;
if (*ptr != GAIA_NET_TO)
return NULL;
ptr++;
len = gaiaImport16 (ptr, 1, endian_arch);
ptr += 2;
to = (char *) ptr;
ptr += len;
if (*ptr != GAIA_NET_GEOM)
return NULL;
ptr++;
len = gaiaImport16 (ptr, 1, endian_arch);
ptr += 2;
geom = (char *) ptr;
ptr += len;
if (net64)
{
if (*ptr != GAIA_NET_NAME)
return NULL;
ptr++;
len = gaiaImport16 (ptr, 1, endian_arch);
ptr += 2;
name = (char *) ptr;
ptr += len;
}
if (net64 && aStar)
{
if (*ptr != GAIA_NET_A_STAR_COEFF)
return NULL;
ptr++;
a_star_coeff = gaiaImport64 (ptr, 1, endian_arch);
ptr += 8;
}
if (*ptr != GAIA_NET_END)
return NULL;
graph = malloc (sizeof (Network));
graph->Net64 = net64;
graph->AStar = aStar;
graph->EndianArch = endian_arch;
graph->CurrentIndex = 0;
graph->NodeCode = node_code;
graph->MaxCodeLength = max_code_length;
graph->NumNodes = nodes;
graph->Nodes = malloc (sizeof (NetworkNode) * nodes);
len = strlen (table);
graph->TableName = malloc (len + 1);
strcpy (graph->TableName, table);
len = strlen (from);
graph->FromColumn = malloc (len + 1);
strcpy (graph->FromColumn, from);
len = strlen (to);
graph->ToColumn = malloc (len + 1);
strcpy (graph->ToColumn, to);
len = strlen (geom);
if (len <= 1)
graph->GeometryColumn = NULL;
else
{
graph->GeometryColumn = malloc (len + 1);
strcpy (graph->GeometryColumn, geom);
}
if (!net64)
{
graph->NameColumn = NULL;
}
else
{
len = strlen (name);
if (len <= 1)
graph->NameColumn = NULL;
else
{
graph->NameColumn = malloc (len + 1);
strcpy (graph->NameColumn, name);
}
}
graph->AStarHeuristicCoeff = a_star_coeff;
return graph;
}
static int
network_block (NetworkPtr graph, const unsigned char *blob, int size)
{
const unsigned char *in = blob;
int nodes;
int i;
int ia;
int index;
char code[256];
double x;
double y;
sqlite3_int64 nodeId = -1;
int arcs;
NetworkNodePtr pN;
NetworkArcPtr pA;
int len;
sqlite3_int64 arcId;
int nodeToIdx;
double cost;
if (size < 3)
goto error;
if (*in++ != GAIA_NET_BLOCK)
goto error;
nodes = gaiaImport16 (in, 1, graph->EndianArch);
in += 2;
for (i = 0; i < nodes; i++)
{
if ((size - (in - blob)) < 5)
goto error;
if (*in++ != GAIA_NET_NODE)
goto error;
index = gaiaImport32 (in, 1, graph->EndianArch);
in += 4;
if (index < 0 || index >= graph->NumNodes)
goto error;
if (graph->NodeCode)
{
if ((size - (in - blob)) < graph->MaxCodeLength)
goto error;
memcpy (code, in, graph->MaxCodeLength);
in += graph->MaxCodeLength;
}
else
{
if (graph->Net64)
{
if ((size - (in - blob)) < 8)
goto error;
nodeId = gaiaImportI64 (in, 1, graph->EndianArch);
in += 8;
}
else
{
if ((size - (in - blob)) < 4)
goto error;
nodeId = gaiaImport32 (in, 1, graph->EndianArch);
in += 4;
}
}
if (graph->AStar)
{
if ((size - (in - blob)) < 8)
goto error;
x = gaiaImport64 (in, 1, graph->EndianArch);
in += 8;
if ((size - (in - blob)) < 8)
goto error;
y = gaiaImport64 (in, 1, graph->EndianArch);
in += 8;
}
else
{
x = DBL_MAX;
y = DBL_MAX;
}
if ((size - (in - blob)) < 2)
goto error;
arcs = gaiaImport16 (in, 1, graph->EndianArch);
in += 2;
if (arcs < 0)
goto error;
pN = graph->Nodes + index;
pN->InternalIndex = index;
if (graph->NodeCode)
{
pN->Id = -1;
len = strlen (code);
pN->Code = malloc (len + 1);
strcpy (pN->Code, code);
}
else
{
pN->Id = nodeId;
pN->Code = NULL;
}
pN->CoordX = x;
pN->CoordY = y;
pN->NumArcs = arcs;
if (arcs)
{
pN->Arcs = malloc (sizeof (NetworkArc) * arcs);
for (ia = 0; ia < arcs; ia++)
{
if (graph->Net64)
{
if ((size - (in - blob)) < 22)
goto error;
}
else
{
if ((size - (in - blob)) < 18)
goto error;
}
if (*in++ != GAIA_NET_ARC)
goto error;
if (graph->Net64)
{
arcId = gaiaImportI64 (in, 1, graph->EndianArch);
in += 8;
}
else
{
arcId = gaiaImport32 (in, 1, graph->EndianArch);
in += 4;
}
nodeToIdx = gaiaImport32 (in, 1, graph->EndianArch);
in += 4;
cost = gaiaImport64 (in, 1, graph->EndianArch);
in += 8;
if (*in++ != GAIA_NET_END)
goto error;
pA = pN->Arcs + ia;
if (nodeToIdx < 0 || nodeToIdx >= graph->NumNodes)
goto error;
pA->NodeFrom = pN;
pA->NodeTo = graph->Nodes + nodeToIdx;
pA->ArcRowid = arcId;
pA->Cost = cost;
}
}
else
pN->Arcs = NULL;
if ((size - (in - blob)) < 1)
goto error;
if (*in++ != GAIA_NET_END)
goto error;
}
return 1;
error:
return 0;
}
static NetworkPtr
load_network (sqlite3 * handle, const char *table)
{
NetworkPtr graph = NULL;
sqlite3_stmt *stmt;
char *sql;
int ret;
int header = 1;
const unsigned char *blob;
int size;
char *xname;
xname = gaiaDoubleQuotedSql (table);
sql = sqlite3_mprintf ("SELECT NetworkData FROM \"%s\" ORDER BY Id", xname);
free (xname);
ret = sqlite3_prepare_v2 (handle, sql, strlen (sql), &stmt, NULL);
sqlite3_free (sql);
if (ret != SQLITE_OK)
goto abort;
while (1)
{
ret = sqlite3_step (stmt);
if (ret == SQLITE_DONE)
break;
if (ret == SQLITE_ROW)
{
if (sqlite3_column_type (stmt, 0) == SQLITE_BLOB)
{
blob =
(const unsigned char *) sqlite3_column_blob (stmt, 0);
size = sqlite3_column_bytes (stmt, 0);
if (header)
{
graph = network_init (blob, size);
header = 0;
}
else
{
if (!graph)
{
sqlite3_finalize (stmt);
goto abort;
}
if (!network_block (graph, blob, size))
{
sqlite3_finalize (stmt);
goto abort;
}
}
}
else
{
sqlite3_finalize (stmt);
goto abort;
}
}
else
{
sqlite3_finalize (stmt);
goto abort;
}
}
sqlite3_finalize (stmt);
return graph;
abort:
network_free (graph);
return NULL;
}
static int
vnet_create (sqlite3 * db, void *pAux, int argc, const char *const *argv,
sqlite3_vtab ** ppVTab, char **pzErr)
{
VirtualNetworkPtr p_vt;
int err;
int ret;
int i;
int n_rows;
int n_columns;
char *vtable = NULL;
char *table = NULL;
const char *col_name = NULL;
char **results;
char *err_msg = NULL;
char *sql;
int ok_id;
int ok_data;
char *xname;
NetworkPtr graph = NULL;
if (pAux)
pAux = pAux;
if (argc == 4)
{
vtable = gaiaDequotedSql (argv[2]);
table = gaiaDequotedSql (argv[3]);
}
else
{
*pzErr =
sqlite3_mprintf
("[VirtualNetwork module] CREATE VIRTUAL: illegal arg list {NETWORK-DATAtable}\n");
goto error;
}
err = 0;
ok_id = 0;
ok_data = 0;
xname = gaiaDoubleQuotedSql (table);
sql = sqlite3_mprintf ("PRAGMA table_info(\"%s\")", xname);
free (xname);
ret = sqlite3_get_table (db, sql, &results, &n_rows, &n_columns, &err_msg);
sqlite3_free (sql);
if (ret != SQLITE_OK)
{
err = 1;
goto illegal;
}
if (n_rows > 1)
{
for (i = 1; i <= n_rows; i++)
{
col_name = results[(i * n_columns) + 1];
if (strcasecmp (col_name, "id") == 0)
ok_id = 1;
if (strcasecmp (col_name, "networkdata") == 0)
ok_data = 1;
}
sqlite3_free_table (results);
if (!ok_id)
err = 1;
if (!ok_data)
err = 1;
}
else
err = 1;
illegal:
if (err)
{
*pzErr =
sqlite3_mprintf
("[VirtualNetwork module] cannot build a valid NETWORK\n");
return SQLITE_ERROR;
}
p_vt = (VirtualNetworkPtr) sqlite3_malloc (sizeof (VirtualNetwork));
if (!p_vt)
return SQLITE_NOMEM;
graph = load_network (db, table);
if (!graph)
{
*pzErr =
sqlite3_mprintf
("[VirtualNetwork module] cannot build a valid NETWORK\n");
goto error;
}
p_vt->db = db;
p_vt->graph = graph;
p_vt->currentAlgorithm = VNET_DIJKSTRA_ALGORITHM;
p_vt->routing = NULL;
p_vt->pModule = &my_net_module;
p_vt->nRef = 0;
p_vt->zErrMsg = NULL;
xname = gaiaDoubleQuotedSql (vtable);
if (p_vt->graph->NodeCode)
{
if (p_vt->graph->NameColumn)
{
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (Algorithm TEXT, "
"ArcRowid INTEGER, NodeFrom TEXT, NodeTo TEXT,"
" Cost DOUBLE, Geometry BLOB, Name TEXT)",
xname);
}
else
{
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (Algorithm TEXT, "
"ArcRowid INTEGER, NodeFrom TEXT, NodeTo TEXT,"
" Cost DOUBLE, Geometry BLOB)", xname);
}
}
else
{
if (p_vt->graph->NameColumn)
{
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (Algorithm TEXT, "
"ArcRowid INTEGER, NodeFrom INTEGER, NodeTo INTEGER,"
" Cost DOUBLE, Geometry BLOB, Name TEXT)",
xname);
}
else
{
sql = sqlite3_mprintf ("CREATE TABLE \"%s\" (Algorithm TEXT, "
"ArcRowid INTEGER, NodeFrom INTEGER, NodeTo INTEGER,"
" Cost DOUBLE, Geometry BLOB)", xname);
}
}
free (xname);
if (sqlite3_declare_vtab (db, sql) != SQLITE_OK)
{
*pzErr =
sqlite3_mprintf
("[VirtualNetwork module] CREATE VIRTUAL: invalid SQL statement \"%s\"",
sql);
sqlite3_free (sql);
goto error;
}
sqlite3_free (sql);
*ppVTab = (sqlite3_vtab *) p_vt;
p_vt->routing = routing_init (p_vt->graph);
free (table);
free (vtable);
return SQLITE_OK;
error:
if (table)
free (table);
if (vtable)
free (vtable);
return SQLITE_ERROR;
}
static int
vnet_connect (sqlite3 * db, void *pAux, int argc, const char *const *argv,
sqlite3_vtab ** ppVTab, char **pzErr)
{
return vnet_create (db, pAux, argc, argv, ppVTab, pzErr);
}
static int
vnet_best_index (sqlite3_vtab * pVTab, sqlite3_index_info * pIdxInfo)
{
int i;
int errors = 0;
int err = 1;
int from = 0;
int to = 0;
int cost = 0;
int i_from = -1;
int i_to = -1;
int i_cost = -1;
if (pVTab)
pVTab = pVTab;
for (i = 0; i < pIdxInfo->nConstraint; i++)
{
struct sqlite3_index_constraint *p = &(pIdxInfo->aConstraint[i]);
if (p->usable)
{
if (p->iColumn == 2 && p->op == SQLITE_INDEX_CONSTRAINT_EQ)
{
from++;
i_from = i;
}
else if (p->iColumn == 3 && p->op == SQLITE_INDEX_CONSTRAINT_EQ)
{
to++;
i_to = i;
}
else if (p->iColumn == 4 && p->op == SQLITE_INDEX_CONSTRAINT_LE)
{
cost++;
i_cost = i;
}
else
errors++;
}
}
if (from == 1 && to == 1 && errors == 0)
{
if (i_from < i_to)
pIdxInfo->idxNum = 1;
else
pIdxInfo->idxNum = 2;
pIdxInfo->estimatedCost = 1.0;
for (i = 0; i < pIdxInfo->nConstraint; i++)
{
if (pIdxInfo->aConstraint[i].usable)
{
pIdxInfo->aConstraintUsage[i].argvIndex = i + 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
}
}
err = 0;
}
if (from == 1 && cost == 1 && errors == 0)
{
if (i_from < i_cost)
pIdxInfo->idxNum = 3;
else
pIdxInfo->idxNum = 4;
pIdxInfo->estimatedCost = 1.0;
for (i = 0; i < pIdxInfo->nConstraint; i++)
{
if (pIdxInfo->aConstraint[i].usable)
{
pIdxInfo->aConstraintUsage[i].argvIndex = i + 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
}
}
err = 0;
}
if (err)
{
pIdxInfo->idxNum = 0;
}
return SQLITE_OK;
}
static int
vnet_disconnect (sqlite3_vtab * pVTab)
{
VirtualNetworkPtr p_vt = (VirtualNetworkPtr) pVTab;
if (p_vt->routing)
routing_free (p_vt->routing);
if (p_vt->graph)
network_free (p_vt->graph);
sqlite3_free (p_vt);
return SQLITE_OK;
}
static int
vnet_destroy (sqlite3_vtab * pVTab)
{
return vnet_disconnect (pVTab);
}
static void
vnet_read_row (VirtualNetworkCursorPtr cursor)
{
if (cursor->solution->Mode == VNET_RANGE_SOLUTION)
{
if (cursor->solution->CurrentNodeRow == NULL)
cursor->eof = 1;
else
cursor->eof = 0;
}
else
{
if (cursor->solution->CurrentRow == NULL)
cursor->eof = 1;
else
cursor->eof = 0;
}
return;
}
static int
vnet_open (sqlite3_vtab * pVTab, sqlite3_vtab_cursor ** ppCursor)
{
VirtualNetworkCursorPtr cursor =
(VirtualNetworkCursorPtr)
sqlite3_malloc (sizeof (VirtualNetworkCursor));
if (cursor == NULL)
return SQLITE_ERROR;
cursor->pVtab = (VirtualNetworkPtr) pVTab;
cursor->solution = alloc_solution ();
cursor->eof = 0;
*ppCursor = (sqlite3_vtab_cursor *) cursor;
return SQLITE_OK;
}
static int
vnet_close (sqlite3_vtab_cursor * pCursor)
{
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
delete_solution (cursor->solution);
sqlite3_free (pCursor);
return SQLITE_OK;
}
static int
vnet_filter (sqlite3_vtab_cursor * pCursor, int idxNum, const char *idxStr,
int argc, sqlite3_value ** argv)
{
int node_code = 0;
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
VirtualNetworkPtr net = (VirtualNetworkPtr) cursor->pVtab;
if (idxStr)
idxStr = idxStr;
node_code = net->graph->NodeCode;
reset_solution (cursor->solution);
cursor->eof = 1;
if (idxNum == 1 && argc == 2)
{
if (node_code)
{
if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
cursor->solution->From =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[0]));
if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
cursor->solution->To =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[1]));
}
else
{
if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
cursor->solution->From =
find_node_by_id (net->graph,
sqlite3_value_int (argv[0]));
if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
cursor->solution->To =
find_node_by_id (net->graph,
sqlite3_value_int (argv[1]));
}
}
if (idxNum == 2 && argc == 2)
{
if (node_code)
{
if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
cursor->solution->To =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[0]));
if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
cursor->solution->From =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[1]));
}
else
{
if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
cursor->solution->To =
find_node_by_id (net->graph,
sqlite3_value_int (argv[0]));
if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
cursor->solution->From =
find_node_by_id (net->graph,
sqlite3_value_int (argv[1]));
}
}
if (idxNum == 3 && argc == 2)
{
if (node_code)
{
if (sqlite3_value_type (argv[0]) == SQLITE_TEXT)
cursor->solution->From =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[0]));
}
else
{
if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
cursor->solution->From =
find_node_by_id (net->graph,
sqlite3_value_int (argv[0]));
}
if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
{
int cost = sqlite3_value_int (argv[1]);
cursor->solution->MaxCost = cost;
}
else if (sqlite3_value_type (argv[1]) == SQLITE_FLOAT)
cursor->solution->MaxCost = sqlite3_value_double (argv[1]);
}
if (idxNum == 4 && argc == 2)
{
if (node_code)
{
if (sqlite3_value_type (argv[1]) == SQLITE_TEXT)
cursor->solution->From =
find_node_by_code (net->graph,
(char *)
sqlite3_value_text (argv[1]));
}
else
{
if (sqlite3_value_type (argv[1]) == SQLITE_INTEGER)
cursor->solution->From =
find_node_by_id (net->graph,
sqlite3_value_int (argv[1]));
}
if (sqlite3_value_type (argv[0]) == SQLITE_INTEGER)
{
int cost = sqlite3_value_int (argv[0]);
cursor->solution->MaxCost = cost;
}
else if (sqlite3_value_type (argv[0]) == SQLITE_FLOAT)
cursor->solution->MaxCost = sqlite3_value_double (argv[0]);
}
if (cursor->solution->From && cursor->solution->To)
{
cursor->eof = 0;
cursor->solution->Mode = VNET_ROUTING_SOLUTION;
if (net->currentAlgorithm == VNET_A_STAR_ALGORITHM)
astar_solve (net->db, net->graph, net->routing, cursor->solution);
else
dijkstra_solve (net->db, net->graph, net->routing,
cursor->solution);
return SQLITE_OK;
}
if (cursor->solution->From && cursor->solution->MaxCost > 0.0)
{
int srid = find_srid (net->db, net->graph);
cursor->eof = 0;
cursor->solution->Mode = VNET_RANGE_SOLUTION;
if (net->currentAlgorithm == VNET_DIJKSTRA_ALGORITHM)
{
dijkstra_within_cost_range (net->routing, cursor->solution,
srid);
cursor->solution->CurrentRowId = 0;
cursor->solution->CurrentNodeRow = cursor->solution->FirstNode;
}
return SQLITE_OK;
}
cursor->eof = 0;
cursor->solution->Mode = VNET_ROUTING_SOLUTION;
return SQLITE_OK;
}
static int
vnet_next (sqlite3_vtab_cursor * pCursor)
{
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
if (cursor->solution->Mode == VNET_RANGE_SOLUTION)
{
cursor->solution->CurrentNodeRow =
cursor->solution->CurrentNodeRow->Next;
if (!(cursor->solution->CurrentNodeRow))
{
cursor->eof = 1;
return SQLITE_OK;
}
}
else
{
if (cursor->solution->CurrentRowId == 0)
cursor->solution->CurrentRow = cursor->solution->First;
else
cursor->solution->CurrentRow = cursor->solution->CurrentRow->Next;
if (!(cursor->solution->CurrentRow))
{
cursor->eof = 1;
return SQLITE_OK;
}
}
(cursor->solution->CurrentRowId)++;
vnet_read_row (cursor);
return SQLITE_OK;
}
static int
vnet_eof (sqlite3_vtab_cursor * pCursor)
{
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
return cursor->eof;
}
static int
vnet_column (sqlite3_vtab_cursor * pCursor, sqlite3_context * pContext,
int column)
{
RowSolutionPtr row;
RowNodeSolutionPtr row_node;
int node_code = 0;
const char *algorithm;
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
VirtualNetworkPtr net = (VirtualNetworkPtr) cursor->pVtab;
node_code = net->graph->NodeCode;
if (cursor->solution->Mode == VNET_RANGE_SOLUTION)
{
row_node = cursor->solution->CurrentNodeRow;
if (column == 0)
{
algorithm = "Dijkstra";
sqlite3_result_text (pContext, algorithm, strlen (algorithm),
SQLITE_STATIC);
}
if (column == 1)
{
sqlite3_result_null (pContext);
}
if (column == 2)
{
if (node_code)
sqlite3_result_text (pContext, cursor->solution->From->Code,
strlen (cursor->solution->From->Code),
SQLITE_STATIC);
else
sqlite3_result_int64 (pContext, cursor->solution->From->Id);
}
if (column == 3)
{
if (node_code)
sqlite3_result_text (pContext, row_node->Node->Code,
strlen (row_node->Node->Code),
SQLITE_STATIC);
else
sqlite3_result_int64 (pContext, row_node->Node->Id);
}
if (column == 4)
{
sqlite3_result_double (pContext, row_node->Cost);
}
if (column == 5)
{
if (row_node->Srid == VNET_INVALID_SRID)
sqlite3_result_null (pContext);
else
{
int len;
unsigned char *p_result = NULL;
gaiaGeomCollPtr geom = gaiaAllocGeomColl ();
geom->Srid = row_node->Srid;
gaiaAddPointToGeomColl (geom, row_node->Node->CoordX,
row_node->Node->CoordY);
gaiaToSpatiaLiteBlobWkb (geom, &p_result, &len);
sqlite3_result_blob (pContext, p_result, len, free);
gaiaFreeGeomColl (geom);
}
}
if (column == 6)
{
sqlite3_result_null (pContext);
}
}
else
{
if (cursor->solution->CurrentRow == 0)
{
if (column == 0)
{
if (net->currentAlgorithm == VNET_A_STAR_ALGORITHM)
algorithm = "A*";
else
algorithm = "Dijkstra";
sqlite3_result_text (pContext, algorithm,
strlen (algorithm), SQLITE_STATIC);
}
if (cursor->solution->From == NULL
|| cursor->solution->To == NULL)
{
if (column > 0)
sqlite3_result_null (pContext);
return SQLITE_OK;
}
if (column == 1)
{
sqlite3_result_null (pContext);
}
if (column == 2)
{
if (node_code)
sqlite3_result_text (pContext,
cursor->solution->From->Code,
strlen (cursor->solution->From->
Code), SQLITE_STATIC);
else
sqlite3_result_int64 (pContext,
cursor->solution->From->Id);
}
if (column == 3)
{
if (node_code)
sqlite3_result_text (pContext,
cursor->solution->To->Code,
strlen (cursor->solution->To->
Code), SQLITE_STATIC);
else
sqlite3_result_int64 (pContext,
cursor->solution->To->Id);
}
if (column == 4)
{
sqlite3_result_double (pContext,
cursor->solution->TotalCost);
}
if (column == 5)
{
if (!(cursor->solution->Geometry))
sqlite3_result_null (pContext);
else
{
int len;
unsigned char *p_result = NULL;
gaiaToSpatiaLiteBlobWkb (cursor->solution->Geometry,
&p_result, &len);
sqlite3_result_blob (pContext, p_result, len, free);
}
}
if (column == 6)
{
sqlite3_result_null (pContext);
}
}
else
{
row = cursor->solution->CurrentRow;
if (column == 0)
{
if (net->currentAlgorithm == VNET_A_STAR_ALGORITHM)
algorithm = "A*";
else
algorithm = "Dijkstra";
sqlite3_result_text (pContext, algorithm,
strlen (algorithm), SQLITE_STATIC);
}
if (column == 1)
{
sqlite3_result_int64 (pContext, row->Arc->ArcRowid);
}
if (column == 2)
{
if (node_code)
sqlite3_result_text (pContext,
row->Arc->NodeFrom->Code,
strlen (row->Arc->NodeFrom->
Code), SQLITE_STATIC);
else
sqlite3_result_int64 (pContext,
row->Arc->NodeFrom->Id);
}
if (column == 3)
{
if (node_code)
sqlite3_result_text (pContext, row->Arc->NodeTo->Code,
strlen (row->Arc->NodeTo->Code),
SQLITE_STATIC);
else
sqlite3_result_int64 (pContext, row->Arc->NodeTo->Id);
}
if (column == 4)
{
sqlite3_result_double (pContext, row->Arc->Cost);
}
if (column == 5)
{
sqlite3_result_null (pContext);
}
if (column == 6)
{
if (row->Name)
sqlite3_result_text (pContext, row->Name,
strlen (row->Name),
SQLITE_STATIC);
else
sqlite3_result_null (pContext);
}
}
}
return SQLITE_OK;
}
static int
vnet_rowid (sqlite3_vtab_cursor * pCursor, sqlite_int64 * pRowid)
{
VirtualNetworkCursorPtr cursor = (VirtualNetworkCursorPtr) pCursor;
*pRowid = cursor->solution->CurrentRowId;
return SQLITE_OK;
}
static int
vnet_update (sqlite3_vtab * pVTab, int argc, sqlite3_value ** argv,
sqlite_int64 * pRowid)
{
VirtualNetworkPtr p_vtab = (VirtualNetworkPtr) pVTab;
if (pRowid)
pRowid = pRowid;
if (argc == 1)
{
return SQLITE_READONLY;
}
else
{
if (sqlite3_value_type (argv[0]) == SQLITE_NULL)
{
return SQLITE_READONLY;
}
else
{
if (argc == 9)
{
p_vtab->currentAlgorithm = VNET_DIJKSTRA_ALGORITHM;
if (sqlite3_value_type (argv[2]) == SQLITE_TEXT)
{
const unsigned char *algorithm =
sqlite3_value_text (argv[2]);
if (strcmp ((char *) algorithm, "A*") == 0)
p_vtab->currentAlgorithm =
VNET_A_STAR_ALGORITHM;
if (strcmp ((char *) algorithm, "a*") == 0)
p_vtab->currentAlgorithm =
VNET_A_STAR_ALGORITHM;
}
if (p_vtab->graph->AStar == 0)
p_vtab->currentAlgorithm = VNET_DIJKSTRA_ALGORITHM;
}
return SQLITE_OK;
}
}
return SQLITE_READONLY;
}
static int
vnet_begin (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vnet_sync (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vnet_commit (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vnet_rollback (sqlite3_vtab * pVTab)
{
if (pVTab)
pVTab = pVTab;
return SQLITE_OK;
}
static int
vnet_rename (sqlite3_vtab * pVTab, const char *zNew)
{
if (pVTab)
pVTab = pVTab;
if (zNew)
zNew = zNew;
return SQLITE_ERROR;
}
static int
spliteVirtualNetworkInit (sqlite3 * db)
{
int rc = SQLITE_OK;
my_net_module.iVersion = 1;
my_net_module.xCreate = &vnet_create;
my_net_module.xConnect = &vnet_connect;
my_net_module.xBestIndex = &vnet_best_index;
my_net_module.xDisconnect = &vnet_disconnect;
my_net_module.xDestroy = &vnet_destroy;
my_net_module.xOpen = &vnet_open;
my_net_module.xClose = &vnet_close;
my_net_module.xFilter = &vnet_filter;
my_net_module.xNext = &vnet_next;
my_net_module.xEof = &vnet_eof;
my_net_module.xColumn = &vnet_column;
my_net_module.xRowid = &vnet_rowid;
my_net_module.xUpdate = &vnet_update;
my_net_module.xBegin = &vnet_begin;
my_net_module.xSync = &vnet_sync;
my_net_module.xCommit = &vnet_commit;
my_net_module.xRollback = &vnet_rollback;
my_net_module.xFindFunction = NULL;
my_net_module.xRename = &vnet_rename;
sqlite3_create_module_v2 (db, "VirtualNetwork", &my_net_module, NULL, 0);
return rc;
}
SPATIALITE_PRIVATE int
virtualnetwork_extension_init (void *xdb)
{
sqlite3 *db = (sqlite3 *) xdb;
return spliteVirtualNetworkInit (db);
}