This project provides a simple and flexible way to geolocate multiple objects based on environment variable. Each object is associated with a specific source from which its geolocation data is retrieved.
- Configurable Maps: Define multiple maps with specific parameters (default latitude/longitude, zoom, etc.).
- Interactive Objects: Each object has its own interactive map displaying its geolocation data. Data is fetched from a specified URL.
- Easy Integration: The maps can be seamlessly embedded into other websites as iframes. You can also define the height of the iframe by passing
heightas a query parameter. - Sandbox mode: Mock some random coordinate to test the module
This project is ideal for scenarios where you need to display geolocation data for multiple objects on individual maps, with the ability to integrate these maps into external web pages.
Geoloc-Map uses JSON configuration to define maps and their geolocatable objects. You can configure using either:
- External JSON file (recommended) -
geoloc.json - Environment variable -
GEOLOC_OBJECTS
Quick example:
[
{
"mapName": "my_car",
"default_latitude": 48.8575,
"default_longitude": 2.3514,
"default_zoom_level": 12,
"refresh_interval": 5000,
"custom_message": "Véhicule non disponible",
"time_ranges": [
{
"days": ["bastille_day"],
"startTime": "10:00",
"endTime": "14:00"
},
{
"days": ["french_holidays"],
"startTime": "closed"
},
{
"days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"startTime": "08:00",
"endTime": "12:00"
},
{
"days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"],
"startTime": "14:00",
"endTime": "18:00"
},
{
"days": ["Saturday"],
"startTime": "09:00",
"endTime": "12:00"
}
],
"objects": [
{
"name": "Mon super véhicule",
"url": "https://my_jeedom_domain.com/core/api/jeeApi.php",
"query_params": {
"apikey": "**JEEDOM_API_KEY**",
"method": "get",
"plugin": "jMQTT",
"type": "cmd",
"id": "[779,780]"
},
"latitude_json_path": "779",
"longitude_json_path": "780"
}
]
}
]Access your map at: https://your-domain.com/my_car
📖 For complete configuration options, examples, and best practices, see:
- Configuration Guide - Detailed documentation
- geoloc.example.json - Template with examples
The easiest way to deploy in production is using our pre-built Docker image.
docker volume create caddy_data
docker volume create caddy_configThen deploy the container:
With inline JSON configuration:
docker run -d \
--name geoloc-map \
-p 80:80 \
-p 443:443 \
-v caddy_data:/data \
-v caddy_config:/config \
-e SERVER_NAME="your-domain.example.com" \
-e APP_SECRET="$(openssl rand -hex 32)" \
-e GEOLOC_OBJECTS='[{"mapName":"example","default_latitude":48.8575,"default_longitude":2.3514,"default_zoom_level":12,"refresh_interval":5000,"objects":[{"name":"Test Object","enable_sandbox":true}]}]' \
nicolascodemate/geoloc-map:latestWith external JSON file (Recommended):
# First, create your geoloc.json file, then:
docker run -d \
--name geoloc-map \
-p 80:80 \
-p 443:443 \
-v caddy_data:/data \
-v caddy_config:/config \
-v $(pwd)/geoloc.json:/app/geoloc.json:ro \
-e SERVER_NAME="your-domain.example.com" \
-e APP_SECRET="$(openssl rand -hex 32)" \
-e GEOLOC_OBJECTS="/app/geoloc.json" \
nicolascodemate/geoloc-map:latestRequired Environment Variables:
SERVER_NAME: Your domain name or:80for HTTP-onlyAPP_SECRET: Generate withopenssl rand -hex 32GEOLOC_OBJECTS: JSON configuration string OR path to json file
Optional:
CADDY_SERVER_EXTRA_DIRECTIVES: For custom SSL certificates
Configuration Examples:
geoloc.example.json: Template file with examples- Configuration Guide: Complete documentation
caddy_data and caddy_config
Then deploy the container:
- Open Docker Desktop
- Go to Images → Search for
nicolascodemate/geoloc-map - Click Run → Optional settings
- Port mappings: Add
80:80and443:443 - Volumes:
- Host path:
caddy_data→ Container path:/data - Host path:
caddy_config→ Container path:/config
- Host path:
- Environment variables:
SERVER_NAME=localhost APP_SECRET=your-generated-secret-32-chars GEOLOC_OBJECTS=your-json-config - Run the container
- Access your maps at:
http://localhost/{mapName}
- Name:
caddy_data→ Create - Name:
caddy_config→ Create
Then create the container:
-
Go to Containers → Add container
-
Image:
nicolascodemate/geoloc-map:latest -
Network ports:
80:80/tcp443:443/tcp
-
Environment variables (see Configuration Guide for GEOLOC_OBJECTS format):
SERVER_NAME=your-domain.example.com APP_SECRET=your-generated-secret GEOLOC_OBJECTS=[{"mapName":"demo","default_latitude":48.8575,...}] -
Volumes (required for certificate persistence):
caddy_data:/datacaddy_config:/config
Optional (if using custom certificates):
/host/path/to/certs:/etc/caddy/certs:ro
-
Deploy the container
For customization or development.
docker volume create caddy_data
docker volume create caddy_configThen build and deploy:
docker compose -f compose.yaml -f compose.prod.yaml build --pull --no-cache
SERVER_NAME=your-domain-name.example.com \
docker compose -f compose.yaml -f compose.prod.yaml up --waitMount your certificates and configure Caddy:
docker run -d \
--name geoloc-map \
-p 80:80 -p 443:443 \
-v /path/to/your/certs:/etc/caddy/certs:ro \
-e SERVER_NAME="your-domain.example.com" \
-e APP_SECRET="your-secure-secret" \
-e GEOLOC_OBJECTS='your-json-config' \
-e CADDY_SERVER_EXTRA_DIRECTIVES="tls /etc/caddy/certs/cert.crt /etc/caddy/certs/key.key" \
nicolascodemate/geoloc-map:latestmapName: The name of the map, which also serves as the URI of the iframe. For example, my_car will be accessible at https://mydomain.com/my_car.default_latitude: The default latitude for the map when no object is visible.default_longitude: The default longitude for the map when no object is visible.default_zoom_level: The default zoom level for the map when no object is visible.refresh_interval: The interval (in milliseconds) at which the map will refresh to fetch new geolocation data.- Optional
time_ranges: An array of time ranges with priority-based matching (first match wins).- If omitted, objects will always be visible (24/7).
- Outside the specified time ranges, the map will display the default latitude and longitude with a message indicating no geolocatable objects.
- Priority System: Time ranges are evaluated in order. The first rule that matches the current date/time determines visibility. Place more restrictive rules (holidays) before general rules (weekdays).
- Each time range includes:
days: An array of day specifiers. Supported formats:- Day of week:
"Monday","Tuesday", etc. - Fixed date (MM-DD):
"05-01"for May 1st (any year) - Full date (YYYY-MM-DD):
"2025-12-24"for a specific date - All French holidays:
"french_holidays"for all 11 French public holidays at once - Individual French holiday keywords:
"labor_day","easter_monday","bastille_day", etc. (see list below)
- Day of week:
startTime: The start time in HH:MM format (e.g.,"08:00"), or special keywords:"closed": Force closure (objects hidden)"open": Force opening (objects shown 24/7)
endTime: The end time in HH:MM format (e.g.,"18:00"). Ignored ifstartTimeis"closed"or"open".
- French Holiday Keywords (automatically calculated):
new_year- January 1steaster_monday- Monday after Easter (mobile date)labor_day- May 1stvictory_day- May 8th (Victory in Europe Day)ascension- 39 days after Easter (mobile date)whit_monday- 50 days after Easter (mobile date)bastille_day- July 14thassumption- August 15thall_saints- November 1starmistice- November 11thchristmas- December 25th
- Example Configuration:
In this example (evaluated in order, first match wins):
"time_ranges": [ { "days": ["bastille_day", "assumption"], "startTime": "10:00", "endTime": "14:00" }, { "days": ["french_holidays"], "startTime": "closed" }, { "days": ["2025-12-24"], "startTime": "08:00", "endTime": "12:00" }, { "days": ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"], "startTime": "08:00", "endTime": "18:00" }, { "days": ["Saturday", "Sunday"], "startTime": "closed" } ]
- Bastille Day and Assumption: open 10am-2pm (exception to general holiday closure)
- All other French holidays: completely closed
- December 24, 2025: open 8am-12pm (special Christmas Eve hours)
- Weekdays: open 8am-6pm
- Weekends: closed
- Optional
custom_message: A custom message to display when no geolocation data is available (outside time ranges or when objects cannot be fetched). Default:"Aucune donnée de géolocalisation".- Example:
"Service momentanément indisponible"or"Véhicule non disponible"
- Example:
objects: An array of objects to display on the map:name: The name of the object.url: The URL to retrieve the geolocation data for the object.query_params: The query parameters to include in the request.latitude_json_path: The JSON path to extract the latitude from the response.longitude_json_path: The JSON path to extract the longitude from the response.enable_sandbox: set to true and omit all other param except name to enable a sandbox mode. It will generate some random coordinate.
On Linux, the following CLI commands are based on a recent Ubuntu release. Please adjust them as needed for your specific distribution.
- A Linux server (Ubuntu 20.04 or later is recommended)
- A user with sudo privileges
- Docker and Docker Compose installed
- Git installed
Follow the steps below to install the necessary dependencies, or skip to the Start the Application section if you already have them installed.
- Update the package manager
sudo apt update- Install GIT
sudo apt install git- Clone the repository in wanted directory
git clone https://github.com/nicolas-codemate/geoloc-map.git# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
# Add the repository to Apt sources:
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get updateInstall Docker Engine, CLI, and Containerd:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin- Allow your user to run Docker commands without sudo
sudo groupadd docker
sudo usermod -aG docker $USER- Log out and log back in to apply the group changes.
You can verify everything is working by running:
docker run hello-world- Navigate to the cloned repository directory:
cd geoloc-map- Build the Docker images:
docker compose -f compose.yaml -f compose.prod.yaml build --pull --no-cache- Create a
.env.prod.localfile in the root of your project with the following content:
APP_SECRET=AnyRandomStringToSecureYourApp
GEOLOC_OBJECTS='[...]' # Replace with your actual JSON configuration- Start the application:
SERVER_NAME=your-domain-name.example.com \
docker compose -f compose.yaml -f compose.prod.yaml up --wait By default, the application uses Let's Encrypt to automatically generate a TLS certificate for your domain. To disable HTTPS, set SERVER_NAME to :80 instead of your domain name.
If your server is behind a firewall, ensure that ports 80 and 443 are open to allow incoming traffic for successful TLS certificate generation with Let's Encrypt.
docker volume create caddy_data
docker volume create caddy_configThis ensures certificates persist across container restarts. See docs/letsencrypt.md for detailed information.
Put your TLS certificates in the frankenphp/certs
And run
SERVER_NAME=your-domain-name.example.com:443 \
CADDY_SERVER_EXTRA_DIRECTIVES="tls /etc/caddy/certs/your_custom_public_certificate.crt /etc/caddy/certs/your_custom_private_certificate.key" \
docker compose -f compose.yaml -f compose.prod.yaml up --wait