Leaflet offline grid layer - javascript

I want leaflet to work offline, without title, to show only a grid as titles. To have all the features of Leaflet, add plugins to draw a line, pin a marker, draw a polygon, zoom in / zoom out on shapes etc.
Is there a simple way to show just a simple grid?

Here's a custom GridLayer (that's already been implemented by the Leaflet authors). All you have to do is copy L.GridLayer.DebugCoords where you would normally load a tile layer.
var map = L.map('map', {
center: [0, 0],
zoom: 0
});
L.GridLayer.DebugCoords = L.GridLayer.extend({
createTile: function (coords, done) {
var tile = document.createElement('div');
//this adds tile coordinates; you may or may not want this
tile.innerHTML = [coords.x, coords.y, coords.z].join(', ');
tile.style.outline = '1px solid red';
/* // you don't need this artificial timeout for your application
setTimeout(function () {
done(null, tile); // Syntax is 'done(error, tile)'
}, 500 + Math.random() * 1500);
*/
return tile;
}
});
L.gridLayer.debugCoords = function(opts) {
return new L.GridLayer.DebugCoords(opts);
};
map.addLayer( L.gridLayer.debugCoords() );
Stand-alone, working example: http://leafletjs.com/examples/extending/gridcoords.html
Code taken from: http://leafletjs.com/examples/extending/extending-2-layers.html

Related

leaflet invert when there are multiple polygons

I'm using this library to invert leaflet layers.
https://github.com/ebrelsford/Leaflet.snogylop
This works perfectly when there is only one polygon. But when are there are multiple polygons, the invert applies for each and every polygon so it overlaps resulting invert feature applies on top of each polygon. Like the image attached below.
When trying on a single polygon it works perfectly.
How can i fix this issue when multiple polygons are there.
function handleJson(data) {
var selectedArea = L.geoJson(data, {
invert: true,
worldLatLngs: [
L.latLng([90, 360]),
L.latLng([90, -180]),
L.latLng([-90, -180]),
L.latLng([-90, 360])
]
});
selectedArea.addTo(lmap);
lmap.fitBounds(selectedArea.getBounds());
}
PS:
poly.toGeoJSON()
{"type":"Feature","properties":{},"geometry":{"type":"MultiPolygon","coordinates":[[[[null,null],[null,null]]],[[[null,null],[null,null]]],[[[null,null],[null,null]]],[[[null,null],[null,null]]],[[[null,null],[null,null]]],[[[null,null],[null,null]]],[[[null,null],[null,null]]]]}}
multipolyCoords
[[[[{"lat":35.41263883,"lng":63.02502113},{"lat":35.41251986,"lng":63.02571771},{"lat":35.41329411,"lng":63.02584793},{"lat":35.41378402,"lng":63.02643689},{"lat":35.41402821,"lng":63.02723164},{"lat":35.41342648,"lng":63.02885513},{"lat":35.41342962,"lng":63.02891854},{"lat":35.4100508,"lng":63.02941312},
.....................
]]]],"_initHooksCalled":true
You have to create a MultiPolygon out of the polygons.
Try this (I'm assuming that you only have polygons in data):
var layers = L.geoJSON(data);
var multipolyCoords = [];
layers.eachLayer((l)=>{
if(l instanceof L.Polygon){
multipolyCoords.push(l.getLatLngs())
}
})
var poly = L.polygon(multipolyCoords);
var selectedArea = L.geoJson(poly.toGeoJSON(), {
invert: true,
worldLatLngs: [
L.latLng([90, 360]),
L.latLng([90, -180]),
L.latLng([-90, -180]),
L.latLng([-90, 360])
]
});

Mapbox popups at wrong location when map is at max zoom (multiple world copies visible)

I have an issue with a Mapbox map where Popups attached to a Marker are displayed the wrong position, when the map is zoomed out, so we see multiple world copies. See sample below. The Popup are displayed at the correct position when the map is zoomed in and only one world is visible.
This is a simplified version of the code used to add Markers + Popups and display them:
// Add markers to the map
features.forEach(function (marker: any, i: number) {
const popUpContent = '<div>Sample</div>'
// Create the popup
const popup = new mapboxgl.Popup({ offset: 25 })
.setHTML(popUpContent)
.on('open', function (event: any) {
const activePoi = document.getElementsByClassName(poiId)[0]
activePoi.classList.add('active')
})
.on('close', function () {
const activePoi = document.getElementsByClassName(poiId)[0]
if (activePoi) {
activePoi.classList.remove('active')
}
})
let mark = new mapboxgl.Marker(markerElement)
.setLngLat(marker.geometry.coordinates)
.setPopup(popup)
.addTo(map)
})
Question
How can I solve this issue, so that popup appears correctly above their respective Marker, even when multiple world copies are visible?
You can use Mapbox's solution to this: https://docs.mapbox.com/mapbox-gl-js/example/popup-on-click/
// When a click event occurs on a feature in the places layer, open a popup at the
// location of the feature, with description HTML from its properties.
map.on('click', 'places', function(e) {
var coordinates = e.features[0].geometry.coordinates.slice();
var description = e.features[0].properties.description;
// Ensure that if the map is zoomed out such that multiple
// copies of the feature are visible, the popup appears
// over the copy being pointed to.
while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {
coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;
}
new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML(description)
.addTo(map);
});
// ...
}

