Posting Map Locations to a Plone Web Site
If you use the Maps add-on software in Plone, you may wish to use a Python script to post locations from a list of latitude and longitude and other data for the locations. The script described here does this for locations of birding spots and hiking trails in southeast Texas. The script was used on the Golden Triangle Audubon Society web site.
This script adds map locations to a Plone web site that is running the Maps add-on software. In this example, we add locations for birding locations and hiking trails in southeast Texas, for the Golden Triangle Audubon Society's web site. Data for these locations includes the county, short name (the name to be used within Plone, as part of the web address of the location), title (name of the location), and latitude and longitude coordinates. This location data was actually created on an earlier version of the web site, and it was saved off for recreation later, which this script does.
The data was pasted into a Python file as line data. You will see the data contained within a triple-quoted string. This string is split into lines of data by use of Python's string.split() method, which we tell to split on the newline, the end-of-line marker, '\n'.
Once we have the data as lines, we loop through the lines and call split() again, this time telling it to split each line on commas. That gives us the data broken out into variables: county, short_name, title, latitude, and longitude.
We loop through one time and add the data to a dictionary using the county name for the key. The values stored in the dictionary, which is called counties_and_locations, is the rest of the data put in a tuple. A tuple is a list of data variables marked by containing parentheses. Data in a tuple is immutable, or unchanging, so this is a fitting storage choice for this static data.
The script will create folders for each county, and will create separate map location objects in the county folders. First , we call Plone's invokeFactory() method (or, the underlying Zope's) to create a high-level folder to contain the county folders. Then we sort the county names, and loop through them, using them in their role as keys to the data dictionary. For each county, we first use invokeFactory() method to create a folder for the county. Then we get the list of locations in the county, which is stored in the dictionary. We sort the location data by the title, so that our locations will be added to the county folder in alphabetical order. Then we are ready to loop through the locations for a given county and call invokeFactory(), this time to create Location objects, passing the location's short_name, title, and latitude and longitude values.
And that's it. We could have the script publish the items, but I did that manually by clicking the main containing folder and using the Contents tab to publish the whole batch of folders and locations in one fell swoop.
I also set the Display for each county folder to be "Map View" -- the desired result, a Google map. For example:

