Open In App

Understanding Efficient Spatial Indexing

Last Updated : 16 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In this article, we will be understanding Efficient Spatial Indexing. Efficient spatial indexing is a critical component of data structures and databases. It deals with organizing and accessing spatial data, and spatial data consists of objects with geographic or geometric coordinates such as points, lines, and polygons and spatial index structures are designed with the objective of increasing query performance and reducing computation time when dealing with large spatial datasets.

  • Spatial indexing is a technique used to organize and access spatial data efficiently. It is particularly important when dealing with large datasets of points, lines, or polygons in two-dimensional or higher-dimensional spaces.
  • The Spatial indexing structures help reduce the time complexity of the spatial queries from O(N) to O(log N) or better. where N is the number of spatial objects.

Popular spatial indexing structures are:

  • Quadtree: A hierarchical tree data structure that recursively subdivides a 2D space into quadrants. Each node in a quadtree represents a quadrant and can contain one or more spatial objects. Quadtree is mainly used for two-dimensional data.
  • R-tree: A balanced tree data structure that represents bounding rectangles of spatial objects. R-tree is the more flexible and can be used for both two-dimensional and higher-dimensional data. It groups spatial objects hierarchically based on their bounding rectangles.
  • KD Tree: The KD Tree is a binary tree data structure used for organizing points in a k-dimensional space. It is primarily used for efficient range searches and nearest neighbor searches in multi-dimensional datasets.
  • Geo Hash: The Geo Hash is a technique used to convert geographical coordinates into a single-string representation. This representation can then be used to index and search spatial data efficiently. It works by dividing the Earth’s surface into a grid of cells and assigning a unique hash code to each cell based on its coordinates.

R-tree:

An R-tree is a hierarchical spatial index structure that groups close spatial objects together. Improves query efficiency for range and nearest neighbour queries.

  • R-tree organizes spatial objects using the bounding rectangles. Each node in the R-tree represents a bounding rectangle that encloses a group of the spatial objects. R-tree is suitable for both 2D and higher-dimensional data.
  • The R-tree hierarchically groups spatial objects based on their bounding rectangles.
  • The rectangles at the higher levels of the tree are larger and encompass more objects. As we move down the tree the rectangles become smaller focusing on specific areas of interest.
  • R-tree is a powerful spatial indexing structure for the efficient spatial queries.

Implementation of R-Tree:

C++
#include <iostream>
#include <limits>
#include <vector>

struct GFG {
    double minX, maxX, minY, maxY;
    GFG* parent;
    std::vector<GFG*> children;
    GFG(double minX, double maxX, double minY, double maxY,
        GFG* parent)
        : minX(minX)
        , maxX(maxX)
        , minY(minY)
        , maxY(maxY)
        , parent(parent)
    {
    }
};
class RTree {
public:
    RTree()
        : root(nullptr)
    {
    }
    void insert(double minX, double maxX, double minY,
                double maxY)
    {
        root
            = insert(root, minX, maxX, minY, maxY, nullptr);
    }
    bool search(double x, double y)
    {
        return search(root, x, y);
    }

private:
    GFG* root;
    GFG* insert(GFG* node, double minX, double maxX,
                double minY, double maxY, GFG* parent)
    {
        if (!node) {
            return new GFG(minX, maxX, minY, maxY, parent);
        }
        if (node->children.empty()) {
            GFG* newNode = new GFG(minX, maxX, minY, maxY,
                                node->parent);
            node->children.push_back(newNode);
            return newNode;
        }
        double minEnlargement
            = std::numeric_limits<double>::max();
        GFG* bestChild = nullptr;
        for (GFG* child : node->children) {
            double newMinX = std::min(child->minX, minX);
            double newMaxX = std::max(child->maxX, maxX);
            double newMinY = std::min(child->minY, minY);
            double newMaxY = std::max(child->maxY, maxY);
            double enlargement
                = (newMaxX - newMinX) * (newMaxY - newMinY)
                - (child->maxX - child->minX)
                        * (child->maxY - child->minY);
            if (enlargement < minEnlargement) {
                minEnlargement = enlargement;
                bestChild = child;
            }
        }
        insert(bestChild, minX, maxX, minY, maxY, node);
        return node;
    }
    bool search(GFG* node, double x, double y)
    {
        if (!node) {
            return false;
        }
        if (node->children.empty()) {
            return (x >= node->minX && x <= node->maxX
                    && y >= node->minY && y <= node->maxY);
        }
        for (GFG* child : node->children) {
            if (x >= child->minX && x <= child->maxX
                && y >= child->minY && y <= child->maxY) {
                return search(child, x, y);
            }
        }
        return false;
    }
};
int main()
{
    RTree rtree;
    rtree.insert(7.0, 20.0, 5.0, 10.0);
    rtree.insert(30.0, 35.0, 15.0, 20.0);
    double x = 7.0, y = 7.0;
    std::cout << "Search Result for (" << x << ", " << y
            << "): "
            << (rtree.search(x, y) ? "Found"
                                    : "Not Found")
            << std::endl;
    x = 23.0;
    y = 19.0;
    std::cout << "Search Result for (" << x << ", " << y
            << "): "
            << (rtree.search(x, y) ? "Found"
                                    : "Not Found")
            << std::endl;
    return 0;
}
Java
// Java program for the above approach
import java.util.ArrayList;
import java.util.List;

// Node class for representing the rectangle region in the
// R-tree
class GFG {
    double minX, maxX, minY,
        maxY; // Coordinates of the rectangle
    GFG parent; // Reference to the parent node
    List<GFG> children; // List of child nodes

    // Constructor to initialize the node
    GFG(double minX, double maxX, double minY, double maxY,
        GFG parent)
    {
        this.minX = minX;
        this.maxX = maxX;
        this.minY = minY;
        this.maxY = maxY;
        this.parent = parent;
        this.children = new ArrayList<>();
    }
}

// RTree class for managing the R-tree data structure
class RTree {
    private GFG root; // Root node of the R-tree

    // Constructor to initialize the R-tree
    RTree() { this.root = null; }

    // Method to insert a rectangle region into the R-tree
    void insert(double minX, double maxX, double minY,
                double maxY)
    {
        root = insert(root, minX, maxX, minY, maxY, null);
    }

    // Method to search for a point in the R-tree
    boolean search(double x, double y)
    {
        return search(root, x, y);
    }

    // Recursive method to insert a rectangle region into
    // the R-tree
    private GFG insert(GFG node, double minX, double maxX,
                       double minY, double maxY, GFG parent)
    {
        if (node == null) {
            return new GFG(minX, maxX, minY, maxY, parent);
        }
        if (node.children.isEmpty()) {
            GFG newNode = new GFG(minX, maxX, minY, maxY,
                                  node.parent);
            node.children.add(newNode);
            return newNode;
        }
        double minEnlargement = Double.MAX_VALUE;
        GFG bestChild = null;
        for (GFG child : node.children) {
            double newMinX = Math.min(child.minX, minX);
            double newMaxX = Math.max(child.maxX, maxX);
            double newMinY = Math.min(child.minY, minY);
            double newMaxY = Math.max(child.maxY, maxY);
            double enlargement
                = (newMaxX - newMinX) * (newMaxY - newMinY)
                  - (child.maxX - child.minX)
                        * (child.maxY - child.minY);
            if (enlargement < minEnlargement) {
                minEnlargement = enlargement;
                bestChild = child;
            }
        }
        insert(bestChild, minX, maxX, minY, maxY, node);
        return node;
    }

