Markdown OpenStreetMap Maps
There does not seem to be a convention for showing maps or
co-ordinates in Markdown, but almost anything gets put in square
brackets [<anything>]
.
I had a request from Marco the other day to put OpenStreetMap maps in my Diary app using
[<lat>,<lng>]
syntax. So I did a bit of a search to see if there was a convention in Markdown for co-ordinates and couldn’t find anything. So we seem to have broken new ground here.
The OpenStreetMap api for accessing their maps is quite
straightforward, you just use
http://www.openstreetmap.org/#map/<level>/<lat>/<lng>
or various
variations on that. However the syntax for their embedded iframe API
doesn’t seem to be documented, but you can get the HTML for an
embedded map from their map page and customise it.
<iframe width="425" height="350"
src="https://www.openstreetmap.org/export/embed.html?
bbox=3.1528186798095708%2C42.2577141531011%2C3.1886529922485356%2C42.27241862881183&
layer=mapnik"
style="border: 1px solid black">
</iframe>
<br/>
<small>
<a href="https://www.openstreetmap.org/#map=16/42.2651/3.1707">
View Larger Map
</a>
</small>
I have removed some non HTML5 code from the above. So the goal is to
parse something that looks like [42.265,3.17]
and produce something
that looks like the above. The obvious tool in android Java is the
Pattern and Matcher classes, which parse regular
expressions. The regular expression for the pattern is
"\\[(?:osm:)?(-?\\d+[,.]\\d+)[,;](-?\\d+[,.]\\d+)\\]"
The double backslashes are because it’s a string, so one of them will
be removed before the matcher gets to see it. I also have to deal with
European locales which use different conventions for co-ordinates. I
added the option to have a prefix ‘osm:
’, as using prefixes seems to
be a markdown convention. It also allows for having different
maps. The template for the iframe is
"<iframe width=\"560\" height=\"420\"
src=\"https://www.openstreetmap.org/export/embed.html?
bbox=%f,%f,%f,%f&layer=mapnik\"
style=\"border: 1px solid black\">
</iframe><br/>
<small><a href=\"https://www.openstreetmap.org/#map=16/%f/%f\">
View Larger Map</a></small>\n"
I have replaced the ‘%2C
’s with commas, and changed the dimensions
slightly, which seems to work OK. The android Java code to do this is
// loadMarkdown
private void loadMarkdown(String text)
{
markdownView.loadMarkdown(getBaseUrl(), markdownCheck(text),
getStyles());
}
// markdownCheck
private String markdownCheck(String text)
{
// Check for media
text = mediaCheck(text);
// Check for map
return mapCheck(text);
}
// mapCheck
private String mapCheck(String text)
{
StringBuffer buffer = new StringBuffer();
Pattern pattern = Pattern.compile(MAP_PATTERN, Pattern.MULTILINE);
Matcher matcher = pattern.matcher(text);
// Find matches
while (matcher.find())
{
double lat = 1.0;
double lng = 1.0;
try
{
lat = Double.parseDouble(matcher.group(1));
lng = Double.parseDouble(matcher.group(2));
}
// Ignore parse error
catch (Exception e)
{
continue;
}
// Create replacement iframe
String replace =
String.format(Locale.ENGLISH, MAP_TEMPLATE,
lng - 0.005, lat - 0.005,
lng + 0.005, lat + 0.005,
lat, lng);
// Substitute replacement
matcher.appendReplacement(buffer, replace);
}
// Append rest of entry
matcher.appendTail(buffer);
return buffer.toString();
}
The input string is the markdown code to be parsed, the output is the
same code with matched template substitutions. I have just added and
subtracted 0.005 from the given co-ordinates to create a bounding box
so the given point is in the centre. The API doesn’t seem to be too
fussy. It is important to use the ENGLISH
locale so the output uses
dots for decimal points. The appendReplacement and
appendTail Java methods are ready made for this pattern matching
and replacement.
Update
After I had implemented this It was suggested that perhaps I should be
using a geo Uri (![](geo:<lat>,<lng>)
) for the map markdown syntax. I
hadn’t thought of that, and it raises the possibility of receiving a
geo Uri and turning it into a map. So I decided to convert existing
[<lat>,<lng>]
markdown to ![osm](geo:<lat>,<lng>)
markdown, and
convert that to an iframe as before. The code to do that is very
similar to the above with updated patterns and templates.
"geo:(-?\\d+[.]\\d+), ?(-?\\d+[.]\\d+)"
"![osm](geo:%f,%f)"
The pattern is just to match a geo Uri, the template is to output a geo Uri.
In recent versions of android http://
URLs can be rejected by the
web view, so I have updated the code to use https://
. Also, to allow
the use of http://
URLs in the markdown, I have added a line to the
application
element in the app manifest.
android:usesCleartextTraffic="true"
See Also
- Derive Edit Position from Markdown
- Flutter app bar search widget
- Android Navigation Menu
- Update App Widget
- Create a Word Grid