Annoshape is a URL-based format that allows you to draw shapes on top of images, maps, and videos on any page via a URL parameter. It's suitable for use with annotation formats, or as a standalone utility. Currently, Annoshape is not a standard, just a small script library, though it may be considered as part of a standard at some point in the future; it may be currently be described as a non-standard profile of SVG.
Annoshape is the SVG equivalent of Well-known text (WKT), simplified for only geometric elements, and serialized to be usable as a URL parameter.
Note that that scope of this proposal is limited to providing a link into a view of a visual resource (like an image or map), and provide highlights within that view. It is intended to be complementary to other technologies, not a replacement.
See the source on github.
Details about syntax, usage, and more are below, but first, it is helpful to see set the stage with a demonstration. To see Annoshape in action, click on the links below each example, which showcase the features of Annoshape. Each link will highlight different parts of the images.
In addition, once an image already has a highlight on it, you can click to make a shape or a line. Each click will add another point to the line, and clicking on the initial point will complete the shape; a link to the new Annoshape highlight will appear in the list below, as both a polyline or a polygon. Making new shapes is not part of the Annoshape proposal itself, but just a demonstration to show how easy it is to make a simple Annoshape highlight URL.
This demo shows the basic highlighting and linking capabilities of Annoshape on a static image.
This demo is simply an image of a map, not a true mapping service. It only demonstrates the highlighting and linking capabilities of Annoshape, not the correspondence between the SVG overlay and an interactive tiled map. However, this specification does address how a mapping service could support fetching and rendering the appropriate area for a client-side SVG overlay.
Examples:
Note: This is experimental syntax, and it's subject to change if anyone has a better idea. In particular, having the root element be named something other than svg
might be a good idea.
Annoshape is intended for use as a URL parameter, so it uses a different syntax than SVG markup; the syntax is inspired by CSS and WKT. However, all the essential aspects of the language (e.g., element names, attribute names and values, property names and values) remain the same.
Permitted SVG elements in Annoshape are line
, rect
, circle
, ellipse
, polyline
, polygon
, and path
. (In SVG 2, this list may include the point
element and the star
element.) Currently, a point can be represented by a circle
element, with a radius of 0 or more (which may be used to represent a measure of precision, accuracy, or scope).
Each element is simply the name of the element as a string (e.g., polyline
), followed by an optional set of CSS properties contained in parentheses (e.g., (stroke:red;)
), followed by the geometry attributes contained in curly brackets with comma-separated values (e.g., {300,131.5,207,174.5,261,290.5,411,220.5,432,265.5,417,272.5}
).
For example:
polyline(stroke:red;){300,131.5,207,174.5,261,290.5,411,220.5,432,265.5,417,272.5}
For elements with multiple geometry attributes, such as line
, rect
, circle
, and ellipse
, the attributes are separated by a semicolon, with attribute names and values separated by a colon; for example
circle{cx:368;cy:181;r:18}
As with element names, these attribute names are identical to their use in normal SVG markup.
For elements with a single geometry attribute, such as polyline
, polygon
, and path
, the attribute name and/or trailing colon can optionally be omitted; for example, all of these are valid and identical:
polygon{points:351,319,355,317,357,320,361,318,360,314.5,365,312,371,323.5,366,326,363.5,322.5,359.5,324.5,361,328,357,330;}
polygon{points:351,319,355,317,357,320,361,318,360,314.5,365,312,371,323.5,366,326,363.5,322.5,359.5,324.5,361,328,357,330}
polygon{351,319,355,317,357,320,361,318,360,314.5,365,312,371,323.5,366,326,363.5,322.5,359.5,324.5,361,328,357,330}
All numerical geometric values must be unitless (which may be thought of as pixels, but which is actually relative portions of the declared SVG viewbox). Percentages must not be used, and are not needed; the geometry is meant to indicate a fixed area, not a percentage of the viewport, and the SVG viewbox scales the overlay to meet the appropriate size and dimensions of the target image.
As with normal SVG, all elements that apply to a specific image are contained by the SVG root; in the case of Annoshape, this follows the same convention of the name of the element as a string (i.e., svg
), followed by an optional set of CSS properties contained in parentheses (e.g., (stroke:red;)
). There is no space or other delimiter between the end of one element declaration and the start of another. The whole string is treated as a single URL parameter.
For example:
svg(){circle{cx:368;cy:181;r:18}rect{x:10;y:15;width:42;height:23}}
Each Annoshape string is treated as a single URL parameter, and the initial Annoshape string should be prefixed with the query delimiter ?
or the hash delimiter #
. Each Annoshape declaration can target a specific image or video resource via Annoshape-specific target-selector
property, and multiple image resources on the same page can be targeted by having multiple Annoshape declarations delimited by the ampersand &
. The value of the target-selector
property must be a valid selector string as described in the Selectors API specification (e.g. it may be an id
, a classname, an element name, etc.); note that the selector string must be properly URL-encoded (e.g., for an id
, the #
should be percent-encoded as %23
). For example:
?svg(target-selector:%23map;){circle{cx:368;cy:181;r:18}}&svg(target-selector:%23house;){rect{x:10;y:15;width:42;height:23}}
Although multiple elements are allowed within a single Annoshape declaration, only one Annoshape declaration must be applied to a single target resource; if more than one Annoshape declaration in a single URL targets the same resource, the final Annoshape declaration must replace all previous Annoshape declarations for that same resource. Note that the same image may be loaded onto a page with multiple img
elements; these must be considered separate resources. (The single-declaration restriction may be lifted for timed media where the Annoshape root contains a specific time range; in this case, only one Annoshape declaration per time range may be applied.) (Note: This is how it's currently implemented, but maybe it's a bad idea...? Maybe if an additional declaration is made, all of the declaration can apply. That might not be the end of the world, though it would mean that multiple SVGs would stack and that might hurt performance.)
If a URL of a loaded page is changed to remove any Annoshape declaration, that Annoshape instances on all target resources on that page must be removed.
An Annoshape declaration can be used as either a URL fragment (or “hash”), or as a URL query, depending on the content type that it's targeting. There is no syntactic difference between an Annoshape fragment or query, other than the initial delimiter.
A fragment should be used if the target resource is loaded with no extra information needed, such as a static image. An Annoshape fragment must be declared after the end of any query string, and must begin with a hash symbol (#
), like so:
#svg(target-selector:%23house;){rect{x:10;y:15;width:42;height:23}}
Unlike typical fragment strings, which consist solely of the id
of the target element, an Annoshape fragment declaration can include multiple targets; an Annoshape fragment string must begin with a hash symbol (#
), and must be composed of one or more Annoshape declarations separated by an ampersand (&
), like so:
#svg(target-selector:%23image1;){circle{cx:368;cy:181;r:18}}
&svg(target-selector:%23image2;){rect{x:10;y:15;width:42;height:23}}
Because a URL can have only one fragment string, a URL with an Annoshape fragment string cannot also have a fragment identifier to scroll the target element into view, which would be inconvenient; therefore, the target element of the first Annoshape declaration in fragment string must be scrolled into view.
A query string should be used if the target resource needs extra information to be properly delivered by the server, such as a particular tile, section, or projection of a map, or a specific time range for a streamed video. Note that it is a common convention for a query string to be comprised of both a name and a value (known as a “name-value pair”), or often several name-value pairs separated by an ampersand (&
), but this is not mandatory syntax, and Annoshape does not follow this convention; instead, an Annoshape query string must begin with a question mark (?
), must be composed of one or more Annoshape declarations separated by an ampersand (&
), like so:
?svg(target-selector:%23map1;){circle{cx:368;cy:181;r:18}}
&svg(target-selector:%23map2;){rect{x:10;y:15;width:42;height:23}}
Both a query string and a fragment can be used, such as when multiple resources are being targeted, as below:
http://example.com/for-sale.html
?svg(target-selector:%23map;){circle{cx:368;cy:181;r:18}}
#svg(target-selector:%23house;){rect{x:10;y:15;width:42;height:23}}
Note that only the single-declaration restriction also applies to Annoshape declarations that target the same resource via a query string and a fragment string in the same URL.
The default style for all shapes in Annoshape is different than regular SVG. The style defaults are fill:none
, stroke:black
, and stroke-width:3px
. All of these style can be overridden, and additional styles can be applied; some style properties are particularly useful for delineating overlay shapes, such as opacity
, stroke-linecap
, stroke-linejoin
, and stroke-dasharray
. Markers are also useful, but more complicated.
For use with mapping, rather than simple images, several more pieces of information are needed in order to establish the proper context for the shapes within the coordinate system of their root SVG canvas. For example, a link to a map webapp needs to indicate to the map which zoom and pan states are needed to properly apply the Annoshape overlay. In addition, because the map is in its own Coordinate Reference System (CRS), with its own horizontal and vertical bounds (expressed as X-min, X-max, Y-min, and Y-max), and possibly with its own projection, all these details need to be supplied in order to properly overlay the SVG shapes, lines, and points to express the original intent.
In the disciplines of Geographic Information Systems (GIS) and geodesy, a Coordinate Reference System (CRS) –also known as a Spatial Reference System (SRS)– is a codified framework to specify any given location on Earth by a set of three numbers (coordinates) describing the latitude, the longitude, and optionally the altitude; each CRS defines this framework in a different way, with different reference points. There are two types of CRS: Geographic Coordinate Systems (GCS); and projected coordinate systems. One of the most common GCSes is WGS84 (World Geodetic System 1984), which is a coordinate system that defines an angular unit of measure, a prime meridian, and a datum; the datum defines an ellipsoid that describes the idealized shape of the Earth and the relationship of the ellipsoid to the Earth itself; together, all of these components allow us to describe a global location with functional precision. Even with this information, however, features of a map may look distorted because the surface of the Earth is a spheroid, not a plane; thus, a view of a map also includes a “projection”, or a systematic transformation of the raw latitude and longitude coordinates onto an appropriate mathematical surface, is used. No map projection is “true” –each is a distortion of the coordinate data– but different projections are good for different tasks, such as measuring distance, area, shape, direction or bearing, scale, and so on, and the selection of a projection may depend on its suitability for different scales, whether the area has a larger east-west extent or north-south extent, or the latitude and longitude of the region.
Each projection explicitly operates within the context of a CRS, and the concept of a CRS incorporates both the Geographic Coordinate System (like WSG84 or NAD83) and the projection (like EPSG-2264). Each CRS has a unique code with the EPSG registry (maintained by OGP, the International Association of Oil and Gas Producers), which describes its applicable region, units of measure, underlying CRS for projections, reference latitude and longitude, and many other parameters that allow for its transformation into another CRS.
No single map, projection, or GCS is best for all purposes, and many different systems are in place around the world, each with many different datasets that use that CRS. Thus, a robust system for mapping should include the ability to adapt to any of these geodetic localizations, and to use the data that is available. To that end, Annoshape does not define a single canonical CRS, but relies on its own geometric (not geographic) coordinate system and established dimensions and viewbox, and provides a set of properties that allow that coordinate system to be transformed into the CRS that was used to create the original overlay graphics (and from there, potentially transformed into any other desired CRS).
Annoshape does this by defining 5 GIS-specific properties: gis-x-min
, gis-x-max
, gis-y-min
, gis-y-max
, and gis-crs
. These properties, while not formal CSS properties, are nevertheless included in the list of parameters in the SVG root, along with its CSS properties, like so:
svg(width:830;height:582;viewbox:0,0,830,582;
gis-x-min:1981140.803;gis-x-max:1989960.339;gis-y-min:783308.997;gis-y-max:789490.642;gis-crs:EPSG-2264;)
{circle{cx:368;cy:181;r:18}}
Unlike CSS properties, these properties are not intended to change the appearance of the SVG in the browser, but rather to inform the underlying mapping system which parameters should be used when fetching the desired map. This is intended as a universal system for marking up web maps (as well as other images).
gis-x-min
The eastern (left) boundary of the described area, in the default units of the GCS, expressed in decimal degrees where applicable.
gis-x-max
The western (right) boundary of the described area, in the default units of the GCS, expressed in decimal degrees where applicable.
gis-y-min
The northern (top) boundary of the described area, in the default units of the GCS, expressed in decimal degrees where applicable.
gis-y-max
The southern (bottom) boundary of the described area, in the default units of the GCS, expressed in decimal degrees where applicable.
gis-crs
The EPSG code for the Coordinate Reference System (CRS) used in the described area, in the format EPSG-number
.
Annoshape is not intended to provide a layer of graphics over texts documents, because positions in such documents change with alterations to display size, font size, content, and other factors; it is only intended for use with visual media with relatively fixed dimension ratios, such as images, videos, and maps.
An Annoshape declaration is not restricted to the URL of the main resource (e.g. the containing page). Any element which references an external reference, such as an HTML img
element, may contain an Annoshape declaration in the URL of its source attribute (or CSS property value). If a target resource contains Annoshape declaration in its URL, then that Annoshape declaration must apply to that element, and only to that element; the target-selection
property of the Annoshape declaration must not be required, and if present, must be ignored.
Note that only the single-declaration restriction also applies to Annoshape declarations in the external-resource URL of a target resource. If both the page URL and target-resource URL both contain Annoshape declarations that target the same resource, the page URL takes precedence. (ISSUE: This order of precedence is to allow external resources to annotate an image even if the original page source also contains annotations... this is tricky, and may be a reason to allow multiple declarations to apply at the same time.)
Implementation Note: This is not currently implemented, though it probably wouldn't be hard.
SVG can contain text, both for display and for metadata. Annoshape does not include text capabilities, by design; it is intended more for indicating specific locations, rather than describing them.
One of the goals of Annoshape is to serve as a simple resource to link a specific location (or set of locations) on an image or map to a resource that describes or adds information about that location. For example, an annotation (such as an a document in the Open Annotation format) could use a URL which includes an Annoshape parameter as the annotation's target or selector, to make a comment or tag for a specific section of an image.
Each Annoshape declaration is intended to serve as a discrete connection to a specific data resource; obviously, the same Annoshape declaration could be used independently by multiple data resources concurrently. Using a Annoshape directly in a linking resource provides a one-direction association from the resource to the described object. To enable multidirectional or abstracted associations, Annoshape can have an id
or a data-*
attribute that acts as a key or code for the Annoshape shape, like so:
svg(){circle{id:building-4223;cx:368;cy:181;r:18;}}
svg(){polyline{data-category:road;points:174.5,261,290.5,411,220.5;}}
Other semantic data attributes can be added as well, such as RDFa or microdata, though nesting structures are not supported.
It is common in GIS to include non-geographic data associated with the geographic data, such as place-names, categories, population, rainfall, area, permeability of surfaces, and so on. Each piece of data is associated with a particular vector object, called a “feature”, which represents a building, road or road segment, waterway, or other object; in the parlance of GIS, an “attribute” (not to be confused with HTML or XML element attributes) is the discrete piece of data that applies to a feature. (Note that in GIS, these attributes are not generally considered metadata, but rather is considered as part of the whole dataset; they would consider metadata to be such things as author, creation date, authoring tool, data sources, methodology, and so forth.) Annoshape is similar in function (if not in scope) to a GIS feature, and provides mechanism to associate it with a GIS attribute.
Technically, it's possible to include a title
attribute to the shape declaration, but this is discouraged for two reasons: separation of functionality; and overall length of the URL. Regarding the separation of functionality, Annoshape is intended as an indicator for a resource, and the body of the description resource should be separate, to keep a many-to-many relationship between Annoshape selectors and the description resources, and to make it easy to change one or the other independently. Regarding the length of the URL, while theoretically URLs can be of unbounded length, per the RFC-2616 specification, the practical limit is 2048 characters for some browsers; even some servers have a limit to how long a URL they will accept. Annoshape should not be used to pass text in the URL, in general.
There are other languages and formats with similar features to Annoshape. However, none of them are intended to be compact expressions as a URL parameter, which makes Annoshape well-suited for various web tasks and services, like exchanging annotations and other information on images and maps.
Note that Annoshape is not intended to represent a whole map or dataset in SVG, but rather to provide links into a specific view, and highlights within that view.
Scalable Vector Graphics (SVG) was developed by W3C as a web graphics format based on XML, and is widely deployed in vector drawing tools and web browsers. Annoshape (Scalable Vector Geodata) is an attempt to merge the best features of both WKT and SVG, and to add the ability to express the shapes as a URL parameter.
Annoshape is a simplified profile of SVG, using a different serialization as its representation.
Annoshape is not intended to include text, grouping or nesting, or more complex SVG graphics features.
Well-known text (WKT) was defined by the Open Geospatial Consortium (OGC) in their Simple Feature Access and Coordinate Transformation Service specifications, and standardized is in the ISO/IEC 13249-3:2011 standard, "Information technology -- Database languages -- SQL multimedia and application packages -- Part 3: Spatial" (SQL/MM). WKT is commonly used in many geographical and database products.
Annoshape has several advantages over WKT:
WKT still has a few advantages over this early Annoshape experiment:
Geography Markup Language (GML) is an XML language for geographic modeling and interchange. It has many features beyond the vector representations of geometries, including raster images, topology, and a full CRS. A significant aspect of GML is its abstract notion of “features”, which may have one or many geometric representations, or no geometric representation at all. GML is common and widely accepted.
There are several difference in scope between Annoshape and GML:
In general, though SVG and GML are both XML graphics formats, Annoshape's goals and format are much more similar to WKT.
Keyhole Markup Language (KML) is an XML language for describing 2D and 3D shapes in geographic space, which allows annotations to be embedded in text or HTML. It was developed by Keyhole Inc, and acquired by Google, and subsequently standardized by the Open Geospatial Consortium in 2008. It's supported by Google Maps, Google Earth, and some other web mapping software.
As with GML, there are several difference in scope between Annoshape and KML:
In general, though SVG and KML are both XML graphics formats, Annoshape's goals and format are much more similar to WKT.
GeoJSON is a geospatial data interchange format, based on JavaScript Object Notation (JSON). GeoJSON represents geospatial features, their characteristics, and their related geometry; the geometry features are similar to those of WKT, with points, multipoints, lines, multilines, polygons, and multipolygons. GeoJSON-LD is a way to extend GeoJSON to add defined terms from other vocabularies. GeoJSON is a defacto standard developed by an informal group, and is widely used.
As with other technologies, the scope and focus of GeoJSON is different than that of Annoshape.
A geo URI is a simple URI scheme to define a single point in a defined CRS (the default is WGS-84); it has parameters for a set of coordinates (i.e. latitude, longitude, and optionally altitude), a precision uncertainty factor in meters, and a CRS (if not WGS-84). It was defined by IETF in the A Uniform Resource Identifier for Geographic Locations ('geo' URI) RFC (RFC-5870) in 2010.
As with other technologies, there are several difference in scope between Annoshape and geo URIs:
A data URI is a URI (Uniform Resource Identifier) scheme which describes a method of including arbitrary data (such as an SVG or a raster image) inline into a web resource (such as an HTML or CSS file), as if the data were an external resource. The body of a data URI may be encoded as a base64 binary representation to compress it, or left as plain-text markup. The data URI specification was defined in RFC-2397 by the Internet Engineering Task Force (IETF) in 1998. Annoshape has different capabilities and functions compared to a data URI:
Media Fragments is a URI scheme to link to a specific time range, viewport, and tracks within timed media. It was defined by W3C in the Media Fragments URI 1.0 specification in 2012.
Thanks to Megan Culler, a GIS analyst consultant at the EPA (and my wife), for patiently educating me about Coordinate Reference Systems, Geographic Coordinate Systems, and projections, for showing me mapping tool workflows, and for creating (and recreating) the maps I used in this document and demo. Of course, any mistakes are mine.
Thanks also to David Kirstein for helping me with the parsing regex; and to Raquel Alegre, University of Reading, UK, and Robert Casties, Max Planck Institute, Germany, for telling me about WKT and inspiring me to create Annoshape. And thanks to the Open Annotation community (and Community Group) for highlighting the need for a linking scheme for annotating images.