|
@@ -1,7 +1,7 @@
|
|
|
/*!
|
|
|
\file Vlib/sindex.c
|
|
|
|
|
|
- \brief Vector library - spatial index
|
|
|
+ \brief Vector library - select vector features
|
|
|
|
|
|
Higher level functions for reading/writing/manipulating vectors.
|
|
|
|
|
@@ -11,263 +11,349 @@
|
|
|
(>=v2). Read the file COPYING that comes with GRASS for details.
|
|
|
|
|
|
\author Radim Blazek
|
|
|
- \author Martin Landa <landa.martin gmail.com> (storing sidx to file)
|
|
|
*/
|
|
|
|
|
|
#include <grass/config.h>
|
|
|
#include <stdlib.h>
|
|
|
-#include <string.h>
|
|
|
-#include <grass/glocale.h>
|
|
|
#include <grass/gis.h>
|
|
|
#include <grass/vector.h>
|
|
|
-#include <grass/glocale.h>
|
|
|
|
|
|
/*!
|
|
|
- \brief Initialize spatial index structure
|
|
|
+ \brief Select lines by box.
|
|
|
|
|
|
- \param si pointer to spatial index structure
|
|
|
+ Select lines whose boxes overlap specified box!!! It means that
|
|
|
+ selected line may or may not overlap the box.
|
|
|
|
|
|
- \return void
|
|
|
- */
|
|
|
-void Vect_spatial_index_init(SPATIAL_INDEX *si)
|
|
|
-{
|
|
|
- G_debug(1, "Vect_spatial_index_init()");
|
|
|
-
|
|
|
- si->root = RTreeNewIndex();
|
|
|
-}
|
|
|
-
|
|
|
-/*!
|
|
|
- \brief Destroy existing spatial index
|
|
|
-
|
|
|
- Vect_spatial_index_init() must be call before new use.
|
|
|
-
|
|
|
- \param si pointer to spatial index structure
|
|
|
+ \param Map vector map
|
|
|
+ \param Box bounding box
|
|
|
+ \param type line type
|
|
|
+ \param[out] list output list, must be initialized
|
|
|
|
|
|
- \return void
|
|
|
+ \return number of lines
|
|
|
*/
|
|
|
-void Vect_spatial_index_destroy(SPATIAL_INDEX *si)
|
|
|
+int
|
|
|
+Vect_select_lines_by_box(struct Map_info *Map, const BOUND_BOX * Box,
|
|
|
+ int type, struct ilist *list)
|
|
|
{
|
|
|
- G_debug(1, "Vect_spatial_index_destroy()");
|
|
|
-
|
|
|
- RTreeDestroyNode(si->root);
|
|
|
-}
|
|
|
+ int i, line, nlines;
|
|
|
+ struct Plus_head *plus;
|
|
|
+ P_LINE *Line;
|
|
|
+ static struct ilist *LocList = NULL;
|
|
|
|
|
|
-/*!
|
|
|
- \brief Add a new item to spatial index structure
|
|
|
+ G_debug(3, "Vect_select_lines_by_box()");
|
|
|
+ G_debug(3, " Box(N,S,E,W,T,B): %e, %e, %e, %e, %e, %e", Box->N, Box->S,
|
|
|
+ Box->E, Box->W, Box->T, Box->B);
|
|
|
+ plus = &(Map->plus);
|
|
|
|
|
|
- \param[in,out] si pointer to spatial index structure
|
|
|
- \param id item identifier
|
|
|
- \param box pointer to item bounding box
|
|
|
+ if (!(plus->Spidx_built)) {
|
|
|
+ G_debug(3, "Building spatial index.");
|
|
|
+ Vect_build_sidx_from_topo(Map);
|
|
|
+ }
|
|
|
|
|
|
- \return void
|
|
|
- */
|
|
|
-void Vect_spatial_index_add_item(SPATIAL_INDEX *si, int id, const BOUND_BOX *box)
|
|
|
-{
|
|
|
- struct Rect rect;
|
|
|
+ list->n_values = 0;
|
|
|
+ if (!LocList)
|
|
|
+ LocList = Vect_new_list();
|
|
|
+
|
|
|
+ nlines = dig_select_lines(plus, Box, LocList);
|
|
|
+ G_debug(3, " %d lines selected (all types)", nlines);
|
|
|
+
|
|
|
+ /* Remove lines of not requested types */
|
|
|
+ for (i = 0; i < nlines; i++) {
|
|
|
+ line = LocList->value[i];
|
|
|
+ if (plus->Line[line] == NULL)
|
|
|
+ continue; /* Should not happen */
|
|
|
+ Line = plus->Line[line];
|
|
|
+ if (!(Line->type & type))
|
|
|
+ continue;
|
|
|
+ dig_list_add(list, line);
|
|
|
+ }
|
|
|
|
|
|
- G_debug(3, "Vect_spatial_index_add_item(): id = %d", id);
|
|
|
+ G_debug(3, " %d lines of requested type", list->n_values);
|
|
|
|
|
|
- rect.boundary[0] = box->W;
|
|
|
- rect.boundary[1] = box->S;
|
|
|
- rect.boundary[2] = box->B;
|
|
|
- rect.boundary[3] = box->E;
|
|
|
- rect.boundary[4] = box->N;
|
|
|
- rect.boundary[5] = box->T;
|
|
|
- RTreeInsertRect(&rect, id, &(si->root), 0);
|
|
|
+ return list->n_values;
|
|
|
}
|
|
|
|
|
|
/*!
|
|
|
- \brief Delete item from spatial index structure
|
|
|
+ \brief Select areas by box.
|
|
|
|
|
|
- \param[in,out] si pointer to spatial index structure
|
|
|
- \param id item identifier
|
|
|
+ Select areas whose boxes overlap specified box!!!
|
|
|
+ It means that selected area may or may not overlap the box.
|
|
|
|
|
|
- \return void
|
|
|
+ \param Map vector map
|
|
|
+ \param Box bounding box
|
|
|
+ \param[out] output list, must be initialized
|
|
|
+
|
|
|
+ \return number of areas
|
|
|
*/
|
|
|
-void Vect_spatial_index_del_item(SPATIAL_INDEX * si, int id)
|
|
|
+int
|
|
|
+Vect_select_areas_by_box(struct Map_info *Map, const BOUND_BOX * Box,
|
|
|
+ struct ilist *list)
|
|
|
{
|
|
|
- int ret;
|
|
|
- struct Rect rect;
|
|
|
+ int i;
|
|
|
+ const char *dstr;
|
|
|
+ int debug_level;
|
|
|
|
|
|
- G_debug(3, "Vect_spatial_index_del_item(): id = %d", id);
|
|
|
+ dstr = G__getenv("DEBUG");
|
|
|
|
|
|
- /* TODO */
|
|
|
- G_fatal_error("Vect_spatial_index_del_item() %s", _("not implemented"));
|
|
|
+ if (dstr != NULL)
|
|
|
+ debug_level = atoi(dstr);
|
|
|
+ else
|
|
|
+ debug_level = 0;
|
|
|
|
|
|
- /* Bounding box of item would be needed, which is not stored in si. */
|
|
|
+ G_debug(3, "Vect_select_areas_by_box()");
|
|
|
+ G_debug(3, "Box(N,S,E,W,T,B): %e, %e, %e, %e, %e, %e", Box->N, Box->S,
|
|
|
+ Box->E, Box->W, Box->T, Box->B);
|
|
|
|
|
|
- /*
|
|
|
- rect.boundary[0] = ; rect.boundary[1] = ; rect.boundary[2] = ;
|
|
|
- rect.boundary[3] = ; rect.boundary[4] = ; rect.boundary[5] = ;
|
|
|
- */
|
|
|
+ if (!(Map->plus.Spidx_built)) {
|
|
|
+ G_debug(3, "Building spatial index.");
|
|
|
+ Vect_build_sidx_from_topo(Map);
|
|
|
+ }
|
|
|
|
|
|
- ret = RTreeDeleteRect(&rect, id, &(si->root));
|
|
|
+ dig_select_areas(&(Map->plus), Box, list);
|
|
|
+ G_debug(3, " %d areas selected", list->n_values);
|
|
|
+ /* avoid loop when not debugging */
|
|
|
+ if (debug_level > 2) {
|
|
|
+ for (i = 0; i < list->n_values; i++) {
|
|
|
+ G_debug(3, " area = %d pointer to area structure = %lx",
|
|
|
+ list->value[i],
|
|
|
+ (unsigned long)Map->plus.Area[list->value[i]]);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- if (ret)
|
|
|
- G_fatal_error(_("Unable to delete item %d from spatial index"), id);
|
|
|
+ return list->n_values;
|
|
|
}
|
|
|
|
|
|
+
|
|
|
/*!
|
|
|
- \brief Create spatial index if necessary.
|
|
|
+ \brief Select isles by box.
|
|
|
|
|
|
- To be used in modules.
|
|
|
- Map must be opened on level 2.
|
|
|
+ Select isles whose boxes overlap specified box!!!
|
|
|
+ It means that selected isle may or may not overlap the box.
|
|
|
|
|
|
- \param[in,out] Map pointer to vector map
|
|
|
-
|
|
|
- \return 0 OK
|
|
|
- \return 1 error
|
|
|
+ \param Map vector map
|
|
|
+ \param Box bounding box
|
|
|
+ \param[out] list output list, must be initialized
|
|
|
+
|
|
|
+ \return number of isles
|
|
|
*/
|
|
|
-int Vect_build_spatial_index(struct Map_info *Map)
|
|
|
+int
|
|
|
+Vect_select_isles_by_box(struct Map_info *Map, const BOUND_BOX * Box,
|
|
|
+ struct ilist *list)
|
|
|
{
|
|
|
- if (Map->level < 2) {
|
|
|
- G_fatal_error(_("Unable to build spatial index from topology, "
|
|
|
- "vector map is not opened at topology level 2"));
|
|
|
- }
|
|
|
+ G_debug(3, "Vect_select_isles_by_box()");
|
|
|
+ G_debug(3, "Box(N,S,E,W,T,B): %e, %e, %e, %e, %e, %e", Box->N, Box->S,
|
|
|
+ Box->E, Box->W, Box->T, Box->B);
|
|
|
+
|
|
|
if (!(Map->plus.Spidx_built)) {
|
|
|
- return (Vect_build_sidx_from_topo(Map));
|
|
|
+ G_debug(3, "Building spatial index.");
|
|
|
+ Vect_build_sidx_from_topo(Map);
|
|
|
}
|
|
|
- return 0;
|
|
|
+
|
|
|
+ dig_select_isles(&(Map->plus), Box, list);
|
|
|
+ G_debug(3, " %d isles selected", list->n_values);
|
|
|
+
|
|
|
+ return list->n_values;
|
|
|
}
|
|
|
|
|
|
/*!
|
|
|
- \brief Create spatial index from topology if necessary
|
|
|
+ \brief Select nodes by box.
|
|
|
|
|
|
- \param Map pointer to vector map
|
|
|
+ \param Map vector map
|
|
|
+ \param Box bounding box
|
|
|
+ \param[out] list output list, must be initialized
|
|
|
|
|
|
- \return 0 OK
|
|
|
- \return 1 error
|
|
|
+ \return number of nodes
|
|
|
*/
|
|
|
-int Vect_build_sidx_from_topo(struct Map_info *Map)
|
|
|
+int
|
|
|
+Vect_select_nodes_by_box(struct Map_info *Map, const BOUND_BOX * Box,
|
|
|
+ struct ilist *list)
|
|
|
{
|
|
|
- int i, total, done;
|
|
|
struct Plus_head *plus;
|
|
|
- BOUND_BOX box;
|
|
|
- P_LINE *Line;
|
|
|
- P_NODE *Node;
|
|
|
- P_AREA *Area;
|
|
|
- P_ISLE *Isle;
|
|
|
|
|
|
- G_debug(3, "Vect_build_sidx_from_topo()");
|
|
|
+ G_debug(3, "Vect_select_nodes_by_box()");
|
|
|
+ G_debug(3, "Box(N,S,E,W,T,B): %e, %e, %e, %e, %e, %e", Box->N, Box->S,
|
|
|
+ Box->E, Box->W, Box->T, Box->B);
|
|
|
|
|
|
plus = &(Map->plus);
|
|
|
|
|
|
- dig_spidx_init(plus);
|
|
|
-
|
|
|
- total = plus->n_nodes + plus->n_lines + plus->n_areas + plus->n_isles;
|
|
|
-
|
|
|
- /* Nodes */
|
|
|
- for (i = 1; i <= plus->n_nodes; i++) {
|
|
|
- G_percent(i, total, 3);
|
|
|
-
|
|
|
- Node = plus->Node[i];
|
|
|
- if (!Node)
|
|
|
- G_fatal_error(_("BUG (Vect_build_sidx_from_topo): node does not exist"));
|
|
|
-
|
|
|
- dig_spidx_add_node(plus, i, Node->x, Node->y, Node->z);
|
|
|
+ if (!(plus->Spidx_built)) {
|
|
|
+ G_debug(3, "Building spatial index.");
|
|
|
+ Vect_build_sidx_from_topo(Map);
|
|
|
}
|
|
|
|
|
|
- /* Lines */
|
|
|
- done = plus->n_nodes;
|
|
|
- for (i = 1; i <= plus->n_lines; i++) {
|
|
|
- G_percent(done + i, total, 3);
|
|
|
+ list->n_values = 0;
|
|
|
|
|
|
- Line = plus->Line[i];
|
|
|
- if (!Line)
|
|
|
- G_fatal_error(_("BUG (Vect_build_sidx_from_topo): line does not exist"));
|
|
|
+ dig_select_nodes(plus, Box, list);
|
|
|
+ G_debug(3, " %d nodes selected", list->n_values);
|
|
|
|
|
|
- box.N = Line->N;
|
|
|
- box.S = Line->S;
|
|
|
- box.E = Line->E;
|
|
|
- box.W = Line->W;
|
|
|
- box.T = Line->T;
|
|
|
- box.B = Line->B;
|
|
|
-
|
|
|
- dig_spidx_add_line(plus, i, &box);
|
|
|
- }
|
|
|
-
|
|
|
- /* Areas */
|
|
|
- done += plus->n_lines;
|
|
|
- for (i = 1; i <= plus->n_areas; i++) {
|
|
|
- G_percent(done + i, total, 3);
|
|
|
-
|
|
|
- Area = plus->Area[i];
|
|
|
- if (!Area)
|
|
|
- G_fatal_error(_("BUG (Vect_build_sidx_from_topo): area does not exist"));
|
|
|
-
|
|
|
- box.N = Area->N;
|
|
|
- box.S = Area->S;
|
|
|
- box.E = Area->E;
|
|
|
- box.W = Area->W;
|
|
|
- box.T = Area->T;
|
|
|
- box.B = Area->B;
|
|
|
-
|
|
|
- dig_spidx_add_area(plus, i, &box);
|
|
|
- }
|
|
|
+ return list->n_values;
|
|
|
+}
|
|
|
|
|
|
- /* Isles */
|
|
|
- done += plus->n_areas;
|
|
|
- for (i = 1; i <= plus->n_isles; i++) {
|
|
|
- G_percent(done + i, total, 3);
|
|
|
+/*!
|
|
|
+ \brief Select lines by Polygon with optional isles.
|
|
|
|
|
|
- Isle = plus->Isle[i];
|
|
|
- if (!Isle)
|
|
|
- G_fatal_error(_("BUG (Vect_build_sidx_from_topo): isle does not exist"));
|
|
|
+ Polygons should be closed, i.e. first and last points must be identical.
|
|
|
|
|
|
- box.N = Isle->N;
|
|
|
- box.S = Isle->S;
|
|
|
- box.E = Isle->E;
|
|
|
- box.W = Isle->W;
|
|
|
- box.T = Isle->T;
|
|
|
- box.B = Isle->B;
|
|
|
+ \param Map vector map
|
|
|
+ \param Polygon outer ring
|
|
|
+ \param nisles number of islands or 0
|
|
|
+ \param Isles array of islands or NULL
|
|
|
+ \param type line type
|
|
|
+ \param[out] list output list, must be initialised
|
|
|
|
|
|
- dig_spidx_add_isle(plus, i, &box);
|
|
|
+ \return number of lines
|
|
|
+ */
|
|
|
+int
|
|
|
+Vect_select_lines_by_polygon(struct Map_info *Map, struct line_pnts *Polygon,
|
|
|
+ int nisles, struct line_pnts **Isles, int type,
|
|
|
+ struct ilist *List)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+ BOUND_BOX box;
|
|
|
+ static struct line_pnts *LPoints = NULL;
|
|
|
+ static struct ilist *LocList = NULL;
|
|
|
+
|
|
|
+ /* TODO: this function was not tested with isles */
|
|
|
+ G_debug(3, "Vect_select_lines_by_polygon() nisles = %d", nisles);
|
|
|
+
|
|
|
+ List->n_values = 0;
|
|
|
+ if (!LPoints)
|
|
|
+ LPoints = Vect_new_line_struct();
|
|
|
+ if (!LocList)
|
|
|
+ LocList = Vect_new_list();
|
|
|
+
|
|
|
+ /* Select first all lines by box */
|
|
|
+ dig_line_box(Polygon, &box);
|
|
|
+ Vect_select_lines_by_box(Map, &box, type, LocList);
|
|
|
+ G_debug(3, " %d lines selected by box", LocList->n_values);
|
|
|
+
|
|
|
+ /* Check all lines if intersect the polygon */
|
|
|
+ for (i = 0; i < LocList->n_values; i++) {
|
|
|
+ int j, line, intersect = 0;
|
|
|
+
|
|
|
+ line = LocList->value[i];
|
|
|
+ /* Read line points */
|
|
|
+ Vect_read_line(Map, LPoints, NULL, line);
|
|
|
+
|
|
|
+ /* Check if any of line vertices is within polygon */
|
|
|
+ for (j = 0; j < LPoints->n_points; j++) {
|
|
|
+ if (Vect_point_in_poly(LPoints->x[j], LPoints->y[j], Polygon) >= 1) { /* inside polygon */
|
|
|
+ int k, inisle = 0;
|
|
|
+
|
|
|
+ for (k = 0; k < nisles; k++) {
|
|
|
+ if (Vect_point_in_poly(LPoints->x[j], LPoints->y[j], Isles[k]) >= 1) { /* in isle */
|
|
|
+ inisle = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!inisle) { /* inside polygon, outside isles -> select */
|
|
|
+ intersect = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (intersect) {
|
|
|
+ dig_list_add(List, line);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check intersections of the line with area/isles boundary */
|
|
|
+ /* Outer boundary */
|
|
|
+ if (Vect_line_check_intersection(LPoints, Polygon, 0)) {
|
|
|
+ dig_list_add(List, line);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Islands */
|
|
|
+ for (j = 0; j < nisles; j++) {
|
|
|
+ if (Vect_line_check_intersection(LPoints, Isles[j], 0)) {
|
|
|
+ intersect = 1;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (intersect) {
|
|
|
+ dig_list_add(List, line);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- Map->plus.Spidx_built = 1;
|
|
|
+ G_debug(4, " %d lines selected by polygon", List->n_values);
|
|
|
|
|
|
- G_debug(3, "Spatial index was built");
|
|
|
-
|
|
|
- return 0;
|
|
|
+ return List->n_values;
|
|
|
}
|
|
|
|
|
|
-/************************* SELECT BY BOX *********************************/
|
|
|
-/* This function is called by RTreeSearch() to add selected item to the list */
|
|
|
-static int _add_item(int id, struct ilist *list)
|
|
|
-{
|
|
|
- dig_list_add(list, id);
|
|
|
- return 1;
|
|
|
-}
|
|
|
|
|
|
/*!
|
|
|
- \brief Select items by bounding box to list
|
|
|
+ \brief Select areas by Polygon with optional isles.
|
|
|
+
|
|
|
+ Polygons should be closed, i.e. first and last points must be identical.
|
|
|
|
|
|
- \param si pointer to spatial index structure
|
|
|
- \param box bounding box
|
|
|
- \param[out] list pointer to list where selected items are stored
|
|
|
+ Warning : values in list may be duplicate!
|
|
|
|
|
|
- \return number of selected items
|
|
|
+ \param Map vector map
|
|
|
+ \param Polygon outer ring
|
|
|
+ \param nisles number of islands or 0
|
|
|
+ \param Isles array of islands or NULL
|
|
|
+ \param[out] list output list, must be initialised
|
|
|
+
|
|
|
+ \return number of areas
|
|
|
*/
|
|
|
int
|
|
|
-Vect_spatial_index_select(const SPATIAL_INDEX * si, const BOUND_BOX * box,
|
|
|
- struct ilist *list)
|
|
|
+Vect_select_areas_by_polygon(struct Map_info *Map, struct line_pnts *Polygon,
|
|
|
+ int nisles, struct line_pnts **Isles,
|
|
|
+ struct ilist *List)
|
|
|
{
|
|
|
- struct Rect rect;
|
|
|
-
|
|
|
- G_debug(3, "Vect_spatial_index_select()");
|
|
|
-
|
|
|
- Vect_reset_list(list);
|
|
|
+ int i, area;
|
|
|
+ static struct ilist *BoundList = NULL;
|
|
|
+
|
|
|
+ /* TODO: this function was not tested with isles */
|
|
|
+ G_debug(3, "Vect_select_areas_by_polygon() nisles = %d", nisles);
|
|
|
+
|
|
|
+ List->n_values = 0;
|
|
|
+ if (!BoundList)
|
|
|
+ BoundList = Vect_new_list();
|
|
|
+
|
|
|
+ /* Select boundaries by polygon */
|
|
|
+ Vect_select_lines_by_polygon(Map, Polygon, nisles, Isles, GV_BOUNDARY,
|
|
|
+ BoundList);
|
|
|
+
|
|
|
+ /* Add areas on left/right side of selected boundaries */
|
|
|
+ for (i = 0; i < BoundList->n_values; i++) {
|
|
|
+ int line, left, right;
|
|
|
+
|
|
|
+ line = BoundList->value[i];
|
|
|
+
|
|
|
+ Vect_get_line_areas(Map, line, &left, &right);
|
|
|
+ G_debug(4, "boundary = %d left = %d right = %d", line, left, right);
|
|
|
+
|
|
|
+ if (left > 0) {
|
|
|
+ dig_list_add(List, left);
|
|
|
+ }
|
|
|
+ else if (left < 0) { /* island */
|
|
|
+ area = Vect_get_isle_area(Map, abs(left));
|
|
|
+ G_debug(4, " left island -> area = %d", area);
|
|
|
+ if (area > 0)
|
|
|
+ dig_list_add(List, area);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (right > 0) {
|
|
|
+ dig_list_add(List, right);
|
|
|
+ }
|
|
|
+ else if (right < 0) { /* island */
|
|
|
+ area = Vect_get_isle_area(Map, abs(right));
|
|
|
+ G_debug(4, " right island -> area = %d", area);
|
|
|
+ if (area > 0)
|
|
|
+ dig_list_add(List, area);
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- rect.boundary[0] = box->W;
|
|
|
- rect.boundary[1] = box->S;
|
|
|
- rect.boundary[2] = box->B;
|
|
|
- rect.boundary[3] = box->E;
|
|
|
- rect.boundary[4] = box->N;
|
|
|
- rect.boundary[5] = box->T;
|
|
|
- RTreeSearch(si->root, &rect, (void *)_add_item, list);
|
|
|
+ /* But the Polygon may be completely inside the area (only one), in that case
|
|
|
+ * we find the area by one polygon point and add it to the list */
|
|
|
+ area = Vect_find_area(Map, Polygon->x[0], Polygon->y[0]);
|
|
|
+ if (area > 0)
|
|
|
+ dig_list_add(List, area);
|
|
|
|
|
|
- G_debug(3, " %d items selected", list->n_values);
|
|
|
+ G_debug(3, " %d areas selected by polygon", List->n_values);
|
|
|
|
|
|
- return (list->n_values);
|
|
|
+ return List->n_values;
|
|
|
}
|
|
|
-
|