All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.conversantmedia.util.collection.spatial.Leaf Maven / Gradle / Ivy

package com.conversantmedia.util.collection.spatial;

/*
 * #%L
 * Conversant RTree
 * ~~
 * Conversantmedia.com © 2016, Conversant, Inc. Conversant® is a trademark of Conversant, Inc.
 * ~~
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import java.util.function.Consumer;

/**
 * Node that will contain the data entries. Implemented by different type of SplitType leaf classes.
 *
 * Created by jcairns on 4/30/15.
 */
abstract class Leaf implements Node {
    protected final int mMax;       // max entries per node
    protected final int mMin;       // least number of entries per node
    protected HyperRect mbr;
    protected final HyperRect[] r;
    protected final T[]    entry;
    protected final RectBuilder builder;
    protected int size;
    protected RTree.Split splitType;

    protected Leaf(final RectBuilder builder, final int mMin, final int mMax, final RTree.Split splitType) {
        this.mMin = mMin;
        this.mMax = mMax;
        this.mbr = null;
        this.builder = builder;
        this.r  = new HyperRect[mMax];
        this.entry = (T[]) new Object[mMax];
        this.size = 0;
        this.splitType = splitType;
    }

    @Override
    public Node add(final T t) {
        if(size < mMax) {
            final HyperRect tRect = builder.getBBox(t);
            if(mbr != null) {
                mbr = mbr.getMbr(tRect);
            } else {
                mbr = tRect;
            }

            r[size] = tRect;
            entry[size++] = t;
        } else {
            for(int i = 0; i < size; i++){
                if(entry[i] == null){
                    entry[i] = t;
                    r[i] = builder.getBBox(t);
                    mbr = mbr.getMbr(r[i]);
                    return this;
                }
            }
            return split(t);
        }

        return this;
    }

    @Override
    public Node remove(final T t) {
        for(int i = 0; i < size; i++){
            if(entry[i].equals(t)){
                entry[i] = null;
                r[i] = null;
                if(i < (size-1)){
                    entry[i] = entry[size-1];
                    r[i] = r[size-1];
                    entry[size-1] = null;
                    r[size-1] = null;
                }
                size--;
                if(size > 0) {
                    mbr = r[0];
                    for (i = 1; i < size; i++) {
                        mbr = mbr.getMbr(r[i]);
                    }
                }
                return this;
            }
        }
        return null;
    }

    @Override
    public Node update(final T told, final T tnew) {

        remove(told);
        add(tnew);

        return this;
    }

    @Override
    public int search(final HyperRect rect, final T[] t, int n) {
        final int tLen = t.length;
        final int n0 = n;

        for(int i=0; i Node create(final RectBuilder builder, final int mMin, final int M, final RTree.Split splitType) {

        switch(splitType) {
            case AXIAL:
                return new AxialSplitLeaf<>(builder, mMin, M);
            case LINEAR:
                return new LinearSplitLeaf<>(builder, mMin, M);
            case QUADRATIC:
                return new QuadraticSplitLeaf<>(builder, mMin, M);
            default:
                return new AxialSplitLeaf<>(builder, mMin, M);

        }
    }

    /**
     * Splits a lead node that has the maximum number of entries into 2 leaf nodes of the same type with half
     * of the entries in each one.
     *
     * @param t entry to be added to the full leaf node
     * @return newly created node storing half the entries of this node
     */
    protected abstract Node split(final T t);

    @Override
    public void forEach(Consumer consumer) {
        for(int i = 0; i < size; i++) {
            consumer.accept(entry[i]);
        }
    }

    @Override
    public void forEach(Consumer consumer, HyperRect rect) {
        for(int i = 0; i < size; i++) {
            if(rect.intersects(r[i])) {
                consumer.accept(entry[i]);
            }
        }
    }

    @Override
    public void collectStats(Stats stats, int depth) {
        if (depth > stats.getMaxDepth()) {
            stats.setMaxDepth(depth);
        }
        stats.countLeafAtDepth(depth);
        stats.countEntriesAtDepth(size, depth);
    }

    /**
     * Figures out which newly made leaf node (see split method) to add a data entry to.
     *
     * @param l1Node left node
     * @param l2Node right node
     * @param t data entry to be added
     */
    protected final void classify(final Node l1Node, final Node l2Node, final T t) {
        final HyperRect tRect = builder.getBBox(t);
        final HyperRect l1Mbr = l1Node.getRect().getMbr(tRect);
        final HyperRect l2Mbr = l2Node.getRect().getMbr(tRect);
        final double l1CostInc = Math.max(l1Mbr.cost() - (l1Node.getRect().cost() + tRect.cost()), 0.0);
        final double l2CostInc = Math.max(l2Mbr.cost() - (l2Node.getRect().cost() + tRect.cost()), 0.0);
        if(l2CostInc > l1CostInc) {
            l1Node.add(t);
        }
        else if(RTree.isEqual(l1CostInc, l2CostInc)) {
            final double l1MbrCost = l1Mbr.cost();
            final double l2MbrCost = l2Mbr.cost();
            if(l1MbrCost < l2MbrCost) {
                l1Node.add(t);
            } else if(RTree.isEqual(l1MbrCost, l2MbrCost)) {
                final double l1MbrMargin = l1Mbr.perimeter();
                final double l2MbrMargin = l2Mbr.perimeter();
                if(l1MbrMargin < l2MbrMargin) {
                    l1Node.add(t);
                } else if(RTree.isEqual(l1MbrMargin, l2MbrMargin)) {
                    // break ties with least number
                    if (l1Node.size() < l2Node.size()) {
                        l1Node.add(t);
                    } else {
                        l2Node.add(t);
                    }
                } else {
                    l2Node.add(t);
                }
            } else {
                l2Node.add(t);
            }
        }
        else {
            l2Node.add(t);
        }

    }

    @Override
    public Node instrument() {
        return new CounterNode<>(this);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy