Coder Social home page Coder Social logo

islandora-labs / islandora_simple_map Goto Github PK

View Code? Open in Web Editor NEW
7.0 6.0 7.0 1023 KB

Islandora module that adds a Google map to an object's display if the object's MODS datastream contains cartographic coordinates.

License: GNU General Public License v3.0

PHP 84.16% CSS 0.70% JavaScript 15.14%

islandora_simple_map's Introduction

Islandora Simple Map Build Status

Islandora module that provides the ability to add a map to an object's display. You can see it in action here.

Overview

This module can use geographic coordinates and place names in MODS (or other) elements to populate a Google or OpenStreetMaps map that is then appended to the object's display or displayed in a block.

This module allows use of either Google Map's Embed API or, for more functionality, the Google Maps Javascript API or OpenStreetMap API:

Feature Embed JavaScript / OpenStreetMaps
API key not required required
blocks not supported supported
multivalued coordinates not supported (first coordinate/place name only) supported
place names supported not supported *
collection maps not supported supported

* You can write a custom place name lookup to make use of the hooks but this is not provided currently.

This module exposes two hooks to allow developers to write their own function to extract information and return coordinates for display on the map and to parse coordinates provided in various formats to decimal format for display.

See Configuration for instructions.

Requirements

Install as usual, see this for further information.

Configuration

This module allows you to provide maps produced from Google Maps or OpenStreetMap

Admin settings are available at admin/islandora/tools/islandora_simple_map:

You can select which to use with the Choose map type drop down.

Google Maps

Google Maps Admin screenshot

If you choose to use the Javascript API you will need to get an API Key. If you choose to use the Embed API, you do not need an API key.

If you do not use the Javascript API some options are not available.

Embed API Google Maps Embed Admin screenshot

Javascript API Google Maps Javascript options screenshot

Enabling the Google Maps Javascript API opens configuration for:

  • Your API key (required).
  • Disabling mouse wheel from causing map zoom.
  • Additional enhanced options described below.

The Embed API only displays the first coordinate found.

OpenStreetMap

OSM Admin screenshot

The OpenStreetMap allows you to choose between 3 tile providers (OpenStreetMap, MapBox and Thunderforest)

Note: The OpenStreetMap tile provider is for testing and very small sites. Read the OpenStreetMap Tile Usage Policy for more information.

The other two providers will require you to register to get an API key.

The OpenStreetMap maps also provide additional enhanced options described below.

Enhanced options

Using Google Maps with the Javascript API or using OpenStreetMap opens additional functionality including.

  • Display all coordinates found and show multiple points on a map.
  • Display a map in a block.
  • Disable the map embed in the page. Useful if using the above map block.
  • Collection maps.

Common configuration options

Common configuration options are:

  • A delimiter to split multiple coordinates on.
  • the XPath expressions to the MODS elements where your map data is stored
  • the map's height, width, default zoom level, and whether or not the map is collapsed or expanded by default, and
  • option to clean up the data before it is passed to the map provider.
  • option to display maps for compounds (in addition to their first child).

Once you enable the module, any object whose MODS file contains coordinates in the expected element will have a Google map appended to its display.

There is also the Coordinates Solr field option if you index your object's coordinates. If you fill this in, then a Solr query will be done to retrieve the collections coordinates instead of parsing each collection member's MODS record.

Extract from MODS using XPath

Site admins can configure multiple MODS elements in a preferred order by entering XPath expressions in the admin setting's "XPath expressions to MODS elements containing map data" field. Data from the first element to match one of the configured XPath expressions is used to render the map. The module provide sensible default values that prefer <subject><cartographics><coordinates> over <subject><geographic>.

Using geographic coordinates to create maps

By default, the MODS element this module expects geographic coordintates to be in is <subject><cartographics><coordinates>. Geographic coordinates must be in "decimal degrees" format with latitude listed first, then longitude.

Google Maps is fairly forgiving of the specific formatting of the values when using the Embed API. All of these work:

+49.05444,-121.985
+49.05444 -121.985
49.05444 N 121.985 W
49.05444N121.985w

When using the Javascript API the coordinates need to be in a comma separated decimal format.

+49.05444,-121.985

If your data is not in that format, you can create a small module using the API to transform your data.

Semicolons separating the latitude and longitude are not allowed, resulting in a map with no points on it:

+49.05444;-121.985

There is an admin option to "Attempt to clean up map data".

If this is enabled (which it is by default), a semicolon in the data will be replaced with a comma before it is passed to Google Maps.

This option can be enabled, but this cleaning occurs before splitting multiple coordinates. So if semi-colons are used to delineate multiple coordinates this can cause problems.

Example:

+49.05444,-121.985;+49.895077,-97.138451 becomes +49.05444,-121.985,+49.895077,-97.138451

Then splitting on ; would fail.

Alternatively you can use multiple <cartographic><coordinates></coordinates></cartographic> elements and place one coordinate in each.

Using place names and other non-coordinate data to create maps

Embed API Only

If you configure this module to use MODS elements that do not contain coordinate data, such as <subject><geographic>, Google Maps will attempt to generate a map based on whatever data it finds in the configured element. However, the results of using non-cartographic coordinates are not always predictable. For example, the following two values for <subject><geographic> produce accurate maps, presumably because they are unambiguous:

Dublin, Ireland
Dublin, Ohio

but a value of just Dublin results in a map showing the Irish city. Another example that illustrates Google Maps' behavior when it is given ambiguous data is a <subject><geographic> value of City of Light, which results in a map showing a church by that name in the US Northwest, not Paris, France, probably because when I wrote this I was closer to that location than to Paris. From Europe, for example, you get a completely different location for a <subject><geographic> value of City of Light. Also, if Google Maps cannot associate the data with a geographic location (predictable from a user's perspective or not), it produces a map showing a large portion of the world (depending on the default zoom level in effect) with no points on it.

The XPath expressions used to retrieve map data are executed in the order they are listed in the admin settings. So, for best results, listing the expressions in decreasing likelihood they will contain reliable and unambiguous data is the best strategy. The defaults values do this.

Note: If you wish to reproduce this behaviour using the Javascript API you would need a new hook implementation using the Google Places API to convert place names to coordinates.

Collection maps

Collection maps provide a single map with markers for all items directly in the collection (does not include sub-directories).

If you have checked the "Enable collection level maps?" option, you can then enable a map for each collection within the collection's Manage subtabs.

Collection maps admin screenshot

You can also cache collection points for anonymous users, this can significantly improve performance on maps with large numbers of points.

To enable a collection map, manage the collection and choose the Collection tab. Collection manage tab screenshot

Choose the Manage Islandora Simple Map tab. Collection manage Islandora Simple Map screenshot

Check the "Display map of resources in this collection" checkbox and click Save. You can also un-check and Save to disable a collection's map.

You can also find a listing of all enabled collection maps on the Collection Maps tab of the admin screen. From here you can disable one or more collection maps. Collection maps tab screenshot

Collection maps appear as a new tab on the Collection. Collection map example screenshot

Each marker is clickable with a tooltip of the title and thumbnail of the object and a link back to the resource. Collection map tooltip screenshot

API

hook_islandora_simple_map_get_coordinates(AbstractObject $object)

Implementations of this hook should return an array of decimal coordinates. These are merged with all other implementations. If you are using the Javascript API, they are then validated/filtered to ensure they are decimal coordinates. Lastly (using either API) they are de-duplicated to determine the points to show on the map.

hook_islandora_simple_map_parse_coordinates_callback()

Implementations of this hook should return an array of the format.

  array(
    'my_module_implementation' => array(
      'function_name' => 'islandora_test_parse_coordinates',
      'file' => drupal_get_path('module', 'islandora_simple_map') .
      'includes/test_functions.inc',
      'weight' => 100,
    ),
  );

