Paning the bubbles - here-api

is there a way of paning the bubble in the current view when there are regions which are outside the mapview?
E.g. https://dev2.gruppenunterkuenfte.de/nordrhein-westfalen__r187.html?vs=1
You can click on a bubble at the edge and you see them outside.
Using google: https://www.gruppenunterkuenfte.de/nordrhein-westfalen__r187.html?vs=1
will pan automatically in the full view ...
Regards
Chris

Might not be available out of the box, but something as follows could be done to check when opening the bubble and move the map center.
var checkBubble = function(evt) {
setTimeout(function() {
if(infoBubble && infoBubble.getState() == "open"){
var border = 50;
var objRect = infoBubble.getContentElement().parentElement.getBoundingClientRect();
var objStyleRight = Math.abs(parseInt(infoBubble.getContentElement().parentElement.style.right));
objStyleRight = objStyleRight ? objStyleRight : 0;
var mapRect = map.getElement().getBoundingClientRect();
var shiftX = 0;
var shiftY = 0;
// check, if infobubble isn't too far to up
if ((objRect.top-border) < mapRect.top) {
shiftY = (mapRect.top - (objRect.top-border));
}
// check, if infobubble isn't too far to the left
var objLeft = (objRect.left - objStyleRight);
if ((objLeft-border) < mapRect.left) {
shiftX = (mapRect.left - (objLeft-border));
} // check, if infobubble isn't too far to the right
else if ((objRect.right+border) > mapRect.right) {
shiftX = -(objRect.right - (mapRect.right-border));
}
if ((shiftX == 0) && (shiftY == 0)) {
return;
}
var currScreenCenter = map.geoToScreen(map.getCenter());
var newY = (currScreenCenter.y - shiftY);
var newX = (currScreenCenter.x - shiftX);
var newGeoCenter = map.screenToGeo(newX, newY);
map.setCenter(newGeoCenter, true);
}
}, 20);
}
map.addEventListener("mapviewchange",checkBubble);

Thanks a lot works great! I extend to the case that the bubble is outside at the bottom:
...
// check, if infobubble isn't too far to up
if ((objRect.top-border) < mapRect.top) {
shiftY = (mapRect.top - (objRect.top-border));
} else {
if ((objRect.bottom+border) > mapRect.bottom) {
shiftY = -(objRect.bottom - (mapRect.bottom-border));
}
}
...
Regards
Chris

Related

Select symbol definition path

I need to view the segments and handles of the path that defines a SymbolItem. It is a related issue to this one but in reverse (I want the behavior displayed on that jsfiddle).
As per the following example, I can view the bounding box of the SymbolItem, but I cannot select the path itself in order to view its segments/handles. What am I missing?
function onMouseDown(event) {
project.activeLayer.selected = false;
// Check whether there is something on that position already. If there isn't:
// Add a circle centered around the position of the mouse:
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = '#' + Math.floor(Math.random() * 16777215).toString(16);
var circleSymbol = new SymbolDefinition(circle);
multiply(circleSymbol, event.point);
}
// If there is an item at that position, select the item.
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
var next = item.place(location);
next.position.x = next.position.x + 20 * i;
}
}
Using SymbolDefinition/SymbolItem prevent you from changing properties of each symbol items.
The only thing you can do in this case is select all symbols which share a common definition.
To achieve what you want, you have to use Path directly.
Here is a sketch showing the solution.
function onMouseDown(event) {
project.activeLayer.selected = false;
if (event.item === null) {
var circle = new Path.Circle(new Point(0, 0), 10);
circle.fillColor = Color.random();
// just pass the circle instead of making a symbol definition
multiply(circle, event.point);
}
else {
event.item.selected = true;
}
}
function multiply(item, location) {
for (var i = 0; i < 10; i++) {
// use passed item for first iteration, then use a clone
var next = i === 0 ? item : item.clone();
next.position = location + [20 * i, 0];
}
}

How to scale a Path to the direction it is pointed towards

