summaryrefslogtreecommitdiff
path: root/IkiWiki/Plugin/osm.pm
blob: c7078ca59b55f4e0ec8bd276ce714fb16703d7e0 (plain)
  1. #!/usr/bin/perl
  2. # Copyright 2011 Blars Blarson
  3. # Released under GPL version 2
  4. package IkiWiki::Plugin::osm;
  5. use utf8;
  6. use strict;
  7. use warnings;
  8. use IkiWiki 3.0;
  9. sub import {
  10. add_underlay("osm");
  11. hook(type => "getsetup", id => "osm", call => \&getsetup);
  12. hook(type => "format", id => "osm", call => \&format);
  13. hook(type => "preprocess", id => "osm", call => \&preprocess);
  14. hook(type => "preprocess", id => "waypoint", call => \&process_waypoint);
  15. hook(type => "savestate", id => "waypoint", call => \&savestate);
  16. hook(type => "cgi", id => "osm", call => \&cgi);
  17. }
  18. sub getsetup () {
  19. return
  20. plugin => {
  21. safe => 1,
  22. rebuild => 1,
  23. section => "special-purpose",
  24. },
  25. osm_default_zoom => {
  26. type => "integer",
  27. example => "15",
  28. description => "the default zoom when you click on the map link",
  29. safe => 1,
  30. rebuild => 1,
  31. },
  32. osm_default_icon => {
  33. type => "string",
  34. example => "ikiwiki/images/osm.png",
  35. description => "the icon shown on links and on the main map",
  36. safe => 0,
  37. rebuild => 1,
  38. },
  39. osm_alt => {
  40. type => "string",
  41. example => "",
  42. description => "the alt tag of links, defaults to empty",
  43. safe => 0,
  44. rebuild => 1,
  45. },
  46. osm_format => {
  47. type => "string",
  48. example => "KML",
  49. description => "the output format for waypoints, can be KML, GeoJSON or CSV (one or many, comma-separated)",
  50. safe => 1,
  51. rebuild => 1,
  52. },
  53. osm_tag_default_icon => {
  54. type => "string",
  55. example => "icon.png",
  56. description => "the icon attached to a tag, displayed on the map for tagged pages",
  57. safe => 0,
  58. rebuild => 1,
  59. },
  60. osm_openlayers_url => {
  61. type => "string",
  62. example => "http://www.openlayers.org/api/OpenLayers.js",
  63. description => "Url for the OpenLayers.js file",
  64. safe => 0,
  65. rebuild => 1,
  66. },
  67. osm_leaflet_url_stem => {
  68. type => "string",
  69. example => "http://cdn.leafletjs.com/leaflet-0.6.4/leaflet",
  70. description => "Url used as stem for the Leaflet files",
  71. safe => 0,
  72. rebuild => 1,
  73. },
  74. osm_layers => {
  75. type => "string",
  76. example => { 'OSM', 'GoogleSatellite' },
  77. description => "Layers to use in the map. Can be either the 'OSM' string or a type option for Google maps (GoogleNormal, GoogleSatellite, GoogleHybrid or GooglePhysical). It can also be an arbitrary URL in a syntax acceptable for OpenLayers.Layer.OSM.url parameter.",
  78. safe => 0,
  79. rebuild => 1,
  80. },
  81. osm_google_apikey => {
  82. type => "string",
  83. example => "",
  84. description => "Google maps API key, Google layer not used if missing, see https://code.google.com/apis/console/ to get an API key",
  85. safe => 1,
  86. rebuild => 1,
  87. },
  88. osm_slippy_engine => {
  89. type => "string",
  90. example => "openlayers",
  91. description => "the slippy map engine to use, can be openlayers or leaflet (leaflet always uses GeoJSON, disregarding osm_format)",
  92. safe => 0,
  93. rebuild => 1,
  94. },
  95. }
  96. sub register_rendered_files {
  97. my $map = shift;
  98. my $page = shift;
  99. my $dest = shift;
  100. if ($page eq $dest) {
  101. my %formats = get_formats();
  102. if ($formats{'GeoJSON'}) {
  103. will_render($page, "$map/pois.json");
  104. }
  105. if ($formats{'CSV'}) {
  106. will_render($page, "$map/pois.txt");
  107. }
  108. if ($formats{'KML'}) {
  109. will_render($page, "$map/pois.kml");
  110. }
  111. }
  112. }
  113. sub preprocess {
  114. my %params=@_;
  115. my $page = $params{page};
  116. my $dest = $params{destpage};
  117. my $loc = $params{loc}; # sanitized below
  118. my $lat = $params{lat}; # sanitized below
  119. my $lon = $params{lon}; # sanitized below
  120. my $href = $params{href};
  121. my ($width, $height, $float);
  122. $height = scrub($params{'height'} || "300px", $page, $dest); # sanitized here
  123. $width = scrub($params{'width'} || "500px", $page, $dest); # sanitized here
  124. $float = (defined($params{'right'}) && 'right') || (defined($params{'left'}) && 'left'); # sanitized here
  125. my $zoom = scrub($params{'zoom'} // $config{'osm_default_zoom'} // 15, $page, $dest); # sanitized below
  126. my $map;
  127. $map = $params{'map'} || 'map';
  128. $map = scrub($map, $page, $dest); # sanitized here
  129. my $name = scrub($params{'name'} || $map, $page, $dest);
  130. if (defined($lon) || defined($lat) || defined($loc)) {
  131. ($lon, $lat) = scrub_lonlat($loc, $lon, $lat);
  132. }
  133. if ($zoom !~ /^\d\d?$/ || $zoom < 2 || $zoom > 18) {
  134. error("Bad zoom");
  135. }
  136. if (! defined $href || ! length $href) {
  137. $href=IkiWiki::cgiurl(
  138. do => "osm",
  139. map => $map,
  140. );
  141. }
  142. register_rendered_files($map, $page, $dest);
  143. $pagestate{$page}{'osm'}{$map}{'displays'}{$name} = {
  144. height => $height,
  145. width => $width,
  146. float => $float,
  147. zoom => $zoom,
  148. fullscreen => 0,
  149. editable => defined($params{'editable'}),
  150. lat => $lat,
  151. lon => $lon,
  152. href => $href,
  153. google_apikey => $config{'osm_google_apikey'},
  154. };
  155. return "<div id=\"mapdiv-$name\"></div>";
  156. }
  157. sub process_waypoint {
  158. my %params=@_;
  159. my $loc = $params{'loc'}; # sanitized below
  160. my $lat = $params{'lat'}; # sanitized below
  161. my $lon = $params{'lon'}; # sanitized below
  162. my $page = $params{'page'}; # not sanitized?
  163. my $dest = $params{'destpage'}; # not sanitized?
  164. my $hidden = defined($params{'hidden'}); # sanitized here
  165. my ($p) = $page =~ /(?:^|\/)([^\/]+)\/?$/; # shorter page name
  166. my $name = scrub($params{'name'} || $p, $page, $dest); # sanitized here
  167. my $desc = scrub($params{'desc'} || '', $page, $dest); # sanitized here
  168. my $zoom = scrub($params{'zoom'} // $config{'osm_default_zoom'} // 15, $page, $dest); # sanitized below
  169. my $icon = $config{'osm_default_icon'} || "ikiwiki/images/osm.png"; # sanitized: we trust $config
  170. my $map = scrub($params{'map'} || 'map', $page, $dest); # sanitized here
  171. my $alt = $config{'osm_alt'} ? "alt=\"$config{'osm_alt'}\"" : ''; # sanitized: we trust $config
  172. if ($zoom !~ /^\d\d?$/ || $zoom < 2 || $zoom > 18) {
  173. error("Bad zoom");
  174. }
  175. ($lon, $lat) = scrub_lonlat($loc, $lon, $lat);
  176. if (!defined($lat) || !defined($lon)) {
  177. error("Must specify lat and lon");
  178. }
  179. my $tag = $params{'tag'};
  180. foreach my $t (keys %{$typedlinks{$page}{'tag'}}) {
  181. if ($icon = get_tag_icon($t)) {
  182. $tag = $t;
  183. last;
  184. }
  185. $t =~ s!/$config{'tagbase'}/!!;
  186. if ($icon = get_tag_icon($t)) {
  187. $tag = $t;
  188. last;
  189. }
  190. }
  191. $icon = urlto($icon, $dest, 1);
  192. $icon =~ s!/*$!!; # hack - urlto shouldn't be appending a slash in the first place
  193. $tag = '' unless $tag;
  194. register_rendered_files($map, $page, $dest);
  195. $pagestate{$page}{'osm'}{$map}{'waypoints'}{$name} = {
  196. page => $page,
  197. desc => $desc,
  198. icon => $icon,
  199. tag => $tag,
  200. lat => $lat,
  201. lon => $lon,
  202. # How to link back to the page from the map, not to be
  203. # confused with the URL of the map itself sent to the
  204. # embeded map below. Note: used in generated KML etc file,
  205. # so must be absolute.
  206. href => urlto($page),
  207. };
  208. my $mapurl = IkiWiki::cgiurl(
  209. do => "osm",
  210. map => $map,
  211. lat => $lat,
  212. lon => $lon,
  213. zoom => $zoom,
  214. );
  215. my $output = '';
  216. if (defined($params{'embed'})) {
  217. $output .= preprocess(%params,
  218. href => $mapurl,
  219. );
  220. }
  221. if (!$hidden) {
  222. $output .= "<a href=\"$mapurl\"><img class=\"img\" src=\"$icon\" $alt /></a>";
  223. }
  224. return $output;
  225. }
  226. # get the icon from the given tag
  227. sub get_tag_icon($) {
  228. my $tag = shift;
  229. # look for an icon attached to the tag
  230. my $attached = $tag . '/' . $config{'osm_tag_default_icon'};
  231. if (srcfile($attached)) {
  232. return $attached;
  233. }
  234. else {
  235. return undef;
  236. }
  237. }
  238. sub scrub_lonlat($$$) {
  239. my ($loc, $lon, $lat) = @_;
  240. if ($loc) {
  241. if ($loc =~ /^\s*(\-?\d+(?:\.\d*°?|(?:°?|\s)\s*\d+(?:\.\d*\'?|(?:\'|\s)\s*\d+(?:\.\d*)?\"?|\'?)°?)[NS]?)\s*\,?\;?\s*(\-?\d+(?:\.\d*°?|(?:°?|\s)\s*\d+(?:\.\d*\'?|(?:\'|\s)\s*\d+(?:\.\d*)?\"?|\'?)°?)[EW]?)\s*$/) {
  242. $lat = $1;
  243. $lon = $2;
  244. }
  245. else {
  246. error("Bad loc");
  247. }
  248. }
  249. if (defined($lat)) {
  250. if ($lat =~ /^(\-?)(\d+)(?:(\.\d*)°?|(?:°|\s)\s*(\d+)(?:(\.\d*)\'?|(?:\'|\s)\s*(\d+(?:\.\d*)?\"?)|\'?)|°?)\s*([NS])?\s*$/) {
  251. $lat = $2 + ($3//0) + ((($4//0) + (($5//0) + (($6//0)/60.)))/60.);
  252. if (($1 eq '-') || (($7//'') eq 'S')) {
  253. $lat = - $lat;
  254. }
  255. }
  256. else {
  257. error("Bad lat");
  258. }
  259. }
  260. if (defined($lon)) {
  261. if ($lon =~ /^(\-?)(\d+)(?:(\.\d*)°?|(?:°|\s)\s*(\d+)(?:(\.\d*)\'?|(?:\'|\s)\s*(\d+(?:\.\d*)?\"?)|\'?)|°?)\s*([EW])?$/) {
  262. $lon = $2 + ($3//0) + ((($4//0) + (($5//0) + (($6//0)/60.)))/60.);
  263. if (($1 eq '-') || (($7//'') eq 'W')) {
  264. $lon = - $lon;
  265. }
  266. }
  267. else {
  268. error("Bad lon");
  269. }
  270. }
  271. if ($lat < -90 || $lat > 90 || $lon < -180 || $lon > 180) {
  272. error("Location out of range");
  273. }
  274. return ($lon, $lat);
  275. }
  276. sub savestate {
  277. my %waypoints = ();
  278. my %linestrings = ();
  279. foreach my $page (keys %pagestate) {
  280. if (exists $pagestate{$page}{'osm'}) {
  281. foreach my $map (keys %{$pagestate{$page}{'osm'}}) {
  282. foreach my $name (keys %{$pagestate{$page}{'osm'}{$map}{'waypoints'}}) {
  283. debug("found waypoint $name");
  284. $waypoints{$map}{$name} = $pagestate{$page}{'osm'}{$map}{'waypoints'}{$name};
  285. }
  286. }
  287. }
  288. }
  289. foreach my $page (keys %pagestate) {
  290. if (exists $pagestate{$page}{'osm'}) {
  291. foreach my $map (keys %{$pagestate{$page}{'osm'}}) {
  292. # examine the links on this page
  293. foreach my $name (keys %{$pagestate{$page}{'osm'}{$map}{'waypoints'}}) {
  294. if (exists $links{$page}) {
  295. foreach my $otherpage (@{$links{$page}}) {
  296. if (exists $waypoints{$map}{$otherpage}) {
  297. push(@{$linestrings{$map}}, [
  298. [ $waypoints{$map}{$name}{'lon'}, $waypoints{$map}{$name}{'lat'} ],
  299. [ $waypoints{$map}{$otherpage}{'lon'}, $waypoints{$map}{$otherpage}{'lat'} ]
  300. ]);
  301. }
  302. }
  303. }
  304. }
  305. }
  306. # clear the state, it will be regenerated on the next parse
  307. # the idea here is to clear up removed waypoints...
  308. $pagestate{$page}{'osm'} = ();
  309. }
  310. }
  311. my %formats = get_formats();
  312. if ($formats{'GeoJSON'}) {
  313. writejson(\%waypoints, \%linestrings);
  314. }
  315. if ($formats{'CSV'}) {
  316. writecsvs(\%waypoints, \%linestrings);
  317. }
  318. if ($formats{'KML'}) {
  319. writekml(\%waypoints, \%linestrings);
  320. }
  321. }
  322. sub writejson($;$) {
  323. my %waypoints = %{$_[0]};
  324. my %linestrings = %{$_[1]};
  325. eval q{use JSON};
  326. error $@ if $@;
  327. foreach my $map (keys %waypoints) {
  328. my %geojson = ( "type" => "FeatureCollection", "features" => []);
  329. foreach my $name (keys %{$waypoints{$map}}) {
  330. my %marker = ( "type" => "Feature",
  331. "geometry" => { "type" => "Point", "coordinates" => [ $waypoints{$map}{$name}{'lon'}, $waypoints{$map}{$name}{'lat'} ] },
  332. "properties" => $waypoints{$map}{$name} );
  333. push @{$geojson{'features'}}, \%marker;
  334. }
  335. foreach my $linestring (@{$linestrings{$map}}) {
  336. my %json = ( "type" => "Feature",
  337. "geometry" => { "type" => "LineString", "coordinates" => $linestring });
  338. push @{$geojson{'features'}}, \%json;
  339. }
  340. writefile("pois.json", $config{destdir} . "/$map", to_json(\%geojson));
  341. }
  342. }
  343. sub writekml($;$) {
  344. my %waypoints = %{$_[0]};
  345. my %linestrings = %{$_[1]};
  346. eval q{use XML::Writer};
  347. error $@ if $@;
  348. foreach my $map (keys %waypoints) {
  349. my $output;
  350. my $writer = XML::Writer->new( OUTPUT => \$output,
  351. DATA_MODE => 1, DATA_INDENT => ' ', ENCODING => 'UTF-8');
  352. $writer->xmlDecl();
  353. $writer->startTag("kml", "xmlns" => "http://www.opengis.net/kml/2.2");
  354. $writer->startTag("Document");
  355. # first pass: get the icons
  356. my %tags_map = (); # keep track of tags seen
  357. foreach my $name (keys %{$waypoints{$map}}) {
  358. my %options = %{$waypoints{$map}{$name}};
  359. if (!$tags_map{$options{tag}}) {
  360. debug("found new style " . $options{tag});
  361. $tags_map{$options{tag}} = ();
  362. $writer->startTag("Style", id => $options{tag});
  363. $writer->startTag("IconStyle");
  364. $writer->startTag("Icon");
  365. $writer->startTag("href");
  366. $writer->characters($options{icon});
  367. $writer->endTag();
  368. $writer->endTag();
  369. $writer->endTag();
  370. $writer->endTag();
  371. }
  372. $tags_map{$options{tag}}{$name} = \%options;
  373. }
  374. foreach my $name (keys %{$waypoints{$map}}) {
  375. my %options = %{$waypoints{$map}{$name}};
  376. $writer->startTag("Placemark");
  377. $writer->startTag("name");
  378. $writer->characters($name);
  379. $writer->endTag();
  380. $writer->startTag("styleUrl");
  381. $writer->characters('#' . $options{tag});
  382. $writer->endTag();
  383. #$writer->emptyTag('atom:link', href => $options{href});
  384. # to make it easier for us as the atom:link parameter is
  385. # hard to access from javascript
  386. $writer->startTag('href');
  387. $writer->characters($options{href});
  388. $writer->endTag();
  389. $writer->startTag("description");
  390. $writer->characters($options{desc});
  391. $writer->endTag();
  392. $writer->startTag("Point");
  393. $writer->startTag("coordinates");
  394. $writer->characters($options{lon} . "," . $options{lat});
  395. $writer->endTag();
  396. $writer->endTag();
  397. $writer->endTag();
  398. }
  399. my $i = 0;
  400. foreach my $linestring (@{$linestrings{$map}}) {
  401. $writer->startTag("Placemark");
  402. $writer->startTag("name");
  403. $writer->characters("linestring " . $i++);
  404. $writer->endTag();
  405. $writer->startTag("LineString");
  406. $writer->startTag("coordinates");
  407. my $str = '';
  408. foreach my $coord (@{$linestring}) {
  409. $str .= join(',', @{$coord}) . " \n";
  410. }
  411. $writer->characters($str);
  412. $writer->endTag();
  413. $writer->endTag();
  414. $writer->endTag();
  415. }
  416. $writer->endTag();
  417. $writer->endTag();
  418. $writer->end();
  419. writefile("pois.kml", $config{destdir} . "/$map", $output);
  420. }
  421. }
  422. sub writecsvs($;$) {
  423. my %waypoints = %{$_[0]};
  424. foreach my $map (keys %waypoints) {
  425. my $poisf = "lat\tlon\ttitle\tdescription\ticon\ticonSize\ticonOffset\n";
  426. foreach my $name (keys %{$waypoints{$map}}) {
  427. my %options = %{$waypoints{$map}{$name}};
  428. my $line =
  429. $options{'lat'} . "\t" .
  430. $options{'lon'} . "\t" .
  431. $name . "\t" .
  432. $options{'desc'} . '<br /><a href="' . $options{'page'} . '">' . $name . "</a>\t" .
  433. $options{'icon'} . "\n";
  434. $poisf .= $line;
  435. }
  436. writefile("pois.txt", $config{destdir} . "/$map", $poisf);
  437. }
  438. }
  439. # pipe some data through the HTML scrubber
  440. #
  441. # code taken from the meta.pm plugin
  442. sub scrub($$$) {
  443. if (IkiWiki::Plugin::htmlscrubber->can("sanitize")) {
  444. return IkiWiki::Plugin::htmlscrubber::sanitize(
  445. content => shift, page => shift, destpage => shift);
  446. }
  447. else {
  448. return shift;
  449. }
  450. }
  451. # taken from toggle.pm
  452. sub format (@) {
  453. my %params=@_;
  454. if ($params{content}=~m!<div[^>]*id="mapdiv-[^"]*"[^>]*>!g) {
  455. if (! ($params{content}=~s!</body>!include_javascript($params{page})."</body>"!em)) { #"
  456. # no <body> tag, probably in preview mode
  457. $params{content}=$params{content} . include_javascript($params{page});
  458. }
  459. }
  460. return $params{content};
  461. }
  462. sub preferred_format() {
  463. if (get_slippy_engine() eq 'leaflet') {
  464. $config{'osm_format'} = 'GeoJSON';
  465. }
  466. if (!defined($config{'osm_format'}) || !$config{'osm_format'}) {
  467. $config{'osm_format'} = 'KML';
  468. }
  469. my @spl = split(/, */, $config{'osm_format'});
  470. return shift @spl;
  471. }
  472. sub get_formats() {
  473. if (get_slippy_engine() eq 'leaflet') {
  474. $config{'osm_format'} = 'GeoJSON';
  475. }
  476. if (!defined($config{'osm_format'}) || !$config{'osm_format'}) {
  477. $config{'osm_format'} = 'KML';
  478. }
  479. map { $_ => 1 } split(/, */, $config{'osm_format'});
  480. }
  481. sub get_slippy_engine() {
  482. if (!defined($config{'osm_slippy_engine'}) || !$config{'osm_slippy_engine'}) {
  483. $config{'osm_slippy_engine'} = 'openlayers';
  484. }
  485. return $config{'osm_slippy_engine'};
  486. }
  487. sub include_javascript ($) {
  488. my $page=shift;
  489. my $loader;
  490. if (exists $pagestate{$page}{'osm'}) {
  491. foreach my $map (keys %{$pagestate{$page}{'osm'}}) {
  492. foreach my $name (keys %{$pagestate{$page}{'osm'}{$map}{'displays'}}) {
  493. $loader .= map_setup_code($map, $name, %{$pagestate{$page}{'osm'}{$map}{'displays'}{$name}});
  494. }
  495. }
  496. }
  497. if ($loader) {
  498. return embed_map_code($page) . "<script type=\"text/javascript\" charset=\"utf-8\">$loader</script>";
  499. }
  500. else {
  501. return '';
  502. }
  503. }
  504. sub cgi($) {
  505. my $cgi=shift;
  506. return unless defined $cgi->param('do') &&
  507. $cgi->param("do") eq "osm";
  508. IkiWiki::loadindex();
  509. IkiWiki::decode_cgi_utf8($cgi);
  510. my $map = $cgi->param('map');
  511. if (!defined $map || $map !~ /^[a-z]*$/) {
  512. error("invalid map parameter");
  513. }
  514. print "Content-Type: text/html\r\n";
  515. print ("\r\n");
  516. print "<html><body>";
  517. print "<div id=\"mapdiv-$map\"></div>";
  518. print embed_map_code();
  519. print "<script type=\"text/javascript\" charset=\"utf-8\">";
  520. print map_setup_code($map, $map,
  521. lat => "urlParams['lat']",
  522. lon => "urlParams['lon']",
  523. zoom => "urlParams['zoom']",
  524. fullscreen => 1,
  525. editable => 1,
  526. google_apikey => $config{'osm_google_apikey'},
  527. );
  528. print "</script>";
  529. print "</body></html>";
  530. exit 0;
  531. }
  532. sub embed_map_code(;$) {
  533. my $page=shift;
  534. my $code;
  535. if (get_slippy_engine() eq 'openlayers') {
  536. my $olurl = $config{osm_openlayers_url} || "http://www.openlayers.org/api/OpenLayers.js";
  537. $code = '<script src="'.$olurl.'" type="text/javascript" charset="utf-8"></script>'."\n".
  538. '<script src="'.urlto("ikiwiki/osm.js", $page).
  539. '" type="text/javascript" charset="utf-8"></script>'."\n";
  540. }
  541. if (get_slippy_engine() eq 'leaflet') {
  542. my $leafurlbase = $config{osm_leaflet_url_stem} || "http://cdn.leafletjs.com/leaflet-0.6.4/leaflet";
  543. $code = '<link href="'.$leafurlbase.'.css" rel="stylesheet" type="text/css">'."\n".
  544. '<!--[if lte IE 8]>'."\n".
  545. '<link href="'.$leafurlbase.'.ie.css" rel="stylesheet" type="text/css">'."\n".
  546. '<![endif]-->'."\n".
  547. '<script src="'.$leafurlbase.'.js" type="text/javascript" charset="utf-8"></script>'."\n".
  548. '<script src="'.urlto("ikiwiki/leaflet.js", $page).
  549. '" type="text/javascript" charset="utf-8"></script>'."\n";
  550. }
  551. if ($config{'osm_google_apikey'}) {
  552. $code .= '<script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key='.$config{'osm_google_apikey'}.'&sensor=false" type="text/javascript" charset="utf-8"></script>';
  553. }
  554. return $code;
  555. }
  556. sub map_setup_code($;@) {
  557. my $map=shift;
  558. my $name=shift;
  559. my %options=@_;
  560. my $mapurl = $config{osm_map_url};
  561. eval q{use JSON};
  562. error $@ if $@;
  563. $options{'format'} = preferred_format();
  564. my %formats = get_formats();
  565. if ($formats{'GeoJSON'}) {
  566. $options{'jsonurl'} = urlto($map."/pois.json");
  567. }
  568. if ($formats{'CSV'}) {
  569. $options{'csvurl'} = urlto($map."/pois.txt");
  570. }
  571. if ($formats{'KML'}) {
  572. $options{'kmlurl'} = urlto($map."/pois.kml");
  573. }
  574. if ($mapurl) {
  575. $options{'mapurl'} = $mapurl;
  576. }
  577. $options{'layers'} = $config{osm_layers};
  578. return "mapsetup('mapdiv-$name', " . to_json(\%options) . ");";
  579. }
  580. 1;