I am working on an open layers map with different vector layers. I would like to add the function, that a certain vector layer is only displayed at a specific zoom value, for example at zoom >= 18. I tried it with the function minZoom and minScale / maxScale, but it didn't work. The vector layer I would like to display at zoom level >= 18 is called "dach layer". I also tried to solve it with an if function:
// dach layer anzeigen versuch
const dach = document.getElementById('dach');
dach.addEventListener('click', function (event) {
var checkBox = document.getElementById("dach");
if (checkBox.checked == true) {
if (map.getZoom() > 17) {
dachLayer.setMap(map);
//hitzeLayer.setVisible(true);
}
} else {
//hitzeLayer.setVisible(false);
dachLayer.setMap(undefined);
}
});
This is the code I use:
import 'ol/ol.css';
import Map from 'ol/Map';
import View from 'ol/View';
import TileLayer from 'ol/layer/Tile';
import Stamen from 'ol/source/Stamen';
import VectorLayer from 'ol/layer/Vector';
import Vector from 'ol/source/Vector';
import GeoJSON from 'ol/format/GeoJSON';
import Style from 'ol/style/Style';
import Circle from 'ol/style/Circle';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import Overlay from 'ol/Overlay';
import {
fromLonLat,
toLonLat
} from 'ol/proj';
import sync from 'ol-hashed';
import OSM from 'ol/source/OSM';
import Feature from 'ol/Feature';
import {
circular
} from 'ol/geom/Polygon';
import Point from 'ol/geom/Point';
import Control from 'ol/control/Control';
import * as olProj from 'ol/proj';
import XYZ from 'ol/source/XYZ';
// define the map
const map = new Map({
target: 'map',
view: new View({
center: fromLonLat([16.37, 48.2]),
zoom: 13
})
});
sync(map);
//Adresssuche
const searchResultSource = new Vector();
const searchResultLayer = new VectorLayer({
source: searchResultSource
});
searchResultLayer.setStyle(new Style({
image: new Circle({
fill: new Fill({
color: 'rgba(0, 128, 0, 1)'
}),
stroke: new Stroke({
color: '#000000',
width: 1.25
}),
radius: 15
})
}));
var element = document.getElementById('search');
element.addEventListener('keydown', listenerFunction);
function listenerFunction(event) {
console.log(event);
console.log(event.keyCode);
if (event.keyCode === 13) {
const xhr = new XMLHttpRequest;
xhr.open('GET', 'https://photon.komoot.de/api/?q=' + element.value + '&limit=3');
xhr.onload = function () {
const json = JSON.parse(xhr.responseText);
const geoJsonReader = new GeoJSON({
featureProjection: 'EPSG:3857'
});
searchResultSource.clear();
const features = geoJsonReader.readFeatures(json);
console.log(features);
searchResultSource.addFeatures(features);
if (!searchResultSource.isEmpty()) {
map.getView().fit(searchResultSource.getExtent(), {
maxZoom: 18,
duration: 500
});
}
};
xhr.send();
}
}
//OpenStreetMap
const OSMbaseLayer = new TileLayer({
type: 'base',
source: new OSM()
});
// Statellit
const satellitLayer = new TileLayer({
source: new XYZ({
attributions: ['Powered by Esri', 'Source: Esri, DigitalGlobe, GeoEye, Earthstar Geographics, CNES/Airbus DS, USDA, USGS, AeroGRID, IGN, and the GIS User Community'],
attributionsCollapsible: false,
url: 'https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
maxZoom: 30
})
});
//shape
const dachLayer = new VectorLayer({
source: new Vector({
name: 'dach',
url: 'data/dach.geojson',
format: new GeoJSON()
})
});
dachLayer.setStyle(new Style({
fill: new Fill({
color: 'brown'
}),
stroke: new Stroke({
color: 'brown',
width: 0.25
}),
}));
// Layer hinzufügen
map.addLayer(OSMbaseLayer);
map.addLayer(searchResultLayer);
dachLayer.setZIndex(15);
// dach layer anzeigen
const dach = document.getElementById('dach');
dach.addEventListener('click', function (event) {
var checkBox = document.getElementById("dach");
if (checkBox.checked == true) {
dachLayer.setMap(map);
//hitzeLayer.setVisible(true);
} else {
//hitzeLayer.setVisible(false);
dachLayer.setMap(undefined);
}
});
// Get the OSMbase Base-Button
const OSMbase = document.getElementById('OSMbase');
OSMbase.addEventListener('click', function (event) {
//contr.style.color = 'ffffff';
//Andere Layer entfernen
map.removeLayer(satellitLayer);
map.removeLayer(searchResultLayer);
//OSM Layer hinzufügen
map.addLayer(OSMbaseLayer);
map.addLayer(searchResultLayer);
});
// Get the satellit Base-Button
const satellit = document.getElementById('satellit');
satellit.addEventListener('click', function (event) {
//Andere Layer entfernen
map.removeLayer(OSMbaseLayer);
map.removeLayer(searchResultLayer);
//Satelliten Layer hinzufügen
map.addLayer(satellitLayer);
map.addLayer(searchResultLayer);
});
The getZoom() method is derived from the View module:
https://openlayers.org/en/latest/apidoc/module-ol_View-View.html#getZoom
The following works:
dach.addEventListener('click', function (event) {
var checkBox = document.getElementById("dach");
if (checkBox.checked == true) {
if (map.getview().getZoom() > 17) {
dachLayer.setMap(map);
//hitzeLayer.setVisible(true);
}
} else {
//hitzeLayer.setVisible(false);
dachLayer.setMap(undefined);
}
});
At the same time you might want to look into the 'moveend' event on the map to listen for zoom changes instead of click:
map.on('moveend', () => {
// do kewl things
}
Related
How can I fix the camera rotation on mobile? I've tried to handle the rotation event and override the handle gesture by referring to this link: https://www.keanw.com/2017/04/fixing-pinch-zoom-in-forge-viewer-applications.html
I've fixed the rotation but I can't pinch-to-zoom to the position I touched.
Please review this article. Here is the code snippet:
let viewer = null;
function onGestureEnd() {
var hitTest = viewer.clientToWorld(window.innerWidth/2, window.innerHeight/2, true);
viewer.navigation.setPivotPoint(hitTest.point);
}
function loadModel(urn) {
const options = {
env: 'AutodeskProduction',
accessToken: _adsk.token.access_token,
};
Autodesk.Viewing.Initializer(options, () => {
const div = document.getElementById('forgeViewer');
viewer = new Autodesk.Viewing.Private.GuiViewer3D(div);
viewer.start();
Autodesk.Viewing.Document.load(`urn:${urn}`, (doc) => {
var viewables = doc.getRoot().getDefaultGeometry();
viewer.loadDocumentNode(doc, viewables).then( onLoadFinished );
});
});
function onLoadFinished() {
document.addEventListener('touchend', onGestureEnd);
}
}
I have an existing web service that handles user input of a photo (as well as some other number and text data). The photo is captured via the input tag:
<input type="file" id="image-input-solo" accept="image/*" capture="capture" />
In the javascript I grab the image via the Files API like so:
$('#image-input-solo').on('change', function() {
window.__file = this.files[0];
});
// which gives me a File Object like this:
File {
lastModified: 1507749812264
lastModifiedDate: Wed Oct 11 2017 12:23:32 GMT-0700 (PDT) {}
name: "image000987u.jpg"
size: 441738
type: "image/jpeg"
webkitRelativePath: ""
__proto__: File
}
So in my native app context (using Cordova and the Camera Plugin) I am successfully grabbing the photo file:
//HTML
<div id="button-drink-it-now" ng-click="nativeCamera();">
// JAVASCRIPT
$scope.nativeCamera = function() {
if (!navigator.camera) {
alert("Camera API not supported", "Error");
return;
}
var options = { quality: 100,
destinationType: Camera.DestinationType.FILE_URI,
sourceType: 1, // 0:Photo Library, 1=Camera, 2=Saved Album
encodingType: 0 // 0=JPG 1=PNG
};
navigator.camera.getPicture(
function(imgData) {
var fd = new FormData();
var reader;
var imgBlob;
window.resolveLocalFileSystemURL(imgData, function(fileEntry) {
fileEntry.file(function(file) {
reader = new FileReader();
reader.onloadend = function(e) {
imgBlob = new Blob([ this.result ], { type: "image/jpeg" } );
fd.append('attachment', imgBlob);
window.__file = imgBlob; // MY ATTEMPT TO GET THE IMAGE IN THE CORRECT WAY
};
reader.readAsArrayBuffer(file);
}, function(e){
console.log('error with photo file');
});
}, function(e){
console.log('error with photo file');
});
},
function() {
alert('Error taking picture', 'Error');
},
options);
};
// THE OBJECT I GET FORM THE imgBlob:
Blob {
size: 6268043
type: "image/jpeg"
}
My question is, how can I get the photo file from the native camera and format it into the same File Object as I get from the HTML input, this.files[0] so I can continue using my existing web service to store the photo?
Turns out it was a simple fix:
navigator.camera.getPicture(
function(imgData) {
var fd = new FormData();
var reader;
var imgBlob;
window.resolveLocalFileSystemURL(imgData, function(fileEntry) {
fileEntry.file(function(file) {
reader = new FileReader();
reader.onloadend = function(e) {
imgBlob = new Blob([ this.result ], { type: "image/jpeg" } );
window.__file = imgBlob; // PLACE THE FILE ASSIGNMENT HERE AFTER THE READER HAS INGESTED THE FILE BYTES
};
reader.readAsArrayBuffer(file);
// window.__file = imgBlob; // FILE ASSIGNMENT USED TO BE HERE
}, function(e){
console.log('error with photo file');
});
}, function(e){
console.log('error with photo file');
});
},
function() {
alert('Error taking picture', 'Error');
},
options);
};
I'm using dburles:google-maps in my Meteor app. I have a collection called recorridos, where each document has a two arrays (ida and vuelta) of objects {lat: xxx, lng: yyy}. I'm retrieving one doc from the collection, and I'm creating two PolyLine's and setting them to the instance of the map, with a switch to alternate between ida and vuelta.
It works fine, but sometimes, when I reload the page I get the following error:
Exception from Tracker recompute function:
TypeError: Cannot read property 'instance' of undefined
at Blaze.View.<anonymous> (recorrido-mapa.js:28)
at blaze.js?hash=f33d3df…:1934
at Function.Template._withTemplateInstanceFunc (blaze.js?hash=f33d3df…:3744)
at blaze.js?hash=f33d3df…:1932
at Object.Blaze._withCurrentView (blaze.js?hash=f33d3df…:2271)
at viewAutorun (blaze.js?hash=f33d3df…:1931)
at Tracker.Computation._compute (tracker.js?hash=9f8a0ce…:339)
at Tracker.Computation._recompute (tracker.js?hash=9f8a0ce…:358)
at Object.Tracker._runFlush (tracker.js?hash=9f8a0ce…:523)
at onGlobalMessage (meteor.js?hash=27829e9…:401)
This is the code:
recorridos-mapa.js
// Packages
import { Template } from 'meteor/templating';
import { ReactiveVar } from 'meteor/reactive-var';
import { GoogleMaps } from 'meteor/dburles:google-maps';
import './recorrido-mapa.html';
// Components
import '../../components/map/map.js';
import '../../components/linea-card/linea-card.js';
// Methods
import { getRecorrido } from '../../../api/recorridos/methods.js';
var idaPath, vueltaPath, map, IdaStartPoint, idaEndPoint, startMarker, endMarker;
Template.Recorrido_mapa.onCreated(function() {
var self = this;
self.autorun(() => {
var subscription = this.subscribe('recorridos');
console.log(subscription.ready());
if(subscription.ready()) {
// Obtenemos la linea de los queryParams y llamamos a metodo para obtener recorrido
linea = parseInt(FlowRouter.getQueryParam("linea"));
res = getRecorrido.call(linea);
// Si recibimos datos del metodo, instanciamos marcadores y polilineas para mapa
map = GoogleMaps.maps.colesMap.instance;
idaStartPoint = res.ida[0];
idaEndPoint = res.ida[res.ida.length - 1];
vueltaStartPoint = res.vuelta[res.vuelta.length - 1];
vueltaEndPoint = res.ida[0];
// Marker de start
startMarker = new google.maps.Marker({
map: map,
animation: google.maps.Animation.DROP,
label: "I"
});
startMarker.setMap(map);
//Marker de end
endMarker = new google.maps.Marker({
map: map,
animation: google.maps.Animation.DROP,
label: "F"
});
endMarker.setMap(map);
// Polilineas de recorrido
idaPath = new google.maps.Polyline({
path: res.ida,
geodesic: true,
strokeColor: '#26a69a',
strokeOpacity: 1.0,
strokeWeight: 3
});
vueltaPath = new google.maps.Polyline({
path: res.vuelta,
geodesic: true,
strokeColor: '#9d1322',
strokeOpacity: 1.0,
strokeWeight: 3
});
setIdaVuelta(idaStartPoint, idaEndPoint, idaPath, vueltaPath);
}
});
});
Template.Recorrido_mapa.events({
'change #switch-ida-vuelta': function(event) {
let checked = event.target.checked;
if(!checked)
setIdaVuelta(idaStartPoint, idaEndPoint, idaPath, vueltaPath);
else
setIdaVuelta(vueltaStartPoint, vueltaEndPoint, vueltaPath, idaPath);
}
});
function setIdaVuelta(startPoint, endPoint, pathShow, pathHide) {
startMarker.setPosition(startPoint);
endMarker.setPosition(endPoint);
pathShow.setMap(map);
pathHide.setMap(null);
}
recorrido.js
// Main Template
import './recorridos.html'
// Componentes
import '../../components/linea-card/linea-card.js'
// Methods
import { getLineas } from '../../../api/lineas/methods.js'
// Helpers
Template.Recorridos_page.helpers({
lineas() {
res = getLineas.call().fetch()
return res
}
})
Template.Linea_card.events({
"click .list": function(event, template) {
let l = template.data.lineas
FlowRouter.go('/recorridos/mapa?linea=' + l.numero)
}
})
Template.Recorridos_page.onCreated(function() {
this.autorun(() => {
this.subscribe('lineas')
})
})
And this is the markup:
recorridos.html
<template name="Recorridos_page">
{{> Navbar main_title="Recorridos"}}
<div style="padding-top: 60px; padding-bottom: 20px;">
{{#each l in lineas}}
{{> Linea_card notUnique=true lineas=l}}
{{/each}}
</div>
</template>
recorridos-mapa.html
<template name="Recorrido_mapa">
{{> Map}}
{{> Linea_card notUnique=false}}
</template>
I really want to understand why this Exception is being trhow sometimes.
Thanks!
map = GoogleMaps.maps.colesMap.instance;
Is where the error is pointing. I would guess that the page is rendering before the GoogleMaps.maps has been initiated/loaded completely.
If you share the map setup code I can perhaps give you a specific example.
I have in my Phonegap app this JQM; I create a Google map and I load markers from json file.
When i launch page2, i see the first console.log (coordinates) and the last console.log (2222222) - The intermediate console.log that contains numberOfElements is displays only the first time. If I see the map and i return back the whole script isn't loaded.
Why?
$(document).on('pageshow', '#page2', function () {
var latnow = Number(localStorage.getItem("lat"));
var lngnow = Number(localStorage.getItem("lng"));
var coordinate = new google.maps.LatLng(latnow, lngnow);
console.log(latnow + ' ' + lngnow);
$('#map_canvas').gmap({
'center': coordinate,
'disableDefaultUI': true,
'zoom': 5,
'scrollwheel': false,
'panControl': false
});
$('#map_canvas').gmap().bind('init', function () {
var images = "img/icon.png";
var images2 = "img/icon2.png";
$.getJSON('http://www.site.com/app/json.php?lat=' + latnow + '&lat=' + lngnow + '', function (data) {
var myObject = data;
var numberOfElements = data.markers.length;
console.log(numberOfElements); // <- !!!!!!
if (numberOfElements == 0) {
alert("no result");
$.mobile.changePage("#home");
}
var myObject = JSON.stringify(myObject);
localStorage.setItem("json_near", myObject);
$('#map_canvas').gmap('addMarker', {
'position': coordinate,
'icon': images2,
'bounds': true
});
//marker da json
$.each(data.markers, function (i, marker) {
$('#map_canvas').gmap('addMarker', {
'position': new google.maps.LatLng(marker.latitude, marker.longitude),
'draggable': false,
'bounds': true,
'icon': images
}).click(function () {
$('#map_canvas').gmap('openInfoWindow', {
'content': marker.content,
'maxWidt': 200,
'maxHeight': 400,
'autoScroll': true
}, this);
});
});
});
});
map_element = document.getElementById("map_canvas");
var mapwidth = $(window).width();
var mapheight = $(window).height();
$("#map_canvas").height(mapheight);
$("#map_canvas").width(mapwidth);
google.maps.event.trigger(map_element, 'resize');
console.log("2222222");
});
I assume you are using this plugin - http://code.google.com/p/jquery-ui-map/ , right?
I think it is not a problem of jquery mobile. The pageshow event is called as you can see from those console.log messages.
Try to stick console.log("xxx") into the gmap init handler. I think this is the one which is NOT called at the second time.
Something like
$('#map_canvas').gmap().bind('init', function () {
console.log("xxxxxx");
var images = "img/icon.png";
var images2 = "img/icon2.png";
...
...
});
do you see xxxxxxx in the console every time you get in the page?
If not, try to find the way how to check whether the map was already initialized (second, third, fourth time, etc.) and then, call getJSON directly.
Is it possible to zoom in on cluster on click? I also don't know how to disable cluster popup. I read this question , but still have no idea how to do it.
Here is the code:
<html>
<script src="../ol/OpenLayers.js"></script>
<script>
var map, select;
var lat = 53.507;
var lon = 28.145;
var zoom = 7;
function init() {
map = new OpenLayers.Map("map",
{ maxExtent: new OpenLayers.Bounds(-20037508.34,-20037508.34,20037508.34,20037508.34),
numZoomLevels: 19,
maxResolution: 156543.0399,
units: 'm',
projection: new OpenLayers.Projection("EPSG:900913"),
displayProjection: new OpenLayers.Projection("EPSG:4326"),
controls: [
new OpenLayers.Control.Navigation(),
new OpenLayers.Control.PanZoomBar(),
new OpenLayers.Control.ScaleLine(),
new OpenLayers.Control.Permalink('permalink'),
new OpenLayers.Control.Attribution(),
new OpenLayers.Control.MousePosition()
] });
var osm = new OpenLayers.Layer.OSM("OpenStreetMap");
map.addLayer(osm);
var lonLat = new OpenLayers.LonLat(lon, lat).transform(map.displayProjection, map.projection);
if (!map.getCenter()) map.setCenter (lonLat, zoom);
var MyStyle = new OpenLayers.Style({
// 'cursor' : 'pointer',
fillColor : "#336699",
fillOpacity : 0.9,
fontColor: "#000080",
fontFamily: "sans-serif, Arial",
// fontWeight: "bold",
externalGraphic: "atm.png",
graphicWidth: 32,
graphicHeight: 37,
label: "${count}",
labelAlign: "ct",
fontSize: "15px",
});
var layer = new OpenLayers.Layer.Vector("Atm", {
protocol: new OpenLayers.Protocol.HTTP({
url: "atm.txt",
format: new OpenLayers.Format.Text({extractStyles: true}),
params: {
extractAttributes: false,
}
}),
styleMap: MyStyle, <!-- --------------------- style -->
projection: map.displayProjection,
strategies: [
new OpenLayers.Strategy.BBOX({ratio: 1, resFactor: 1.1}),
new OpenLayers.Strategy.Cluster({distance: 50, threshold: 3})
]
});
map.addLayer(layer);
// Interaction; not needed for initial display.
selectControl = new OpenLayers.Control.SelectFeature(layer);
map.addControl(selectControl);
selectControl.activate();
layer.events.on({
'featureselected': onFeatureSelect,
'featureunselected': onFeatureUnselect
});
}
// Needed only for interaction, not for the display.
function onPopupClose(evt) {
// 'this' is the popup.
var feature = this.feature;
if (feature.layer) { // The feature is not destroyed
selectControl.unselect(feature);
} else { // After "moveend" or "refresh" events on POIs layer all
// features have been destroyed by the Strategy.BBOX
this.destroy();
}
}
function onFeatureSelect(evt) {
feature = evt.feature;
popup = new OpenLayers.Popup.FramedCloud("featurePopup",
feature.geometry.getBounds().getCenterLonLat(),
new OpenLayers.Size(100,100),
"<h2>"+feature.attributes.title + "</h2>" +
feature.attributes.description,
null, true, onPopupClose);
feature.popup = popup;
popup.feature = feature;
map.addPopup(popup, true);
}
function onFeatureUnselect(evt) {
feature = evt.feature;
if (feature.popup) {
popup.feature = null;
map.removePopup(feature.popup);
feature.popup.destroy();
feature.popup = null;
}
}
</script>
</head>
<body onload="init()">
<div id="map"></div>
</body>
</html>
Thanks. Your post does not have much context to explain the code sections; please explain your scenario more clearly.
function onFeatureSelect(event) {
if(!event.feature.cluster) // if not cluster
{
// handle your popup code for the individual feature
}
else
{
// fetch the cluster's latlon and set the map center to it and call zoomin function
// which takes you to a one level zoom in and I hope this solves your purpose :)
map.setCenter(event.feature.geometry.getBounds().getCenterLonLat());
map.zoomIn();
}
}
Using the example code in the linked question I would iterate over all features in the cluster to create a BBX, and then zoom into that extent.
var cluster_bounds=new OpenLayers.Bounds();
event.feature.cluster.forEach(function(feature){
clouster_bounds.extend(feature.geometry);
})
map.zoomToExtent(cluster_bounds)
If you really don't know how to disable the popups then remove these functions:
function onFeatureSelect(evt) {
function onFeatureUnselect(evt) {
And replace it with:
function onFeatureSelect(event) {
var cluster_bounds=new OpenLayers.Bounds();
event.feature.cluster.forEach(function(feature){
cluster_bounds.extend(feature.geometry);
});
map.zoomToExtent(cluster_bounds);
}