In my demo. I'm able to create a few ellipses that overlay each other. Each ellipse is slightly rotated and when click should stretch outwards.
However, right now I'm using .scale(x,y) and the ellipses's height increases vertically.
I'm not sure how I would accomplish this type of effect using paper.js
DEMO
Code Pen Demo
paper.install(window);
var canvas = document.getElementById("myCanvas");
window.onload = function() {
paper.setup('myCanvas');
var numberOfRings = 6,
rings = [],
size = [225,400],
colors = ['black','green','orange','blue','yellow','grey'],
max_frame = 50,
negative_scale = 0.99,
positive_scale = 1.01;
for(var i = 0; i < numberOfRings; i++)
{
var path = new Path.Ellipse({
center:view.center,
size: size,
strokeColor: colors[i],
strokeWidth :10
});
var rotate = 30*i +30;
path.rotate(rotate);
path.animation = false;
path.rotateValue = rotate;
path.animationStartFrame = 0;
path.animationScale = positive_scale;
path.smooth();
path.animationIndex = i;
path.onClick = function(event) {
rings[this.animationIndex].animation = true;
}
rings.push(path);
}
view.onFrame = function(event) {
for(var i = 0; i < numberOfRings; i++)
{
if (rings[i].animation == true){
if (rings[i].animationStartFrame == 0)
{
rings[i].animationStartFrame = event.count;
}
if (rings[i].animationStartFrame > 0 && event.count < (rings[i].animationStartFrame + max_frame)){
// TODO
rings[i].scale(1,rings[i].animationScale);
} else if ( event.count > (rings[i].animationStartFrame + max_frame)){
rings[i].animation = false;
rings[i].animationStartFrame = 0;
if (rings[i].animationScale == negative_scale)
rings[i].animationScale = positive_scale;
else
rings[i].animationScale = negative_scale;
}
}
}
}
}
canvas{
width: 100%;
height: 100%;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-full.min.js"></script>
</head>
<body>
<canvas id="myCanvas" resize></canvas>
</body>
</html>
first for this kind of things i think its easier to use paper.js / items with the applyMatrix option set to false - this way transformations are not applied / baked into the item/children/paths but stay separate in the transformation and so can also get manipulated later in an absolute fashion..
additionally to get to your desired effect i used a trick: Group
i encapsulate the ellipse path in an group. so i can rotate the group only.
the coordinate-system of the child is not modified - and i can manipulate the ellipses scaling as in its original coordinate-system / as it was not rotated..
i have made some other modifications based on your example - mainly so its easier to test in different canvas-sizes and with different numbers of ellipses.
i first tested / developed it at sketch.paperjs.org (i find it easier to test/debug it there)
and then converted it to fit the the plain js version here.
if you want to do more complex animations please checkout the great library animatePaper.js - i used it heavily and loved to work with it :-)
it supports simple and more complex animations of attributes of paper-objects.
paper.install(window);
var canvas = document.getElementById('myCanvas');
window.onload = function() {
paper.setup('myCanvas');
var numberOfRings = 5;
var rings = [];
let view_max_length = Math.min(view.size.width, view.size.height);
var ringStartSize = new Size(view_max_length * 0.4, view_max_length * 0.45);
var ringTargetLength = view_max_length * 0.9;
var scaleStepSize = 0.01;
function createRing(index) {
let path = new Path.Ellipse({
center: view.center,
size: ringStartSize,
strokeColor: {
hue: (360 / numberOfRings) * index,
saturation: 1,
brightness: 1
},
strokeWidth: 10,
applyMatrix: false,
strokeScaling: false,
});
// add custom properties
path.animate = false;
path.animationDirectionOut = true;
path.animationIndex = index;
path.onClick = function(event) {
//console.log(this);
this.animate = true;
};
let rotationgroup = new Group(path);
rotationgroup.applyMatrix = false;
let offsetAngle = (360 / 2) / numberOfRings;
let rotate = offsetAngle * index;
rotationgroup.pivot = path.bounds.center;
rotationgroup.rotation = rotate;
return path;
}
function init() {
for (let i = 0; i < numberOfRings; i++) {
rings.push(createRing(i));
}
}
function animateRing(event, ring) {
if (ring.animate) {
let tempScaleStep = scaleStepSize;
if (!ring.animationDirectionOut) {
tempScaleStep = tempScaleStep * -1;
}
ring.scaling.y += tempScaleStep;
// test if we have reached destination size.
if (
(ring.bounds.height >= ringTargetLength) ||
(ring.bounds.height <= ringStartSize.height)
) {
ring.animate = false;
// change direction
ring.animationDirectionOut = !ring.animationDirectionOut;
}
}
}
view.onFrame = function(event) {
if (rings && (rings.length > 0)) {
for (var i = 0; i < numberOfRings; i++) {
animateRing(event, rings[i]);
}
}
};
init();
}
canvas {
widht: 100%;
height: 100%;
background-color: rgb(0,0,50);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/paper.js/0.11.5/paper-full.min.js"></script>
</head>
<body>
<canvas id="myCanvas" resize></canvas>
</body>
</html>

How to normal-scale using threejs to make objects 'thicker' or 'thinner'

I'm trying to figure out how to extrude an already loaded .obj object to make it "thicker". I think I'm looking for a way to scale my object not from its anchor point but scaling it by each polygon normal.
A classic example would be to take a "ring" object. If you scale it up with just the normal scale methods it just gets bigger from the center but I want the ring to become thicker/thinner. Its called a 'normal scale' in cinema 4d.
Here's some example code of what I currently have, and which isn't giving me the expected result.
var objLoader = new THREE.OBJLoader();
var material = new THREE.MeshLambertMaterial({color:'yellow', shading:THREE.FlatShading});
objLoader.load('objects/gun/M1911.obj', function (obj) {
obj.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.geometry.computeVertexNormals();
child.material = material;
}
});
obj.material = material;
obj.scale.set(7, 7, 7);
scene.add(obj);
});
scaleGeo: function (geo, ratio) {
for (var i = 0; i < geo.faces.length; i++) {
var faceI = geo.faces[i];
var vertexArr = [faceI.a, faceI.b, faceI.c];
for (var j = 0; j < vertexArr.length; j++) {
var vertexJ = geo.vertices[vertexArr[j]];
var normalJ = faceI.vertexNormals[j];
if (vertexJ.hasScale) continue;
vertexJ.x += normalJ.x * ratio;
vertexJ.y += normalJ.y * ratio;
vertexJ.z += normalJ.z * ratio;
vertexJ.hasScale = true;
}
}
return geo;
},

