# GeoCLIP treats coordinates as a function and returns 512-dimensional vectors

> GeoCLIP is the official PyTorch implementation of a 2023 paper that learns a location encoder mapping latitude and longitude to CLIP-aligned embeddings, so a coordinate pair can be used as an input feature by another model. The package ships its own weights and gallery tables, so one install gives you both prediction and encoding.

**VicenteVivan/geo-clip** — This is an official PyTorch implementation of our NeurIPS 2023 paper "GeoCLIP: Clip-Inspired Alignment between Locations and Images for Effective Worldwide Geo-localization"

- Repository: https://github.com/VicenteVivan/geo-clip
- Website: https://arxiv.org/abs/2309.16020
- Stars: 407 · Forks: 51
- Language: Python
- License: MIT
- Published: 2026-09-10 · Updated: 2026-09-10 · Language: en
- Canonical page: https://hysenlabs.com/projects/vicentevivan-geo-clip

## One package, two entry points

The module exposes two classes, and the distinction between them is the whole API.

The first predicts. You construct the model with no arguments, hand it a path to an image, and get back a list of coordinate pairs and a list of probabilities, with the number of predictions set by a keyword argument. The example asks for five and prints each latitude and longitude to six decimal places, which is roughly a tenth of a metre of precision on the printed value, whether or not the prediction is that good.

The second encodes. You construct a location encoder, pass it a tensor of latitude and longitude rows, and get back a matrix of embeddings whose second dimension is 512. The example uses New York and Los Angeles, prints the shape, and that is the entire contract.

Both come from the same package, and both work after a single install from the package index because the wheel carries the model weights and the gallery tables as package data rather than downloading them on first use. That is why there is no model download step in the getting started instructions.

Installation is one command from the package index, or three from source:

```bash
pip install geoclip
```

```bash
git clone https://github.com/VicenteVivan/geo-clip
cd geo-clip
python -m pip install .
```

A third path exists for people who want the arguments spelled out: the README links a hosted notebook demo, and a demo extra installs a mapping library and a plotting library to draw the predictions.

## The Earth is modelled as a function, not a lookup

The core idea is described in one sentence: the location encoder models the Earth as a continuous function.

That word continuous is doing the work. A lookup table over places can only answer for places it has seen, so generalising to a street nobody photographed requires either interpolation or something else entirely. A function over coordinates has no such gap, which is what makes worldwide generalisation and the GPS-to-vector task possible at the same time.

The output is described as semantically rich and aligned with the image features CLIP learns, and that alignment is the other half of the design. If the location vector lived in its own space, concatenating it with an image vector would be meaningless. Because both are trained to match paired inputs, they land in a space where distance means something across the two modalities.

The architecture is also described as generalising, which is a claim about reuse rather than about accuracy: the encoder is presented as a pre-trained component that other geo-aware architectures can borrow rather than a model you must run end to end.

## 512 dimensions is the interface contract

The documented encoder example is two lines of real work: build a tensor of two coordinate pairs, pass it to the encoder, and read the shape. The output is two rows by 512 columns.

That number is a contract, not a detail. It is what downstream code depends on when it concatenates a location embedding onto an image embedding, and it is what you would have to retrain to change. A model that returns a different width would silently break every consumer that assumed 512.

The example is also the sanity check worth running first. Two well-known cities going through a geographic encoder should produce two distinct vectors rather than two near-identical ones, and if they do not, the weights did not load. It is a two-line test that catches the most common failure of an academic checkpoint: a model that imports cleanly and returns plausible shapes for garbage.

The inputs are a plain float tensor of coordinate pairs, latitude first, with no normalisation applied by the caller. Nothing in the documented interface requires a projection or a bounding box, which is what makes it usable as a feature extractor rather than only as a predictor.

## The second claim is the more useful one

Two results are reported, and the second is the one that matters for anyone building something on top of this.

The first is that concatenating GPS features from the pre-trained location encoder with an image's visual features reaches state-of-the-art results on the geo-tagged NUS-Wide dataset. That is an improvement claim, and improvements get superseded.

The second is that the GPS features alone, without extra information, are effective for geo-aware image classification, reaching state-of-the-art performance in the GPS-only multi-class classification task on the same dataset. GPS-only means the classifier never sees the image.

Read carefully, that says the encoder has learned something about location that predicts image class on its own. It implies the representation carries priors about what tends to be photographed where, which is a different claim from being a good coordinate predictor, and it is the reason to treat the encoder as a reusable component rather than a preprocessing step.

The corollary is a limit. If the features encode what people tend to photograph in a place, then a classification task about unusual content in unusual places is exactly where this representation would be least useful, and the repository publishes no analysis of that failure mode.

## Contrastive training on 4.7 million geo-tagged images

Training follows CLIP closely: images and locations are paired and matched contrastively, so the model is pulling matched pairs together and pushing mismatched ones apart. The training set is called MP-16 and is described as 4.7 million images taken across the globe.

