Do you have any idea about implementing the draw functionality to react-leafletV3?
I have added an event listener but it is not working and reacting to anything.
useEffect(() => {
if (!map) return;
map.addEventListener("mousemove", function (event) {
setCoords({ lat: event.latlng.lat, lng: event.latlng.lng });
});
map.on("draw:drawstart", function (event) {
console.log("event here", event);
});
const drawControl = new L.Control.Draw({
position: "topleft",
});
drawControl.addTo(map);
const leftLayer = L.tileLayer(
layers[leftLayerIndex].url,
layerOptions
).addTo(map);
const rightLayer = L.tileLayer(
layers[rightLayerIndex].url,
layerOptions
).addTo(map);
const sbs = L.control.sideBySide(leftLayer, rightLayer).addTo(map);
setSideBySide(sbs);
}, [map]);
useEffect(() => {
if (sideBySide) {
sideBySide.setLeftLayers(
L.tileLayer(layers[leftLayerIndex].url, layerOptions).addTo(map)
);
}
}, [leftLayerIndex, rightLayerIndex, sideBySide]);
This is the code that worked for me. I added an event listener that listens to the created layer and after creating the layer I added the feature group layer. I have mention that this part is required as I have commented in the options object.
edit: {
featureGroup: editableLayers, //REQUIRED!!
}
useEffect(() => {
if (!map) return;
map.addEventListener("mousemove", function (event) {
setCoords({ lat: event.latlng.lat, lng: event.latlng.lng });
});
const editableLayers = new L.FeatureGroup();
map.addLayer(editableLayers);
const options = {
position: "topleft",
draw: {
polyline: false,
polygon: false,
rectangle: {
shapeOptions: {
clickable: false,
color: "dodgerblue",
},
},
},
edit: {
featureGroup: editableLayers, //REQUIRED!!
},
};
const drawControl = new L.Control.Draw(options);
map.addControl(drawControl);
map.on(L.Draw.Event.CREATED, function (e) {
var type = e.layerType,
layer = e.layer;
if (type === "marker") {
layer.bindPopup("A popup!");
}
editableLayers.addLayer(layer);
});
const leftLayer = L.tileLayer(
layers[leftLayerIndex].url,
layerOptions
).addTo(map);
const rightLayer = L.tileLayer(
layers[rightLayerIndex].url,
layerOptions
).addTo(map);
const sbs = L.control.sideBySide(leftLayer, rightLayer).addTo(map);
setSideBySide(sbs);
}, [map]);
Related
This is a custom control created for my previous question Custom font for currency signs
I have two span elements coming next to each other. They sit in the FormattedText. The FormattedText itself sits in the HBox.
I want popover fired when user mouses over/out from the hbox. Unfortunately, as I have 2 spans this fires separately when user hovers overs them (thus showing 2 separate popovers, when in fact it should be one). My assumption is that this causes because onmouseover/out is attached to both spans under the hood. Can I restrict these events to hbox only?
sap.ui.define([
'sap/ui/core/Control',
'sap/m/FormattedText',
'sap/m/HBox',
], function (Control, FormattedText, HBox) {
return Control.extend('drex.control.TherapyCosts', {
metadata: {
properties: {
rank: {
type: 'int',
defaultValue: 0
},
},
aggregations: {
_rankedTherapyCost: {type: 'sap.m.FormattedText', multiple: false, singularName: '_rankedTherapyCost'},
_popover: {type: 'sap.m.Popover', multiple: false, singularName: '_popover'},
_hbox: {type: 'sap.m.HBox', multiple: false}
}
},
init: function () {
Control.prototype.init.apply(this, arguments);
},
onBeforeRendering: function () {
const highlighedCurrency = this.getCurrency().repeat(this.getRank());
const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
const _popover = new sap.m.Popover({
showHeader: false,
placement: 'VerticalPreferredBottom',
content: new sap.m.Text({text: 'test'})
});
this.setAggregation('_popover', _popover);
const _formattedText = new FormattedText({
htmlText:
`<span class="currencyHighlighted">${highlighedCurrency}</span>` +
`<span class="currencyFaded">${fadedCurrency}</span>`
});
this.setAggregation('_rankedTherapyCost', _formattedText);
const _hbox = new HBox(
{ items: [this.getAggregation('_rankedTherapyCost')]})
.addEventDelegate({
onmouseover: () => {
this.getAggregation('_popover').openBy(this);
},
onmouseout: () => {
this.getAggregation('_popover').close()
}
});
this.setAggregation('_hbox', _hbox)
},
renderer: function (rm, oControl) {
const _hbox = oControl.getAggregation('_hbox');
rm.write('<div');
rm.writeControlData(oControl);
rm.write('>');
rm.renderControl(_hbox);
rm.write('</div>');
}
});
});
Here is the video of the issue
https://streamable.com/fjw408
The key here is the mouseout event is triggered when the mouse moves out of any child element of the element that is listening for the event. In this case, for example, moving from the emphasised text to the faded text, you get a mouseout event when moving off the emphasised text, which closes the popup, then a mouseover event on the faded text which opens it again.
Since you opening an already open popup doesn't do anything, you only need add a line on mouseout to inspect the element that you've gone to. If it is not a child of the current element, only then close the popover.
if (!this.getDomRef().contains(e.toElement)) {
popOver.close()
}
Building on D.Seah's answer, I've added this to the JSBin. I personally don't like using onBeforeRendering and onAfterRendering like this, so I've refactored a little to construct everything on init and simply change the controls on property change. This way, you're doing less on rendering for performance reasons.
sap.ui.define([
'sap/ui/core/Control',
'sap/m/FormattedText',
], function (Control, FormattedText) {
Control.extend('TherapyCosts', {
metadata: {
properties: {
rank: {
type: 'int',
defaultValue: 0
},
currency: {
type: "string",
defaultValue: "$"
}
},
aggregations: {
_highlighted: {type: 'sap.m.FormattedText', multiple: false},
_faded: {type: 'sap.m.FormattedText', multiple: false},
_popover: {type: 'sap.m.Popover', multiple: false, singularName: '_popover'}
}
},
init: function () {
Control.prototype.init.apply(this, arguments);
this.addStyleClass('therapy-cost');
const highlight = new FormattedText();
highlight.addStyleClass("currency-highlight");
this.setAggregation('_highlighted', highlight);
const faded = new FormattedText();
faded.addStyleClass("currency-faded");
this.setAggregation('_faded', faded);
const _popover = new sap.m.Popover({
showHeader: false,
placement: 'VerticalPreferredBottom',
content: new sap.m.Text({text: 'test'})
});
this.setAggregation('_popover', _popover);
},
_changeAggr: function () {
const highlighedCurrency = this.getCurrency().repeat(this.getRank());
const highlight = this.getAggregation("_highlighted");
highlight.setHtmlText(highlighedCurrency);
const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
const faded = this.getAggregation("_faded");
faded.setHtmlText(fadedCurrency);
},
setRank: function(rank) {
if (this.getProperty("rank") !== rank) {
this.setProperty("rank", rank);
this._changeAggr();
}
},
setCurrency: function(curr) {
if (this.getProperty("currency") !== curr) {
this.setProperty("currency", curr);
this._changeAggr();
}
},
renderer: function (rm, oControl) {
rm.write('<div');
rm.writeControlData(oControl);
rm.writeClasses(oControl);
rm.writeStyles(oControl);
rm.write('>');
rm.renderControl(oControl.getAggregation('_highlighted'));
rm.renderControl(oControl.getAggregation('_faded'));
rm.write('</div>');
},
onmouseover: function (e) {
const popOver = this.getAggregation('_popover');
popOver.openBy(this);
},
onmouseout: function (e) {
if (!this.getDomRef().contains(e.toElement)) {
const popOver = this.getAggregation('_popover');
popOver.close();
}
}
});
(new TherapyCosts({
rank: 5,
currency: "£"
})).placeAt('content')
});
.therapy-cost {
display: inline-flex;
flex-direction: row;
}
.therapy-cost .currency-highlighted {
color: black;
}
.therapy-cost .currency-faded {
color: lightgrey;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>JS Bin</title>
<script
src="https://openui5.hana.ondemand.com/resources/sap-ui-core.js"
id="sap-ui-bootstrap"
data-sap-ui-theme="sap_bluecrystal"
data-sap-ui-xx-bindingSyntax="complex"
data-sap-ui-libs="sap.m"></script>
</head>
<body class="sapUiBody sapUiSizeCompact">
<div id='content'></div>
</body>
</html>
https://jsbin.com/gukedamemu/3/edit?css,js,output
i think you can do without the hbox; by just having two formatted text or any sapui5 controls will be ok too.
sap.ui.define([
'sap/ui/core/Control',
'sap/m/FormattedText',
], function (Control, FormattedText) {
Control.extend('TherapyCosts', {
metadata: {
properties: {
rank: {
type: 'int',
defaultValue: 0
},
currency: {
type: "string",
defaultValue: "$"
}
},
aggregations: {
_highlighted: {type: 'sap.m.FormattedText', multiple: false},
_faded: {type: 'sap.m.FormattedText', multiple: false},
_popover: {type: 'sap.m.Popover', multiple: false, singularName: '_popover'}
}
},
init: function () {
Control.prototype.init.apply(this, arguments);
this.addStyleClass('therapy-cost')
},
onBeforeRendering: function () {
const highlighedCurrency = this.getCurrency().repeat(this.getRank());
const highlight = new FormattedText({
htmlText: highlighedCurrency
});
this.setAggregation('_highlighted', highlight);
const fadedCurrency = this.getCurrency().repeat(7 - this.getRank());
const faded = new FormattedText({
htmlText: fadedCurrency
});
this.setAggregation('_faded', faded);
const _popover = new sap.m.Popover({
showHeader: false,
placement: 'VerticalPreferredBottom',
content: new sap.m.Text({text: 'test'})
});
this.setAggregation('_popover', _popover);
},
renderer: function (rm, oControl) {
rm.write('<div');
rm.writeControlData(oControl);
rm.writeClasses(oControl);
rm.writeStyles(oControl);
rm.write('>');
rm.renderControl(oControl.getAggregation('_highlighted'));
rm.renderControl(oControl.getAggregation('_faded'));
rm.write('</div>');
},
onAfterRendering: function () {
const popOver = this.getAggregation('_popover');
const highlighted = this.getAggregation("_highlighted");
highlighted.$().addClass("currency-highlighted");
highlighted.$().hover(function() {
popOver.openBy(highlighted);
}, function() {
popOver.close();
});
const faded = this.getAggregation("_faded");
faded.$().addClass("currency-faded");
},
});
(new TherapyCosts({
rank: 5
})).placeAt('content')
});
https://jsbin.com/razebuq/edit?css,js,output
I am trying to add an event listener to an image layer with mapbox gl, but it doesn't seem to work, is it even possible with this type of layer? Here is a codepen demonstrating the problem:
https://codepen.io/anon/pen/drBdLm
var mapStyle = {
'version': 8,
'sources': {},
'layers': []
}
var map = new mapboxgl.Map({
container: 'map',
maxZoom: 5,
minZoom: 1,
zoom: 5,
center: [-75.789, 41.874],
style: mapStyle
})
map.on('load', function() {
map.addSource('firstim', {
type: 'image',
url: 'https://small-systems.org/dev/093-hvb/cms/site/assets/files/1104/6_umspannwerk_sellerstrasse.700x0.jpg',
coordinates: [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
})
map.addLayer({
id: 'images',
type: 'raster',
source: 'firstim',
paint: { 'raster-opacity': 1 }
})
map.on('click', function (e) {
var features = map.queryRenderedFeatures(e.point, { layers: ['images'] });
var clusterId = features[0];
// console.log(clusterId)
});
map.on('click', 'images', function (e) {
console.log(e)
});
})
I've tried adding a an event on the layer with map.on('click', layerID, function), but it does't work. Neither does using queryRenderedFeatures.
Thanks for any help.
This is not implemented yet https://github.com/mapbox/mapbox-gl-js/issues/1404.
As a workaround you'll need to listen to all click events and then do your own test to see if the click is within the bounds of your image.
so I'm gonna show two things,
1) This is the image
2) This is my code:
This is my code:
// center of the map
var center = [14.240861626831018, 121.12966240455648];
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osm = L.tileLayer(osmUrl,
{
maxZoom: 17,
minZoom:13
}),
map = new L.Map('map',
{
attributionControl: false,
center: new L.LatLng(14.240861626831018, 121.12966240455648), zoom: 13
}),
drawnItems = L.featureGroup().addTo(map);
L.control.layers({
'osm': osm.addTo(map),
"google": L.tileLayer('http://www.google.cn/maps/vt?lyrs=s#189&gl=cn&x={x}&y={y}&z={z}',
{
attribution: 'google',
maxZoom: 20,
minZoom:13
}),
"monolight": L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw',
{
attribution: 'google',
maxZoom: 20,
minZoom:13,
id: 'mapbox.light'
}),
"dark": L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png',
{
attribution: '© OpenStreetMap © CartoDB',
subdomains: 'abcd',
maxZoom: 20,
minZoom:13
})
}, { 'Enable markers': drawnItems },
{
position: 'bottomleft',
collapsed: true
}).addTo(map);
map.addControl(new L.Control.Draw({
position: 'bottomleft',
edit: {
edit:false,
remove: true,
featureGroup: drawnItems,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
},
// disable toolbar item by setting it to false
polygon: true,
polyline:true,
circle: false,
rectangle:false,
marker: true,
circlemarker: false
}
}));
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
map.addLayer(layer);
if (type === 'marker') {
layer.bindPopup('LatLng: ' + layer.getLatLng()).openPopup();
}
});
I was wondering if separating the markers using checkbox is possible?
I mean I want to make separate checkboxes for each marker. Like if I only want to show the lines, I can hide the markers and polygons. Sorry for the mess. Thanks!
I'm trying to add the Leaflet Draw library / control.
I can add a marker, and it persists (and stays visible). But when I add a line or polygon they aren't visible (persisted though if I inspect the .layers, also check the screenshot. The element seems to be there, but it just isn't visible).
Any guidance would be highly appreciated!
Edit: I'm using the following code to initialize the map:
var elem = angular.element('#datasetLeafletMap'); //using Angular
console.log('initting map', elem[0]);
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© OpenStreetMap contributors',
osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });
var map = new L.Map(elem[0], { layers: [osm], center: new L.LatLng(52.105289405897, 5.2629891004852425), zoom: 13 });
console.log('map ready');
var drawnItems = new L.FeatureGroup();
var coords = [new L.latLng(51.2, 4.5), new L.latLng(51.2, 4.6), new L.latLng(51.2, 4.9)];
var poly = new L.Polyline(coords, {
color: 'blue',
opacity: 1,
weight: 5
});
drawnItems.addLayer(poly);
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
draw: {
position: 'right',
polygon: {
title: 'Polygon',
allowIntersection: false,
drawError: {
color: '#b00b00',
timeout: 1000
},
shapeOptions: {
color: '#bada55'
},
showArea: true
},
polyline: {
metric: false
},
circle: {
shapeOptions: {
color: '#662d91'
}
}
},
edit: {
featureGroup: drawnItems
}
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var type = e.layerType,
layer = e.layer;
if (type === 'marker') {
layer.bindPopup('A popup!');
}
drawnItems.addLayer(layer);
console.log('adding layer', layer, drawnItems);
});
In short, I have a login panel, which will be shown when onClick event is true, but I get error Cannot read property 'dom' of null. When I click second time - the panel is shown, but some elements of it are not shown. When I click for 3th time, the other elements has show, an last, I ger error "Cannot call method 'bringToFront' of undefined".
Ext.onReady(function() {
Ext.QuickTips.init();
Ext.ns('Site.login');
var globalKeyMap = new Ext.KeyMap(document);
globalKeyMap.accessKey = function(key, handler, scope) {
var h = function(n, e) {
e.preventDefault();
handler.call(scope || this, n, e);
};
this.on(key, h, scope);
};
var trackEnter = function(tf, e) {
if (e.keyCode == 13) {
e.stopPropagation();
doLogin();
}
}
var doLogin = function() {
var f = loginPanel.getForm();
if (f.isValid()) {
f.doAction(new Hypo.form.Action.DwrSubmit(f, {
waitMsg: 'Authentication is in progress',
invoker: function(form, cb) {
Login.login(form.getValues(), cb);
},
success: function(fp, o) {
loginForm.hide();
Ext.getBody().addClass("x-body-masked");
var w = loginForm;
w.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));
w.mask.show();
if(o.result.data[0].authorities[0].authority=='ROLE_SUPERVISOR')
{
setTimeout('document.location = "AdminUsers.jsp"');
}
else if(o.result.data[0].authorities[0].authority=='ROLE_ADMIN')
{
setTimeout('document.location = "AdminUsers.jsp"');
}
else
{
setTimeout('document.location = "List.jsp"');
}
}
}));
}
}
var loginPanel = new Ext.FormPanel({
id: 'loginPanel',
labelWidth: 75, // label settings here cascade unless overridden
frame: false,
border: false,
bodyStyle:'padding:5px 5px 0',
width: 350,
defaults: {
width: 230
},
defaultType: 'textfield',
autoHeight: true,
items: [{
id: 'idxLogin',
fieldLabel: 'Login',
name: 'login',
allowBlank: false,
enableKeyEvents: true,
maxLength: 50,
listeners: {
'keypress': trackEnter
}
}, new Ext.form.TextField({
fieldLabel: 'Password',
name: 'password',
inputType: 'password',
enableKeyEvents: true,
maxLength: 50,
listeners: {
'keypress': trackEnter
}
})
]
});
var loginForm = new Ext.Window({
layout : 'fit',
width : 350,
closable : false,
modal: true,
plain : true,
title: 'User Authentication',
items: loginPanel,
autoHeight: true,
buttons: [{
text: 'OK',
handler: doLogin
},{
text: 'Cancel',
handler: function() {
loginForm.hide();
}
}],
listeners: {
'activate': function() {
setTimeout(function() {
Ext.getCmp("idxLogin").focus();
}, 100);
}
}
});
Site.login.window = loginForm;
});
Login
I tryed to add renderTo:'someDiv' and add to body a div with id "someDiv". But I received the same error message.
Please, help with that.