Where

  • function_name is a function that accepts an array of coordinates of various formats and returns an associative array of coordinates that it could parse where the key is the original value and value is the parsed value.
  • file is the file to include to access this function. (Optional)
  • weight is to order the hooks. Default is 100. (Optional)

Maintainer

To do

  • Add a Drupal permission to "View Islandora Simple Map maps".

Development and feedback

Pull requests are welcome, as are use cases and suggestions. For example, if your coordinate data results in maps with no points on them, please suggest some ways that the data could be normalized (and don't forget to include some sample data).

License

islandora_simple_map's People

Contributors

axfelix avatar bgilhome avatar bryce-gilhome avatar mjordan avatar monkii-dev avatar whikloj avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

islandora_simple_map's Issues

Make results page size configurable

For collection maps we currently grab 20 items at a pass, this was a random number I chose. This ticket is to make this a configurable variable with a default of 20.

Handle repeated geocoordinate values in one field better

For example, http://digital.lib.sfu.ca/alping-460/niut-range-camp-ridge-north-camel-mountain-aug-22-1967 has two sets of geocordinates in one element:

<cartographics>
<coordinates>+51.585,-124.71722; +51.52333,-124.97861</coordinates>
</cartographics>

In cases like this, we should give admins the choice of whether to not render a map, or to render a map showing only the first coordinate. We probably need to give the admin an option to define what characters to separate repeated coordinate on

Add support for Solr field containing coordinates.

I'll need this for my collection work in #17 but it could also make #10 possible.

This task is to add a simple textfield to the admin pages.

The field should be stored in a variable and the field should use the Solr autocomplete to populate.

The Solr field can store any form of coordinates (DSM, decimal, place names, etc) and (building on the work in #25) they can be parsed to the final format after retrieval.

This Solr field would be an opt-in replacement for the built-in XPath query of the MODS record. Other hook_islandora_simple_maps_get_coordinates() implementations would still get run as per normal. Therefore this could also speed up the execution of normal maps as XPaths in MODS would not be necessary.

Alternatively, we can split up these choices and add checkboxes to the admin pages to determine whether:

  • single Islandora objects should use Solr
  • collections of Islandora objects should use Solr

To make #10 happen we would almost definitely need Solr.

Cache marker coordinates for collection maps

When displaying huge maps (like http://digital.lib.sfu.ca/islandora/object/bcp%3Acollection/maps) the system queries for all the collection coordinates each time the map is rendered.

If we can agree on a safe way to avoid revealing objects a user doesn't have permission to see, we could cache the array which should really improve performance.

In past I have used the users roles as a key for the cache. So if two users have the same roles then they can share the cache entry. This only works if you don't assign XACML permissions based on username.

Alternatively, we could just enable a cache for anonymous users and logged in users will suffer the consequences.

Make this a block?

Hello @mjordan, I am back to steal more of your wonderful code.

We are migrating a Public Art project, which I built using HTML and javascript many years ago. http://umanitoba.ca/libraries/units/archfa/art_tour/

My idea is to have a map on the collection page that aggregates the points from the items in the collection. For this I thought a block might be better.

I'm gonna fork this and I'll try to not move to far away from your existing implementation.

Accept multiple elements for geographic reference

Include in the config form additional (if not then..) options for elements that could contain a geographic reference.

Example: some objects may contain a geographic subject, but no cartographic coordinates. Others may have coordinates but no geographic subject. Module should ideally look first at one element (specified in configuration), then at another, and optionally another, etc.

GeoJson breaks collection map markers

If you change the Output to use to pass co-ordinates to maps setting from Co-ordinates (default) to GeoJson and then view a collection map with multiple markers.

The map displays correctly but when you click on a marker you do not see the popup with the title and thumbnail image as well as the link back to the original image.

Include Administrator permission by default

Administrator is not given permission by default to configure the module, and the configure option does not appear in admin menu until the permission is checked off. Permission should be granted by default, or should accept administrator permissions bypass.

Not working with Thesis CModel

Looks like this module doesn't work on objects with the Thesis CModel. Just to be sure, I've tried copying the geographic subject MODS directly from objects that do display a map - but no map on the Thesis.

Add support for InfoWindows on markers

Again for #17 and #10 it would be nice to get a small InfoWindow with some basic information displayed as well as a link back to the Islandora object.

Depending on the use cases it could be as simple or as complex as

  • Title as a link back
  • Title as a link back, thumbnail
  • A set of whichever Solr fields you want to add and organized as you choose.

Better support for controlled Geographical Subjects

LoC method of writing geographical subjects is different from how Google Maps processes them. Google maps prefers "Armstrong, BC" while LoC prefers "Armstrong (B.C.)".

Because of this, Simple Map using the Geographic Subject could end up showing the wrong location. "Armstrong (B.C.)" shows a bunch of shops with the name Armstrong, whereas "Armstrong, BC" shows the correct city area.

Proposing some functionality that looks for those parentheses and turns them into commas.

Display the points from all objects in a collection

This needs to pull the entries from Solr because parsing each MODS would be too crazy. Either that or aggregate the points from all the child objects into the Collections MODS record?

I'm thinking only one level deep, again it would be too crazy otherwise. Not sure how to determine specify that a collection should display the map or not? Perhaps make it another block, then the inherent (Show block on specific pages) config can be used?

My use case is migrating this Public Art map (http://umanitoba.ca/libraries/units/archfa/art_tour/) to Islandora. So I think I will also need to be able to create custom info windows, but that may have to be discussed with the collection owner.

If anyone else has a use case, please add it in and I'll keep it in mind when I am working on this.

Compound images get 2 maps

Due to the way compounds render as themselves and as their first child you get a map for each.

I have tested not allowing the rendering of the map when their is a islandora:compoundCModel in the $object->models. This makes only the map provided by the underlying islandora:sp_large_image_cmodel renders.

Not sure if that is okay with other users?

Modularize MODS parsing to other metadata schemas and add KML support

We previously developed a module called Islandora Gmap, or Islandora Google Maps to provide functionality for automatically embedding Google Maps on Islandora object landing pages. This module was inspired by Islandora Simple Map, and at the time provided a superset of Islandora Simple Map functionality, but Islandora Simple Map has since been made an an official Islandora Labs module. Other functionality which is of use to us -- the ability to display a single map with multiple annotations from all of its child objects on a collection landing page, to serve as an alternate entry point to the collection -- has since been added to Islandora Simple Map. Because Islandora Simple Map now has a more sustainable maintenance structure, it behooves us to merge our Islandora Gmap functionality into Simple Map.

This functionality includes:

  • Modularizing the code that reads geographical coordinates out of MODS datastreams to also support DDI, or any other relevant datastream
  • Displaying KML layers on Google Maps in addition to latitude/longitude coordinates.

Implement permission for collections with maps admin sub-tab?

I was updating documentation and I realize that if you have access to the Islandora Simple Maps admin interface you can disable anyone's collection map.

I then realized that I had half implemented this by specifying a non-existent permission to that tab.
https://github.com/mjordan/islandora_simple_map/blob/7.x/islandora_simple_map.module#L45

This task is to either change that permission to be "administer site configuration" like the others or implement the other permission?

@mjordan?

Adding support for OpenStreetMap

I can't find much by way of a direct API for using OpenStreetMap similar to how this module uses Google Maps, but the HTML that needs to be generated looks like this:

<iframe width="425" height="350" frameborder="0" scrolling="no" marginheight="0" marginwidth="0" src="http://www.openstreetmap.org/export/embed.html?bbox=-123.38882446289061%2C49.14555903725487%2C-122.93563842773436%2C49.35353208188311&amp;layer=mapnik&amp;marker=49.24965507167121%2C-123.1622314453125" style="border: 1px solid black"></iframe><br/><small><a href="http://www.openstreetmap.org/?mlat=49.2497&amp;mlon=-123.1622#map=12/49.2497/-123.1622">View Larger Map</a></small>

The difficulty in assembling this markup is computing the bbox values from the lat/long. A Perl implementation of how to do this is available on the OSM wiki.

Add hook for parsing formats?

I added a hook for retrieving coordinates but even my example module still uses the MODS datastream. Then it parses the new coordinates.

Perhaps it would be better to use 2 hooks.

The existing hook_islandora_simple_map_get_coordinates(AbstractObject $object) to extract from new locations (ie. alternate datastreams) and the module provides the MODS XPath one.

Then add a new hook hook_islandora_simple_map_parse_coordinates() to allow for custom parsing functions.

This new hook would return (for example)

function example_islandora_simple_map_parse_coordinates() {
   return array(
      'match_regex' => 'a regular expression string to match against this coordinate type',
      'function' => 'the name of the function to call with the coordinate(s) as the only argument',
      'file' => 'the file in the hook implementing module to find the above function',
      'weight' => 'some way to order the parsing so more common types appear first.',
   );
}

The called function above would return a string or array of strings with one or more coordinates parsed into decimal coordinates. This would be what my example module should be called for.

So once we get an array of coordinate strings from the old hook_islandora_simple_map_get_coordinates(), we can run them through the functions returned by the new hook.

Once one matches we use that result (hence the weight ordering).

Might be overkill, but otherwise if you have multiple coordinate systems used in your MODS, you'll have multiple modules parsing your MODS for each set of XPaths just to get the results and then parse them differently.

What I'm not sure about is should we pass the full array of coordinates to the function and let it parse those it matches on or do loop through the coordinates and process each against all hook implementations in order.

thoughts?

Last commit breaks existing implementations without reinstall - warn existing users?

After the last time I pulled Islandora Simple Map, I could no longer access my Collections tab when managing a collection. Here's what I got in my log:

PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'drupal.islandora_simple_map_collections_map' doesn't exist: SELECT pid from {islandora_simple_map_collections_map} WHERE pid = :pid; Array ( [:pid] => doh:root ) in _islandora_simple_map_display_collection_map() (line 201 of /opt/rh/httpd24/root/var/www/drupal7/sites/all/modules/islandora_simple_map/includes/utilities.inc).

Uninstalled the module and re-enabled it, and it works fine again. I see there's a "create table" command in the Install file that was added in December, which of course doesn't get run if you're just updating the code without reinstalling.

What would be the best way to inform users that they should uninstall and reinstall if they update their code? Google group perhaps?

Map not displaying on book object

Hey @mjordan, not sure if this is your module, or something to do with the book solution pack, but I'm not able to get the map to display on any book solution pack objects.

From what I can tell, the XPath if exactly the same, and I'm not seeing anything obvious in the preprocess hook, or template files for the large image solution pack or the book solution pack that would cause the map not to display.

Two examples:

Any ideas?

Google Maps does not zoom in for Maps

A collection map should zoom in to the tightest zoom level that allows all points to be seen at its initial display.

This occurs when using OpenStreetMaps but seems to not be happening for Google Maps.

Move variables with _mapS to _map

This is my fault (again), but I added two variables with islandora_simple_maps_*.

This task is to:

  1. Change the variable name in all locations.
  2. Add a hook_update_N task to:
    1. migrate the old variable (with S) to the new (without S) variables.
    2. Uninstall the old incorrect (with S) variables.

Not working for Book CModel?

We have Islandora Simple Map enabled on several sites in Arca, and while it works perfectly well on Basic and Large Images, the map does not display on Book objects.

Example: http://arcabc.ca/islandora/object/ufv%3A378

Configuration is with the following XPaths:

//mods:subject/mods:cartographics/mods:coordinates
//mods:subject/mods:geographic
//mods:subject/mods:hierarchicalGeographic

Metadata is present in each of the above elements, but no map is generated.

Add aggregated map page?

Would be neat to generate a page that generates a map of all Islandora objects with location information, with a Solr query.

Add hook to allow custom coordinate format

Google Maps loves decimal coordinates, but if you have your data in a different format or stored using a custom way we can allow you to write a "getter" that would get the data and reformat it into the standardized way.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.