    // Recursive method to search for a point in the R-tree
    private boolean search(GFG node, double x, double y)
    {
        if (node == null) {
            return false;
        }
        if (node.children.isEmpty()) {
            return x >= node.minX && x <= node.maxX
                && y >= node.minY && y <= node.maxY;
        }
        for (GFG child : node.children) {
            if (x >= child.minX && x <= child.maxX
                && y >= child.minY && y <= child.maxY) {
                return search(child, x, y);
            }
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args)
    {
        // Create an R-tree instance
        RTree rtree = new RTree();

        // Insert rectangle regions into the R-tree
        rtree.insert(7.0, 20.0, 5.0, 10.0);
        rtree.insert(30.0, 35.0, 15.0, 20.0);

        // Search for points in the R-tree and print the
        // result
        double x = 7.0, y = 7.0;
        System.out.println(
            "Search Result for (" + x + ", " + y + "): "
            + (rtree.search(x, y) ? "Found" : "Not Found"));
        x = 23.0;
        y = 19.0;
        System.out.println(
            "Search Result for (" + x + ", " + y + "): "
            + (rtree.search(x, y) ? "Found" : "Not Found"));
    }
}

// This code is contributed by Susobhan Akhuli
C#
using System;
using System.Collections.Generic;

// Define a class to represent each node in the R-tree
public class RTreeNode
{
    public double MinX { get; set; }
    public double MaxX { get; set; }
    public double MinY { get; set; }
    public double MaxY { get; set; }
    public RTreeNode Parent { get; set; }
    public List<RTreeNode> Children { get; set; }

    // Constructor
    public RTreeNode(double minX, double maxX, double minY, double maxY, RTreeNode parent)
    {
        MinX = minX;
        MaxX = maxX;
        MinY = minY;
        MaxY = maxY;
        Parent = parent;
        Children = new List<RTreeNode>();
    }
}

// Define the R-tree class
public class RTree
{
    private RTreeNode root;

    // Constructor
    public RTree()
    {
        root = null;
    }

    // Method to insert a rectangle into the R-tree
    public void Insert(double minX, double maxX, double minY, double maxY)
    {
        root = Insert(root, minX, maxX, minY, maxY, null);
    }

    // Recursive method to insert a rectangle into the R-tree
    private RTreeNode Insert(RTreeNode node, double minX, double maxX, double minY, double maxY, RTreeNode parent)
    {
        if (node == null)
        {
            return new RTreeNode(minX, maxX, minY, maxY, parent);
        }
        if (node.Children.Count == 0)
        {
            RTreeNode newNode = new RTreeNode(minX, maxX, minY, maxY, node.Parent);
            node.Children.Add(newNode);
            return newNode;
        }
        double minEnlargement = double.MaxValue;
        RTreeNode bestChild = null;
        foreach (RTreeNode child in node.Children)
        {
            double newMinX = Math.Min(child.MinX, minX);
            double newMaxX = Math.Max(child.MaxX, maxX);
            double newMinY = Math.Min(child.MinY, minY);
            double newMaxY = Math.Max(child.MaxY, maxY);
            double enlargement = (newMaxX - newMinX) * (newMaxY - newMinY) - (child.MaxX - child.MinX) * (child.MaxY - child.MinY);
            if (enlargement < minEnlargement)
            {
                minEnlargement = enlargement;
                bestChild = child;
            }
        }
        Insert(bestChild, minX, maxX, minY, maxY, node);
        return node;
    }

    // Method to search for a point in the R-tree
    public bool Search(double x, double y)
    {
        return Search(root, x, y);
    }

    // Recursive method to search for a point in the R-tree
    private bool Search(RTreeNode node, double x, double y)
    {
        if (node == null)
        {
            return false;
        }
        if (node.Children.Count == 0)
        {
            return (x >= node.MinX && x <= node.MaxX && y >= node.MinY && y <= node.MaxY);
        }
        foreach (RTreeNode child in node.Children)
        {
            if (x >= child.MinX && x <= child.MaxX && y >= child.MinY && y <= child.MaxY)
            {
                return Search(child, x, y);
            }
        }
        return false;
    }
}

// Main class
public class Program
{
    public static void Main(string[] args)
    {
        RTree rtree = new RTree();
        rtree.Insert(7.0, 20.0, 5.0, 10.0);
        rtree.Insert(30.0, 35.0, 15.0, 20.0);

        double x = 7.0, y = 7.0;
        Console.WriteLine($"Search Result for ({x}, {y}): {(rtree.Search(x, y) ? "Found" : "Not Found")}");

        x = 23.0;
        y = 19.0;
        Console.WriteLine($"Search Result for ({x}, {y}): {(rtree.Search(x, y) ? "Found" : "Not Found")}");
    }
}
Javascript
class GFG {
    constructor(minX, maxX, minY, maxY, parent) {
        this.minX = minX;
        this.maxX = maxX;
        this.minY = minY;
        this.maxY = maxY;
        this.parent = parent;
        this.children = [];
    }
}

class RTree {
    constructor() {
        this.root = null;
    }

    insert(minX, maxX, minY, maxY) {
        this.root = this._insert(this.root, minX, maxX, minY, maxY, null);
    }

    search(x, y) {
        return this._search(this.root, x, y);
    }

    _insert(node, minX, maxX, minY, maxY, parent) {
        if (!node) {
            return new GFG(minX, maxX, minY, maxY, parent);
        }

        if (node.children.length === 0) {
            const newNode = new GFG(minX, maxX, minY, maxY, node.parent);
            node.children.push(newNode);
            return newNode;
        }

        let minEnlargement = Number.MAX_VALUE;
        let bestChild = null;

        for (const child of node.children) {
            const newMinX = Math.min(child.minX, minX);
            const newMaxX = Math.max(child.maxX, maxX);
            const newMinY = Math.min(child.minY, minY);
            const newMaxY = Math.max(child.maxY, maxY);

            const enlargement =
                (newMaxX - newMinX) * (newMaxY - newMinY) -
                (child.maxX - child.minX) * (child.maxY - child.minY);

            if (enlargement < minEnlargement) {
                minEnlargement = enlargement;
                bestChild = child;
            }
        }

        this._insert(bestChild, minX, maxX, minY, maxY, node);
        return node;
    }

    _search(node, x, y) {
        if (!node) {
            return false;
        }

        if (node.children.length === 0) {
            return (
                x >= node.minX &&
                x <= node.maxX &&
                y >= node.minY &&
                y <= node.maxY
            );
        }

        for (const child of node.children) {
            if (
                x >= child.minX &&
                x <= child.maxX &&
                y >= child.minY &&
                y <= child.maxY
            ) {
                return this._search(child, x, y);
            }
        }

        return false;
    }
}

const rtree = new RTree();
rtree.insert(7.0, 20.0, 5.0, 10.0);
rtree.insert(30.0, 35.0, 15.0, 20.0);

let x = 7.0,
    y = 7.0;
console.log(
    `Search Result for (${x}, ${y}): ${
        rtree.search(x, y) ? 'Found' : 'Not Found'
    }`
);

x = 23.0;
y = 19.0;
console.log(
    `Search Result for (${x}, ${y}): ${
        rtree.search(x, y) ? 'Found' : 'Not Found'
    }`
);
Python3
class GFG:
    def __init__(self, minX, maxX, minY, maxY, parent):
        """
        Constructor to initialize a GFG node.
        """
        self.minX = minX  # Minimum x-coordinate
        self.maxX = maxX  # Maximum x-coordinate
        self.minY = minY  # Minimum y-coordinate
        self.maxY = maxY  # Maximum y-coordinate
        self.parent = parent  # Parent node
        self.children = []  # List of child nodes

class RTree:
    def __init__(self):
        """
        Constructor to initialize an R-Tree.
        """
        self.root = None  # Root node of the R-Tree

    def insert(self, minX, maxX, minY, maxY):
        """
        Method to insert a rectangle into the R-Tree.
        """
        self.root = self._insert(self.root, minX, maxX, minY, maxY, None)

    def search(self, x, y):
        """
        Method to search for a point in the R-Tree.
        """
        return self._search(self.root, x, y)

    def _insert(self, node, minX, maxX, minY, maxY, parent):
        """
        Internal method to recursively insert a rectangle into the R-Tree.
        """
        if not node:
            # If node is None, create a new node
            return GFG(minX, maxX, minY, maxY, parent)
        
        if not node.children:
            # If node has no children, create a new child node
            new_node = GFG(minX, maxX, minY, maxY, node.parent)
            node.children.append(new_node)
            return new_node
        
        # Calculate the enlargement of the potential child nodes
        min_enlargement = float('inf')
        best_child = None
        
        for child in node.children:
            newMinX = min(child.minX, minX)
            newMaxX = max(child.maxX, maxX)
            newMinY = min(child.minY, minY)
            newMaxY = max(child.maxY, maxY)
            enlargement = (newMaxX - newMinX) * (newMaxY - newMinY) - (child.maxX - child.minX) * (child.maxY - child.minY)
            
