Embedding an OpenLayers slippy map into Sphinx reStructuredText page - leaflet

I want to embed a slippy map into my sphinx page.
I'm trying this simple example: https://wiki.openstreetmap.org/wiki/OpenLayers_Marker_Example
So my rst document is:
.. raw:: html
<body>
<div id="mapdiv"></div>
<script src="http://www.openlayers.org/api/OpenLayers.js"></script>
<script>
map = new OpenLayers.Map("mapdiv");
map.addLayer(new OpenLayers.Layer.OSM());
var lonLat = new OpenLayers.LonLat( -0.1279688 ,51.5077286 )
.transform(
new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
map.getProjectionObject() // to Spherical Mercator Projection
);
var zoom=16;
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(lonLat));
map.setCenter (lonLat, zoom);
</script>
</body>
But nothing appears on the page.
I have tried and failed trying to use other javascript mapping api's such as leaflet but with no luck. I'm new to using sphinx/reStructuredText so maybe there's something obivous I am missing?

<body> already exists on your page, so you need to remove it from your rst.
You also need to specify height and width for the mapdiv element, for instance, something like this:
.. raw:: html
<div id="mapdiv" style="height: 200px; width: 100%"></div>
<script src="http://www.openlayers.org/api/OpenLayers.js"></script>
<script>
map = new OpenLayers.Map("mapdiv");
map.addLayer(new OpenLayers.Layer.OSM());
var lonLat = new OpenLayers.LonLat( -0.1279688 ,51.5077286 )
.transform(
new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
map.getProjectionObject() // to Spherical Mercator Projection
);
var zoom=16;
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(lonLat));
map.setCenter (lonLat, zoom);
</script>

