# geointel: the web extra installs nothing extra, and the interface template glob points out of the package

> A small Python wrapper that sends an image to Gemini and returns a guessed city with coordinates and a confidence label, offered as a command line tool and as a local web interface. The stated limits are firm and worth reading before anything else. The packaging tells you the interface was added recently and bolted on, and the documentation has a table that renders as a code block.

**atiilla/GeoIntel** — GeoIntel using Google's Gemini API to uncover the location where photos were taken through AI-powered geo-location analysis.

- Repository: https://github.com/atiilla/GeoIntel
- Website: https://pypi.org/project/geointel/
- Stars: 1,144 · Forks: 159
- Language: HTML
- License: MIT
- Published: 2026-09-30 · Updated: 2026-09-30 · Language: en
- Canonical page: https://hysenlabs.com/projects/atiilla-geointel

## The prohibited uses come before the features

The disclaimer is longer than most disclaimers in projects this size, and it is specific.

The stated purpose is educational and research use only. The tool estimates where an image was taken using a model, and the predictions are explicitly not guaranteed to be accurate. Then the sentence that matters for any deployment: do not use it for surveillance, for stalking, for law enforcement, or for anything that infringes on personal privacy, violates laws, or causes harm.

There is also a liability statement: the authors and contributors are not responsible for damages, legal issues or consequences arising from use or misuse, use is at your own risk and discretion, and users are told to comply with local, national and international law when using tools of this kind.

Put together with how the tool works, the caution is not decorative. The inputs include an optional context string and an optional guess of where the photo was taken, which is a way to bias a model toward an answer, and the output is a country, a state, a city, coordinates and a confidence label of high, medium or low. That is a geolocation estimate derived from a photograph of a person, their home, or their daily route, which is exactly the input class the prohibited list is about.

Nothing in the response format includes an uncertainty score beyond the three label, and nothing tells you how to calibrate it.

## The argument table is inside a code fence, so the page shows it as code

The documentation has a formatting problem that costs a reader a minute every time.

The argument reference is a table with two columns, one for the flag and one for its description, and it is wrapped in a fenced block with no language marker. Rendered on a page, that table is a code block: eleven lines of plain text with the column tabs preserved, and no column alignment.

The web interface examples have the same problem in a different form. That block is marked as a shell script, but its contents are two prose lines saying standard and custom host and port, followed by two lines that each begin with a shell prompt. Copy and paste of that block gives you a syntax error on the first line.

The examples section repeats the pattern: prose lines such as launching the web interface and saving results to a file sit inside a shell fence alongside the commands.

The API key section wraps its two bullets in a bare fence, so the environment variable line and the parameter line appear as code with the explanatory text swallowed into it.

The contributing section has the same character: five numbered steps written as plain prose with the shell commands inline in backticks, including the branch creation, the commit and the push, rather than as a fenced list anyone could run.

None of this affects behaviour. All of it affects the first impression, and for a project whose headline feature is a browser interface, having the argument list render as a wall of unaligned text is the sort of thing that pushes a reader to the packaging metadata instead.

## The SDK example stops one line after assigning a latitude

The library example is the sharpest thing in the file, because it is nearly complete.

It imports the class, constructs it with no arguments, and calls the locate method with an image path to get a JSON result. It then checks for an error key and prints it if present, and otherwise checks that a locations array exists and is non-empty, takes the first entry, and prints its city and country.

The next step is labelled as getting a Google Maps URL. It checks that the location has coordinates, and assigns the latitude.

And there the example ends, inside the conditional block, with the longitude unread, the URL never built and the coordinate never printed. Everything after that line is absent: the indentation is left open, so the snippet is not valid Python as printed.

That matches the features list, which promises Google Maps links generated from image coordinates. So the capability is claimed in one section and demonstrated one line short of demonstrating it in another.

For a reader copying the snippet, the fix is obvious and for anyone checking the documentation for completeness it is a one line omission that would have been caught by running the example. Everything the file claims about response shape is verifiable from the lines that do exist, which is the saving grace.

## The web extra duplicates the base requirements

Installation is one line:

```bash
# Basic installation
pip install geointel
```

The packaging declares three runtime dependencies: an HTTP client library, the Flask web framework, and a cross-origin helper for it. Then it declares an extra named web, and that extra contains the framework and the cross-origin helper again, at the same version floors. The HTTP client is not repeated.

So the extra is a subset of the base install. Installing the package with the web extra and installing it plainly resolve the same three dependencies, and the only difference is the string in the command.

The requirements file at the repository root repeats those same three lines, so there are two places to update when a floor changes, and the file has no lock and no upper bounds.

That is a small thing, but it is the kind of thing that matters when you are reading a project to decide how it was built. An extra exists to make an optional dependency optional. Here the dependencies are unconditional, which means the command line only pays for Flask whether or not you ever start the web interface.

The reasonable reading is that the web interface was added after the command line tool and the dependency list was updated in one place rather than split between the base list and the extra. The entry in the file labelled as new is consistent with that timeline.

## The interface template lives outside the package and the glob points up

The repository root holds two directories with similar names: the Python package, and a separate directory for the interface template. The template is what the web mode serves.

The packaging maps the package's data files to a glob with a parent directory segment in it, pointing at the template directory that sits beside the package rather than inside it. Package data globs are resolved relative to the package directory, so the parent segment in that pattern is the part to check before you rely on the web interface. The build also sets the include-package-data flag, which means included files are governed by version control rather than only by that glob, so what ends up in a wheel depends on which of the two mechanisms applies.

Either way the arrangement is unusual, and it is the one place in this repository where the web interface can fail for a reason that has nothing to do with your API key or your network.