            if enlargement < min_enlargement:
                min_enlargement = enlargement
                best_child = child
        
        # Recursively insert the rectangle into the best child node
        self._insert(best_child, minX, maxX, minY, maxY, node)
        return node

    def _search(self, node, x, y):
        """
        Internal method to recursively search for a point in the R-Tree.
        """
        if not node:
            # If node is None, return False
            return False
        
        if not node.children:
            # If node has no children, check if the point lies within the node's boundaries
            return x >= node.minX and x <= node.maxX and y >= node.minY and y <= node.maxY
        
        for child in node.children:
            # Recursively search for the point in each child node
            if x >= child.minX and x <= child.maxX and y >= child.minY and y <= child.maxY:
                return self._search(child, x, y)
        
        return False

def main():
    # Create an instance of the R-Tree
    rtree = RTree()

    # Insert rectangles into the R-Tree
    rtree.insert(7.0, 20.0, 5.0, 10.0)
    rtree.insert(30.0, 35.0, 15.0, 20.0)

    # Search for points in the R-Tree
    x, y = 7.0, 7.0
    print("Search Result for (", x, ",", y, "):", "Found" if rtree.search(x, y) else "Not Found")
    x, y = 23.0, 19.0
    print("Search Result for (", x, ",", y, "):", "Found" if rtree.search(x, y) else "Not Found")

if __name__ == "__main__":
    main()
#this code is contributed by Monu.

Output
Search Result for (7, 7): Not Found
Search Result for (23, 19): Not Found










Quadtree:

A quadtree is a recursive tree-based spatial index structure. It divides 2D space into quadrants. It is best suited for dynamic datasets and spatial queries.


QuadTree-example

Quad-Tree Example


  • The quadtree divides a 2D space into quadrants recursively until each quadrant contains a limited number of the points or reaches a specified depth. Each node in the quadtree can have four children corresponding to the four quadrants.
  • the quadtree organizes points in a 2D space. The root node represents the entire space and it is recursively divided into the quadrants as we move down the tree.
  • Each node can hold a maximum of four points.
  • Quadtree efficiently indexes points in different areas of space allowing faster spatial queries.

Implementation of Quad-Tree:

C++
#include <iostream>
#include <vector>

class GFG {
public:
    double x;
    double y;
    GFG(double x, double y)
        : x(x)
        , y(y)
    {
    }
};
class Quadtree {
private:
    static const int MAX_CAPACITY = 4;
    static const int MAX_DEPTH = 10;
    class QuadtreeNode {
    public:
        double xMin;
        double yMin;
        double xMax;
        double yMax;
        std::vector<GFG> points;
        QuadtreeNode* nw;
        QuadtreeNode* ne;
        QuadtreeNode* sw;
        QuadtreeNode* se;
        QuadtreeNode(double xMin, double yMin, double xMax,
                    double yMax)
            : xMin(xMin)
            , yMin(yMin)
            , xMax(xMax)
            , yMax(yMax)
            , nw(nullptr)
            , ne(nullptr)
            , sw(nullptr)
            , se(nullptr)
        {
        }
    };
    QuadtreeNode* root;
    void insertHelper(QuadtreeNode* node, const GFG& point,
                    int depth);
    void queryRangeHelper(QuadtreeNode* node,
                        const double& xMin,
                        const double& yMin,
                        const double& xMax,
                        const double& yMax,
                        std::vector<GFG>& result) const;

public:
    Quadtree(double xMin, double yMin, double xMax,
            double yMax);
    ~Quadtree();
    void insert(const GFG& point);
    std::vector<GFG> queryRange(const double& xMin,
                                const double& yMin,
                                const double& xMax,
                                const double& yMax) const;
};
Quadtree::Quadtree(double xMin, double yMin, double xMax,
                double yMax)
{
    root = new QuadtreeNode(xMin, yMin, xMax, yMax);
}
Quadtree::~Quadtree() {}
void Quadtree::insertHelper(QuadtreeNode* node,
                            const GFG& point, int depth)
{
    if (depth >= MAX_DEPTH || node == nullptr)
        return;
    if (node->points.size() < MAX_CAPACITY) {
        node->points.push_back(point);
    }
    else {
        double xMid = (node->xMin + node->xMax) / 2.0;
        double yMid = (node->yMin + node->yMax) / 2.0;

        if (point.x <= xMid) {
            if (point.y <= yMid) {
                if (node->nw == nullptr)
                    node->nw = new QuadtreeNode(
                        node->xMin, node->yMin, xMid, yMid);
                insertHelper(node->nw, point, depth + 1);
            }
            else {
                if (node->sw == nullptr)
                    node->sw = new QuadtreeNode(
                        node->xMin, yMid, xMid, node->yMax);
                insertHelper(node->sw, point, depth + 1);
            }
        }
        else {
            if (point.y <= yMid) {
                if (node->ne == nullptr)
                    node->ne = new QuadtreeNode(
                        xMid, node->yMin, node->xMax, yMid);
                insertHelper(node->ne, point, depth + 1);
            }
            else {
                if (node->se == nullptr)
                    node->se = new QuadtreeNode(
                        xMid, yMid, node->xMax, node->yMax);
                insertHelper(node->se, point, depth + 1);
            }
        }
    }
}
void Quadtree::insert(const GFG& point)
{
    insertHelper(root, point, 0);
}
void Quadtree::queryRangeHelper(
    QuadtreeNode* node, const double& xMin,
    const double& yMin, const double& xMax,
    const double& yMax, std::vector<GFG>& result) const
{
    if (node == nullptr)
        return;
    for (const GFG& point : node->points) {
        if (point.x >= xMin && point.x <= xMax
            && point.y >= yMin && point.y <= yMax)
            result.push_back(point);
    }
    double xMid = (node->xMin + node->xMax) / 2.0;
    double yMid = (node->yMin + node->yMax) / 2.0;
    if (xMin <= xMid && yMin <= yMid)
        queryRangeHelper(node->nw, xMin, yMin, xMax, yMax,
                        result);
    if (xMin <= xMid && yMax >= yMid)
        queryRangeHelper(node->sw, xMin, yMin, xMax, yMax,
                        result);
    if (xMax >= xMid && yMin <= yMid)
        queryRangeHelper(node->ne, xMin, yMin, xMax, yMax,
                        result);
    if (xMax >= xMid && yMax >= yMid)
        queryRangeHelper(node->se, xMin, yMin, xMax, yMax,
                        result);
}
std::vector<GFG>
Quadtree::queryRange(const double& xMin, const double& yMin,
                    const double& xMax,
                    const double& yMax) const
{
    std::vector<GFG> result;
    queryRangeHelper(root, xMin, yMin, xMax, yMax, result);
    return result;
}
int main()
{
    Quadtree quadtree(0.0, 0.0, 100.0, 100.0);
    quadtree.insert(GFG(20.0, 30.0));
    quadtree.insert(GFG(40.0, 50.0));
    quadtree.insert(GFG(60.0, 70.0));
    quadtree.insert(GFG(80.0, 90.0));
    std::vector<GFG> pointsInRange
        = quadtree.queryRange(0.0, 0.0, 90.0, 90.0);
    for (const GFG& point : pointsInRange) {
        std::cout << "Point: (" << point.x << ", "
                << point.y << ")\n";
    }
    return 0;
}
Java
// Java code for the above approach

import java.util.ArrayList;
import java.util.List;

class GFG {
    double minX, maxX, minY, maxY;
    GFG parent;
    List<GFG> children;

    public GFG(double minX, double maxX, double minY,
               double maxY, GFG parent)
    {
        this.minX = minX;
        this.maxX = maxX;
        this.minY = minY;
        this.maxY = maxY;
        this.parent = parent;
        this.children = new ArrayList<>();
    }
}

class RTree {
    private GFG root;

    public RTree() { this.root = null; }

    public void insert(double minX, double maxX,
                       double minY, double maxY)
    {
        root = insert(root, minX, maxX, minY, maxY, null);
    }

    public boolean search(double x, double y)
    {
        return search(root, x, y);
    }