Google Maps JS API - extend a circle

The User is allowed to draw a circle on the map, and then you can add markers - if the Marker is outside of the circle, then the circle should be enlarged to take the marker inside.
I could not find anything in the documentation:
Is there a possibility like: UserCircle.extend(event.overlay.location);
While event overlay is the marker.
I also tried to make this function on my own - but I get stuck on the radius.
Which unit is it? How can I convert it into degrees?
Any Ideas?
Thats what i made now...
function extendRegion(marker, drawing){
marker_lat = marker.getPosition().lat();
marker_lng = marker.getPosition().lng();
if(drawing.type == google.maps.drawing.OverlayType.CIRCLE){
var path = [marker.getPosition(), drawing.overlay.getCenter()];
var length = google.maps.geometry.spherical.computeDistanceBetween(path[0], path[1]);
if(length>drawing.overlay.getRadius()){
consloe.log('The Marker was outside the drawing, it is extended now.');
drawing.overlay.setRadius(length);
}
}
else if(drawing.type == google.maps.drawing.OverlayType.RECTANGLE){
ne_lat = drawing.overlay.getBounds().getNorthEast().lat();
ne_lng = drawing.overlay.getBounds().getNorthEast().lng();
sw_lat = drawing.overlay.getBounds().getSouthWest().lat();
sw_lng = drawing.overlay.getBounds().getSouthWest().lng();
var error=0;
if(marker_lat<sw_lat){
sw_lat = marker_lat;
error=1;
}
if(marker_lat>ne_lat){
ne_lat = marker_lat;
error=1;
}
if(marker_lng<sw_lng){
sw_lng = marker_lng;
error=1;
}
if(marker_lng>ne_lng){
ne_lng = marker_lng;
error=1;
}
if(error) {
console.log('The Marker was outside the drawing, it is extended now.');
drawing.overlay.setBounds({north: ne_lat, south: sw_lat, east: ne_lng, west: sw_lng});
}
}
}

Activation/deactivation of OpenLayers.Controls.DrawFeature fails