So the practical check before you use the interface mode is to look at what your install actually contains, rather than trusting that the file describes it. The command line path does not touch the template at all, which is another reason to start there.

## The web mode puts your API key in a browser field and offers to bind every interface

There are three places a key can go, and each has a cost that is worth knowing before you choose.

The environment variable is the cleanest. The documentation also names a command line parameter for a custom key, which means the key appears in your shell history and in the process list while the command runs. The third option is the one specific to the web interface: the key is configured in the browser.

The interface is documented with two invocations. The standard one uses the default host, which is the loopback address, and the default port of 5000. The second example binds all interfaces on a different port. And the cross-origin helper is a hard dependency of the package, not an extra, which means the API is reachable from another origin by design rather than by accident.

Put together: a service whose API key is typed into a page, reachable from other origins, with a documented example that listens on every network interface. On a laptop that is fine. On a shared network, a conference wifi network or a cloud instance with a public address, it means anyone who can reach the port can spend your quota and read the results.

The command line side has its own smaller version of the same problem. The image flag takes a local path or a URL, the output flag writes the JSON result to a file, and the context and guess strings are passed inline. All of that, including a key passed as a parameter, ends up in your shell history, so the environment variable remains the better habit even on a single machine.

The safe default is the loopback address, and the value of that public-bind example command is much lower than it looks at first glance.

## setup.py advertises Gemini 3 and multi-model support that the file never explains

Two descriptions of the same tool exist and they do not match.

The repository description says the tool uses Google's Gemini API to uncover where photos were taken. The packaging description is more specific: AI powered geolocation analysis using Google Gemini 3 with multi-model support.

The file itself never mentions a model version, and never mentions multi-model support. There is no flag for choosing a model, no environment variable for a model name, and no section on a fallback when one model is unavailable. The only model-adjacent surface documented anywhere is the API key.

Two conclusions, and they point in opposite directions. Either multi-model support exists and is undocumented, which means the packaging metadata describes an aspiration or an internal default rather than a user facing feature. Or it does not exist and the description is aspirational. Either way, the file is the place to look, and it is silent.

The version story has the same shape. The packaging says 0.2.0, the package is published on the Python Package Index, and the repository has no releases at all, so there is no tag to compare against a published build.

The rest of the packaging is conventional and worth noting: Python 3.8 and newer, a console entry point wired to the command line module, a beta development status, and project links for bug reports and source.

## The root holds a terminal recording and two loose photographs

The repository layout tells you what kind of project this is. There is the Python package, the interface template directory, a setup file, a requirements file, a licence, and an examples directory holding its own readme, a library usage script and its own requirements file.

Then there are three files at the root that are not part of the package: a terminal recording in the asciinema cast format, and two images, one of which is a screenshot referenced by the interface section and one named after a particular photograph. The cast is what the usage section links as its demo, so it is documentation, sitting beside the documentation.

The primary language is recorded as HTML rather than Python, which follows from the interface template being a directory of markup rather than from anything in the package. The package itself is a thin client: three dependencies, one class, one command line module.

The examples directory carries its own requirements file, which means the library usage example can drift from the package's pinned floors.

There is no tests directory in the tree, even though the packaging excludes one by name when it collects packages, which suggests the directory existed at some point or was planned. There is no workflow directory either, so nothing in the repository runs the examples or checks the packaging on a change.

The usage section also links a terminal recording as its demo, which is the most current description of the command line flow available in the project.

The last commit is dated 9 March 2026, and the repository is not archived. Given that date, treat anything version specific in the packaging as a snapshot rather than a moving target.

## Conclusion

geointel is worth reading as a small, readable example of wrapping a multimodal model call, and the disclaimer is the part to take seriously: it states plainly that the tool is for education and research and must not be used for surveillance, stalking or law enforcement. Before you run it, check three things. Do not expose the web interface on a shared network, because the documented example binds all interfaces and the interface takes your API key in a browser field. Verify that the interface template actually shipped in your install, since the packaging glob points at a directory outside the package. And if you only want the command line, skip the web extra, because everything in it is already a base requirement.

## FAQ

### What does geointel do with an image?

It sends the image to Google's Gemini API and returns a JSON response with an interpretation field and a locations array. Each location carries country, state and city, a confidence label of high, medium or low, latitude and longitude coordinates, and an explanation of the reasoning behind the guess.

### Is geointel allowed for surveillance or law enforcement work?

No. The project states it is for educational and research purposes, that predictions are not guaranteed to be accurate, and that it must not be used for surveillance, stalking, law enforcement, or anything that infringes personal privacy, violates laws or causes harm. It also asks users to comply with local, national and international law.

### How do I give geointel my Gemini API key?

Three ways: set it in the environment variable named in the file, pass it with the custom key parameter on the command line, or enter it in the browser when using the web interface. The file says to get the key from Google AI Studio.

### What does installing geointel with the web extra add?

Nothing that the base install does not already pull in. The framework and its cross-origin helper appear both in the base requirements and again in the web extra, at the same version floors, so installing with the extra resolves the same dependency set as installing plainly.

### What are geointel's command line options?

A web flag to launch the browser interface, host and port for it defaulting to loopback and port 5000, an image flag taking a local path or a URL, context and guess strings that bias the answer, an output path to save JSON results, and an api key flag for a custom key. The image flag is required outside web mode.

## Sources

- [atiilla/GeoIntel on GitHub](https://github.com/atiilla/GeoIntel)
- [Issues](https://github.com/atiilla/GeoIntel/issues)
- [License: MIT](https://github.com/atiilla/GeoIntel/blob/main/LICENSE)
- [Project website](https://pypi.org/project/geointel/)
- [README](https://github.com/atiilla/GeoIntel/blob/main/README.md)

---

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