    private GFG insert(GFG node, double minX, double maxX,
                       double minY, double maxY, GFG parent)
    {
        if (node == null) {
            return new GFG(minX, maxX, minY, maxY, parent);
        }
        if (node.children.isEmpty()) {
            GFG newNode = new GFG(minX, maxX, minY, maxY,
                                  node.parent);
            node.children.add(newNode);
            return newNode;
        }
        double minEnlargement = Double.MAX_VALUE;
        GFG bestChild = null;
        for (GFG child : node.children) {
            double newMinX = Math.min(child.minX, minX);
            double newMaxX = Math.max(child.maxX, maxX);
            double newMinY = Math.min(child.minY, minY);
            double newMaxY = Math.max(child.maxY, maxY);
            double enlargement
                = (newMaxX - newMinX) * (newMaxY - newMinY)
                  - (child.maxX - child.minX)
                        * (child.maxY - child.minY);
            if (enlargement < minEnlargement) {
                minEnlargement = enlargement;
                bestChild = child;
            }
        }
        insert(bestChild, minX, maxX, minY, maxY, node);
        return node;
    }

    private boolean search(GFG node, double x, double y)
    {
        if (node == null) {
            return false;
        }
        if (node.children.isEmpty()) {
            return (x >= node.minX && x <= node.maxX
                    && y >= node.minY && y <= node.maxY);
        }
        for (GFG child : node.children) {
            if (x >= child.minX && x <= child.maxX
                && y >= child.minY && y <= child.maxY) {
                return search(child, x, y);
            }
        }
        return false;
    }
}

public class Main {
    public static void main(String[] args)
    {
        RTree rtree = new RTree();
        rtree.insert(7.0, 20.0, 5.0, 10.0);
        rtree.insert(30.0, 35.0, 15.0, 20.0);
        double x = 7.0, y = 7.0;
        System.out.println(
            "Search Result for (" + x + ", " + y + "): "
            + (rtree.search(x, y) ? "Found" : "Not Found"));
        x = 23.0;
        y = 19.0;
        System.out.println(
            "Search Result for (" + x + ", " + y + "): "
            + (rtree.search(x, y) ? "Found" : "Not Found"));
    }
}

// This code is contributed by ragul21
C#
using System;
using System.Collections.Generic;

class GFG
{
    public double x;
    public double y;

    public GFG(double x, double y)
    {
        this.x = x;
        this.y = y;
    }
}

class Quadtree
{
    private const int MAX_CAPACITY = 4;
    private const int MAX_DEPTH = 10;

    private class QuadtreeNode
    {
        public double xMin;
        public double yMin;
        public double xMax;
        public double yMax;
        public List<GFG> points;
        public QuadtreeNode nw;
        public QuadtreeNode ne;
        public QuadtreeNode sw;
        public QuadtreeNode se;

        public QuadtreeNode(double xMin, double yMin, double xMax, double yMax)
        {
            this.xMin = xMin;
            this.yMin = yMin;
            this.xMax = xMax;
            this.yMax = yMax;
            this.points = new List<GFG>();
        }
    }

    private QuadtreeNode root;

    // Constructor to initialize the Quadtree
    public Quadtree(double xMin, double yMin, double xMax, double yMax)
    {
        root = new QuadtreeNode(xMin, yMin, xMax, yMax);
    }

    // Inserting a point into the Quadtree
    private void InsertHelper(QuadtreeNode node, GFG point, int depth)
    {
        if (depth >= MAX_DEPTH || node == null)
            return;

        if (node.points.Count < MAX_CAPACITY)
        {
            node.points.Add(point);
        }
        else
        {
            double xMid = (node.xMin + node.xMax) / 2.0;
            double yMid = (node.yMin + node.yMax) / 2.0;

            if (point.x <= xMid)
            {
                if (point.y <= yMid)
                {
                    if (node.nw == null)
                        node.nw = new QuadtreeNode(node.xMin, node.yMin, xMid, yMid);
                    InsertHelper(node.nw, point, depth + 1);
                }
                else
                {
                    if (node.sw == null)
                        node.sw = new QuadtreeNode(node.xMin, yMid, xMid, node.yMax);
                    InsertHelper(node.sw, point, depth + 1);
                }
            }
            else
            {
                if (point.y <= yMid)
                {
                    if (node.ne == null)
                        node.ne = new QuadtreeNode(xMid, node.yMin, node.xMax, yMid);
                    InsertHelper(node.ne, point, depth + 1);
                }
                else
                {
                    if (node.se == null)
                        node.se = new QuadtreeNode(xMid, yMid, node.xMax, node.yMax);
                    InsertHelper(node.se, point, depth + 1);
                }
            }
        }
    }

    // Public method to insert a point into the Quadtree
    public void Insert(GFG point)
    {
        InsertHelper(root, point, 0);
    }

    // Querying points within a range
    private void QueryRangeHelper(QuadtreeNode node, double xMin, double yMin, double xMax, double yMax, List<GFG> result)
    {
        if (node == null)
            return;

        foreach (GFG point in node.points)
        {
            if (point.x >= xMin && point.x <= xMax && point.y >= yMin && point.y <= yMax)
                result.Add(point);
        }

        double xMid = (node.xMin + node.xMax) / 2.0;
        double yMid = (node.yMin + node.yMax) / 2.0;

        if (xMin <= xMid && yMin <= yMid)
            QueryRangeHelper(node.nw, xMin, yMin, xMax, yMax, result);
        if (xMin <= xMid && yMax >= yMid)
            QueryRangeHelper(node.sw, xMin, yMin, xMax, yMax, result);
        if (xMax >= xMid && yMin <= yMid)
            QueryRangeHelper(node.ne, xMin, yMin, xMax, yMax, result);
        if (xMax >= xMid && yMax >= yMid)
            QueryRangeHelper(node.se, xMin, yMin, xMax, yMax, result);
    }

    // Public method to query points within a given range
    public List<GFG> QueryRange(double xMin, double yMin, double xMax, double yMax)
    {
        List<GFG> result = new List<GFG>();
        QueryRangeHelper(root, xMin, yMin, xMax, yMax, result);
        return result;
    }

    // Main method to test Quadtree functionality
    static void Main()
    {
        Quadtree quadtree = new Quadtree(0.0, 0.0, 100.0, 100.0);
        quadtree.Insert(new GFG(20.0, 30.0));
        quadtree.Insert(new GFG(40.0, 50.0));
        quadtree.Insert(new GFG(60.0, 70.0));
        quadtree.Insert(new GFG(80.0, 90.0));

        List<GFG> pointsInRange = quadtree.QueryRange(0.0, 0.0, 90.0, 90.0);

        foreach (GFG point in pointsInRange)
        {
            Console.WriteLine($"Point: ({point.x}, {point.y})");
        }
    }
}
Javascript
class GFG {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

class Quadtree {
    constructor(xMin, yMin, xMax, yMax) {
        this.root = new QuadtreeNode(xMin, yMin, xMax, yMax);
    }

    insert(point) {
        this.insertHelper(this.root, point, 0);
    }

    queryRange(xMin, yMin, xMax, yMax) {
        const result = [];
        this.queryRangeHelper(this.root, xMin, yMin, xMax, yMax, result);
        return result;
    }

    insertHelper(node, point, depth) {
        const MAX_CAPACITY = 4;
        const MAX_DEPTH = 10;

        if (depth >= MAX_DEPTH || node === null) return;

        if (node.points.length < MAX_CAPACITY) {
            node.points.push(point);
        } else {
            const xMid = (node.xMin + node.xMax) / 2;
            const yMid = (node.yMin + node.yMax) / 2;

            const { x, y } = point;
            let childNode;

            if (x <= xMid) {
                if (y <= yMid) {
                    if (!node.nw) {
                        node.nw = new QuadtreeNode(node.xMin, node.yMin, xMid, yMid);
                    }
                    childNode = node.nw;
                } else {
                    if (!node.sw) {
                        node.sw = new QuadtreeNode(node.xMin, yMid, xMid, node.yMax);
                    }
                    childNode = node.sw;
                }
            } else {
                if (y <= yMid) {
                    if (!node.ne) {
                        node.ne = new QuadtreeNode(xMid, node.yMin, node.xMax, yMid);
                    }
                    childNode = node.ne;
                } else {
                    if (!node.se) {
                        node.se = new QuadtreeNode(xMid, yMid, node.xMax, node.yMax);
                    }
                    childNode = node.se;
                }
            }

            this.insertHelper(childNode, point, depth + 1);
        }
    }

