UCamGeoJSON: Difference between revisions
Line 330: | Line 330: | ||
Header add Access-Control-Allow-Methods "GET" | Header add Access-Control-Allow-Methods "GET" | ||
:If you want to restrict the data only to the map, use ''<nowiki> | :If you want to restrict the data only to the map, use ''<nowiki>https://map.cam.ac.uk</nowiki>'' instead of the asterisk in the first line (the asterisk means you are giving permission to any requesting page). Note that when using Desktop Services [http://www.ucs.cam.ac.uk/desktop-services/ds-web DS-Web] service these lines need to appear in a file called 'htaccess', not '.htaccess'. | ||
* '''Use a site that already has ''CORS'' turned on for you'''. That is, a storage provider who recognises that the data will be required by other sites. In this case you need do nothing, the request should just work. | * '''Use a site that already has ''CORS'' turned on for you'''. That is, a storage provider who recognises that the data will be required by other sites. In this case you need do nothing, the request should just work. | ||
Line 336: | Line 336: | ||
:'''However''', note that the now ancient and unsupported, but still sometimes used, Internet Explorer 8 (and earlier) does not support CORS. | :'''However''', note that the now ancient and unsupported, but still sometimes used, Internet Explorer 8 (and earlier) does not support CORS. | ||
: Note also that it is not permissible to load data via http from any https site, so | : Note also that it is not permissible to load data via http from any https site, so,mbecause the map is served over https at https://map.cam.ac.uk, your annotation must also be fetched over https, as must any images it references. | ||
* '''Let the map sort it out'''. If the map is unable to fetch the JSON, it will then automatically try to fetch is using its CORS adapter proxy - it asks the map server to fetch the data rather than youir browser doing it directly. This means making two attempts, but will work in most instances (providing the JSON contains a "signature" - see below). However, this does not work with password protected sites, because even though you may have logged in, it is the map server asking for the data, not your own browser, so you will not be logged in: see below. | * '''Let the map sort it out'''. If the map is unable to fetch the JSON, it will then automatically try to fetch is using its CORS adapter proxy - it asks the map server to fetch the data rather than youir browser doing it directly. This means making two attempts, but will work in most instances (providing the JSON contains a "signature" - see below). However, this does not work with password protected sites, because even though you may have logged in, it is the map server asking for the data, not your own browser, so you will not be logged in: see below. |
Revision as of 22:39, 20 October 2017
Please read Terms and conditions, Copyright, Fair Use, etc. if you are going to use the facilities described here.
Introduction
You can annotate and overlay the University Map with your own information. Typically you would draw on the map using the Annotate Map web app (Annotate map on the More menu) - see the documentation - but for those who want to derive overlays programmatically from existing data or otherwise roll their own, this describes how.
Note: if all you want to do is create a link to show your department etc on the map, you don't need to create any annotation. Just use the link shown in your browser address bar when you are looking at the full results for the institution. For example: https://map.cam.ac.uk/Department+of+Geography . This also means if you move, the link will show up-to-date information without any changes.
Also, you can obtain a URL of the current map view, drop a custom marker onto the map and get a URL which includes that, from the map itself. See the help page.
GeoJSON
Overlays are specified using UCamGeoJSON, a file format based on GeoJSON (see specification), which in turn is based on the JSON file format. UCamGeoJSON differs in three respects from GeoJSON:
- It does not implement Co-ordinate Reference Systems. All points are given as latitude and longitude using the WGS84 datum.
- While GeoJSON only provides geometry, UCamGeoJSON defines the recognized content of the properties element of Feature objects, used to define what the shapes are for: things like colour of shapes, what text and icons are to appear at points, and so on.
- Additional members of the top level JSON object (e.g. pos) can be given, to set the initial view of the map. See Initial map view below.
Note that in JSON, unlike the somewhat more relaxed syntax for Javascript objects, key values must be included in double quotes, and comments are not permitted.
Using UCamGeoJSON
The map is told to overlay custom annotation by giving it some UCamGeoJSON data in the URL. This is done in the fragment part of the URL (that is, the bit after a '#'), like this:
https://map.cam.ac.uk/...#data
One can still include other parts of the URL as normal, for example to search for a particular institution. The whole of such URLs can be shortened using any shortening service, such as bit.ly.
The data can be included in one of three ways:
- Referencing the UCamGeoJSON via a URL. The content provided by the URL is the UCamGeoJSON data, and the response to the request will use content-type 'application/json'. The URL will usually be absolute, that is start with 'http://', but in many cases it will be relative to the map itself, and therefore start with a '/' (see especially Adapters below). For example:
https://map.cam.ac.uk/#https://www.example.com/my-ucamgeojson.json https://map.cam.ac.uk/#/annotate/adapters/osm.json?src=http://www.example.com/my-osmfile.osm
- Do percent-escape these embedded URLs properly, but don't double escape them.
- You can also abbreviate the full URLs of files stored on the map server itself (usually by the Annotate Map web app), jut to their assigned identifier, like this:
https://map.cam.ac.uk/#YA45-YH8G-4VT4
- which is short for
https://map.cam.ac.uk/#https://annotate.map.cam.ac.uk/get.json?id=YA45-YH8G-4VT4
- If your URL does not refer to the University's map server, you will have to consider cross site scripting restrictions otherwise your file may not be accessible to the map.
- Sending the UCamGeoJSON data via a HTTP POST. This allows you to embed larger JSON data directly in a web page (in a form) and submit it on a user's action. This could be a 'submit' button, or you could hide the form and use Javascript's form.submit() to send it by clicking a link. In this case, set the hash fragment to 'annotate' and use the name 'annotation' as the POST variable. Note particularly the fragment (hash) part of the Map URL addressed by the form is #annotation For example:
<form action='https://map.cam.ac.uk/#annotate' method='POST'> <input type='hidden' name='annotation' value='{"type":"Feature","geometry":{...},"properties":{...}}'/> <input type='submit' value='Go To Map'/> </form>
If you want to embed the map in the same page where you have the annotation to hand, you can use the same API with a hidden form. The key to this is to use a target form attribute which is the name of an IFRAME. Submit the form using JavaScript. (Typically your annotation JSON would be substituted by the script creating the page, and would require escaping for special HTML characters when used with a TEXTAREA like this, but shown inline here for illustration):
<form id='myform' action='https://map.cam.ac.uk/#annotate' method='POST' target='myiframe' style='display: none;'> <textarea name='annotation'>{"type":"Feature","geometry":{...},"properties":{...}}</textarea> </form> <iframe name='myiframe' style='width: 600px; height: 400px'></iframe> <script>document.getElementById("myform").submit();</script> <!-- or using jQuery: $("#myform").submit; -->
- Giving numbers after the '#', separated by commas, thus:
https://map.cam.ac.uk/#z https://map.cam.ac.uk/#lat,lon https://map.cam.ac.uk/#lat,lon,z https://map.cam.ac.uk/#lat,lon,mlat,mlon https://map.cam.ac.uk/#lat,lon,z,mlat,mlon
- where lat,lon are the latititude and longitude of the centre of the map view, z is the zoom level (between 13 and 19) and mlat,mlon are the latitude and longitude of the tip of a default marker. There is no need to percent-escape any of the numbers or commas.
- These are equivalent to files containing the following (respectively):
{"z":z} {"pos":{"type":"Point","coordinates":[lon,lat]}} {"pos":{"type":"Point","coordinates":[lon,lat]}, "z":z} {"pos":{"type":"Point","coordinates":[lon,lat]}, feature}} {"pos":{"type":"Point","coordinates":[lon,lat]}, "z":z, feature}}
- where feature represents the marker, for example:
{"type":"Feature", "geometry":{"type":"Point","coordinates":[mlon,mlat]}, "properties":{"src":"/annotate/markers/pling.png"},"top":"-40px","left":"-13px"}
Further pairs of numbers represent additional markers. You can have any reasonable number of markers, but there are only six distinct colours which will be repeated if you ask for more than six markers.
Initial map view
The top level GeoJSON object may additionally contain some or all of the members z, pos, expand, nearby and title to control the initial map view. Indeed, any or all of these may be the entire content of the object if all you want to do is override the default view determined by the map automatically.
- z is the initial zoom level. For the University Map, the lowest zoom level (smallest scale, most zoomed out, when the scale on the map shows "2km") is 13 and the highest (largest scale, most zoomed in, when the scale shows "25m") is 19.
- pos is the point on which the map is initial centred. The value of pos is a GeoJSON Point object, that is it provides a latitude and longitude on which the map is to be centred.
- nearby is either a boolean value or a GeoJSON Point object. If a Point object, this causes the map to display institutions near to the Point. If true, then pos (which must be present) is used as the point. This is equivalent to the user choosing the 'show nearby' function on the relevant point on the map before the annotation is displayed.
- title is a text heading that will be placed in the map's information panel, to title the page. This is text, encoded in UTF-8, and not HTML.
- bodytext is text to appear below the title in the map's information panel.
- markdown is a boolean, which if present and true indicates that bodytext should be interpreted as markdown, the text markup language enabling rich text and images in the information panel.
For example:
{ "pos": {"type":"Point", "coordinates":[52.2036,0.1202]}, "z": 19, "nearby": true, "type": "FeatureCollection", "title": "My Overlay", "bodytext": "Here is some text which is to be '''bold'''", "markdown": true, "features": [...] }
Properties
The content of the properties member of the GeoJSON Feature object is not defined by GeoJSON other than as a set of arbitrary key/value pairs. The intention is that these specify the appearance of the shapes described by the GeoJSON geometry.
UCamGeoJSON does define properties, as follows. In general the properties are a subset of CSS and SVG, so the detailed formats of each need not be defined here, only any special interpretation of them. Refer to CSS and SVG for the details. Many of these are indeed simply applied as CSS styles and SVG attributes to the graphic objects created in the HTML DOM.
In broad terms, the geometry provides filled areas, lines and points.
Areas and lines are drawn by applying a few styling properties, such as color (note US spelling, as in CSS).
Points are represented by icons (indeed any images) and/or text drawn at the position indicated according to the style indicated by the properties. You can have both an icon and text at a single point. These are grouped into an HTML div (hereinafter the box), which allows for flexible and controllable layout using CSS.
properties applied to filled shapes and lines
Namely: shapes as GeoJSON Polygon and MultiPolygon objects and lines as LineString and MultiLineString objects.
Note: line and outline widths are in pixels at whatever zoom level they are displayed. The width of a line is invariant and does not scale according to the zoom level.
property name | filled shapes | lines | notes |
color | colour with which the shape is filled | colour in which the line is drawn (note: stroke-color overrides this) | any CSS colour specification is allowed, e.g. a name like 'green', hash and six hex digits, or 'rgb(...)' etc. Default is tomato (a bright-ish red). Note US spelling of color. |
stroke-width | the width in pixels of the outline drawn around the shape. If not given, or zero, no outline is drawn. | the width in pixels of the line | |
opacity | of the filled shape (and the outline, if stroke-opacity is not given separately). | of the line (note: ignored if stroke-opacity also given). | |
stroke-color | colour of outline | colour of the line | |
stroke-opacity | the opacity of outline | the opacity of the line | a number between zero and one e.g. 0.5. If not given, opacity is used in both cases (so if you just give opacity it applies to both the filled shape and its outline). |
stroke-linecap, stroke-linejoin, stroke-miterlimit |
specialized properties for outline | specialized properties for line | see the SVG specification for details of these |
stroke-dasharray | dash pattern for outline | dash pattern of line | a string, one of "-", ".", "-.", "-..", ". ", "- ", "--", "- .", "--.", "--.." forming a mixture of dashes and dots accordingly. Note that this is not the same as the SVG property stroke-dasharray, but derives from the cross-browser Raphaël system (documentation). |
properties applied to both text and images
Namely, GeoJSON Point and MultiPoint objects.
Either text or an image or both may be displayed at a Point. This is done within a DIV box to which these properties are applied - see above.
The same content (typically several marker icons) is displayed for each point in a MultiPoint.
property name | meaning |
background, background-color | the background of the box. Default is transparent. |
border, border-color, border-width, border-left, border-right, border-top, border-bottom |
the border styling of the box. For text and icons, note that with judicious use of border images and backgrounds, one can produce things like "speech bubbles" pointing at the map, a-la-Google maps. |
border-radius | of the box. Rounded corners don't work in older browsers. |
opacity | of the background of the box |
width, height | of the box; if you only give width, that means you can have a box which all the text fits in, but wraps when too wide to fit across. Note: the CSS unit "px" must be included, e.g. 10px, not just 10. |
overflow | of the box contents, defaults to hidden if height is also given. |
href | a URL: the whole of the box content will link to the given URL. |
popup | Content to be displayed in a pop-up "speech bubble" when the user clicks on the annotation. This can be either plain text or formatted rich text and images if popupmarkdown is also set to true. |
popupmarkdown | If set to true, popup is interpreted as markdown formatted text. This allows for lots of variation in the content of the pop-up. In particular, note that markdown can also include raw HTML, and that can include an IFRAME. Iframe's content is evaluated as it is loaded, so such bubbles can include changing and real-time information. |
target | The name of the target window in the browser in which the link referenced by href should be opened. By default this is the page in which the map is displayed or embedded (in an iframe), that is, equivalent to _self for the ordinary map or _top when embedded in an iframe. To make the link also open inside the iframe, you need to specify _self for target. In either case, to open in a new window or tab use _blank. |
top, left | the box is offset by the amounts given. This means you can center the content over the point rather than at the default top left corner. Note: the CSS unit "px" must be included, e.g. -20px, not just -20. Typically, these will be minus half the width and height, though for an arrow, for example, they might offset to the tip of the arrowhead. |
padding | around the inside of the box (pixels only) |
properties applied to images/icons/markers
property name | meaning |
src | url of icon (any image) to be displayed. Generally you will want this to have a transparent background and will typically use a PNG image. You could use an animated GIF if you insist! A JPG might be appropriate if you are displaying a thumbnail portrait, for example. A few ready made icons and markers are provided at https://map.cam.ac.uk/annotate/markers, though you can, of course, use any image accessible via http. |
title | the title attribute of the image (usually displayed by browsers in a 'tool tip' when hovering over it. |
img-width, img-height | width and height applied to the image including the 'px' unit (remember, width and height apply to the containing box). e.g 40px |
float | left, right: with both images and text floats the image to the left or right of the text within the box so the text wraps around it; otherwise the text starts underneath the image |
br | if there is both image and text, values of above or below insert a newline between image and text and position the image above or below the text, respectively. (Actually, any non-empty value is equivalent to above). |
properties applied to text
property name | meaning |
content | Text string to be displayed. This is text, not HTML, and is encoded as UTF-8. If you need a line break, you need to provide it as a newline in the data (not <br/>). You can write a newline in JSON with backslash-n: "...\n...". You can also cause strings to wrap if you use the width property, but where the break occurs is then not easily predictable. |
color | colour of the text; any CSS colour specification is allowed, e.g. a name like green, #ccaa12, or rgb(12,128,44) etc. Default is black. |
font-family | Just because you have a particular font installed on your computer doesn't mean the recipient has - so use web-safe fonts, e.g. Verdana, Tahoma, Trebuchet, Georgia, Times, Helvetica |
font-size | in pixels only, e.g. 12px |
font-weight, font-style, text-decoration, letter-spacing, word-spacing, line-height | per CSS |
text-align | center (US spelling), left, right within the box. This will also apply to any image which isn't floated. |
Markers
Ready-made markers are available for use with the src property, as follows. These are located in /annotate/markers, hence you might write:
"src": "/annotate/markers/circle-r.png"
Adapters
Though the map understands UCamGeoJSON, certain other formats can be managed using adapters. These are scripts which dynamically convert data (identified via their query strings etc) into UCamGeoJSON.
Some default scripts are provided at https://map.cam.ac.uk/annotate/adapters:
- University map, version 4: v4.json. version 4 applied limited annotation to its maps entirely within its URL query string; v4.json takes exactly the same query string and produces equivalent UCamGeoJSON.
- GBN: gbn.json. Reads the OpenStreetMap XML file containing Granta Backbone network description. This operation requires appropriate privileges. For example:
https://map.cam.ac.uk/#https://annotate.map.cam.ac.uk/adapters/gbn.json
- College staircases, staircases.json. For example, for Pembroke College:
https://map.cam.ac.uk/#/annotate/adapters/staircases.json?ref=PEM
Other useful candidates would be: KML, GPX.
Of course, as the URL for UCamGeoJSON content is arbitrary, it is possible to provide other adapters on any web site.
URL referencing considerations
Cross-site scripting
If you use a URL to access JSON data and that URL indicates somewhere other than the University's map server, you have to consider cross site scripting restrictions.
In general, a web page from one server cannot request data from another, for security reasons. This means you will get an error if you naïvely use a non-map URL after the hash.
There are several ways in which this can be worked around.
- Use CORS. You need access to the web site files for the server providing the data for this to work. The server on which the data is stored responds with some additional information which says it is OK for the map to use the data. For Apache servers, (at least) the following can be put in a .htaccess file in the directory where the data is being retrieved from (whether the data is static file or scripted). This depends on the web server allowing these options to be set in .htaccess.
Header add Access-Control-Allow-Origin "*" Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type" Header add Access-Control-Allow-Methods "GET"
- If you want to restrict the data only to the map, use https://map.cam.ac.uk instead of the asterisk in the first line (the asterisk means you are giving permission to any requesting page). Note that when using Desktop Services DS-Web service these lines need to appear in a file called 'htaccess', not '.htaccess'.
- Use a site that already has CORS turned on for you. That is, a storage provider who recognises that the data will be required by other sites. In this case you need do nothing, the request should just work.
- However, note that the now ancient and unsupported, but still sometimes used, Internet Explorer 8 (and earlier) does not support CORS.
- Note also that it is not permissible to load data via http from any https site, so,mbecause the map is served over https at https://map.cam.ac.uk, your annotation must also be fetched over https, as must any images it references.
- Let the map sort it out. If the map is unable to fetch the JSON, it will then automatically try to fetch is using its CORS adapter proxy - it asks the map server to fetch the data rather than youir browser doing it directly. This means making two attempts, but will work in most instances (providing the JSON contains a "signature" - see below). However, this does not work with password protected sites, because even though you may have logged in, it is the map server asking for the data, not your own browser, so you will not be logged in: see below.
- Use the CORS adapter explicitly, if you know ahead of time that CORS is not supported by the supplying server:
...#/annotate/adapters/cors.json?url=your-url-here
- This will bypass the first request by the browser for the bald URL, which is bound to fail if you know your server does not support CORS. This has the effect of asking the map server to retrieve your file for you. However the same caveat for login protected sites also applies, of course, and the JSON must also contain a "signature".
To use the CORS adapter proxy, your UCamGeoJSON must include a field "signature" with the value "UCamGeoJSON" (or some string containing that). Files produced with the annotation editor automatically do so. e.g.
{"signature":"UCamGeoJSON Department-of-Physics", ...}
This is a precaution to prevent the adapter being used to proxy arbitrary files, especially unprotected files only accessible within the University network by any arbitrary site outside the University.
Login-protected sources
Sometimes overlays may be located on sites which require a login, especially when these are confidential.
Note, however, that Dropbox in particular now offers unprotected URLs to shared files, so by advertising that URL one can store overlays in a Dropbox folder (with the necessary CORS headers - see below). Just put ?raw=1 on the end of the Dropbox URL so it gives the original file not their preview page for the file.
Where a resource is Raven protected (perhaps further restricted to a particular Lookup group of people), this will be detected and you will be asked to log in to Raven and try again. For generic links, we will indicate that you may have to log in, but it is not possible to tell you where. Paste your JSON link into the browser and complete the login there, then try the map URL again.
Because the content is accessed by the browser, which is what your login applies to, and not by the server, your content should be accessible once you have logged in.
However, this can only work when
- the client's browser supports CORS - which means users working with protected data cannot be using Internet Explorer 8 or earlier - and
- the file or adapter which supplies the data must supply more specific CORS headers, either in .htaccess as follows, or the equivalent form your script (e.g. using header(...) in PHP).
Header add Access-Control-Allow-Origin "https://map.cam.ac.uk" Header add Access-Control-Allow-Headers "origin, x-requested-with, content-type" Header add Access-Control-Allow-Methods "GET" Header add Access-Control-Allow-Credentials: true
This allows the map to access your server, and include the login cookie with the request. But the Access-Control-Allow-Origin header must then be a specific URL, not the wildcard '*' as in the earlier example, because browser security restrictions require this when cookies are supplied.
Similar considerations apply to non-University sites which are protected by a login controlled by a cookie.
The consequence of this is that it is not possible to support login protected annotations for users whose browsers do not support CORS; thankfully, these are now rare.