# What Is the Role of Polar Coordinate Features in Edge Representation in doc2graph?

> Discover how polar coordinate features in doc2graph capture spatial layout using distance and angle to improve edge representation for graph neural networks.

- Repository: [Andrea Gemelli/doc2graph](https://github.com/andreagemelli/doc2graph)
- Tags: deep-dive
- Published: 2026-02-24

---

**Polar coordinate features encode the geometric relationship between document text boxes as distance and angle pairs, enabling the graph neural network to reason about spatial layout when predicting edges.**

In the `andreagemelli/doc2graph` repository, polar coordinate features serve as the geometric backbone for edge representation, transforming raw bounding box coordinates into learnable signals that capture relative positioning. These features allow the model to understand directional relationships—such as whether a label appears above or to the left of its corresponding field—by representing each edge as a combination of Euclidean distance and clockwise angle between node centers.

## How Polar Coordinate Features Are Computed

The conversion from raw pixel coordinates to polar representations happens through a two-stage process involving geometric calculation and discretization.

### Computing Distance and Angle

The `polar()` function in [`doc2graph/data/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/utils.py) calculates the Euclidean distance and clockwise angle from the center of a source rectangle to the center of a destination rectangle.

```python

# doc2graph/data/utils.py

def polar(rect_src: list, rect_dst: list) -> Tuple[int, int]:
    ...
    angle = int(math.degrees(math.atan2(new_ec[1], new_ec[0])) % 360)
    ...
    return dist, angle

```

This function returns the distance in pixel units and an angle ranging from 0 to 360 degrees, providing a complete polar coordinate system for every node pair.

### Discretization into Bins

Raw polar values are converted into fixed-size binary encodings using the `to_bin()` function, which quantizes the continuous distance and angle values into discrete bins.

```python

# doc2graph/data/feature_builder.py

polar_coordinates = to_bin(distances, angles, self.num_polar_bins)
g.edata["feat"] = polar_coordinates

```

This binning process, implemented in [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py), stores the resulting binary vectors in the graph's edge data under the key `"feat"`, creating a sparse representation that the neural network can process efficiently.

### Configuration Options

The resolution of the polar encoding is controlled by the `num_polar_bins` hyperparameter, defined in the configuration files:

```yaml

# configs/models/edge.yaml

FEATURES:
  num_polar_bins: 8

```

By default, the system uses 8 bins, though this value can be adjusted to trade off between spatial precision and model complexity.

## How Polar Coordinate Features Are Used in Edge Prediction

During the edge prediction phase, polar coordinate features are concatenated with node embeddings to provide geometric context for classification decisions.

### Standard Edge Prediction

In [`doc2graph/models/graphs.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/models/graphs.py), the `MLPPredictor` class incorporates polar features within its `apply_edges` method:

```python

# doc2graph/models/graphs.py – MLPPredictor

def apply_edges(self, edges):
    h_u = edges.src["h"]                # source node embedding

    h_v = edges.dst["h"]                # destination node embedding

    polar = edges.data["feat"]          # polar-coordinate edge feature

    x = F.relu(self.norm(self.W1(torch.cat((h_u, h_v), dim=1))))
    x = torch.cat((x, polar), dim=1)    # polar features become part of edge vector

    score = self.drop(self.W2(x))
    return {"score": score}

```

This concatenation ensures that the model considers both semantic node representations and geometric relationships when scoring potential edges.

### End-to-End Variant

The `MLPPredictor_E2E` class extends this approach by combining polar features with class-score vectors:

```python

# doc2graph/models/graphs.py – MLPPredictor_E2E

x = F.relu(self.norm(self.W1(torch.cat((h_u, cls_u, polar, h_v, cls_v), dim=1))))

```

This richer representation allows the model to jointly reason about node classes, spatial orientation, and relative distance within a single edge feature vector.

## Why Polar Coordinate Features Matter

Polar coordinate features provide three critical capabilities that enhance document understanding:

- **Spatial awareness**: By encoding explicit distance and direction, the model learns structural patterns such as labels typically appearing above their input fields or headers preceding content blocks.
- **Scale invariance**: The binning mechanism compresses raw pixel distances into a fixed-size representation, making the model robust to variations in image resolution and document scanning quality.
- **Complementarity**: When combined with textual embeddings, visual features, and histogram descriptors, polar features enrich the edge vector with geometric context, improving accuracy in downstream tasks like layout parsing and table extraction.

## Implementation Examples

### Computing Polar Features Manually

You can calculate polar features for specific bounding box pairs using the utility functions:

```python
from doc2graph.data.utils import polar, to_bin

src_box = [100, 50, 200, 150]   # (x0, y0, x1, y1)

dst_box = [250, 80, 350, 180]

dist, angle = polar(src_box, dst_box)      # → (150, 30)

bins = 8
polar_feat = to_bin([dist], [angle], bins) # → tensor([[0.,1.,0.,0.,0.,0.,0.,1.,0.,0.,…]])

```

### Building Features in the Pipeline

During graph construction, the `FeatureBuilder` class automatically computes and attaches polar features:

```python
from doc2graph.data.feature_builder import FeatureBuilder
from doc2graph.data.graph_builder import GraphBuilder

fb = FeatureBuilder()
graphs, features = GraphBuilder(...).build()   # list of DGLGraph objects

graphs, _ = fb.add_features(graphs, features)  # polar features stored in g.edata["feat"]

```

### Using Features in Custom Predictors

When implementing custom edge prediction models, access polar features through the edge data dictionary:

```python
import torch
from doc2graph.models.graphs import MLPPredictor

model = MLPPredictor(in_features=256, hidden_dim=128, out_classes=2, dropout=0.2)
node_repr = torch.randn(num_nodes, 256)        # output of node GNN

edge_scores = model(g, node_repr)              # g.edata["feat"] used automatically

```

## Summary

- Polar coordinate features encode the geometric relationship between document nodes as distance and angle pairs.
- The `polar()` function in [`doc2graph/data/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/utils.py) computes raw Euclidean distance and clockwise angle between rectangle centers.
- The `to_bin()` function discretizes these values into binary vectors stored in `g.edata["feat"]`.
- Edge predictors in [`doc2graph/models/graphs.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/models/graphs.py) concatenate polar features with node embeddings to incorporate spatial context.
- The `num_polar_bins` configuration parameter controls the resolution of the geometric encoding.
- These features provide spatial awareness, scale invariance, and complementarity with textual and visual signals.

## Frequently Asked Questions

### How do polar coordinate features differ from raw bounding box coordinates?

Raw bounding box coordinates represent absolute positions in pixel space, which vary with image resolution and document size. Polar coordinate features convert these into relative measurements—distance and angle between node pairs—creating a translation-invariant representation that focuses on the geometric relationship rather than absolute location. According to the doc2graph source code, this conversion happens in [`doc2graph/data/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/utils.py) before binning occurs.

### Can I adjust the precision of polar coordinate features?

Yes, the precision is controlled by the `num_polar_bins` parameter in [`configs/models/edge.yaml`](https://github.com/andreagemelli/doc2graph/blob/main/configs/models/edge.yaml). Increasing this value creates finer angular and distance divisions, potentially capturing more nuanced spatial relationships but increasing the dimensionality of the edge feature vectors. The default value of 8 bins provides a balance between expressiveness and computational efficiency.

### Why are polar features stored in `g.edata["feat"]` rather than node attributes?

Polar features describe the relationship between two nodes (an edge), not the properties of individual nodes. In [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py), the implementation explicitly assigns these vectors to edge data using `g.edata["feat"]` because they encode geometric information specific to the source-destination pair. This allows edge prediction models in [`doc2graph/models/graphs.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/models/graphs.py) to access geometric context when scoring potential connections between nodes.

### Do polar coordinate features work with rotated documents?

The polar coordinate system implemented in [`doc2graph/data/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/utils.py) uses a fixed 0-360 degree clockwise angle reference. While this works well for axis-aligned documents, rotated text may require preprocessing alignment or additional rotational augmentation during training. The distance component remains valid regardless of document orientation, but angle interpretations assume a standard upright coordinate system.