I am trying to display several maps within a table, using Leaflet & Thymeleaf. This means that I have to name each map container differently or otherwise I get an error because a container with such name is already initialised. I managed to do it using Thymeleaf's standard expression syntax and got no erros so far but the maps are not displaying. I am using these expressions to name the map container's id after a 'route id' which is a numeric string.
if anybody could offer some advice on what am I doing wrong, it will be much appreciated.
<tbody>
<tr th:each="route: ${listRoutes}">
<form th:action="#{/delete_route}" method="get">
<td><input type="hidden" name ="routeID" th:value="${route.id}" class="form-control"></input> [[${route.id}]] </td>
<td><div th:id="${route.id}"></div>Map Container </td>
<td th:text="${route.owner.id}">Owner</td>
<td th:text="${route.participants}">Participants</td>
<td style="cursor:pointer"><input type="submit" value="Delete Route" class="btn btn-primary" /> </td>
<script th:inline="javascript">
var map = new L.map("[[${route.id}]]");
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap ',
maxZoom: 18
}).addTo(map);
L.control.scale().addTo(map);
var gpx = /*[[${route.directions}]]*/ "";
new L.GPX(gpx, {async: true}).on('loaded', function(e) {
map.fitBounds(e.target.getBounds());
}).addTo(map);
</script>
</form>
</tr>
</tbody>
Some notes to expand on my last comment in the question:
(1) Move all your map JavaScript into a single script, after the table; (2) Build an array of map objects var maps = [];. (3) Use Thymeleaf's support for JavaScript inlining to iterate over a list of your map IDs, and create each map by pushing it onto the array.
I assume there is a Route class (or something similar), which looks like the following:
public class Route {
private final int id;
private final int ownerId;
private final String participants;
private final String directions; // the URL of a GPX file
public Route(int id, int ownerId, String participants, String directions) {
this.id = id;
this.ownerId = ownerId;
this.participants = participants;
this.directions = directions;
}
// getters not shown
}
The model then contains a List<Route> listRoutes for Thymeleaf to use.
The Thymeleaf template will look something like the following.
I have simplified this to remove the form, and to just focus on the overall structure & processing of the data:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="/js/my_script.js"></script>
<link rel="stylesheet" type="text/css" href="/css/main.css"/>
<!-- leaflet and gpx -->
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css"
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
crossorigin=""/>
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" crossorigin=""
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==">
</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.min.js"></script>
<style>
.mapContainer {
height: 200px;
width: 500px;
}
</style>
</head>
<body>
<table>
<tbody>
<tr th:each="route: ${listRoutes}">
<td><div th:id="${route.id}" class="mapContainer"></div></td>
</tr>
</tbody>
</table>
<script th:inline="javascript">
var routes = /*[[${listRoutes}]]*/ [];
//var maps = [];
document.addEventListener("DOMContentLoaded", function (event) {
routes.forEach((route) => {
let map = L.map(route.id.toString());
//maps.push(map);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=YOUR TOKEN HERE', {
attribution: 'ATTRIBUTION INFO HERE',
maxZoom: 18,
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1
}).addTo(map);
L.control.scale().addTo(map);
var gpx = route.directions; // the URL of the GPX data
new L.GPX(gpx, {async: true}).on('loaded', function (e) {
map.fitBounds(e.target.getBounds());
}).addTo(map);
});
});
</script>
</body>
</html>
Now, the HTML table is much simpler.
The use of <script th:inline="javascript"> allows Thymeleaf to render the List<Route> data as an array of JavaScript objects:
var routes = /*[[${listRoutes}]]*/ [];
After that, you can build each map as needed.
(I did not actually need to use var maps = []; - that part of my comment was incorrect.)
Related
I am currently using the Mapbox API to retrieve the location that the user enters in the form. I'm also using the leaflet-angular-directive that allows me to render my map with the attributes that are attached to my '$scope'.
While I can retrieve a location and post a pin to my map after the user submits, I can't figure out how to autocomplete the search results similar to this example.
Here is a snippet from my controller. The &autocomplete=true in my API endpoint does not work.
function CreateController ($http, $scope) {
var vm = this;
vm.new_location = {};
vm.locations = [];
vm.submitLocationForm = function(){
vm.geocode(vm.addPin);
}
//GET LOCATION FROM QUERY
vm.geocode = function(addMapData) {
//api from mapbox with access token
var apiEndpoint = 'https://api.mapbox.com/geocoding/v5/mapbox.places/'+vm.new_location.locationName+'.json?access_token=' + MY_API_KEY + '&autocomplete=true'
//ajax call to get location data from the zipcode
$http.get(apiEndpoint)
.then(function(mapData) {
var coordinates = mapData.data.features[0].center; //array [long, lat]
addMapData(coordinates);// callback function that is called only after http call is receives data
})
}
angular.extend($scope, {
center: {
autoDiscover: true
},
markers: {
}, //markers is empty b/c users will add markers
defaults: {
// minZoom: 2,
// doubleClickZoom: true,
markerZoomAnimation: true
}
);
//adding pin
vm.addPin = function(coordinates){
vm.pinCounter += 1;
vm.new_location.pinOrder = vm.pinCounter;
vm.new_location.longitude = coordinates[0];
vm.new_location.latitude = coordinates[1];
$scope.markers[vm.pinCounter] = {
lat: vm.new_location.latitude,
lng: vm.new_location.longitude,
message: vm.new_location.textContent,
draggable: false,
focus: true,
riseOnHover: true,
zoom: 10
}
}
Here's the HTML for the form:
<form ng-submit="CreateController.submitLocationForm()">
<div class="form-group">
<input type="text" ng-model="CreateController.new_location.locationName" class="form-control" placeholder="Location name" autofocus>
</div>
<input type="submit" value="Submit" class="btn btn-block btn-info">
</form>
This is the code that's available on the Mapbox documentation, but I'm unsure how to modify it in order to use it with Angular:
<html>
<head>
<meta charset=utf-8 />
<title>Geocoding with autocomplete</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.js'></script>
<link href='https://api.mapbox.com/mapbox.js/v2.4.0/mapbox.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
L.mapbox.accessToken = '<your access token here>';
L.mapbox.map('map', 'mapbox.streets')
.addControl(L.mapbox.geocoderControl('mapbox.places', {
autocomplete: true
}));
</script>
</body>
</html>
You seem to be looking at our old Mapbox JS library. A newer one has been released that makes maps much more fluid. The documentation has a geocoder example with autocomplete, i'd recommend looking over. If you have any additional question, i'll be glad to answer them.
This my first time programming web application with maps.
I am working with Java servlets, Netbeans IDE, javascript, html and css.
I created algorithm that creates paths from a given Graph (*osm file) from openstreetmap.
*osm file is XML file that represent graph in openstreetmap, more info here.
The path structure has list of Nodes where the first node is the source and the last node is the target:
public class Way
{
private double m_Length;
private long m_Id;
//private List<Long> m_NodesRefs; // this is for <nd ref=123123>
private List<Node> m_Nodes;
...
}
Every Node has Latitude and Longitude:
public class Node implements Comparable<Node>
{
private long m_Id;
private List<Edge> m_Adjacencies = new ArrayList<Edge>();
private double m_Longtitude;
private double m_Latitude;
private Node m_Prev;
private double m_MinDistance = Double.POSITIVE_INFINITY;
...
}
Using Leaflet I succeed to show the map but now I want also to be able to show the some of the paths my algorithm found.
This is an example from openstreetmap how I want to show the path:
I noticed that they are using <path> with positions.
I read here about <path> tag.
I understood that I need to create this tag with content of the positions in order to show the path on the map.
My problem is that I only have the coordinates of each Node and I don't know how to translate it to positions.
For example if I have a path with length of 3:
<node id="2500639640" lat="32.1555549" lon="34.8946619"/>/>
<node id="2500639651" lat="32.1556683" lon="34.8946958"/>
<node id="2500639647" lat="32.1557488" lon="34.8947266"/>
How can I create from this <path> tag with positions ?
My HTML code (map.html):
<!DOCTYPE html>
<html >
<head>
<script type="text/javascript" src="js/jquery-2.1.1.min.js" ></script>
<script type="text/javascript" src="js/map/leaflet.js"></script>
<script type="text/javascript" src="js/map/map.js" ></script>
<meta charset="UTF-8">
<title>MyApp</title>
<link rel="stylesheet" href="css/leaflet.css" />
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/map.css" />
</head>
<body>
<div class="wrapper">
<div class="container">
<img src="Images/AppLogo.png" class="logo">
<div id="dataviewer">
<div id="map">
</div>
</div>
</div>
</div>
</body>
</html>
My javascript code to show the map (map.js):
var g_Map;
$(function() { //on load
g_Map = L.map('map').setView([32.0641463, 34.7811246], 13);
var tilesAttrib = '© OpenStreetMap contributors <small>Data:ODbL, Map:cc-by-sa</small>';
// var tilesUrl = 'https://{s}.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png';
var tilesUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png';
var tiles = new L.TileLayer(tilesUrl, {attribution: tilesAttrib});
g_Map.addLayer(tiles);
scaleControl = new L.Control.Scale({metric: true, imperial: false, });
scaleControl.addTo(g_Map);
});
You should use Leaflet's Polyline, not an SVG path. You'll need to expose your nodes as a JavaScript array (e.g. nodes). Then:
var latlngs = [];
var i = 0;
for (i = 0; i < nodes.length; ++i) {
latlngs.push(L.latLng(nodes[i].lat, nodes[i].lon));
}
var polyline = L.polyline(latlngs);
g_Map.addLayer(polyline);
Polyline takes geographic coordinates, so there's no need to convert. If you ever do need to do this conversion, see Map.latLngToLayerPoint and IProjection.
I'm trying to use the Maps API with random coordinates but I don't know and I need some information how can I put into javascript function on .js file a php value from a php file?. For example if we have the several codes:
1) API code to generate a route between 2 coordinates(order2.js):
(function() {
window.onload = function() {
// Creating a map
var options = {
zoom: 8,
center: new google.maps.LatLng(-33.3834, -70.6),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map'), options);
// Creating an array that will contain the points for the polyline
var route = [
new google.maps.LatLng(-33.02644,-71.539775),
new google.maps.LatLng(-33.605148,-70.702197)
];
// Creating the polyline object
var polyline = new google.maps.Polyline({
path: route,
strokeColor: "#ff0000",
strokeOpacity: 0.6,
strokeWeight: 5
});
// Adding the polyline to the map
polyline.setMap(map);
};
})();
2) Google Maps Api code to show(map2.html):
<html xmlns="http://www.w3.org/1999/xhtml" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Chapter 4 - Google Maps API 3</title>
<link rel="stylesheet" href="css/graphic.css" type="text/css" media="all" />
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="js/order2.js"></script>
</head>
<body>
<input type="button" value="getValues" id="getValues" />
<input type="button" value="changeValues" id="changeValues" />
<div id="map"></div>
</body>
</html>
3)If I have the next float random code(random.php):
<?php
while($x <= 2995 and $y <= 71333)
{
$x = rand(2025,2995);
$y = rand(70116,71333);
$w = $x/100;
$z = $y/1000;
echo $w."<br>".$z."<br>";
}
?>
Now in basis to before codes I'm really need to know:
How can I to transfer $w and $z values from random.php to any function(a,b) related with order2.js ?
Thanks by your useful help.
Regards
1) if its only for creating random numbers, you can easily do that with javascript.
2) if it's really important to get data from php, the best way is to create a textarea that is hidden from the end user.
in the textarea place all the data you want to transfer to the js files in JSON format!
something like this:
<textarea id="phpData" style="display:none">{"J":5,"test2":"N"}</textarea>
now if you would like to access it from js:
var thePhpData = JSON.parse( $("#phpData").val() );
to get values:
thePhpData.J
thePhpData.test2
The only way is that you have to include your JavaScript into a PHP file...
Order2.php
<?php
include("random.php");
echo "
<script type='text/javascript'>
function(){
...
// to use the values in its place
".$w." .... ".$z."
}
";
</script>
?>
.... are other codes
This is a working example of Street View API V3:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google Maps JavaScript API Example: Street View Events</title>
<link href="/maps/documentation/javascript/examples/default.css" rel="stylesheet" type="text/css" />
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"
type="text/javascript"></script>
<script type="text/javascript">
var cafe = new google.maps.LatLng(37.869085,-122.254775);
function initialize() {
var panoramaOptions = {
position: cafe,
pov: {
heading: 270,
pitch: 0,
zoom: 1
},
visible: true
};
var panorama = new google.maps.StreetViewPanorama(document.getElementById("pano"), panoramaOptions);
google.maps.event.addListener(panorama, 'pano_changed', function() {
var panoCell = document.getElementById('pano_cell');
panoCell.innerHTML = panorama.getPano();
});
google.maps.event.addListener(panorama, 'links_changed', function() {
var linksTable = document.getElementById('links_table');
while(linksTable.hasChildNodes()) {
linksTable.removeChild(linksTable.lastChild);
};
var links = panorama.getLinks();
for (var i in links) {
var row = document.createElement("tr");
linksTable.appendChild(row);
var labelCell = document.createElement("td");
labelCell.innerHTML = "<b>Link: " + i + "</b>";
var valueCell = document.createElement("td");
valueCell.innerHTML = links[i].description;
linksTable.appendChild(labelCell);
linksTable.appendChild(valueCell);
}
});
google.maps.event.addListener(panorama, 'position_changed', function() {
var positionCell = document.getElementById('position_cell');
positionCell.firstChild.nodeValue = panorama.getPosition();
});
google.maps.event.addListener(panorama, 'pov_changed', function() {
var headingCell = document.getElementById('heading_cell');
var pitchCell = document.getElementById('pitch_cell');
headingCell.firstChild.nodeValue = panorama.getPov().heading;
pitchCell.firstChild.nodeValue = panorama.getPov().pitch;
});
}
</script>
</head>
<body onload="initialize()">
<div id="pano" style="width: 425px; height: 240px;float:left"></div>
<div id="panoInfo" style="width: 425px; height: 240 px;float:left">
<table>
<tr>
<td><b>Position</b></td><td id="position_cell"> </td>
</tr>
<tr>
<td><b>POV Heading</b></td><td id="heading_cell">270</td>
</tr>
<tr>
<td><b>POV Pitch</b></td><td id="pitch_cell">0.0</td>
</tr>
<tr>
<td><b>Pano ID</b></td><td id="pano_cell"> </td>
</tr>
<table id="links_table">
</table>
</table>
</div>
</body>
</html>
I've been searching all day on how to parse a google maps street view url into a google maps street view embed javascript, but I dont seem to find a solution anywhere, need to parse something like this: http://maps.google.com/?ll=37.588929,-87.337506&spn=1.201384,1.766052&t=m&z=9&layer=c&cbll=37.588929,-87.337506&panoid=C0tiQOjXuQtNmZRPkHbVxw&cbp=12,154.39,,0,-4.06
There's this querystring library: https://github.com/visionmedia/node-querystring. Works in node.js and in the browser.
pano position=37.588929,-87.337506 the "ll="
Looks like "cbp=" will give you heading, pitch and zoom
http://www.geocodezip.com/v3_GoogleEx_streetview-simpleA.htm
See this page on mapki.com for their meaning.
UPDATE (the mapki.com seems to be gone):
According to this site:
The 5 parts of "cbp=" are window size, heading (bearing), tilt, zoom, and pitch (in that order).
I'm very new to HTML and Javascript (less than a week of experience) and am stuck in the following situation. I created 6 google map visualizations using the googleVis package for R (simple stuff--just markers on a map). I've extracted the Javascript out of the googleVis objects in R and have included them in a simple site that contains 6 divs for each of the 6 different maps.
Each Javascript file includes a function to create a JSON object, a function to draw the map, and a function to display the map:
//define JSON
function gvisDataWest ()
{
var data = new google.visualization.DataTable();
var datajson =
[
[
33.5313,
-112.1774,
"<p>2005-2009 Poverty Rate: 40.7%</p> <p>2000 Poverty Rate: 34.3%</p> <p>Significant difference: 0.0 points </p>"
],
... many rows of data ...
];
data.addColumn('number','Latitude');
data.addColumn('number','Longitude');
data.addColumn('string','tip');
data.addRows(datajson);
return(data);
}
//draw chart
function drawChartWest() {
var data = gvisDataWest();
var options = {};
options["showTip"] = true;
options["enableScrollWheel"] = true;
options["width"] = 400;
var chart = new google.visualization.Map(
document.getElementById('West')
);
chart.draw(data,options);
}
//display chart
function displayChartWest()
{
google.load("visualization", "1", { packages:["map"] });
google.setOnLoadCallback(drawChartWest);
}
Each javascript file includes functions and data for a different region of the country (e.g. there would be definitions for gvisDataMidwest(), givsDataSouthEast(), etc.).
Here's roughly what my HTML looks like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<meta content="text/xml; charset=UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=8" />
<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script src="../povscripts/map1.js" type="text/javascript"></script>
<script src="../povscripts/map2.js" type="text/javascript"></script>
<script src="../povscripts/map3.js" type="text/javascript"></script>
<script src="../povscripts/map4.js" type="text/javascript"></script>
<script src="../povscripts/map5.js" type="text/javascript"></script>
<script src="../povscripts/map6.js" type="text/javascript"></script>
</head>
<body>
...a bunch of divs, headers, <p>'s, etc...
...then six divs like this, each with an appropriate id:
<div class="anncdiv">
<div class="bannertitle">
<p class="btxt">West</p>
</div>
<div class="anncdivIn" id="West">
<script type="text/javascript"> displayChartWest() </script>
</div>
</div>
<div class="anncdiv">
<div class="bannertitle">
<p class="btxt">Midwest</p>
</div>
<div class="anncdivIn" id="Midwest">
<script type="text/javascript"> displayChartMidwest() </script>
</div>
</div>
THE PROBLEM: With the way I've currently cobbled this site together, if there are two dsplayChart**() functions being called anywhere on the site (as in this example, with displayChartWest() and displayChartMidwest()) all divs appear blank. But, if I were to just include one of these function calls, then the map displays fine in the appropriate div. Somehow calling two or more of these functions results in a conflict, though I just don't know why. Your help is GREATLY appreciated. Cheers, AR
I think you might need to wait until the DOM is loaded before firing up your charts. Without using a library like jQuery, you could do it using the body onload event:
<body onload="displayCharts()">
...page source...
<script type="text/javascript">
function displayCharts() {
displayChartWest();
displayChartMidwest();
}
</script>
</body>
Instead of doing this:
google.load("visualization", "1", { packages:["map"] });
google.setOnLoadCallback(drawChartWest);
try this instead:
google.load("visualization", "1", { packages:["map"],callback: drawChartWest});