#anatoly answer is correct but there was also another step.
I also had a Blocked loading mixed active content error found when checking the developer tools (thanks #giacomo for pointing me towards this), leading to this answer thread: Why am I suddenly getting a "Blocked loading mixed active content" issue in Firefox? which tells me that the cause is http protocol not being secure. The protocol can be removed altogether.
So the final code is:
.. raw:: html
<div id="mapdiv" style="height: 200px; width: 100%"></div>
<script src="//openlayers.org/api/OpenLayers.js"></script>
<script>
map = new OpenLayers.Map("mapdiv");
map.addLayer(new OpenLayers.Layer.OSM());
var lonLat = new OpenLayers.LonLat( -0.1279688 ,51.5077286 )
.transform(
new OpenLayers.Projection("EPSG:4326"), // transform from WGS 1984
map.getProjectionObject() // to Spherical Mercator Projection
);
var zoom=16;
var markers = new OpenLayers.Layer.Markers( "Markers" );
map.addLayer(markers);
markers.addMarker(new OpenLayers.Marker(lonLat));
map.setCenter (lonLat, zoom);
</script>

Related

Leaflet lowest zoom level is still too high with L.CRS.Simple

Trying to retrieve part of a district, however for some reason cannot see the whole area, even if zoom level is at 0, where (supposedly) we should see the whole world.
I am using L.CRS.Simple because this uses the EPSG:3763 and cannot see that one on the CRS list. I am retrieving the data in JSON cause when tying with geoJSON, was not able to transform the 3D coordinates data into 2D planes ones.
const queryRegionText = "where=OBJECTID > 0"
const geoJsonURL2 = "https://sig.cm-figfoz.pt/arcgis/rest/services/Internet/MunisigWeb_DadosContexto/MapServer/2/query?f=json&returnGeometry=true&geometryType=esriGeometryPolyline&spatialRel=esriSpatialRelIntersects&outFields=*&outSR=3763&" + queryRegionText
var map = L.map('mapid', {
crs: L.CRS.Simple
}).setView([-58216.458338, 42768.347232], 0);
L.control.scale({ metric: true }).addTo(map);
fetch(geoJsonURL2).then(function (response) {
response.json().then(function (data) {
data.features.forEach(element => {
if (element.geometry.rings) {
element.geometry.rings.forEach(point => {
L.polyline(point, { color: 'red' }).addTo(map);
})
}
});
});
});
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
<html>
<head>
<title>Leaflet - testing</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js"></script>
</head>
<body>
<div id="mapid" style="width: 600px; height: 400px;"></div>
</body>
</html>
TL;DR: When creating the map, set the minimum zoom below zero. This should work:
var map = L.map('mapid', {
crs: L.CRS.Simple, minZoom: -6
}).setView([-57728, 55296], -6);
Explanation
Normally, Leaflet translates from a latitude/longitude coordinate system to screen pixels using an assumption that the world is 256 pixels high at Zoom level 0. At each higher Zoom Level, the number of pixels doubles (explained nicely in the Zoom levels tutorial). With this assumption, the options for the map default to {minZoom: 0, maxZoom: Infinity} (as you are not adding any Layer that sets these values to anything different).
When you use L.CRS.Simple, at Zoom level 0 it maps 1 coordinate unit to 1 screen pixel. Your data looks like it is about 18000 coordinate units tall, so it doesn't fit in your 400 pixel high map. To make it fit, we need each screen pixel to map to about 45 coordinate units. 2^5 is 32, and 2^6 is 64, so we need to zoom out between 5 and 6 times. Luckily, Leaflet accepts negative Zoom Levels, so setting zoom to -6 does the trick. But to make it work properly, you need to set {minZoom: -6}, so the map doesn't get stuck at zoom level 0. There's a good worked example in the Non-geographical Maps tutorial.
Using L.CRS.Simple should work for you, so long as the approximation holds that each latitude unit is the same length as each longitude unit (a square world). Since this isn't generally true in the real world, using the Simple projection will cause some distortion. If the distortion is significant for the features you are interested in, then you will need to look up how to use EPSG:3763 properly, using L.CRS and Proj4Leaflet, as suggested by #IvanSanchez.
So, after some reading on the proj4leaflet, come up with this code. Thanks in advance for the comments and the reply above.
const queryRegionText = "where=OBJECTID > 0"
const geoJsonURL2 = "https://sig.cm-figfoz.pt/arcgis/rest/services/Internet/MunisigWeb_DadosContexto/MapServer/2/query?f=geojson&returnGeometry=true&geometryType=esriGeometryPolyline&spatialRel=esriSpatialRelIntersects&outFields=*&outSR=3763&" + queryRegionText
const map = L.map('map', {
center: [40.14791, -8.87009],
zoom: 13
});
proj4.defs("EPSG:3763", "+proj=tmerc +lat_0=39.66825833333333 +lon_0=-8.133108333333334 +k=1 +x_0=0 +y_0=0 +ellps=GRS80 +units=m +no_defs");
fetch(geoJsonURL2).then(function (response) {
response.json().then(function (data) {
L.Proj.geoJson(data).addTo(map);
});
});
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
<head>
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" />
<link rel="stylesheet" href="main.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.7.1/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4js/2.7.4/proj4.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/proj4leaflet/1.0.2/proj4leaflet.js"></script>
</head>
<body class="">
<div id="map" style="width: 600px; height: 400px;"></div>
</div>
<script src="main.js"></script>
</body>

ArcGIS JavaScript API Popup Not Referencing REST Service Layer

The content in the popup created through the variable "popupCustom" is displaying string instead of referencing the specified field {IN_COUNTRY}. I followed the ArcGIS JS API Popup Tutorials, & can't see what my error is in failing to grab the attributes associated with that field. Here's the code -- any help is greatly appreciated!
*note: feature layer url within "Cyber_Areas" variable points to REST URL for referenced Feature Class.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
<title>Search widget with multiple sources - 4.6</title>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
</style>
<link rel="stylesheet" href="https://js.arcgis.com/4.6/esri/css/main.css">
<script src="https://js.arcgis.com/4.6/"></script>
<script>
require([
"esri/Map",
"esri/views/MapView",
"esri/widgets/BasemapToggle",
"esri/widgets/Legend",
"esri/layers/TileLayer",
"esri/layers/FeatureLayer",
"esri/widgets/Search",
"esri/widgets/LayerList",
"esri/PopupTemplate",
"dojo/on",
"dojo/domReady!"
], function(
Map,
MapView,
BasemapToggle,
Legend,
TileLayer,
FeatureLayer,
Search,
LayerList,
PopupTemplate,
on
) {
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
outFields: ["IN_COUNTRY"],
popupTemplate: popupCustom
});
var map = new Map({
basemap: "osm"
});
map.add(Cyber_Areas);
var view = new MapView({
container: "viewDiv",
map: map,
center: [-87.172865, 34.077613], // lon, lat
zoom: 16
});
var searchWidget = new Search({
view: view,
popupOpenOnSelect: false
});
view.ui.add(searchWidget, {
position: "top-left",
index: 0
});
var popupCustom = searchWidget.on('select-result', function(evt){
//console.info(evt);
view.popup.open({
location: evt.result.feature.geometry, // location of the click on the view
title: "Service Availability:", // title displayed in the popup
content: "<p><b>{IN_COUNTRY}"
});
});
});
</script>
</head>
<body>
<div id="viewDiv"></div>
</body>
</html>
From your code you are mixing the popup template value with when to display it. And those are two different things.
First, you are not setting correctly the popup template of the layer. It should be a PopupTemplate.
It seems to me that in you code the layer definition should be something like this,
var Cyber_Areas = new FeatureLayer({
url: "*inserturl*",
popupTemplate: {
outFields: ["IN_COUNTRY"],
title: "Service Availability:",
content: "<p><b>{IN_COUNTRY}</b></p>"
}
});
Now if you don't want the default behavior of the popup (left click on a feature), you cant disable it like this,
view.popup.autoOpenEnabled = false; // <- disable view popup auto open
And then you can open it wherever you want like this,
view.popup.open({ // <- open popup
location: evt.result.feature.geometry, // <- use map point of the event result
fetchFeatures: true // <- fetch the selected features (if any)
});
You have to understand that the fields you use in the content of the popup template are related to the layer. That is why i set in the popup of the view to fetch the results.