Here's the code of the Python script:
context.invokeFactory(id='gtas-locations', type_name='Folder', title='Locations By County')
gtasMapFolder = getattr(context, 'gtas-locations')
# Each line of the data has county, short name for location, location name, latitude, longitude
gtas_data_string = """Angelina County, boykin-springs, Boykin Springs, 31.0891040579 -94.2649269104
Hardin County, firetower-road, Firetower Road, 30.4753075972 -94.2317962646
Hardin County, kirby-nature-trail, Kirby Nature Trail, 30.464877 -94.340544
Hardin County, turkey-creek-trail-at-gore-store-road, Turkey Creek Trail at Gore Store Road, 30.5208642936 -94.3437194824
Jasper County, ebenezer-park, Ebenezer Park, 31.0739613524 -94.128112793
Jasper County, sundew-trail, Sundew Trail, 30.552357 -94.411697
Jasper County, hen-house-unit, Hen House Ridge Unit of MDSP, 30.8459421801 -94.1698265076
Jasper County, walnut-ridge, Walnut Ridge Unit of MDSP, 30.8631840565 -94.1792678833
Jasper County, jasper-state-fish-hatchery, Jasper State Fish Hatchery, 30.9466969075 -94.1282844543
Jasper County, sam-rayburn-reservoir-dam, Sam Rayburn Reservoir Dam, 31.0652862889 -94.0881156921
Jasper County, sandy-creek-park, Sandy Creek Park, 30.8132183064 -94.1634750366
Jefferson County, cattail-marsh, Cattail Marsh, 30.007831 -94.143133
Jefferson County, hillebrandt-bayou-watershed, Hillebrandt Bayou Watershed, 29.9285685604 -94.1104745865
Jefferson County, mcfaddin-national-wildlife-refuge, McFaddin National Wildlife Refuge, 29.6685150554 -94.074382782
Jefferson County, northwest-jefferson-county, Northwest Jefferson County, 29.9580578427 -94.3557357788
Jefferson County, pilot-station-road, Pilot Station Road, 29.6941668206 -93.852596283
Jefferson County, pleasure-island, Pleasure Island, 29.8518853121 -93.9424610138
Jefferson County, sabine-woods, Sabine Woods, 29.6955 -93.958325
Jefferson County, sea-rim-state-park, Sea Rim State Park, 29.675674346 -94.0439987183
Jefferson County, taylor-bayou-watershed, Taylor Bayou Watershed, 29.8806251864 -94.2571806908
Jefferson County, texas-point-national-wildlife-refuge, Texas Point National Wildlife Refuge, 29.7078848448 -93.9207458496
Jefferson County, tyrell-park, Tyrrell Park, 30.0245811058 -94.1486048698
Newton County, bon-wier, Bon Wier, 30.7393275156 -93.6443710327
Newton County, wild-azalea-canyons, Wild Azalea Canyons, 30.8932393995 -93.6531257629
Orange County, bessie-heights, Bessie Heights, 30.0439842247 -93.9228057861
Orange County, claiborne-west-park, Claiborne West Park, 30.13111667 -93.9212822914
Orange County, baileys-fish-camp, Bailey's Fish Camp, 29.9845275279 -93.8405799866
Orange County, tony-houseman-state-park, Tony Houseman State Park (Blue Elbow Swamp), 30.1228950059 -93.7134647369
San Augustine County, san-augustine-park, San Augustine Park, 31.2023771513 -94.0707778931
San Augustine County, turkey-hill-wilderness-area, Turkey Hill Wilderness Area, 31.3652171566 -94.1710281372
San Augustine County, jackson-hill-park, Jackson Hill Park, 31.28500592 -94.310760498
San Augustine County, harvey-creek-park, Harvey Creek Park, 31.2142695783 -94.2644119263
San Augustine County, powell-park, Powell Park, 31.1137976621 -94.1130924225
Tyler County, beechwoods-trail, Beechwoods Trail, 30.731212296 -94.2309379578
Tyler County, campers-cove-park, Campers Cove Park, 30.8220636965 -94.2022705078
Tyler County, cherokee-unit, Cherokee Unit of MDSP, 30.8539003543 -94.2149734497"""
counties_and_locations = {}
for line in gtas_data_string.split('\n'):
line = line.strip()
county,short_name, title,location = line.split(',')
county = county.strip()
short_name = short_name.strip()
title = title.strip()
lat,lon = location.split()
lat = float(lat)
lon = float(lon)
if county in counties_and_locations:
counties_and_locations[county].append((short_name, title, lat, lon))
else:
counties_and_locations[county] = [(short_name, title, lat, lon)]
# Sort the county names
counties = counties_and_locations.keys()
counties.sort()
for county in counties:
county_folder_short_name = '-'.join([word.lower() for word in county.split()])
gtasMapFolder.invokeFactory(id=county_folder_short_name, type_name='Folder', title=county)
countyFolder = getattr(gtasMapFolder, county_folder_short_name)
# Sort the data on title, which is the third data item
counties_and_locations[county].sort(lambda x,y:cmp(x[2],y[2]))
for location_data_item in counties_and_locations[county]:
short_name,title,lat,lon = location_data_item
countyFolder.invokeFactory(id=short_name, title=title, type_name='GeoLocation', geolocation=(lat,lon))
newLocation = getattr(countyFolder, short_name)
print newLocation.geolocation
return printed
To use the script, I pasted it into a Script (Python) using the Plone web site's ZMI (Zope Management Interface), putting the script in the portal_skins/custom folder, the typical place to put such scripts. Then I invoked the script in my home folder on the web site first, to trouble shoot it. I had to do some iterative debugging/copying/re-invoking, as usual, but once it was working, I then invoked it in the main map folder for the site. Putting scripts in portal_skins/custom is handy that way, because you can call them from anywhere on the Plone site (at any folder location).

