I'm trying to create a pulse effect on a point on a AmCharts Map. In order to do this I need to insert HTML at a latitude and longitude point but can't work out how to do it through the api (http://docs.amcharts.com/3/javascriptmaps/)
Here is the effect I'm trying to achieve - http://kevinurrutia.tumblr.com/post/16411271583/creating-a-css3-pulsating-circle
Here is a jsfiddle of the map with the HTML and CSS http://jsfiddle.net/9cBXh/2/
// request #3275
var dataPoints = [{
latitude: '51.000000000000',
longitude: '9.000000000000',
type: 'bubble',
color: '#cc0000',
fixedSize: false,
alpha: 0.9,
height: 30,
width: 30,
centered: true,
id: 'test'
}];
AmCharts.ready(function() {
// create AmMap object
var map = new AmCharts.AmMap();
// set path to images
map.pathToImages = "http://www.ammap.com/lib/images/";
var dataProvider = {
mapVar: AmCharts.maps.worldLow,
getAreasFromMap:false,
images: dataPoints
};
// pass data provider to the map object
map.dataProvider = dataProvider;
map.areasSettings = {
autoZoom: true,
selectedColor: "#CC0000"
};
// write the map to container div
map.write("mapdiv");
});
The red dot is the bubble generated through the api. The blue dot and circle is the html I need to insert at the lat and long co-ordinates...somehow!
Any help would be appreciated.
Here's a complete working example of the AmCharts map with several pulsating HTML elements as map markers:
http://www.amcharts.com/demos/custom-html-elements-map-markers/
(You can view the source by clicking on the EDIT button)
The idea is very simple:
Trap "positionChanged" event. Go throiugh all of the "images" in the map's dataProvider, create HTML elements for each of those, then position them directly over the map by using API functions that resolve longitude/latitude coordinates to screen top/left coordinates:
// add events to recalculate map position when the map is moved or zoomed
map.addListener("positionChanged", updateCustomMarkers);
// this function will take current images on the map and create HTML elements for them
function updateCustomMarkers (event) {
// get map object
var map = event.chart;
// go through all of the images
for( var x in map.dataProvider.images) {
// get MapImage object
var image = map.dataProvider.images[x];
// check if it has corresponding HTML element
if ('undefined' == typeof image.externalElement)
image.externalElement = createCustomMarker(image);
// reposition the element accoridng to coordinates
image.externalElement.style.top = map.latitudeToY(image.latitude) + 'px';
image.externalElement.style.left = map.longitudeToX(image.longitude) + 'px';
}
}
// this function creates and returns a new marker element
function createCustomMarker(image) {
// create holder
var holder = document.createElement('div');
holder.className = 'map-marker';
holder.title = image.title;
holder.style.position = 'absolute';
// create dot
var dot = document.createElement('div');
dot.className = 'dot';
holder.appendChild(dot);
// create pulse
var pulse = document.createElement('div');
pulse.className = 'pulse';
holder.appendChild(pulse);
// append the marker to the map container
image.chart.chartDiv.appendChild(holder);
return holder;
}
Related
I have draggable markers on a Here Map, and I'd like to detect when a marker is dragged and dropped onto a Polyline. I haven't been able to find a good way to detect that the marker is dropped on or near the polyline. It would be nice if I could add an "dragend" event listener to the Polyline object, but that doesn't seem to be supported on objects other than the actual map.
Another approach would be to determine the distance to the Polyline from the point that the dragend occurs, but I haven't found a straightforward way to calculate that distance. (Though I've found the example for finding the nearest marker to a point.)
Any suggestions?
It is possible to add dragend event listener on map objects.
This code should work for you (in 3.0 API version):
var lineString = new H.geo.LineString([0, 0, 0, 50, 50, 0]),
polyline = new H.map.Polyline(lineString, {style: {lineWidth: 15, strokeColor: 'red'}}),
marker = new H.map.Marker({lat: 30, lng: 0});
// Ensure that the marker can receive drag events
marker.draggable = true;
// add objects to the map
map.addObjects([polyline, marker]);
// set map center and zoom
map.setCenter({lat: 30, lng: 0});
map.setZoom(3);
// disable map's behavior and calculate the offset between mouse and target's position
// when starting to drag a marker object:
marker.addEventListener('dragstart', function(ev) {
var target = ev.target,
pointer = ev.currentPointer,
targetPosition = map.geoToScreen(target.getGeometry());
target['offset'] = new H.math.Point(pointer.viewportX - targetPosition.x, pointer.viewportY - targetPosition.y);
behavior.disable();
}, false);
// Listen to the drag event and move the position of the marker
marker.addEventListener('drag', function(ev) {
var target = ev.target,
pointer = ev.currentPointer;
target.setGeometry(map.screenToGeo(pointer.viewportX - target['offset'].x, pointer.viewportY - target['offset'].y));
}, false);
// re-enable behavior and check if marker was dropped on polyline
marker.addEventListener('dragend', function(ev) {
behavior.enable();
var pointer = ev.currentPointer,
objects = map.getObjectsAt(pointer.viewportX, pointer.viewportY),
droppedOnPolyline = false;
objects.forEach(function(object) {
if (object === polyline) {
droppedOnPolyline = true;
}
});
console.log('marker dropped on polyline? %s', droppedOnPolyline);
}, false);
On the same page, i have differents maps loaded with Here maps API for each map i have loaded a specific kml file.
When i try to click, it works only on the last kml loaded and not others one, so how to make working event on each map ? This my code, it's taken from the example but a little bit modified :
function renderSchoenefeldAirport(map, ui, renderControls, kmlfile) {
// Create a reader object, that will load data from a KML file
var reader = new H.data.kml.Reader(kmlfile);
// Request document parsing. Parsing is an asynchronous operation.
reader.parse();
reader.addEventListener('statechange', function () {
// Wait till the KML document is fully loaded and parsed
if (this.getState() === H.data.AbstractReader.State.READY) {
var parsedObjects = reader.getParsedObjects();
// Create a group from our objects to easily zoom to them
var container = new H.map.Group({objects: parsedObjects});
// So let's zoom to them by default
map.setViewBounds(parsedObjects[0].getBounds());
// Let's make kml ballon visible by tap on its owner
// Notice how we are using event delegation for it
container.addEventListener('tap', function (evt) {
var target = evt.target, position;
// We need to calculated a position for our baloon
if (target instanceof H.map.Polygon || target instanceof H.map.Polyline) {
position = target.getBounds().getCenter();
} else if (target instanceof H.map.Marker) {
position = target.getPosition();
}
if (position) {
// Let's use out custom (non-api) function for displaying a baloon
showKMLBallon(position, target.getData(), ui);
}
});
// Make objects visible by adding them to the map
map.addObject(container);
}
});
}
/**
* Boilerplate map initialization code starts below:
*/
// Step 1: initialize communication with the platform
var platform = new H.service.Platform({
'app_id': 'myappid',
'app_code': 'myappcode',
useHTTPS: true
});
var pixelRatio = window.devicePixelRatio || 1;
var defaultLayers = platform.createDefaultLayers({
tileSize: pixelRatio === 1 ? 256 : 512,
ppi: pixelRatio === 1 ? undefined : 320
});
// Step 2: initialize a map
// Please note, that default layer is set to satellite mode
var map = new H.Map(document.getElementById('mapcontainer1'), defaultLayers.satellite.map, {
zoom: 1,
pixelRatio: pixelRatio
});
var map_secondary = new H.Map(document.getElementById('mapcontainer2'), defaultLayers.satellite.map, {
zoom: 1,
pixelRatio: pixelRatio
});
// Step 3: make the map interactive
// MapEvents enables the event system
// Behavior implements default interactions for pan/zoom (also on mobile touch environments)
var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
var behavior_secondary = new H.mapevents.Behavior(new H.mapevents.MapEvents(map_secondary));
// Template function for our controls
function renderControls(buttons) {
var containerNode = document.createElement('div');
containerNode.setAttribute('style', 'position:absolute;top:0;left:0;background-color:#fff; padding:10px;');
containerNode.className = "btn-group";
Object.keys(buttons).forEach(function (label) {
var input = document.createElement('input');
input.value = label;
input.type = 'button';
input.onclick = buttons[label];
input.className="btn btn-sm btn-default"
containerNode.appendChild(input);
});
map.getElement().appendChild(containerNode);
}
function showKMLBallon(position, data, ui) {
var content = data.balloonStyle.text;
if (content) {
// Styling of the balloon text.
// The only supported wilde cards are $[text] and $[description].
content = content
.replace('$[name]', data.name || '')
.replace('$[description]', data.description || '');
// Note how we are caching our infoBubble instance
// We create InfoBubble object only once and then reuse it
var bubble = showKMLBallon.infoBubble;
if (!bubble) {
bubble = new H.ui.InfoBubble(position, {content: content});
ui.addBubble(bubble);
bubble.getContentElement().style.marginRight = "-24px";
// Cache our instance for future use
showKMLBallon.infoBubble = bubble;
} else {
bubble.setPosition(position);
bubble.setContent(content);
bubble.open();
}
}
}
// Step 4: create the default UI component, for displaying bubbles
var ui = H.ui.UI.createDefault(map, defaultLayers);
var ui_secondary = H.ui.UI.createDefault(map_secondary, defaultLayers);
// Step 5: main logic goes here
renderSchoenefeldAirport(map, ui, renderControls, 'path/to/file1.kml');
renderSchoenefeldAirport(map_secondary, ui_secondary, renderControls, 'path/to/file2.kml');
Thanks by advance
In the provided snippet there is a line 106: var bubble = showKMLBallon.infoBubble; where the info bubble is "cached", the problem is that when the user clicks on one of the maps the infobubble is created and cached, and when somebody clicks on the second map the the info bubble from the first is used.
In the simplest case this line should be:
var bubble = ui.infoBubble;
so the bubble for each map instance in cached. In general, depending on the desired outcome, the proper caching strategy should be devised.
Hope this helps.
I am creating a walkthrough for the interior of a building and am wondering how to draw a marker on a StreetViewPanorama. Maybe I'm missing something basic, but everything I have read indicates that you need a lat and lng to draw the marker.
Here is what I have:
var initPosPanoID, streetView;
function initPano() {
// Set StreetView provider.
var streetViewOptions = {
zoom: 0,
panoProvider: getCustomPanorama,
pano : "lobby",
addressControlOptions: {
position: google.maps.ControlPosition.BOTTOM_CENTER
}
};
// Create a StreetView object.
var streetViewDiv = document.getElementById('map');
streetView = new google.maps.StreetViewPanorama(streetViewDiv, streetViewOptions);
// Add links when it happens "links_change" event.
google.maps.event.addListener(streetView, "links_changed", createCustomLink);
google.maps.event.addListener(streetView, "pano_changed", function() {
var panoCell = document.getElementById('pano-cell');
panoCell.innerHTML = panorama.getPano();
});
}
function getCustomPanoramaTileUrl(panoID, zoom, tileX, tileY) {
// Return a pano image given the panoID.
return "images/he/" + panoID + '.jpg';
}
function getHeading(panoID) {
var heading;
switch(panoID) {
case "lobby":
heading = 0;
break;
case "secorner":
heading = 100;
break;
}
return heading;
}
function getCustomPanorama(panoID) {
// get a custom heading for the pano
var heading = getHeading(panoID);
var streetViewPanoramaData = {
links: [],
copyright: 'Imagery (c) HumanElement',
tiles: {
tileSize : new google.maps.Size(1024, 512),
worldSize : new google.maps.Size(1024, 512),
// The heading in degrees at the origin of the panorama
// tile set.
centerHeading : heading,
getTileUrl : getCustomPanoramaTileUrl
}
};
switch(panoID) {
case "lobby":
// Description of this point.
streetViewPanoramaData["location"] = {
pano: 'lobby',
description: "Human Element",
latLng: new google.maps.LatLng(42.282138, -83.751471)
};
return streetViewPanoramaData;
case "secorner":
streetViewPanoramaData["location"] = {
pano: 'secorner',
description: "SouthEast Corner",
latLng: new google.maps.LatLng(42.282078, -83.751413)
};
return streetViewPanoramaData;
}
}
function createCustomLink() {
var links = streetView.getLinks();
var panoID = streetView.getPano();
switch(panoID) {
case "lobby":
links.push({
description : "SouthEast Corner",
pano : "secorner",
heading : 280
});
break;
case "secorner":
links.push({
description : "HumanElement Lobby",
pano : "lobby",
heading : 90
});
break;
}
}
I would like to have different markers or touchpoints at different locations, but am not sure how to get them on there.
Trying to draw a standard marker does not work without the lat/lng.
I thought about trying to create it around a google.maps.Point thinking I might be able to use the x and y from my tiles, but couldn't seem to get that working.
The other options I see are related to google.maps.drawing.DrawingManager.
Does anyone have any advice on this?
Setting up the map this way helped: It creates the map and panorama separately and then sets the panorama into the map, rather than just having the panorama object by itself.
map = new google.maps.Map(document.getElementById('map'), {
center: position,
zoom: 10
});
// Create the panorama and set it into the map
panorama = new google.maps.StreetViewPanorama(
document.getElementById('map'), {
position: position,
pov: {
heading: 0,
pitch: -10
},
// Override the default panoId to outside the building to start
pano:'e_giRekRylYAAAQn7y2xAg'
});
map.setStreetView(panorama);
For placing markers on the map, I found an excellent plugin which creates points and then places elements on those points.
https://github.com/marmat/google-maps-api-addons/tree/master/panomarker
So far this is working very well. If you have an interior walkthrough, you should be able to toggle markers based on the PanoId. You can even add click listeners to open up dialogs.
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);
}
I am experimenting with Meteor and KineticJS. What I'm trying to do is to create a shape, and move it towards a mouse click on the stage. The position should be saved to the mongoDB so that all connected clients can be updated.
I haven't gotten far yet, but this is what I've got. I basically need a way to do two things:
How can I make a shape move towards the mouse click on the stage
and stop when it gets there?
Is there a better way of checking
the current position of a shape, other than the gameLoop that I
tried below? It works, but it feels wrong.
Thank you!
//client.js code
var player = null;
var playerAnim = null;
Template.container.rendered = function () {
var myCanvas = document.getElementById('myCanvas');
var stage = new Kinetic.Stage({
container: myCanvas,
width: 1024,
height: 1024
});
var layer = new Kinetic.Layer();
// add the layer to the stage
stage.add(layer);
// add click listener for the stage
$(stage.getContent()).on('click', function(evt) {
//stage was clicked
//Find the player shape in the database
Players.find().forEach(function (dbShape) {
player = new Kinetic.RegularPolygon(dbShape);
// setup an animation to move the player to the right
playerAnim = new Kinetic.Animation(function(frame) {
var velocity = 50;
var dist = velocity * (frame.timeDiff / 1000);
player.move(dist, 0);
Players.update(dbShape._id, {$set: {x: player.attrs.x, y: player.attrs.y}});
}, layer);
playerAnim.start();
layer.add(player);
layer.draw();
});
});
//make a gameLoop to check the position and stop the animation
Meteor.setInterval(function(gameLoop){
if(player.attrs.x > 500){
playerAnim.stop();
}
}, 500);
Meteor.autosubscribe(function () {
// clear the canvas
if (layer) {
layer.removeChildren();
layer.clear();
}
// populate the canvas with the Shapes collection
Players.find().forEach(function (dbShape) {
var player = new Kinetic.RegularPolygon(dbShape);
layer.add(player);
layer.draw();
});
});
}
I would use a tween to do this. Grab your object, get mouse position, and on mousedown or click create a Tween for your node to that mouse position.
layer.on('mousedown', function() {
var mousePos = stage.getMousePosition();
var tween = new Kinetic.Tween({
node: rect,
duration: 1,
x: mousePos.x,
y: mousePos.y,
opacity: 1,
strokeWidth: 6,
scaleX: 1.5
}).play();
});
JSFiddle
Note: To make the layer clickable, we need to add a transparent rectangle with the size of the width and height of the stage to the layer. See in the jsfiddle the Kinetic.Rect I made named var bg
Would putting the check inside the animation work for you?
playerAnim = new Kinetic.Animation(function(frame) {
var velocity = 50;
var dist = velocity * (frame.timeDiff / 1000);
player.move(dist, 0);
Players.update(dbShape._id, {$set: {x: player.attrs.x, y: player.attrs.y}});
if(player.getX() > 500){
this.stop();
}
}, layer);