/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.caching.grid.spatialindex; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.geotools.caching.spatialindex.AbstractSpatialIndex; import org.geotools.caching.spatialindex.NodeIdentifier; import org.geotools.caching.spatialindex.Region; import org.geotools.caching.spatialindex.RegionNodeIdentifier; import org.geotools.caching.spatialindex.Shape; /** * The root node of a grid, which has n GridNodes as children. As GridNodes do, * it can store data too. * * @author Christophe Rousson, SoC 2007, CRG-ULAVAL * * * * * @source $URL$ */ public class GridRootNode extends GridNode { private static final long serialVersionUID = 4675163856302389522L; protected double tiles_size; protected int[] tiles_number; protected ArrayList<NodeIdentifier> children; // list of NodeIdentifiers protected int childrenCapacity = -1; /** * No-arg constructor for serialization purpose. Deserialized nodes must * call setIdentifier before using * */ GridRootNode() { super(); } /** * the mbr is read from the id; */ protected GridRootNode(int gridsize, RegionNodeIdentifier id) { super(id); init(gridsize); } private void init(int gridsize) { int dims = getShape().getDimension(); Region mbr = (Region) getShape(); tiles_number = new int[dims]; double area = 1; for (int i = 0; i < dims; i++) { area *= (mbr.getHigh(i) - mbr.getLow(i)); } tiles_size = Math.pow(area / gridsize, 1d / dims); int newcapacity = 1; for (int i = 0; i < dims; i++) { int tmp; double dtmp = (mbr.getHigh(i) - mbr.getLow(i)) / tiles_size; tmp = (int) dtmp; if (tmp < dtmp) { tmp += 1; } tiles_number[i] = tmp; newcapacity *= tmp; } assert (newcapacity >= gridsize); // capacity = newcapacity; childrenCapacity = newcapacity; children = new ArrayList<NodeIdentifier>(childrenCapacity); } /** * Creates the grid by appending children to this node. * */ protected void split(GridSpatialIndex index) { int dims = tiles_number.length; double[] pos = new double[dims]; double[] nextpos = new double[dims]; Region mbr = (Region) getShape(); // int id = 0; for (int i = 0; i < dims; i++) { pos[i] = mbr.getLow(i); nextpos[i] = pos[i] + tiles_size; } do { Region reg = new Region(pos, nextpos); RegionNodeIdentifier id = (RegionNodeIdentifier) index .findUniqueInstance(new RegionNodeIdentifier(reg)); GridNode child = createNode(id); index.writeNode(child); this.children.add(child.getIdentifier()); // id++; } while (increment(pos, nextpos)); } public List<Integer> getChildren(Shape shape) { Region mbr = (Region) getShape(); Region childMBR = shape.getMBR(); int dims = mbr.getDimension(); int[] gridminindex = new int[dims]; for (int i = 0; i < gridminindex.length; i++) { if (childMBR.getLow(i) <= mbr.getLow(i)) { gridminindex[i] = 0; } else { gridminindex[i] = (int) Math.floor((childMBR.getLow(i) - mbr.getLow(i))/ tiles_size); } } int[] gridmaxindex = new int[dims]; for (int i = 0; i < gridminindex.length; i++) { if (childMBR.getHigh(i) >= mbr.getHigh(i)) { gridmaxindex[i] = tiles_number[i] - 1; } else { gridmaxindex[i] = (int) Math.floor((childMBR.getHigh(i) - mbr.getLow(i))/ tiles_size); } } // want to iterate from gridminindex to gridmaxindex int current[] = gridminindex.clone(); return iterate(current, gridminindex, gridmaxindex, 0); } private List<Integer> iterate(int current[], int min[], int max[], int index) { ArrayList<Integer> values = new ArrayList<Integer>(); if (index >= min.length) { return Collections.singletonList(new Integer(this.gridIndexToNodeId(current))); } for (int i = min[index]; i <= max[index]; i++) { current[index] = i; values.addAll(iterate(current, min, max, index + 1)); } return values; } protected GridNode createNode(RegionNodeIdentifier id) { // return new GridNode(grid, reg); return new GridNode(id); } /** * Computes sequentially the corner position of each tile in the grid. * * @param pos * @param nextpos * @return false if the upperright corner of the grid has been reached, true * otherwise */ boolean increment(double[] pos, double[] nextpos) { Region mbr = (Region) getShape(); int dims = pos.length; if ((dims != tiles_number.length) || (nextpos.length != tiles_number.length)) { throw new IllegalArgumentException( "Cursor has not the same dimension as grid."); } for (int i = 0; i < dims; i++) { if (((nextpos[i] - mbr.getHigh(i)) > 0) || (Math.abs(nextpos[i] - mbr.getHigh(i)) < AbstractSpatialIndex.EPSILON)) { pos[i] = mbr.getLow(i); nextpos[i] = pos[i] + tiles_size; if (i == (dims - 1)) { return false; } } else { pos[i] = nextpos[i]; nextpos[i] = pos[i] + tiles_size; break; } } return true; } public String toString() { StringBuffer sb = new StringBuffer(); // sb.append("GridRootNode: capacity:" + capacity + ", MBR:" + mbr); sb.append("GridRootNode: MBR: " + this.getShape() + ", capacity: " + getCapacity()); return sb.toString(); } public NodeIdentifier getChildIdentifier(int index) throws IndexOutOfBoundsException { return (NodeIdentifier) children.get(index); } public void setChildIdentifier(int index, NodeIdentifier id) { children.set(index, id); } public int getChildrenCount() { return children.size(); } public int getLevel() { return 1; } public boolean isIndex() { if (children.isEmpty()) { return false; } else { return true; } } public boolean isLeaf() { return !isIndex(); } /** * Converts an array of indexes into the id of a node. * * @param index * @return */ public int gridIndexToNodeId(int[] index) { if (index.length != tiles_number.length) { throw new IllegalArgumentException("Argument has " + index.length + " dimensions whereas grid has " + tiles_number.length); } else { int result = 0; int offset = 1; for (int i = 0; i < index.length; i++) { result += (offset * index[i]); offset *= tiles_number[i]; } return result; } } /** * Only clears data the root node; does not clear the children. */ public void clear() { super.clear(); } public String toReadableText() { StringBuffer sb = new StringBuffer(); sb.append("RootNode *****"); sb.append(super.toReadableText()); return sb.toString(); } /** * The maximum of allowable nodes in the grid. * * @return */ public int getCapacity() { return this.childrenCapacity; } /** * The number of tiles/nodes in a given dimension. * * @param dim * @return */ public int getMaximumTileCount(int dim) { if (dim < 0 || dim > tiles_number.length) { return -1; } return tiles_number[dim]; } /** * @returns the size of the children tiles */ public double getTileSize() { return this.tiles_size; } // public void printGrid(){ // System.out.println(makePolygon((Region)this.getShape())); // // for(int i = 0; i < getChildrenCount(); i ++){ // NodeIdentifier id = this.getChildIdentifier(i); // System.out.println(makePolygon((Region)id.getShape())); // } // } // // public String makePolygon(Region r){ // double minx = r.getLow(0); // double maxx = r.getHigh(0); // double miny = r.getLow(1); // double maxy = r.getHigh(1); // return "POLYGON(( " + minx + " " + miny + ", " + minx + " " + maxy + "," + maxx + " " + maxy + "," + maxx + " " + miny + "," + minx + " " + miny + "))"; // } }