creating a Placemarks that can be hidden

I have been trying to create a Placemark that I can hide and show (like turning visibility on and off) on demand (on click)... I am using this to make the placemark:
function placemark(lat, long, name, url, iconsrc){
var placemark = ge.createPlacemark(name);
ge.getFeatures().appendChild(placemark);
placemark.setName(name);
// Create style map for placemark
var icon = ge.createIcon('');
if(iconsrc == "0")
icon.setHref('http://maps.google.com/mapfiles/kml/paddle/red-circle.png');
else{
icon.setHref(iconsrc);
}
var style = ge.createStyle('');
style.getIconStyle().setIcon(icon);
if(iconsrc != "0")
style.getIconStyle().setScale(2.5);
placemark.setStyleSelector(style);
// Create point
var point = ge.createPoint('');
point.setLatitude(lat);
point.setLongitude(long);
//point.setAltitudeMode(1500);
placemark.setGeometry(point);
google.earth.addEventListener(placemark, 'click', function(event) {
// Prevent the default balloon from popping up.
event.preventDefault();
var balloon = ge.createHtmlStringBalloon('');
balloon.setFeature(placemark); // optional
balloon.setContentString(
'<iframe src="'+ url +'" frameborder="0"></iframe>');
ge.setBalloon(balloon);
});
}
I have tried everything... from this:
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
to using this ge.getFeatures().removeChild(child);
can anyone point me to the right direction on creating a function that will allow me to turn the visibility on/off on demand please.
Your hidePlacemark function is missing some {} in your final IF statement
if(child.getId()== name)
you have
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name)
child.setVisibility(false);
}
}
}
make it
function hidePlacemark(name){
var children = ge.getFeatures().getChildNodes();
for(var i = 0; i < children.getLength(); i++) {
var child = children.item(i);
if(child.getType() == 'KmlPlacemark') {
if(child.getId()== name) {
child.setVisibility(false);
}
}
}
}
HOWEVER ------- you are better off doing this as it is much faster as you don't need to loop through ALL your placemarks
function hidePlacemark(name) {
var placemark = ge.getElementById(name);
placemark.setVisibility(false);
}
I think the plain ge.getFeatures().removeChild(placemark); works.
I played with this GooglePlayground, and just added the following code to line 8 (that is empty in this GooglePlayground Sample):
addSampleButton('Hide Placemark', function(){
ge.getFeatures().removeChild(placemark);
});
Clicking the button Hide Placemark hides the placemark like a charm here. Any chances your problem is somewhere else in your code?

Changing stroke attribute on a single RaphaelJS object, when there are multiple objects on the page

I've got a whole bunch of rects on my canvas.
I'd like to change the stroke on whatever rect the user clicks, as well as running some other javascript. My simplified code is below.
var canvas = Raphael("test");
var st = canvas.set();
for (var i = 0; i < 2; i++) {
var act = canvas.rect(///edited for brevity////).attr({"stroke":"none"});
st.push(act)
act.node.onclick = function() {
st.attr({stroke: "none"});
act.attr({stroke: "yellow"});
}
}
Right now, no matter what rect I click on, it's only changing the stroke on the last rect drawn.
Any ideas?
Not a Raphaƫl problem but rather lack of closure understanding. Easily could be fixed by self invoking function:
for (var i = 0; i < 2; i++) {
var act = canvas.rect(///edited for brevity////).attr({"stroke":"none"});
st.push(act)
(function (act) {
act.node.onclick = function() {
st.attr({stroke: "none"});
act.attr({stroke: "yellow"});
}
})(act);
}
//Try and then embellish
st[i].click(function (e)
{
this.attr({stroke: "yellow"});
}

Resources