    queryRangeHelper(node, xMin, yMin, xMax, yMax, result) {
        if (!node) return;

        for (const point of node.points) {
            if (point.x >= xMin && point.x <= xMax && point.y >= yMin && point.y <= yMax) {
                result.push(point);
            }
        }

        const xMid = (node.xMin + node.xMax) / 2;
        const yMid = (node.yMin + node.yMax) / 2;

        if (xMin <= xMid && yMin <= yMid) this.queryRangeHelper(node.nw, xMin, yMin, xMax, yMax, result);
        if (xMin <= xMid && yMax >= yMid) this.queryRangeHelper(node.sw, xMin, yMin, xMax, yMax, result);
        if (xMax >= xMid && yMin <= yMid) this.queryRangeHelper(node.ne, xMin, yMin, xMax, yMax, result);
        if (xMax >= xMid && yMax >= yMid) this.queryRangeHelper(node.se, xMin, yMin, xMax, yMax, result);
    }
}

class QuadtreeNode {
    constructor(xMin, yMin, xMax, yMax) {
        this.xMin = xMin;
        this.yMin = yMin;
        this.xMax = xMax;
        this.yMax = yMax;
        this.points = [];
        this.nw = null;
        this.ne = null;
        this.sw = null;
        this.se = null;
    }
}

// Main function to test the Quadtree
const quadtree = new Quadtree(0.0, 0.0, 100.0, 100.0);
quadtree.insert(new GFG(20.0, 30.0));
quadtree.insert(new GFG(40.0, 50.0));
quadtree.insert(new GFG(60.0, 70.0));
quadtree.insert(new GFG(80.0, 90.0));

const pointsInRange = quadtree.queryRange(0.0, 0.0, 90.0, 90.0);
for (const point of pointsInRange) {
    console.log(`Point: (${point.x}, ${point.y})`);
}


// This code is contributed by shivamgupta310570
Python3
# Python Implementation
class GFG:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Quadtree:
    MAX_CAPACITY = 4
    MAX_DEPTH = 10

    class QuadtreeNode:
        def __init__(self, xMin, yMin, xMax, yMax):
            self.xMin = xMin
            self.yMin = yMin
            self.xMax = xMax
            self.yMax = yMax
            self.points = []
            self.nw = None
            self.ne = None
            self.sw = None
            self.se = None

    def __init__(self, xMin, yMin, xMax, yMax):
        self.root = self.QuadtreeNode(xMin, yMin, xMax, yMax)

    def insertHelper(self, node, point, depth):
        if depth >= self.MAX_DEPTH or node is None:
            return
        if len(node.points) < self.MAX_CAPACITY:
            node.points.append(point)
        else:
            xMid = (node.xMin + node.xMax) / 2.0
            yMid = (node.yMin + node.yMax) / 2.0

            if point.x <= xMid:
                if point.y <= yMid:
                    if node.nw is None:
                        node.nw = self.QuadtreeNode(node.xMin, node.yMin, xMid, yMid)
                    self.insertHelper(node.nw, point, depth + 1)
                else:
                    if node.sw is None:
                        node.sw = self.QuadtreeNode(node.xMin, yMid, xMid, node.yMax)
                    self.insertHelper(node.sw, point, depth + 1)
            else:
                if point.y <= yMid:
                    if node.ne is None:
                        node.ne = self.QuadtreeNode(xMid, node.yMin, node.xMax, yMid)
                    self.insertHelper(node.ne, point, depth + 1)
                else:
                    if node.se is None:
                        node.se = self.QuadtreeNode(xMid, yMid, node.xMax, node.yMax)
                    self.insertHelper(node.se, point, depth + 1)

    def insert(self, point):
        self.insertHelper(self.root, point, 0)

    def queryRangeHelper(self, node, xMin, yMin, xMax, yMax, result):
        if node is None:
            return
        for point in node.points:
            if point.x >= xMin and point.x <= xMax and point.y >= yMin and point.y <= yMax:
                result.append(point)
        xMid = (node.xMin + node.xMax) / 2.0
        yMid = (node.yMin + node.yMax) / 2.0
        if xMin <= xMid and yMin <= yMid:
            self.queryRangeHelper(node.nw, xMin, yMin, xMax, yMax, result)
        if xMin <= xMid and yMax >= yMid:
            self.queryRangeHelper(node.sw, xMin, yMin, xMax, yMax, result)
        if xMax >= xMid and yMin <= yMid:
            self.queryRangeHelper(node.ne, xMin, yMin, xMax, yMax, result)
        if xMax >= xMid and yMax >= yMid:
            self.queryRangeHelper(node.se, xMin, yMin, xMax, yMax, result)

    def queryRange(self, xMin, yMin, xMax, yMax):
        result = []
        self.queryRangeHelper(self.root, xMin, yMin, xMax, yMax, result)
        return result

quadtree = Quadtree(0.0, 0.0, 100.0, 100.0)
quadtree.insert(GFG(20.0, 30.0))
quadtree.insert(GFG(40.0, 50.0))
quadtree.insert(GFG(60.0, 70.0))
quadtree.insert(GFG(80.0, 90.0))
pointsInRange = quadtree.queryRange(0.0, 0.0, 90.0, 90.0)
for point in pointsInRange:
    print(f"Point: ({point.x}, {point.y})")
    
# This code is contributed by Tapesh(tapeshdua420)

Output
Point: (20, 30)
Point: (40, 50)
Point: (60, 70)
Point: (80, 90)










KD Tree (K-Dimensional Tree):

  • The KD Tree is a binary tree data structure used for organizing points in a k-dimensional space.
  • It is primarily used for the efficient range searches and nearest neighbor searches in multi-dimensional datasets.
  • The idea behind KD Trees is to partition space into smaller regions by alternatingly dividing data along different dimensions. This process continues until each region contains only one point.
  • The KD Trees are well-suited for datasets with a moderate number of dimensions but can suffer from the performance degradation in high-dimensional spaces.
  • The Nearest neighbour searches in KD Trees have a complexity of O(log N).

Implementation KD Tree (K-Dimensional Tree):

C++
#include <algorithm>
#include <iostream>
#include <vector>

struct Point {
    double x, y;
};
struct Node {
    Point point;
    Node* left;
    Node* right;
    Node(const Point& p)
        : point(p)
        , left(nullptr)
        , right(nullptr)
    {
    }
};
class Tree {
private:
    Node* root;
    Node* buildTree(const std::vector<Point>& points,
                    int depth)
    {
        if (points.empty())
            return nullptr;
        int axis = depth % 2;
        int median = points.size() / 2;
        std::vector<Point> sortedPoints = points;
        std::nth_element(
            sortedPoints.begin(),
            sortedPoints.begin() + median,
            sortedPoints.end(),
            [axis](const Point& a, const Point& b) {
                return axis == 0 ? a.x < b.x : a.y < b.y;
            });
        Node* node = new Node(sortedPoints[median]);
        node->left
            = buildTree(std::vector<Point>(
                            sortedPoints.begin(),
                            sortedPoints.begin() + median),
                        depth + 1);
        node->right = buildTree(
            std::vector<Point>(sortedPoints.begin() + median
                                + 1,
                            sortedPoints.end()),
            depth + 1);
        return node;
    }
    Node* search(Node* node, Point target, int depth)
    {
        if (node == nullptr
            || (node->point.x == target.x
                && node->point.y == target.y))
            return node;
        int axis = depth % 2;
        if ((axis == 0 && target.x < node->point.x)
            || (axis == 1 && target.y < node->point.y))
            return search(node->left, target, depth + 1);
        return search(node->right, target, depth + 1);
    }

public:
    Tree(std::vector<Point>& points)
        : root(nullptr)
    {
        root = buildTree(points, 0);
    }
    Node* search(Point target)
    {
        return search(root, target, 0);
    }
};
int main()
{
    std::vector<Point> points
        = { { 2, 3 }, { 5, 4 }, { 9, 4 },
            { 4, 7 }, { 8, 1 }, { 7, 2 } };
    Tree kdtree(points);
    Point target = { 5, 4 };
    Node* result = kdtree.search(target);
    if (result != nullptr) {
        std::cout << "Found point at (" << result->point.x
                << ", " << result->point.y << ")"
                << std::endl;
    }
    else {
        std::cout << "Point not found!" << std::endl;
    }
    return 0;
}
Java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

// Define Point class to represent 2D points
class Point {
    double x, y;

    Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}

// Define Node class for the k-d tree structure
class Node {
    Point point;
    Node left;
    Node right;

