Extend polyline and handle mouse event - google-maps-api-3

I write JS app where I draw a lot of polylines using array of points, but in avery point I have some additional properties in this point (GPS data, speed etc).
I want to show these additional props onmouseover or onmouseclick event.
I have two ways:
use the standard polylines and event handler. But in this case I can't to determine additional properties for start point of this polyline cause I can't to save these props in polyline properties. There is one solution - save in array additional properties and try to find them by LatLng of first point of the polyline, but it's too slow I guess..
extend polyline and save additional properties in new Object, but I can't to extend mouse events :(
To extend polyline I use this code:
function myPolyline(prop, opts){
this.prop = prop;
this.Polyline = new google.maps.Polyline(opts);
}
myPolyline.prototype.setMap = function(map) {
return this.Polyline.setMap(map);
}
myPolyline.prototype.getPath = function() {
return this.Polyline.getPath();
}
myPolyline.prototype.addListener= function(prop) {
return this.Polyline.addListener();
}
myPolyline.prototype.getProp= function() {
return this.prop;
}
myPolyline.prototype.setProp= function(prop) {
return this.prop = prop;
}
and create new object in for loop (i - index of current point in array of points) like that:
var polyline_opts = {
path: line_points,
strokeColor: color,
geodesic: true,
strokeOpacity: 0.5,
strokeWeight: 4,
icons: [
{
icon: lineSymbol,
offset: '25px',
repeat: '50px'
}
],
map: map
};
var add_prop = {
id: i,
device_id: device_id
};
...
devices_properties[device_id].tracks[(i-1)] = new myPolyline(add_prop, polyline_opts);
Where:
line_points - array of points (just two points),
i - current point index
devices_properties[device_id].tracks - array of extended polylines (with add properties) by my device_id index
After that I set event handler like that:
var tmp = devices_properties[device_id].tracks[(i-1)];
google.maps.event.addListener(tmp.Polyline, 'click', function(e) {
...
console.log(tmp.prop.id);
...
}
But in this case I always get the same id in console..
When I use
google.maps.event.addListener(devices_properties[device_id].tracks[(i-1)].Polyline, 'click', function(e) {
...
console.log(???); // How to get parent of polyline fired the event?
...
}
I don't know how to get parent of polyline fired the event?

I answer my own question - It's done, I've just have some troubles with using "for" instead "$.each" :)
Before I use:
for ( i = 1; i < devices_properties[device_id].history_points.length; i++ ) {
...
create myPolyline
...
}
and it's doesn't work - created one event handle.
After:
$.each(devices_properties[device_id].history_points, function(i, tmp){
...
create myPolyline ()
...
}
and it works - create a lot of event handlers.
To handle event I use this:
google.maps.event.addListener(c_polyline.Polyline, 'mouseover', function(e) {
var prop = c_polyline.getProp();
...
console.log(prop.id, prop.device_id);
}

Related

How to listen to changes in the rotation of the camera in A-Frame?

Is there a way to add a listener on the current angle of view?
In other words, I'd like to trigger a function every time the user looks behind him.
The quickest way seems to be having a listener that checks the current head rotation and trust triggers the function if is within a certain range of degrees
Edit
The componentchange event is throttled. And it is more performant to not go through the event system for frequent updates. The camera rotation always changes every frame in VR so there is no need to think whether the camera has changed. So we read rotation every frame with component tick.
AFRAME.registerComponent('rotation-reader', {
tick: function () {
var rotation = this.el.getAttribute('rotation');
if (rotation.y < 180) {
// ...
}
}
});
// <a-camera rotation-reader>
Original
https://aframe.io/docs/0.2.0/core/entity.html#listening-for-component-changes
You can use the componentchanged event to listen to changes in the rotation:
document.querySelector('[camera]').addEventListener('componentchanged', function (evt) {
if (evt.name !== 'rotation') { return; }
if (evt.newData.y < 180) { // ... }
});
Or better as a component (this will trigger an event when rotation is certain amount):
AFRAME.registerComponent('trigger-on-look-behind', {
schema: {type: 'string'},
init: function () {
var eventName = this.data;
this.el.addEventListener('componentchanged', function (evt) {
if (evt.name !== 'rotation') { return; }
if (evt.newData.y < 180) {
this.emit(eventName);
}
});
}
});
And then:
<a-camera trigger-on-look-behind="looked-behind"></a-camera>

Openlayers 2.x : Does not refresh WFS vector layer after adding another vector layer

This is just a snippet of my code:
function removeFilter(a){
if (a === "100-Year Rainfall"){
bldg100yearCBR.filter = null;
bldg100yearCBR.refresh({
force: true
});
}
...
Above is my function to remove filter for WFS vector layer. It works if I won't add another layer. I am wondering why? Here's how I add my layer:
bldg100yearCBR = new OpenLayers.Layer.Vector("100-Year Rainfall Affected Buildings", {
strategies: [new OpenLayers.Strategy.Fixed()],
eventListeners: {
'loadend': function (evt) {
map.zoomToExtent(bldg100yearCBR.getDataExtent());
//map.getExtent().containsBounds(bldg100yearCBR, true);
$("#load_table").removeAttr("disabled", "disabled");
$("#load_table").val("Go");
$("#locateMe").hide();
},
'loadstart': function (evt) {
$("#load_table").attr("disabled", "disabled");
$("#load_table").val("Loading...");
$("#locateMe").show();
}
},
projection: new OpenLayers.Projection("EPSG:4326"),
protocol: new OpenLayers.Protocol.WFS({
version: "1.1.0",
url: a,
featureType: "bldg_100yr",
featureNS: b,
geometryName: "geom"
}),
renderers: renderer,
styleMap: new OpenLayers.StyleMap(style),
displayInLayerSwitcher: !0
});
var selFloodEvent = $('#affectedLayer').val();
var layerName = map.getLayersByName(selFloodEvent);
var len = layerName.length;
if (len < 1){
map.addLayer(bldg100yearCBR);
activateControls(c);
}else{
removeFilter("100-Year Rainfall");
layerName[0].setVisibility(true);
console.log("reset me");
}
myflag1 = true;
toggleStatic1("100-Year Rainfall Affected Buildings");
After adding/loading another vector layer, filter won't work. The scenario is if the layer is already loaded, I want to display it without adding it again but if it is filtered before, I wanted to reset it. That's the problem, the removeFilter function won't work after loading another vector layer.
So, basically filter only works the first time I loaded the layer. I also noticed in console log in my browser, it does not query to the GeoServer like this:
XHR finished loading: POST "http://127.0.0.1:8080/geoserver/wfs".
after loading another layer.

Is it possible to display a UI transition to reflect changes in a collection with meteorjs?

I would like to display a pulse transition when my collection change.
In my html file, I have that:
<template name="menuItemTag">
<div class="app-menu-item-tag ui label">{{ count }}</div>
</template>
In my js file, I expose the count variable for my template like that:
Template.menuItemTag.count = function() {
return MyCollection.find().count();
};
With that the count change in the ui when the collection is updated.
Now, I would like to display a pulse transition on my div label each time the count value change.
I tried to use the cursor.observe
Template.menuItemTag.rendered = function() {
MyCollection.find().observe({
added: function (id, user) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
},
removed: function () {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
});
};
Unfortunately, it is call too many times when the template is rendered the first time.
If initialy I have 40 items in my collection, the transition is played 40 times...
Is there a clean way for playing a ui transition on changes and avoid the collection initialisation?
Try this:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
$('.app-menu-item-tag:nth(0)').transition('pulse');
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};
Thanks sbking for your answer, I still have a problem on initialization of the collection.
I propose below to defer the first animation util the collection will be completely filled:
Template.menuItemTag.count = function() {
return Session.get('count');
};
Template.menuItemTag.rendered = function() {
var that = this;
this.countComputation = Deps.autorun(function() {
Session.set('count', MyCollection.find().count());
// Cancel playing UI transition. The collection is not completely initialized
if (that.handleTimeout) {
Meteor.clearTimeout(that.handleTimeout);
}
// Play the UI transition without the timeout if the collection is initialized
if (that.noNeedTimeoutAnymore) {
$('.app-menu-item-tag:nth(0)').transition('pulse');
}
// Tentative to play the UI transition during the collection feeding
else {
that.handleTimeout = Meteor.setTimeout(function() {
$('.app-menu-item-tag:nth(0)').transition('pulse');
// At this point we know that the collection is totaly initialized
// then we can remove the timeout on animation for the future update
that.noNeedTimeoutAnymore = true;
}, 1500); // You can adjust the delay if needed
}
});
};
Template.menuItemTag.destroyed = function() {
this.countComputation.stop();
};

ExtJS : move a record in a grid

I have a simple grid on ExtJS and would like the user to be able to move the record from its original position.
When the user double clicks on a record, a small window containing a combobox appears, he can choose a value on the combobox and then click the save button to apply the change.
However, it doesn't work, I've searched many solutions for this on different forums and none seems to work. Either nothing happens, or an undefined row is added at the end of the grid. Here is the base code I use :
onEditRank: function(view, cell, cellIndex, record, row, rowIndex, e)
{
var reditor = Ext.create('CMS.view.Views.RankEditor', {id: 'reditorView'});
var form = reditor.down('form');
var oldPos = this.getFlatrq().getView().indexOf(record);
var grStore = this.getGridRnkStoreStore();
var i;
var data = [];
for(i = 1; i <= CMS.global.Variables.limit + 1; i++)
{
data.push(i);
}
var combo = Ext.create
(
'Ext.form.field.ComboBox',
{
fieldLabel: 'Rank',
itemId: 'cmbRank',
store: data
}
);
var saveRnk = Ext.create
(
'Ext.button.Button',
{
text: 'Save',
handler: function()
{
}
}
);
form.add(combo);
form.add(saveRnk);
reditor.show();
}
Now here are the different handlers I have tried for my save button :
handler: function()
{
grStore.remove(record);
grStore.insert(record, combo.getValue() - 1);
this.up('form').up('window').close();
}
handler: function()
{
grStore.removeAt(oldPos);
grStore.insert(record, combo.getValue() - 1);
this.up('form').up('window').close();
}
handler: function()
{
var rec = grStore.getAt(oldPos).copy();
grStore.removeAt(oldPos);
grStore.insert(rec, combo.getValue() - 1);
this.up('form').up('window').close();
}
Those 3 handlers inserted undefined rows at the end of my grid. I displayed the values of oldPos and combo.getValue() and they are correct, I also displayed the record variable before and after the remove because I thought it might be destroyed but it wasn't. I have also tried to add a move function to store and call it :
'CMS.store.GridRnkStore',
{
extend: 'Ext.data.Store',
model: 'CMS.model.GridRnkModel',
autoLoad: false,
filterOnLoad: true,
autoSync: true,
move: function(from, to)
{
console.log(from + " " + to);
var r = this.getAt(from);
this.data.removeAt(from);
this.data.insert(to, r);
this.fireEvent("move", this, from, to);
},
}
);
But it didn't work either, it did nothing actually (I put some console.log in the move function to see if it was called and it was, with the right parameters). I'm running out of ideas, any help would be appreciated.
Thank you.
try to set the private move parameter to true of store.remove():
remove: function(records, /* private */ isMove, silent)

Click Listeners in Loop - Array and Closure

I realise I'm treading on thin ice opening another closure issue, but I have searched and can't find an answer to my issue. I have a Google Maps API v3 page which generates two maps from one block of code - a small map centered on the user's current location and a larger map showing the whole area with the user's location marked where it is, center or not. On top of the map is a rectangle layer consisting of 14 rectangles. In order to generate the two maps, I have had to put the rectangles in a 2 dimensional array, rectangles[1] for 'map', and rectangles[2] for 'map2':
var rectangles = [0,1,2,3,4,5,6,7,8,9,10,11,12,13];
rectangles[1][0]=new google.maps.Rectangle({
bounds:new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
map:map,
fillColor:'red',
fillOpacity: 0.3,
strokeOpacity: 0,
url: 'http://example.com',
clickable: true
});
rectangles[2][0]=new google.maps.Rectangle({
bounds:new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
map:map2,
fillColor:'red',
fillOpacity: 0.3,
strokeOpacity: 0,
url: 'http://example.com',
clickable: true
});
...and so on. It all works fine and the two maps are displayed and the geolocation works. Now I want to add a click listener for each rectangle but I'm not sure who to reference the array. This is what I have now:
for ( i = 0; i < rectangles[1].length; i++ ){
google.maps.event.addListener(rectangles[1][i], 'click', function() {
window.location.href = this.url;
});
}
for ( x = 0; x < rectangles[2].length; x++ ){
google.maps.event.addListener(rectangles[2][x], 'click', function() {
window.location.href = this.url;
});
}
Which obviously won't work. I have seen various solutions to the closure issue, but I'm not sure I'm even heading in the right direction in referencing the two arrays of rectangles - or if I even need to define two different sets of click listeners. I'd be really grateful if someone could point me in the right direction - and sorry if this is just going over old ground that appears obvious. There's always a new learner coming along who is trying hard to catch up.
Thanks.
//First, set up `rectangles` as an array containing two arrays.
var rectangles = [];
rectangles[0] = [];
rectangles[1] = [];
//As `google.maps.Rectangle` doesn't accept a `url` option,
//its url needs to be defined separately from the rectangle itself,
//but in such a way that the two are associated with each other.
//For this we can use a javascript plain object.
rectangles[0][0] = {
rect: new google.maps.Rectangle({
bounds: new google.maps.LatLngBounds(new google.maps.LatLng(a, b), new google.maps.LatLng(x, y)),
map: map,
fillColor: 'red',
fillOpacity: 0.3,
strokeOpacity: 0,
clickable: true
}),
url: 'http://example.com'
};
rectangles[1][0] = new google.maps.Rectangle({
...
});
rectangles[0][1] = new google.maps.Rectangle({
...
});
rectangles[1][1] = new google.maps.Rectangle({
...
});
rectangles[0][2] = new google.maps.Rectangle({
...
});
rectangles[1][2] = new google.maps.Rectangle({
...
});
//Now to attach the click listeners.
//First we define a function that adds a click listener.
//By doing this in its own function, a closure is formed,
//trapping the formal variable `rectObj` and making `rectObj.url`
//accessible to the listener when it is called in response to future clicks.
function addClickListener(rectObj) {
google.maps.event.addListener(rectObj.rect, 'click', function() {
window.location.href = rectObj.url;
});
}
//Now, we can loop through the `rectangles` arrays, adding listeners.
for ( i = 0; i < 2; i++ ) {
for ( j = 0; j < 14; j++ ) {
if(rectangles[i][j]) {//safety
addClickListener(rectangles[i][j]);
}
}
}

Resources