How to go to extent programmatically?

I am new to Openlayers 5 and I want to move the map to a specific extent programmatically.
I tried
var bbox = [485319.36436093575, 5749497.169086075, 498451.8156390643, 5758869.310913925];
map.getView().fit(bbox, {size: map.getSize()});
but nothing happens.
What am I doing wrong? Is there another method I should use?
I didn't find anything in the API docs.
It's working here.
var raster = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
layers: [raster],
target: 'map',
view: new ol.View()
});
var bbox = [485319.36436093575, 5749497.169086075, 498451.8156390643, 5758869.310913925];
map.getView().fit(bbox, {size: map.getSize()});
<link rel="stylesheet" href="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/css/ol.css" type="text/css">
<script src="https://cdn.rawgit.com/openlayers/openlayers.github.io/master/en/v5.3.0/build/ol.js"></script>
<div id="map" class="map"></div>

Leaflet: pan map to new set of coordinates at new zoom-level on user action

I have a web app with a list of GPS-check-ins from field agents.
I would like to pan/zoom the current map/view when a check-in item from the list is clicked.
I have setup the leaflet map and click event like so:
function checkin_clicked(dt, mobile, address, lat, lon) {
console.log(dt, mobile, address, lat, lon);
var html = '<div style="width: 300px;" class="message"><img class="message-avatar" src="media/profile-pics/{mobile}.jpg" alt=""> <a class="message-author" href="#"> {name} </a> <span class="message-date"> {date} </span> <span class="message-content"> {address} </span></div>'
html = html.replace("{address}", address);
html = html.replace("{mobile}", mobile);
html = html.replace("{date}", dt);
html = html.replace("{name}", mobile);
map.setView([lat, lon], 14);
var popup = L.popup()
.setLatLng([lat,lon])
.setContent(html)
.openOn(map);
}
function setup_map() {
var map = L.map('map').setView([8.2, 6.95], 7);
L.tileLayer('http://localhost/tileserver/tiles.aspx?z={z}&x={x}&y={y}', {
minZoom: 7, maxZoom: 18,
}).addTo(map);
}
Please how do I perform this action.
I have seen this: How to change the map center in leaflet and https://stackoverflow.com/a/12735612/44080
But in my case I need to adjust the zoom level while panning.
Edit: I added map.setview in my click event, now i get this error:
"Map container is already initialized"

Getting mapQuest error: map.addControl is not a function

I've loaded the MapQuest JavaScript api module and am able to bring in the basic sample map at http://developer.mapquest.com/web/documentation/sdk/javascript/v7.0/basic-map but when I try to add map controls using their next example I get the JavaScript error:
map.addControl is not a function
I tried window.map.addControl but that generates the same error.
Does anyone know what might be wrong?
Thanks
You need to add the map control code within your MQA.EventUtil.observe
right after your call to window.map = new MQA.TileMap(options);
Code:
<html>
<head>
<script src="http://www.mapquestapi.com/sdk/js/v7.0.s/mqa.toolkit.js?key=Kmjtd%7Cluua2qu7n9%2C7a%3Do5-lzbgq"></script>
<script type="text/javascript">
MQA.EventUtil.observe(window, 'load', function() {
/*Create an object for options*/
var options={
elt:document.getElementById('map'), /*ID of element on the page where you want the map added*/
zoom:10, /*initial zoom level of map*/
latLng:{lat:39.743943, lng:-105.020089}, /*center of map in latitude/longitude*/
mtype:'map' /*map type (map)*/
};
/*Construct an instance of MQA.TileMap with the options object*/
window.map = new MQA.TileMap(options);
MQA.withModule('largezoom','traffictoggle','viewoptions','mousewheel', function() {
map.addControl(
new MQA.LargeZoom(),
new MQA.MapCornerPlacement(MQA.MapCorner.TOP_LEFT, new MQA.Size(5,5))
);
map.addControl(new MQA.TrafficToggle());
map.addControl(new MQA.ViewOptions());
map.enableMouseWheelZoom();
});
});
</script>
</head>
<body>
<div id='map' style='width:750px; height:280px;'></div>
</body>
</html>
try like this map.current.addControl
Make sure you add the module for the control before adding the control to the map.