That scale explains a lot about the design. Contrastive training at this size needs the image side to be a pretrained vision encoder rather than something trained from scratch, which is why a transformer library appears in the dependencies alongside the tensor library.

Evaluation spans four datasets: two geo-localization sets, a large crowdsourced photo set, a set named for world landmarks, and the geo-tagged NUS-Wide set used for the classification results. Two tasks are reported, placing an image on the map, and turning a coordinate into a vector.

The paper is from 2023 and lists three authors. The repository calls itself the official PyTorch implementation of it, which is worth stating plainly because it means there is no claim of being a production system. The README also carries its own construction warning, describing the repository as under construction, which sits oddly beside a package that people are told to install from an index.

## Wide dependency ranges and a shim for old tools

The packaging is more carefully done than most research code.

Dependencies live in a requirements file rather than the manifest, and the manifest declares them dynamic so the two cannot disagree. Every entry is a range with a generous ceiling: the tensor library is held below the next major version, the vision companion below one, image loading from 9 up to but not including 13, the transformer library from 4.28 up to but not including 6, a dataframe library from 1.5 to below 4, arrays from 1.23 to below 3, a geocoding library below 3, and a progress bar below 5.

Those caps are unusual. A lower bound with no ceiling is a hope; a lower bound and a major ceiling is a decision about which breaking changes you have looked at. Whether the upper bounds have been tested against real installations is a different question, and nothing in the repository says.

The build configuration requires a recent setuptools and keeps a separate installation script whose docstring says it is a compatibility entry point for tools that do not yet support the modern build standard. That is a two-line file with one job, and keeping it costs nothing while saving anyone who still uses an old installer.

Package data is declared explicitly: images, a gallery of coordinate tables, and the model weights. A manifest file for source distributions sits beside it, and the test extra pins the test framework across two major versions while the demo extra pins a mapping library below the next major and a plotting library below the next major.

## A beta classification and one release

The version is 1.2.1, the development status classifier says beta, and Python 3.10 through 3.13 are all listed as supported. There is one release tag, cut on the same day as the most recent push.

The single release is the signal to read carefully. A package at 1.2.1 usually implies a sequence of earlier releases, so a first tag at 1.2.1 suggests the tag stream started late or that the earlier versions were published elsewhere. Either way, there is no upgrade history to read in this repository.

The author is named with a contact address, and the manifest links the repository, the package page, and the paper separately, which is a small courtesy that makes it clear which link is the artefact and which is the argument for it.

One cosmetic flaw is worth noting because it is the kind of thing that survives for years. The badge row under the title contains two links, and the second one has an empty target, so it renders as a badge with no image and no destination. Nobody reading the README is harmed, but it is a signal that the header was edited without a final pass.

The acknowledgements credit code incorporated from a third-party random Fourier features implementation, which is the sort of dependency that should be checked when reproducing a paper, because it is usually where a numerical difference hides.

## Conclusion

GeoCLIP suits a project that needs to place an image on a map or inject geographic priors into another vision model, and that is content to depend on an academic implementation for. It does not suit a pipeline needing geolocation accuracy guarantees, since the reported figures are benchmark results from a 2023 paper with no accuracy caveats published in the repository. Before relying on it, check the benchmark numbers against your own tolerance for error, run the documented two-coordinate example to confirm the encoder loads, and note that the README describes the repository as under construction.

## FAQ

### What does GeoCLIP predict from an image?

Given a path to an image it returns the top predicted GPS coordinates and a probability for each, with the count set by a keyword argument. The documented example asks for five predictions and prints latitude and longitude to six decimal places.

### What is the GeoCLIP LocationEncoder for?

It turns latitude and longitude pairs into 512-dimensional embeddings, so a location can be used as a feature vector by another model. The paper reports that those features alone are effective for geo-aware image classification without any extra information.

### How do I install geoclip?

From the package index with a single pip install command, or from source by cloning the repository and installing the directory with pip. The package data includes the model weights and a gallery of coordinate tables, so there is no separate model download step.

### Which datasets was GeoCLIP trained and evaluated on?

It was trained contrastively on the MP-16 dataset of 4.7 million images taken across the globe, and evaluated on geo-localization and GPS-to-vector benchmarks including Im2GPS3k, YFCC26k, GWS15k, and the Geo-Tagged NUS-Wide dataset.

### What extra dependencies does GeoCLIP offer?

A demo extra with a mapping library and matplotlib for drawing predictions, and a test extra with a test framework, both pinned across major version ranges. Python 3.10 or newer is required, and the manifest classifies the project as beta.

## Sources

- [License: MIT](https://github.com/VicenteVivan/geo-clip/blob/main/LICENSE)
- [Project website](https://arxiv.org/abs/2309.16020)
- [README](https://github.com/VicenteVivan/geo-clip/blob/main/README.md)
- [Releases](https://github.com/VicenteVivan/geo-clip/releases)
- [VicenteVivan/geo-clip on GitHub](https://github.com/VicenteVivan/geo-clip)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/vicentevivan-geo-clip
