NAME

makeGeoJSON.pm - Builds GeoJSON files for Google Maps

SYNOPSIS

Drop-in replacement for makeKML.pm:

use makeGeoJSON;

makeGeoJSON->latlongCache("latlong.cache");
makeGeoJSON->useAPI('opencagedata');

my $gj = makeGeoJSON->new({ name => "My Map" });

$gj->add({
  name    => 'The White House',
  desc    => 'Where the president lives',
  link    => 'http://whitehouse.gov/',
  style   => 'yellowpush',
  address => '1600 Pennsylvania Ave NW',
  city    => 'Washington',
  state   => 'DC',
});

$gj->write("Map.json");

DESCRIPTION

makeGeoJSON.pm is a drop-in replacement for makeKML.pm that writes a GeoJSON FeatureCollection instead of KML. It has the same API and uses the same geocoding infrastructure.

Each feature's properties object contains name, description, link, and icon (a resolved icon URL for use with the Google Maps Data layer).

GeoJSON is the preferred format for embedding maps in web pages because it is fetched directly by the browser using the Maps Data layer, whereas KmlLayer requires Google's servers to fetch and render the file — a mechanism that has become unreliable and is being phased out.

GEOCODING

GeoJSON features need latitude and longitude for each point. makeGeoJSON.pm can look these up for you by address (geocoding) and caches the results. Geocoding setup is identical to makeKML.pm.

API KEY

You will need to register with Google or opencagedata to get a key, but it costs nothing.

For Google, simply visit: https://console.developers.google.com/

Turn on the Geocoding API and look at your credentials, you need to copy the API KEY for server applications.

You can also use opencagedata which allows free geocoding limited to 1/sec by signing up at: https://opencagedata.com/

You can give your key to makeGeoJSON.pm a number of ways:

As a value in your script:
makeGeoJSON->Key("TheKeyValueThatYouGetFromGoogleOrOpenCageData");
Saved in a key file.

Put the key on a single line in one of the following locations:

google_api_key
.google_api_key
$HOME/.google_api_key

If you're using opencagedata, replace "google" with "opencagedata":

opencagedata_api_key
.opencagedata_api_key
$HOME/.opencagedata_api_key

Or else put the value in a different file and hand it to the library:

makeGeoJSON->KeyFile("KeyFile");

Results are cached in a simple flat file so that addresses don't need to be looked up again. The default cache file is latlong.cache, changeable with:

makeGeoJSON->latlongCache("CacheFile");

If multiple placemarks are at the same location, makeGeoJSON will offset the lat/long of each by a small amount so they remain distinguishable on the map. This amount can be changed (or set to 0) with:

makeGeoJSON->offsetDuplicatePlacemarks(.001);   # Default value

METHODS

$gj = new makeGeoJSON(%options);

Constructs a new makeGeoJSON object.

Options include 'name' (for the map name) and 'file' (to specify file output). Also see 'write'.

$gj->add(%place);

Add a place to the GeoJSON file. Places can have a number of keys:

  Required fields:
name        Name of the place
address     Street address
city        City name
state       State, country or municipality.

At least one of address/city/state is required — enough for a geocoding lookup.

  And some optional fields:
link        URL
desc        Long description (HTML allowed)
coords      Latitude,longitude  (if not specified, see GEOCODING)
style       One of the predefined styles:
    redpush bluepush whitepush yellowpush greenpush
    ltbluepush purplepush greenpaddle ltbluepaddle bluepaddle
    pinkpaddle purplepaddle redpaddle whitepaddle yellowpaddle

Returns 0 on success, -1 if name is missing, -2 if no location can be determined.

$gj->addIcon($styleName, $url);

Add a custom icon URL as a named style, in addition to the default set. The URL must be publicly accessible from browsers (use https://).

  Required fields:
styleName   A unique style name that can now be specified to add()
url         URL to the icon image (must be browser-accessible)
$gj->write(); $gj->write($file);

Write out the GeoJSON FeatureCollection file.

If a filename is not given, writes to the file specified in new().

DIFFERENCES FROM makeKML

Output format

Writes GeoJSON (FeatureCollection) instead of KML. Use map.data.loadGeoJson() instead of google.maps.KmlLayer to display the map.

Icon URLs

Icon URLs use maps.gstatic.com/mapfiles/ms2/micons/ — these load correctly when fetched directly by browsers. The maps.google.com/mapfiles/kml/ URLs used by makeKML do not load when requested directly by browsers (they only work when Google's servers render KML).

%STYLES hash fix

makeKML.pm has a { vs ( bug that silently breaks all style lookups. This is corrected here. bluepaddle is also added (it was missing from makeKML).

Character encoding

Descriptions are stored as-is (no KML-specific character cleaning). Non-ASCII bytes are escaped as \uXXXX in the JSON output, preserving accented characters correctly.

HTML EXAMPLE

Use the Google Maps Data layer to display the GeoJSON. Load the API with async and loading=async to avoid console warnings, and use window.addEventListener instead of the deprecated addDomListener. Replace YOUR_KEY with your browser application API key.

<script>
function initialize() {
  var map = new google.maps.Map(document.getElementById('map-canvas'), {
    center: { lat: 37.77, lng: -122.44 },
    zoom: 12
  });
  var time5 = parseInt((new Date().getTime()) / 5000);
  var infoWindow = new google.maps.InfoWindow();
  map.data.loadGeoJson('https://example.com/Map.json?a=' + time5);
  map.data.setStyle(function(feature) {
    return { icon: feature.getProperty('icon') };
  });
  map.data.addListener('click', function(event) {
    var name = event.feature.getProperty('name');
    var link = event.feature.getProperty('link');
    var desc = event.feature.getProperty('description');
    var title = link ? '<a href="' + link + '" target="_blank">' + name + '</a>' : name;
    infoWindow.setContent('<b>' + title + '</b><br>' + desc);
    infoWindow.setPosition(event.latLng);
    infoWindow.open(map);
  });
}
window.addEventListener('load', initialize);
</script>
<script async
  src="https://maps.googleapis.com/maps/api/js?key=YOUR_KEY&loading=async">
</script>
<div style="height: 500px; width: 100%;" id="map-canvas"></div>

The ?a=<timestamp> cache-busting parameter ensures browsers reload the JSON when it changes. Adjust the divisor to control how often (5000 = every 5 seconds, 500000 = every ~8 minutes). Since the browser API key is visible in the page source, consider restricting it by HTTP referrer in the Google Developers Console.

COPYRIGHT

Copyright 2004,2020,2026 David Ljung Madison Stellar <http://GetDave.com/>
All rights reserved.
See: http://MarginalHacks.com/