When I break on the first line in the call back function and do this
this.active --> true
this.deactivate() --> true
this.activate() --> true
this.deactivate() --> false
HOW CAN THIS HAPPEN?
Let me explain where this occurs.
This map has two layers and each layer has an EditingToolbar associated with it.
When the user pressed polygon draw (in the EditingToolbar), its being draw on the "polygon_layer".
When the user presses line draw or point draw the layer is switched and the user now draws on the "vectors" layer.
When I switch layers, I need to activate the correct button in the EditingToolbar, example:
The user draws polygons in the "polygon_layer" and now he want to draw lines. He presses draw lines button (in the EditingToolbar associated with the polygon_layer).
I switch layers and activate draw lines button on the EditingToolbar for that layer.
After a while, the user now wants to draw polygons again, so i deactivate all buttons in this layer, switch layers and activate the draw polygon button in the
EditingToolbar for the polygon_layer. and so on.
Now when I do this enough times (3 switches) I notice that the buttons don't get deactivate anymore.
So I tried to debug and got this totally unexpected error described above ( at the very top).
Please tell me what am I doing wrong.
I've appended my code and I'll put ERROR HERE where this occurs. This code is ready to run.
You can use the HTML code below, just change the reference to the JavaScript file that I've provided ( I've provided the contents of the JavaScript file)
JS FILE:
var map;
var editing_toolbar_polygon=null;
var editing_toolbar_vector=null;
var drag_control=null;
var vectors;
var polygon_layer=null;
var epsg900913 = new OpenLayers.Projection('EPSG:900913');
var epsg4326 = new OpenLayers.Projection('EPSG:4326');
//var epsg900913 = new OpenLayers.Projection('EPSG:900913');
// var epsg4326 = new OpenLayers.Projection('EPSG:4326');
var line_control;
var polygon_control;
var renderer;
function initialize() {
line_control, renderer=OpenLayers.Util.getParameters(window.location.href).renderer;
renderer= (renderer) ? [renderer] : OpenLayers.Layer.Vector.prototype.renderers;
// Create the map object
map = new OpenLayers.Map('map');
//Create a Google layer
var gmap = new OpenLayers.Layer.Google(
"Google Streets", // the default
{
numZoomLevels: 20,
projection: new OpenLayers.Projection("EPSG:900913")
}
);
var wms = new OpenLayers.Layer.WMS( "OpenLayers WMS",
"http://vmap0.tiles.osgeo.org/wms/vmap0?", {layers: 'basic',
projection: new OpenLayers.Projection("EPSG:4326")});
var mystyle=new OpenLayers.StyleMap({
"default": new OpenLayers.Style({
fillColor: "#66ccff",
strokeColor: "#3399ff",
graphicZIndex: 2,
strokeWidth: 5,
}),
"temporary": new OpenLayers.Style({
fillColor:"#3399ff",
strokeColor: "#3399ff",
strokeWidth:5,
pointRadius:10
})
});
polygon_layer=new OpenLayers.Layer.Vector(
"Polygon Layer",
{
//renderers:renderer,
}
);
vectors= new OpenLayers.Layer.Vector(
"Vector Layer",
{
//renderers:renderer,
}
);
editing_toolbar_polygon=new OpenLayers.Control.EditingToolbar(polygon_layer);
editing_toolbar_vector=new OpenLayers.Control.EditingToolbar(vectors);
map.addLayers([gmap,wms,vectors,polygon_layer]);
map.addControl(new OpenLayers.Control.LayerSwitcher());
//map.addControl(new OpenLayers.Control.MousePosition());
map.addControl(editing_toolbar_polygon);
map.addControl(editing_toolbar_vector);
editing_toolbar_vector.deactivate();
//for the drag control to work you need to activate it
drag_control=new OpenLayers.Control.DragFeature(vectors);
map.addControl(drag_control);
find_control(editing_toolbar_polygon.getControlsByClass(new RegExp(".*DrawFeature")),"Point").events.register("activate",null,function(e){
//ERROR HERE
this.deactivate();
var picked_button=find_same_control(editing_toolbar_vector.controls,e.object);
change_layer(polygon_layer,vectors);
change_control(editing_toolbar_polygon,editing_toolbar_vector);
picked_button.activate();
});
find_control(editing_toolbar_polygon.getControlsByClass(new RegExp(".*DrawFeature")),"Path").events.register("activate",null,function(e){
//ERROR HERE
this.deactivate();
var picked_button=find_same_control(editing_toolbar_vector.controls,e.object);
change_layer(polygon_layer,vectors);
change_control(editing_toolbar_polygon,editing_toolbar_vector);
picked_button.activate();
});
find_control(editing_toolbar_vector.getControlsByClass(new RegExp(".*DrawFeature")),"Polygon").events.register("activate",null,function(e){
//ERROR HERE
this.deactivate();
var picked_button=find_same_control(editing_toolbar_polygon.controls,e.object);
change_layer(vectors,polygon_layer);
change_control(editing_toolbar_vector,editing_toolbar_polygon);
picked_button.activate();
});
polygon_layer.events.register("beforefeatureadded",null,function(e){
polygon_layer.removeAllFeatures();
});
// line_control=new OpenLayers.Control.DrawFeature(vectors,OpenLayers.Handler.Path);
//polygon_control=new OpenLayers.Control.DrawFeature(vectors,OpenLayers.Handler.RegularPolygon);
//map.addControl(line_control);
//line_control.activate();
//map.addControl(polygon_control);
//polygon_control.activate();
// Zoom to Vancouver, BC
map.setCenter(new OpenLayers.LonLat(-123.12, 49.28).transform(epsg4326, epsg900913), 13);
}
function change_layer(current_layer,next_layer){
current_layer.setVisibility(false);
next_layer.setVisibility(true);
}
function change_control(current_control,next_control){
current_control.deactivate();
map.addControl(next_control);
}
//use this when you want to find a specific control type:
// DrawFeature cntrol has many types, Line, Polygon, Point.
// So what you do is pass an array of DrawFeature controls and a type(string), lets say "Point",
// then this function will return a DrawFeature thats specifically for drawing points
function find_control(controls,type){
var control;
for(var x in controls){
if(controls[x].displayClass.search(new RegExp(type+"$"))>0){
return controls[x];
}
}
return -1;
}
I just tried with a simple test.
When you desactivate a controller, you just remove the events of the controller.
So for OpenLayers.Control.LayerSwitcher, desactivate this controller is useless because nothing change and you can still choose a layer.
I think the best way is to remove the controller for your polygon and to add that for the lines.
When you select the "polygon_layer" :
map.addControl(editing_toolbar_polygon);
map.removeControl(editing_toolbar_vector);
When you select the "vectors" :
map.addControl(editing_toolbar_vector);
map.removeControl(editing_toolbar_polygon);