    Node(Point p) {
        point = p;
        left = null;
        right = null;
    }
}

// Define Tree class to build and search the k-d tree
class Tree {
    private Node root;

    // Method to build the k-d tree
    private Node buildTree(List<Point> points, int depth) {
        if (points.isEmpty())
            return null;

        int axis = depth % 2;
        int median = points.size() / 2;

        // Sort points based on x or y coordinate depending on the depth
        List<Point> sortedPoints = new ArrayList<>(points);
        Collections.sort(sortedPoints, Comparator.comparingDouble(a -> axis == 0 ? a.x : a.y));

        // Create node with the median point and recursively build left and right subtrees
        Node node = new Node(sortedPoints.get(median));
        node.left = buildTree(sortedPoints.subList(0, median), depth + 1);
        node.right = buildTree(sortedPoints.subList(median + 1, sortedPoints.size()), depth + 1);

        return node;
    }

    // Method to search for a point in the k-d tree
    private Node search(Node node, Point target, int depth) {
        if (node == null || (node.point.x == target.x && node.point.y == target.y))
            return node;

        int axis = depth % 2;

        if ((axis == 0 && target.x < node.point.x) || (axis == 1 && target.y < node.point.y))
            return search(node.left, target, depth + 1);
        return search(node.right, target, depth + 1);
    }

    // Constructor for the Tree class
    public Tree(List<Point> points) {
        root = buildTree(points, 0);
    }

    // Public search method to initiate search from the root
    public Node search(Point target) {
        return search(root, target, 0);
    }
}

public class KDTreeExample {
    public static void main(String[] args) {
        List<Point> points = new ArrayList<>();
        points.add(new Point(2, 3));
        points.add(new Point(5, 4));
        points.add(new Point(9, 4));
        points.add(new Point(4, 7));
        points.add(new Point(8, 1));
        points.add(new Point(7, 2));

        // Create k-d tree with the given points
        Tree kdtree = new Tree(points);

        // Define the target point to search for
        Point target = new Point(5, 4);

        // Search for the target point in the k-d tree
        Node result = kdtree.search(target);

        // Output the search result
        if (result != null) {
            System.out.println("Found point at (" + (int) result.point.x + ", " + (int) result.point.y + ")");
        } else {
            System.out.println("Point not found!");
        }
    }
}
C#
using System;
using System.Collections.Generic;

public struct Point
{
    public double x, y;
}

public class Node
{
    public Point point;
    public Node left;
    public Node right;

    // Node constructor
    public Node(Point p)
    {
        point = p;
        left = null;
        right = null;
    }
}

public class Tree
{
    private Node root;

    // BuildTree method to construct the KD tree recursively
    private Node BuildTree(List<Point> points, int depth)
    {
        // Base case: if the list of points is empty, return null
        if (points.Count == 0)
            return null;

        // Determine the axis for splitting based on the current depth
        int axis = depth % 2;

        // Find the median index
        int median = points.Count / 2;

        // Create a sorted list of points based on the current axis
        List<Point> sortedPoints = new List<Point>(points);
        sortedPoints.Sort((a, b) => axis == 0 ? a.x.CompareTo(b.x) : a.y.CompareTo(b.y));

        // Create a new node with the median point
        Node node = new Node(sortedPoints[median]);

        // Recursively build the left and right subtrees
        node.left = BuildTree(sortedPoints.GetRange(0, median), depth + 1);
        node.right = BuildTree(sortedPoints.GetRange(median + 1, sortedPoints.Count - median - 1), depth + 1);

        return node;
    }

    // Search method to find a point in the KD tree
    private Node Search(Node node, Point target, int depth)
    {
        // Base cases: if the node is null or the target point is found
        if (node == null || (node.point.x == target.x && node.point.y == target.y))
            return node;

        // Determine the axis for comparison
        int axis = depth % 2;

        // Recursively search the left or right subtree based on the axis
        if ((axis == 0 && target.x < node.point.x) || (axis == 1 && target.y < node.point.y))
            return Search(node.left, target, depth + 1);

        return Search(node.right, target, depth + 1);
    }

    // Tree constructor to build the KD tree from a list of points
    public Tree(List<Point> points)
    {
        root = BuildTree(points, 0);
    }

    // Public search method to initiate the search from the root
    public Node Search(Point target)
    {
        return Search(root, target, 0);
    }
}

class Program
{
    static void Main()
    {
        // Sample list of points
        List<Point> points = new List<Point>
        {
            new Point { x = 2, y = 3 },
            new Point { x = 5, y = 4 },
            new Point { x = 9, y = 4 },
            new Point { x = 4, y = 7 },
            new Point { x = 8, y = 1 },
            new Point { x = 7, y = 2 }
        };

        // Create a KD tree
        Tree kdtree = new Tree(points);

        // Define a target point for search
        Point target = new Point { x = 5, y = 4 };

        // Perform search and print the result
        Node result = kdtree.Search(target);

        if (result != null)
        {
            Console.WriteLine($"Found point at ({result.point.x}, {result.point.y})");
        }
        else
        {
            Console.WriteLine("Point not found!");
        }
    }
}
// This code is contributed by shivamgupta310570
Javascript
class Point {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
}

class Node {
    constructor(point) {
        this.point = point;
        this.left = null;
        this.right = null;
    }
}

class Tree {
    constructor(points) {
        this.root = this.buildTree(points, 0);
    }

    // Recursive function to build the KD-Tree
    buildTree(points, depth) {
        if (points.length === 0)
            return null;

        const axis = depth % 2;
        const median = Math.floor(points.length / 2);

        // Sort points based on the current axis
        const sortedPoints = [...points];
        sortedPoints.sort((a, b) => (axis === 0 ? a.x - b.x : a.y - b.y));

        // Create a node with the median point
        const node = new Node(sortedPoints[median]);

        // Recursively build the left and right subtrees
        node.left = this.buildTree(sortedPoints.slice(0, median), depth + 1);
        node.right = this.buildTree(sortedPoints.slice(median + 1), depth + 1);

        return node;
    }

    // Recursive function to search for a point in the KD-Tree
    search(node, target, depth) {
        if (node === null || (node.point.x === target.x && node.point.y === target.y))
            return node;

        const axis = depth % 2;

        // Choose the subtree based on the current axis
        if ((axis === 0 && target.x < node.point.x) || (axis === 1 && target.y < node.point.y))
            return this.search(node.left, target, depth + 1);

        return this.search(node.right, target, depth + 1);
    }

    // Public method to search for a point in the KD-Tree
    searchPoint(target) {
        return this.search(this.root, target, 0);
    }
}

// Example usage
const points = [
    new Point(2, 3),
    new Point(5, 4),
    new Point(9, 4),
    new Point(4, 7),
    new Point(8, 1),
    new Point(7, 2)
];

const kdtree = new Tree(points);
const targetPoint = new Point(5, 4);
const result = kdtree.searchPoint(targetPoint);

if (result !== null) {
    console.log(`Found point at (${result.point.x}, ${result.point.y})`);
} else {
    console.log("Point not found!");
}
Python3
import math

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class Node:
    def __init__(self, point):
        self.point = point
        self.left = None
        self.right = None

class Tree:
    def __init__(self, points):
        self.root = self.build_tree(points, 0)
    
    def build_tree(self, points, depth):
        if not points:
            return None
        
        axis = depth % 2
        median = len(points) // 2
        
        # Sort based on the axis
        sorted_points = sorted(points, key=lambda p: (p.x, p.y)[axis])
        
        node = Node(sorted_points[median])
        node.left = self.build_tree(sorted_points[:median], depth + 1)
        node.right = self.build_tree(sorted_points[median + 1:], depth + 1)
        
