Please take a look at the code below. It's drawing a circle onto the map. If the user clicks with the mouse or taps with his finger (on a mobile device) into the circle and drags it, the circle should be moved across the map.
This works for Desktop Firefox, Desktop Chrome, Mobile Firefox. But not with Mobile Chrome. I think that the code in Mobile Firefox maybe just works because the browser emulates touch-inputs to mouse-inputs which of course are working well in Leaflet.
So I need help how to best implement touch events (parallel to mouse events) within Leaflet. Touch events are not mentioned in Leaflet's Doc. But i think the usual Javascript DOM-events should work too? (touchstart, touchmove, touchend). But not within Chrome mobile the way my code is written.
<!DOCTYPE html>
<html>
<head>
<title>Eventtest</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.1.0/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet#1.1.0/dist/leaflet.js"></script>
</head>
<body>
<div id="mapid" style="width: 600px; height: 400px;"></div>
<script>
var mymap = L.map('mapid').setView([51.505, -0.09], 13);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(mymap);
circle = L.circle([51.508, -0.11], 500, {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5
}).addTo(mymap);
circle.on('mousedown touchstart', onCircleDown);
function onCircleDown (e1) {
mymap.dragging.disable();
var mouseStartingLat = e1.latlng.lat;
var mouseStartingLng = e1.latlng.lng;
var circleStartingLat = e1.target._latlng.lat;
var circleStartingLng = e1.target._latlng.lng;
circle.on('mousemove', function (e2) {
var mouseNewLat = e2.latlng.lat;
var mouseNewLng = e2.latlng.lng;
var latDifference = mouseNewLat - mouseStartingLat;
var lngDifference = mouseNewLng - mouseStartingLng;
var currentCircleCoords = L.latLng (circleStartingLat + latDifference, circleStartingLng + lngDifference);
e1.target.setLatLng (currentCircleCoords);
});
circle.on ('mouseup', function () {
circle.off('mousemove');
mymap.dragging.enable();
});
}
</script>
</body>
</html>
Related
I am getting started with GIS.
I am trying to publish a Geography Markup Language (GML) file on geoserver. For this I converted it to a shape file using ogr2ogr tool
ogr2ogr -f "ESRI Shapefile" adur.shp adur.gml
I published the converted Shapefile on geoserver and loaded it in my application as a WMS layer. Here is the jsfiddle link for the below code snippet https://jsfiddle.net/3kmwpsu2/
<!DOCTYPE html>
<html>
<head>
<title>WMS and Google Maps</title>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<style>
/* Always set the map height explicitly to define the size of the div
* element that contains the map. */
#map {
height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html,
body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
const EXTENT = [-Math.PI * 6378137, Math.PI * 6378137];
const xyzToBounds = (x, y, z) => {
let tileSize = (EXTENT[1] * 2) / Math.pow(2, z);
let minx = EXTENT[0] + x * tileSize;
let maxx = EXTENT[0] + (x + 1) * tileSize;
// remember y origin starts at top
let miny = EXTENT[1] - (y + 1) * tileSize;
let maxy = EXTENT[1] - y * tileSize;
return [minx, miny, maxx, maxy];
}
const getWMSTileUrl = (coordinates, zoom) => {
return (
"http://wms.prod.qgiscloud.com/abhilash/cloud" +
"?REQUEST=GetMap&SERVICE=WMS&VERSION=1.1.0" +
"&LAYERS=adur" +
"&FORMAT=image%2Fpng" +
"&SRS=EPSG:3857&WIDTH=256&HEIGHT=256&TRANSPARENT=TRUE" +
"&BBOX=" +
xyzToBounds(coordinates.x, coordinates.y, zoom).join(","))
};
function initMap() {
let map = new google.maps.Map(document.getElementById("map"), {
zoom: 19,
center: {
lat: 50.83238,
lng: -0.330478
},
tilt: 0,
mapTypeId: window.google.maps.MapTypeId.HYBRID
});
let layer = new google.maps.ImageMapType({
getTileUrl: getWMSTileUrl,
minZoom: 0,
maxZoom: 19,
opacity: 1.0
});
layer.addListener("tilesloaded", () => {
console.log("Layers loaded")
});
map.overlayMapTypes.push(layer);
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?&callback=initMap">
</script>
</body>
</html>
I noticed that the boundaries are not aligned properly. I see some shift in the boundaries.
Note : Please note that I used QGIS cloud's WMS link in the demo instead of my local geoserver's WMS URL
I imported the same shape file into QGIS to check if there is anything wrong with the converted Shapefile. But the borders are aligned properly here. I am attaching both the screenshots below for comparison. You can see that the borders are not aligned properly in the first image. There is some shift here.
I am attaching the GML and converted Shape files here for your reference.
This screenshot is from my application where the borders are not aligned properly.
This screenshot is from QGIS where borders are aligned properly
UPDATE :
After several attempts, I added same WMS layer twice in QGIS.
One with EPSG:4326 - WGS 84 CRS
And the other with EPSG:27700 OSGB36 / British National Grid - CRS
This is the result. The WMS layer with EPSG:27700 CRS aligned perfectly.
But I am not able to do the same with GeoServer / in my application. CRS on GeoServer is already in EPSG:27700.
We have a map application that is used to show counties in a state. In many cases local cities have their own jurisdictions, so when showing a county area we need to take the county shape and remove the cities that are separate.
We have been able to do this using the geocode function in the API to get the county shape and then grab the polygon for each city. From there we get the geometries and use pushInterior to create the cutouts.
On the whole, this works fairly well and even handles some pretty complicated overlapping areas. At a very close zoom we see the intricate borders perfectly, but when we zoom out the polygon starts to become unstable.
A clean cutout at the closest zoom, will begin to show random overlay shapes or connections as we zoom out. It almost looks like the polygon is adjusting to a lower resolution and eliminating some of the detail points to draw which results in large blobs or connections between points that fill in large areas based on random lines.
The odd part is that we have it drawing the shapes and lines. The lines are correct and follow the outlines of the shapes as intended but the lighter shape color is the part that is not displaying correctly.
Is there any way to force the precision or redraw of the polygon so the fill content is matching the outline?
Please try with the below code, which explains how we can draw polygon with Holes on the Map.
/**
* Adds a polygon to the map
*
* #param {H.Map} map A HERE Map instance within the application
*/
function addPolygonToMap(map) {
var geoPolygon = new H.geo.Polygon(
// define exterior shape
new H.geo.LineString([52, 13, 100, 48, 2, 100, 48, 16, 100, 52, 13, 100]),
[ // define interior geometries - holes
new H.geo.LineString([48.5, 4.5, 0, 49.5, 8, 0, 48.5, 9, 0]),
new H.geo.LineString([48.5, 15, 0, 50, 11, 0, 51, 13, 0])
]
);
map.addObject(
new H.map.Polygon(geoPolygon, {
style: {
fillColor: '#FFFFCC',
strokeColor: '#829',
lineWidth: 8
}
})
);
}
/**
* Boilerplate map initialization code starts below:
*/
//Step 1: initialize communication with the platform
// In your own code, replace variable window.apikey with your own apikey
var platform = new H.service.Platform({
apikey: window.apikey
});
var defaultLayers = platform.createDefaultLayers();
//Step 2: initialize a map - this map is centered over Europe
var map = new H.Map(document.getElementById('map'),
defaultLayers.vector.normal.map,{
center: {lat:52, lng:5},
zoom: 5,
pixelRatio: window.devicePixelRatio || 1
});
// add a resize listener to make sure that the map occupies the whole container
window.addEventListener('resize', () => map.getViewPort().resize());
//Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
// Create the default UI components
var ui = H.ui.UI.createDefault(map, defaultLayers);
// Add Polygon with holes to the map
addPolygonToMap(map);
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=yes">
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
<title>Polygon with Holes on the Map</title>
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
<link rel="stylesheet" type="text/css" href="demo.css" />
<link rel="stylesheet" type="text/css" href="styles.css" />
<link rel="stylesheet" type="text/css" href="../template.css" />
<script type="text/javascript" src='../test-credentials.js'></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<script>window.ENV_VARIABLE = 'developer.here.com'</script><script src='../iframeheight.js'></script></head>
<body id="markers-on-the-map">
<div class="page-header">
<h1>Polygon with Holes on the Map</h1>
<p>Display a map highlighting a region or area</p>
</div>
<p>This example displays a polygon with two triangular holes in it covering part of Western Europe displayed on a moveable map.</p>
<div id="map"></div>
<h3>Code</h3>
<p>A polygon is created using the <code>H.map.Polygon</code> class, passing in an
<code>H.geo.Polygon</code> holding the vertices for the outline and interior parts of the polygon.
The look-and-feel of the polygon can be altered by adding the <code>style</code> parameter</p>
<script type="text/javascript" src='demo.js'></script>
</body>
</html>
For more details please refer the below link.
https://developer.here.com/documentation/examples/maps-js/geoshapes/polygon-with-holes-on-the-map
Is there a way to change the zIndex of a groundOverlay?
With Geoxml3 I am parsing two KML files, one of them contains a polygon and the other one contains a groundOverlay. Everythings goes perfect except for the fact that i want my groundOverlay OVER the polygon, because now the groundOverlay appears behind the polygon.
Update:
This is my code
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no"/>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Geoxml3</title>
<style>
html{height:100%;}
body{height:100%;margin:0px;}
#map_canvas{height: 90%;width: 90%;}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://geoxml3.googlecode.com/svn/branches/polys/geoxml3.js"></script>
<script type="text/javascript" src="http://geoxml3.googlecode.com/svn/trunk/ProjectedOverlay.js">
</script>
</head>
<body>
<div id="map_canvas"></div>
</div>
<script type="text/javascript">
var geoXml=null, map=null;
function initialize() {
var myOptions = {
center: new google.maps.LatLng(39.397, -100.644),
zoom: 4,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
geoXml = new geoXML3.parser({
map: map,
zoom: true,
createOverlay: addMyOverlay
});
geoXml.parse(['groundOverlay.kml','polygon.kml']);
function addMyOverlay(placemark,doc){
//How to change the the GroundOverlay zIndex
var groundOverlay = geoXml.createOverlay(placemark);
return groundOverlay;
};
};
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</body>
</html>
The test is here:
http://jorgeluisperez.260mb.net/geoxml/
Probably the easiest way would be to specify zIndex for GroundOverlay once the map is loaded:
google.maps.event.addListenerOnce(map, 'idle', function(){
var overlayDiv = document.getElementById(groundOverlay.id_);
overlayDiv.style.zIndex = '999';
console.log(overlayDiv);
});
Note: groundOverlay should be accessible from event
Working example: Plunker
The ProjectedOverlay class which is used to render GroundOverlays in geoxml3 attaches the overlays to the overlayLayer. That is the same pane in which polygons are rendered. The OverlayView class doesn't support zIndex, and the zIndex supported by Polygons specifically states it is only good between "polys". It is possible that the order of adding the Polygons vs. the GroundOverlays might change that, but a quick test didn't work. You could modify the ProjectedOverlay code to append the overlay to a pane above the overlayLayer.
from the documentation on MapPanes:
This object contains the DOM elements in which overlays are rendered. They are listed below with 'Pane 0' at the bottom and 'Pane 4' at the top.
Properties
floatPane | This pane contains the info window. It is above all map overlays. (Pane 4).
mapPane | This pane is the lowest pane and is above the tiles. It may not receive DOM events. (Pane 0).
markerLayer | This pane contains markers. It may not receive DOM events. (Pane 2).
overlayLayer | This pane contains polylines, polygons, ground overlays and tile layer overlays. It may not receive DOM events. (Pane 1).
overlayMouseTarget | This pane contains elements that receive DOM events. (Pane 3).
I have static images of map areas and I need to display them with a clickable marker overlay with multiple dynamic markers on it. The markers have to correspond to specific geo locations. I will know the geo coordinates of the markers, but they will change frequently.
The static images HAVE to be displayed without any of the surrounding areas visible (and- NO zoom, no drag, no pan etc). For example, a map of a state in the USA with NO bordering states and multiple, specifically placed, clickable markers. This makes it impossible to use google maps api as is even with map style customization (at least for the actual map display).
So my question(s) is/are:
Is there a way to do this while still leveraging the google map api marker functionality? Or will I have to rewrite my own version of the marker functionality (click, info box etc)?
Is there a way to load the google map with the markers and then dynamically "swap" the google map with my static one?
Picture this:
(source: nebdev.net)
Where the markers are clickable and positioned based on specific geo codes.
your answers are:
Yes.
Yes.
Disable controls: https://developers.google.com/maps/documentation/javascript/examples/control-disableUI
Stylized map markers: https://developers.google.com/maps/documentation/javascript/examples/icon-complex
Listening to click/DOM events: https://developers.google.com/maps/documentation/javascript/examples/event-domListener
I'm interested in this quesiton. But I'm afraid you cannot make what you want.
At lease, you can try these things with Google Maps:
1. Specify the bounds you need (to match your static image) -- Google API
2. Add markers -- Google API
3. Disable draggable and ScrollWheelZoom -- Google API
4. Add click event on markers -- Google API
5. Replace the map with your static image -- Google API
The problem will be the 1st step, which you can hardly specify the bounds of Google Map as exactly to the area of your static image.
You can specify an area on Google Map though, like area layer , but basically the outer space will still be a rectange(Google Map itself and LatLngBounds)
Ok, I think I got it to work. The concept is pretty simple:
Create a non-Google Map overlay OVER the map area to hide the gmap as it's loading. I had to do this because otherwise, the marker wouldn't load correctly.
Once the map and marker(s) are loaded, remove all images from inside the map element.
Set the background image of the map element to our custom map
Remove the overlay that was obscuring the map area.
Demo: http://web2.nebdev.net/tasks/sandbox/t1234.html
Here is the code (it may explain things better than I am):
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Simple markers</title>
<style>
html, body, #map-canvas {
height: 100%;
margin: 0px;
padding: 0px
}
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>
<script>
function initialize() {
//$('#map-canvas').hide();
var myLatlng = new google.maps.LatLng(-25.363882,131.044922);
var mapOptions = {
zoom: 4,
center: myLatlng,
disableDefaultUI: true,
draggable: false,
disableDoubleClickZoom: true,
keyboardShortcuts: false,
mapTypeControl: false,
noClear: true,
overviewMapControl: false,
panControl: false,
rotateControl: false,
scaleControl: false,
scrollwheel: false,
streetViewControl: false,
zoomControl: false
}
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Hello World!'
});
google.maps.event.addDomListener(marker,'click',function() {alert('Click')});
//google.maps.event.addDomListener(marker,'mouseover',function() {alert('Mouseover')});
google.maps.event.addDomListener(map,'idle',removeMaps);
}
function removeMaps() {
window.setTimeout(function(){
$('img','#map-canvas').remove();
$('.gm-style-cc,.gmnoprint .gm-style-cc,.gmnoprint').remove()
$('#map-canvas').show();
$('#map-canvas').attr('style','background:url(http://web2.nebdev.net/tasks/sandbox/untitled4.png) no-repeat top left transparent');
$('#map-canvas-overlay').hide();
console.log(marker.getVisible());
},1000);
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
</head>
<body>
<div id="map-canvas" style="width:400px;height:400px;"></div>
<div id="map-canvas-overlay" style="width:400px;height:400px;background:url() #CCC;position:absolute;top:0px;left:0px;z-index:10000">Loading...</div>
</body>
</html>
I have the below code, based on one of the API samples. A click on the map creates a marker. A click on the marker opens up an info window. Now I want a click on the info window to do something. E.g. a click anywhere might close it, as opposed to the little cross in the corner. Or a click on it might open a new URL. Etc.
Unfortunately it seems there is no "click" event for info windows.
The closest I've got is shown as a commented out line below: I wrap my info window content in a div, and give that an onClick. This works, but there is a big border around it. I really want to be able to click anywhere in the info window box.
Is there a way?
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
<meta charset="utf-8">
<title>Click Test</title>
<script src="https://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script>
google.maps.visualRefresh = true; //New look visuals.
function initialize() {
var useragent = navigator.userAgent;
var mapdiv = document.getElementById("map-canvas");
if (useragent.indexOf('iPhone') != -1 || useragent.indexOf('Android') != -1 ) {
mapdiv.style.width = '100%';
mapdiv.style.height = '100%';
} else {
mapdiv.style.width = '400px';
mapdiv.style.height = '600px';
}
var mapOptions = {
zoom:3,
center: new google.maps.LatLng(37.09024, -95.712891),
mapTypeId: google.maps.MapTypeId.TERRAIN
};
var map = new google.maps.Map(document.getElementById('map-canvas'),
mapOptions);
google.maps.event.addListener(map, 'click', function(event) {
placeMarker(event.latLng);
});
function placeMarker(location) {
var marker = new google.maps.Marker({
position: location,
map: map
});
var infowindow = new google.maps.InfoWindow({
//content: "<div onClick='test1()'>(lat,lng):<br/>"+location.lat()+","+location.lng()+"</div>"
content: "(lat,lng):<br/>"+location.lat()+","+location.lng()
});
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(marker.get('map'), marker);
infowindow.addListener('click',test1); //Does nothing
});
}
}
google.maps.event.addDomListener(window, 'load', initialize);
function test1(){alert("test1");}
</script>
</head>
<body>
<div id="map-canvas"></div>
</body>
</html>
UPDATE:
This image shows the problem when I use a clickable div inside the content (background set to red to show the extent of the region I can make clickable, and also that I can style; if I set a negative margin I just get scrollbars, not a bigger region!). It is the whole white area I want to be clickable, not just that red rectangle.
I decided to use InfoBox found in the Google Maps Utility Library. So in the header add a link to the library. Then replace the new google.maps.InfoWindow() line with this one:
var infowindow = new InfoBox({
closeBoxURL:"",
content: '<div onClick="test1();return false;" style="background:white;opacity:0.8;padding:8px">(lat,lng):<br/>'+
location.lat()+","+location.lng()+"</div>"
});
By setting closeBoxUrl to a blank string I get no close option. I added a large padding just so you can see that clicking right to the edge does indeed work.
You can also do it this way. I also use the boxClass option so the formatting is done in CSS:
var infoContent=document.createElement('div');
infoContent.innerHTML="(lat,lng):<br/>"+location.lat()+","+location.lng();
infoContent.onclick=test1;
var infowindow = new InfoBox({
closeBoxURL:"",
boxClass:"marker_popup",
content: infoContent,
});
(Aside, if doing it this way, on just some browsers it creates a marker below the InfoBox! Simplest fix is to change test1 so it looks like: function test1(event){alert("test1");event.preventDefault();return false;} )
P.S. I chose InfoBox over InfoBubble, as the latter has no documentation, and it had no obvious advantages to compensate for that major flaw! InfoBox has documentation and a reference. (links are for version 1.1.9)