How can I transform a custom overlay in Google Maps to account for projection?

I'm developing an application that uses Google Maps API v3. I want to make custom overlays that are squares exactly 1000km on each side (each overlay is actually a 1000x1000 transparent png, with each pixel representing 1 square km). My current implementation is this:
function PngOverlay(map,loc) {
this._png = null;
this._location = loc; //lat,lng of the square's center
this.setMap(map);
}
PngOverlay.prototype = new google.maps.OverlayView();
PngOverlay.prototype.onAdd = function() {
this._png = new Element('image',{ //using mootools
'src': pngForLoc(this._location),
'styles': {
'width': 1000,
'height': 1000,
'position': 'absolute'
}
});
var panes = this.getPanes();
panes.overlayLayer.appendChild(this._png);
}
PngOverlay.prototype.onRemove = function() {
this._png.parentNode.removeChild(this._png);
this._png = null;
}
PngOverlay.prototype.draw = function() {
var dp = this.getProjection().fromLatLngToDivPixel(this._location);
var ps = this._png.getSize();
var t = dp.y - (ps.y / 2);
this._png.setStyle('top',t);
var l = dp.x - (ps.x / 2);
this._png.setStyle('left',l);
}
My question is this: what, if anything, do I need to do to account for Google Maps' projection? My limited understanding of Mercator is that it preserves horizontal distance but not vertical. How can I appropriately transform() my png to account for that?
There isn't really enough information, but you might find the open source proj4js library useful as this can perform various projection transformations, eg. To Google's Spherical Mercator projection system.

Categories