        return node
    
    def search(self, target):
        return self._search(self.root, target, 0)
    
    def _search(self, node, target, depth):
        if not node or (node.point.x == target.x and node.point.y == target.y):
            return node
        
        axis = depth % 2
        if (axis == 0 and target.x < node.point.x) or (axis == 1 and target.y < node.point.y):
            return self._search(node.left, target, depth + 1)
        return self._search(node.right, target, depth + 1)

if __name__ == "__main__":
    points = [Point(2, 3), Point(5, 4), Point(9, 4), Point(4, 7), Point(8, 1), Point(7, 2)]
    
    kdtree = Tree(points)
    target = Point(5, 4)
    
    result = kdtree.search(target)
    
    if result:
        print(f"Found point at ({result.point.x}, {result.point.y})")
    else:
        print("Point not found!")
        
# This code is contributed by shivamgupta310570

Output
Found point at (5, 4)















Geo Hash:

  • The Geo Hash is a technique used to convert geographical coordinates into a single-string representation. This representation can then be used to index and search spatial data efficiently.
  • It works by dividing the Earth’s surface into a grid of cells and assigning a unique hash code to each cell based on its coordinates.
  • The length of hash code determines the precision of the spatial indexing.
  • Longer codes represent smaller areas with the higher precision.
  • The Geo Hash is particularly useful for the indexing geographical data in databases and widely used in applications such as geospatial databases, geolocation services and geohashing games.

Implementation Geo Hash:

C++
#include <cmath>
#include <iostream>
#include <string>

class GeoHashExample {
public:
    static std::string
    encode(double latitude, double longitude, int precision)
    {
        std::string base32
            = "0123456789bcdefghjkmnpqrstuvwxyz";
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        std::string hash;
        bool is_even = true;
        int bit = 0, ch = 0;
        while (hash.length() < precision) {
            double mid;
            if (is_even) {
                mid = (minLon + maxLon) / 2;
                if (longitude > mid) {
                    ch |= (1 << (4 - bit));
                    minLon = mid;
                }
                else {
                    maxLon = mid;
                }
            }
            else {
                mid = (minLat + maxLat) / 2;
                if (latitude > mid) {
                    ch |= (1 << (4 - bit));
                    minLat = mid;
                }
                else {
                    maxLat = mid;
                }
            }
            is_even = !is_even;
            if (bit < 4) {
                bit++;
            }
            else {
                hash += base32[ch];
                bit = 0;
                ch = 0;
            }
        }
        return hash;
    }
    static void decode(const std::string& geohash,
                    double& latitude, double& longitude)
    {
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        bool is_even = true;
        for (char ch : geohash) {
            int cd = base32Index(ch);
            for (int i = 4; i >= 0; --i) {
                int mask = 1 << i;
                if (is_even) {
                    divideRangeDecode(minLon, maxLon,
                                    cd & mask, longitude);
                }
                else {
                    divideRangeDecode(minLat, maxLat,
                                    cd & mask, latitude);
                }
                is_even = !is_even;
            }
        }
    }

private:
    static int base32Index(char ch)
    {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        else if (ch >= 'b' && ch <= 'h')
            return ch - 'b' + 10;
        else if (ch >= 'j' && ch <= 'n')
            return ch - 'j' + 17;
        else if (ch >= 'p' && ch <= 'z')
            return ch - 'p' + 22;
        else
            return 0;
    }
    static void divideRangeDecode(double& min, double& max,
                                int b, double& value)
    {
        if (b == 0)
            max = (min + max) / 2;
        else
            min = (min + max) / 2;
        value = (min + max) / 2;
    }
};

int main()
{
    std::string geohash = "9q8yywh";
    double latitude, longitude;
    GeoHashExample::decode(geohash, latitude, longitude);
    std::cout << "Latitude: " << latitude
            << ", Longitude: " << longitude << std::endl;
    return 0;
}
Java
public class GeoHashExample {
    private static final String base32 = "0123456789bcdefghjkmnpqrstuvwxyz";

    public static String encode(double latitude, double longitude, int precision) {
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        StringBuilder hash = new StringBuilder();
        boolean is_even = true;
        int bit = 0, ch = 0;
        while (hash.length() < precision) {
            double mid;
            if (is_even) {
                mid = (minLon + maxLon) / 2;
                if (longitude > mid) {
                    ch |= (1 << (4 - bit));
                    minLon = mid;
                } else {
                    maxLon = mid;
                }
            } else {
                mid = (minLat + maxLat) / 2;
                if (latitude > mid) {
                    ch |= (1 << (4 - bit));
                    minLat = mid;
                } else {
                    maxLat = mid;
                }
            }
            is_even = !is_even;
            if (bit < 4) {
                bit++;
            } else {
                hash.append(base32.charAt(ch));
                bit = 0;
                ch = 0;
            }
        }
        return hash.toString();
    }

    public static void decode(String geohash, double[] latitude, double[] longitude) {
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        boolean is_even = true;
        for (char ch : geohash.toCharArray()) {
            int cd = base32Index(ch);
            for (int i = 4; i >= 0; --i) {
                int mask = 1 << i;
                if (is_even) {
                    divideRangeDecode(minLon, maxLon, cd & mask, longitude);
                } else {
                    divideRangeDecode(minLat, maxLat, cd & mask, latitude);
                }
                is_even = !is_even;
            }
        }
    }

    private static int base32Index(char ch) {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        else if (ch >= 'b' && ch <= 'h')
            return ch - 'b' + 10;
        else if (ch >= 'j' && ch <= 'n')
            return ch - 'j' + 17;
        else if (ch >= 'p' && ch <= 'z')
            return ch - 'p' + 22;
        else
            return 0;
    }

    private static void divideRangeDecode(double min, double max, int b, double[] value) {
        if (b == 0)
            max = (min + max) / 2;
        else
            min = (min + max) / 2;
        value[0] = (min + max) / 2;
    }

    public static void main(String[] args) {
        String geohash = "9q8yywh";
        double[] latitude = new double[1];
        double[] longitude = new double[1];
        decode(geohash, latitude, longitude);
        System.out.println("Latitude: " + latitude[0] + ", Longitude: " + longitude[0]);
    }
}
Python
class GeoHashExample:
    base32 = "0123456789bcdefghjkmnpqrstuvwxyz"

    @staticmethod
    def encode(latitude, longitude, precision):
        minLat, maxLat = -90.0, 90.0
        minLon, maxLon = -180.0, 180.0
        hash_code = ""
        is_even = True
        bit = 0
        ch = 0

        while len(hash_code) < precision:
            mid = 0.0
            if is_even:
                mid = (minLon + maxLon) / 2
                if longitude > mid:
                    ch |= (1 << (4 - bit))
                    minLon = mid
                else:
                    maxLon = mid
            else:
                mid = (minLat + maxLat) / 2
                if latitude > mid:
                    ch |= (1 << (4 - bit))
                    minLat = mid
                else:
                    maxLat = mid

            is_even = not is_even
            if bit < 4:
                bit += 1
            else:
                hash_code += GeoHashExample.base32[ch]
                bit = 0
                ch = 0

        return hash_code

    @staticmethod
    def decode(geohash):
        minLat, maxLat = -90.0, 90.0
        minLon, maxLon = -180.0, 180.0
        is_even = True
        for ch in geohash:
            cd = GeoHashExample.base32_index(ch)
            for i in range(4, -1, -1):
                mask = 1 << i
                if is_even:
                    GeoHashExample.divide_range_decode(minLon, maxLon, cd & mask, longitude)
                else:
                    GeoHashExample.divide_range_decode(minLat, maxLat, cd & mask, latitude)
                is_even = not is_even

    @staticmethod
    def base32_index(ch):
        if '0' <= ch <= '9':
            return ord(ch) - ord('0')
        elif 'b' <= ch <= 'h':
            return ord(ch) - ord('b') + 10
        elif 'j' <= ch <= 'n':
            return ord(ch) - ord('j') + 17
        elif 'p' <= ch <= 'z':
            return ord(ch) - ord('p') + 22
        else:
            return 0

    @staticmethod
    def divide_range_decode(min_value, max_value, b, value):
        mid = (min_value + max_value) / 2
        if b == 0:
            max_value = mid
        else:
            min_value = mid
        value[0] = (min_value + max_value) / 2


geohash = "9q8yywh"
latitude = [0.0]
longitude = [0.0]
GeoHashExample.decode(geohash)
print("Latitude:", latitude[0], ", Longitude:", longitude[0])
C#
using System;

class GeoHashExample
{
    // Method to encode latitude and longitude into a geohash
    public static string Encode(double latitude, double longitude, int precision)
    {
        string base32 = "0123456789bcdefghjkmnpqrstuvwxyz";
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        string hash = "";
        bool is_even = true;
        int bit = 0, ch = 0;
        while (hash.Length < precision)
        {
            double mid;
            if (is_even)
            {
                mid = (minLon + maxLon) / 2;
                if (longitude > mid)
                {
                    ch |= (1 << (4 - bit));
                    minLon = mid;
                }
                else
                {
                    maxLon = mid;
                }
            }
            else
            {
                mid = (minLat + maxLat) / 2;
                if (latitude > mid)
                {
                    ch |= (1 << (4 - bit));
                    minLat = mid;
                }
                else
                {
                    maxLat = mid;
                }
            }
            is_even = !is_even;
            if (bit < 4)
            {
                bit++;
            }
            else
            {
                hash += base32[ch];
                bit = 0;
                ch = 0;
            }
        }
        return hash;
    }

    // Method to decode a geohash into latitude and longitude
    public static void Decode(string geohash, out double latitude, out double longitude)
    {
        double minLat = -90.0, maxLat = 90.0;
        double minLon = -180.0, maxLon = 180.0;
        bool is_even = true;
        latitude = 0;
        longitude = 0;
        foreach (char ch in geohash)
        {
            int cd = Base32Index(ch);
            for (int i = 4; i >= 0; --i)
            {
                int mask = 1 << i;
                if (is_even)
                {
                    DivideRangeDecode(ref minLon, ref maxLon, cd & mask, ref longitude);
                }
                else
                {
                    DivideRangeDecode(ref minLat, ref maxLat, cd & mask, ref latitude);
                }
                is_even = !is_even;
            }
        }
    }

    // Method to convert base32 character to index
    private static int Base32Index(char ch)
    {
        if (ch >= '0' && ch <= '9')
            return ch - '0';
        else if (ch >= 'b' && ch <= 'h')
            return ch - 'b' + 10;
        else if (ch >= 'j' && ch <= 'n')
            return ch - 'j' + 17;
        else if (ch >= 'p' && ch <= 'z')
            return ch - 'p' + 22;
        else
            return 0;
    }

    // Method to divide range and decode
    private static void DivideRangeDecode(ref double min, ref double max, int b, ref double value)
    {
        if (b == 0)
            max = (min + max) / 2;
        else
            min = (min + max) / 2;
        value = (min + max) / 2;
    }
}

class MainClass
{
    public static void Main(string[] args)
    {
        string geohash = "9q8yywh";
        double latitude, longitude;
        GeoHashExample.Decode(geohash, out latitude, out longitude);
        Console.WriteLine("Latitude: " + latitude + ", Longitude: " + longitude);
    }
}
//This code is contributed by Monu.
Javascript
// Javascript program for the above approach
// Class to handle geohash encoding and decoding
class GeoHashExample {
    // Base32 characters for encoding
    static base32 = "0123456789bcdefghjkmnpqrstuvwxyz";

    // Method to encode latitude and longitude into a geohash
    static encode(latitude, longitude, precision) {
        let minLat = -90.0, maxLat = 90.0;
        let minLon = -180.0, maxLon = 180.0;
        let hash = ""; // Initialize the geohash string
        let is_even = true; // Flag to track even or odd iteration
        let bit = 0, ch = 0; // Bit and character placeholders for encoding
        // Loop until the desired precision is achieved
        while (hash.length < precision) {
            let mid; // Midpoint value for latitude or longitude
            if (is_even) { // For even iterations, divide longitude range
                mid = (minLon + maxLon) / 2;
                if (longitude > mid) { // Update character bits accordingly
                    ch |= (1 << (4 - bit));
                    minLon = mid;
                } else {
                    maxLon = mid;
                }
            } else { // For odd iterations, divide latitude range
                mid = (minLat + maxLat) / 2;
                if (latitude > mid) { // Update character bits accordingly
                    ch |= (1 << (4 - bit));
                    minLat = mid;
                } else {
                    maxLat = mid;
                }
            }
            is_even = !is_even; // Toggle the even/odd flag
            if (bit < 4) {
                bit++; // Move to the next bit
            } else {
                hash += GeoHashExample.base32.charAt(ch); // Append character to geohash string
                bit = 0; // Reset bit counter
                ch = 0; // Reset character placeholder
            }
        }
        return hash; // Return the generated geohash
    }

    // Method to decode a geohash into latitude and longitude
    static decode(geohash) {
        let latitude = 0.0, longitude = 0.0; // Initialize latitude and longitude
        let minLat = -90.0, maxLat = 90.0; // Initialize latitude range
        let minLon = -180.0, maxLon = 180.0; // Initialize longitude range
        let is_even = true; // Flag to track even or odd iteration
        // Loop through each character in the geohash
        for (let ch of geohash) {
            let cd = GeoHashExample.base32Index(ch); // Get index of base32 character
            for (let i = 4; i >= 0; --i) { // Iterate through bits of the character
                let mask = 1 << i; // Create bitmask for each bit
                if (is_even) { // For even iterations, decode longitude
                    longitude = GeoHashExample.divideRangeDecode(minLon, maxLon, cd & mask);
                } else { // For odd iterations, decode latitude
                    latitude = GeoHashExample.divideRangeDecode(minLat, maxLat, cd & mask);
                }
                is_even = !is_even; // Toggle the even/odd flag
            }
        }
        return { latitude, longitude }; // Return decoded latitude and longitude
    }

    // Method to convert base32 character to index
    static base32Index(ch) {
        if (ch >= '0' && ch <= '9')
            return ch.charCodeAt(0) - '0'.charCodeAt(0);
        else if (ch >= 'b' && ch <= 'h')
            return ch.charCodeAt(0) - 'b'.charCodeAt(0) + 10;
        else if (ch >= 'j' && ch <= 'n')
            return ch.charCodeAt(0) - 'j'.charCodeAt(0) + 17;
        else if (ch >= 'p' && ch <= 'z')
            return ch.charCodeAt(0) - 'p'.charCodeAt(0) + 22;
        else
            return 0;
    }

    // Method to divide range and decode
    static divideRangeDecode(min, max, b) {
        if (b === 0)
            max = (min + max) / 2;
        else
            min = (min + max) / 2;
        return (min + max) / 2; // Return the decoded value
    }
}

// Usage example
let geohash = "9q8yywh";
let { latitude, longitude } = GeoHashExample.decode(geohash);
console.log("Latitude: " + latitude + ", Longitude: " + longitude);

// This code is contributed by Susobhan Akhuli

Output
Latitude: -45.0, Longitude: -90.0

Comparison table between all four spatial indexing technique:

Spatial Indexing TechniqueProsConsApplications
KD TreeThe Efficient range and nearest neighbor searches in low-dimensional spaces.The Performance degrades in high-dimensional spaces.The Computer graphics (collision detection, ray tracing).<br> Robotics (motion planning).<br>- Geographic Information Systems (GIS).
Geo HashThe Simple and efficient for 2D geographical data indexing.The Precision varies with the length of the hash code.<br>- Suffers from spatial skewness in some cases.The Geospatial databases.<br> Geolocation services.<be> Location-based games.
R-TreeThe Efficient indexing of spatial data with variable-sized rectangles (regions).Might not be optimal for point-only datasets and <br> Costly updates when data changes.The Geographical Information Systems (GIS).<br> Database systems.<br> Image retrieval.
QuadtreeThe Efficient indexing for point data and spatially clustered data.The Inefficient for datasets with irregular spatial distributions and <br> High memory consumption in high-dimensional spaces.The Image processing.<br> Geographical Information Systems (GIS) and <br> Robotics (spatial partitioning).


Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads