openlayers vector features z-indexing - javascript

Im having hard time trying to understand the z-indexing of the vector features.
When i was searching the web for info i found these links:
http://openlayers.org/dev/examples/ordering.html
http://osgeo-org.1803224.n2.nabble.com/Bug-in-using-graphicZIndex-td2648665.html
and
http://osgeo-org.1803224.n2.nabble.com/graphicZIndex-of-vector-features-td3919627.html
What i did, was setting up styles like they are shown on first link:
this.vectorsLayer = new OpenLayers.Layer.Vector("Vectors", {
styleMap: new OpenLayers.StyleMap({
"default": {
'strokeColor': "#ff9933",
'strokeWidth': 5
},
"select": {
'strokeColor': "#3399ff"
}
})
}
);
this.carsLayer = new OpenLayers.Layer.Vector("Cars", {'rendererOptions': {yOrdering: false, zIndexing: true}});
this.startIconStyle = {'externalGraphic':this.startIconUrl};
this.parkIconStyle = {'externalGraphic':this.parkIconUrl};
this.endIconStyle = {'externalGraphic':this.endIconUrl};
this.defaultStyles = {
//'label':getLabel(),
'graphicZIndex':745,
'graphicXOffset':-13,
'graphicYOffset':-41,
'graphicWidth':26,
'graphicHeight':41,
'strokeLinecap':'round',
'strokeColor':"#000000",
'strokeWidth':2,
'strokeOpacity':1,
'fillOpacity':1}
//style of path that car has used
this.drivedStyle = {
'strokeWidth': 3,
'strokeOpacity': 1,
'strokeColor': "#3399ff",
'strokeDashstyle': "dash"
}
And when i create markers, i do it like that:
var routePoint = this.points[this.routePos].clone();
var newstyleSite = OpenLayers.Util.extend(this.startIconStyle, this.defaultStyles ,OpenLayers.Feature.Vector.style['default']);
this.routePointFeature = new OpenLayers.Feature.Vector(routePoint, {}, newstyleSite);
this.vectorsLayer.addFeatures(this.routePointFeature);
And when i look at the z-index of that marker - z-index is set to auto not 745, which is in this.defaultStyles.
This brings us to 3rd link... which i cant understand at all, cause setting styles anywhere else gives exactly as much, as im getting right now.
Neither does
this.routePointFeature.attributes.zIndex = 745;
change anything at all. Even though it apparently works on that first link/example.
How is this z-indexing supposed to work? And why doesnt it work in my case? What am i doing wrong? Or is something bugged there?
Edit: Alot of people have viewed this question and upvoted the answer. Yet i have had to deal with other stuff and have not worked with opelayers for a while now. Can some people who have seen this answer confirm that the answer works so i can accept it?
Alan

You have to enable z-indexing for the vector layer.
this.vectorsLayer = new OpenLayers.Layer.Vector("Vectors", {
styleMap: <your style map>,
rendererOptions: { zIndexing: true }
});
Additionally, OpenLayers.Util.extend only takes two parameters, and the first parameter is the destination (i.e., the second parameter, source, will be combined into it). If you want to combine multiple objects, you can use jQuery.extend or multiple calls to OpenLayers.Util.extend:
OpenLayers.Util.extend(this.startIconStyle, OpenLayers.Feature.Vector.style['default'] );
OpenLayers.Util.extend( this.startIconStyle, this.defaultStyles );
or
jQuery.extend( this.startIconStyle, OpenLayers.Feature.Vector.style['default'], this.defaultStyles );
Both of these will result in this.startIconStyle containing the union of this.startIconStyle, OpenLayers.Feature.Vector.style['default'], and this.defaultStyles.
What you may really want is:
var newstyleSite = {};
jQuery.extend( newstyleSite, OpenLayers.Feature.Vector.style['default'], this.defaultStyles, this.startIconStyle );

Related

Add custom parameter to info.contentsFunction

I need to be able to add some custom info to the pie.info.contentsFunction in Zoomcharts. I have multiple charts on the page, each one created like so...
var pc = new PieChart({
pie: {
innerRadius: 0.5,
},
container: chartContainer1,
area: { height: 500 },
data:chartData,
toolbar: {
"fullscreen": true,
"enabled": true
},
info: {
contentsFunction: boomChartTT
}
});
In the "boomChartTT" function I need to know what chart is being hovered upon. I'd like to be able to do something like this...
info: {
contentsFunction: boomChartTT(i)
}
...where 'i' is the index of the chart.
The reason I need to know the chart index is because I have some other data saved in an indexed array for each chart. The index of the chart matches the index of the data.
EXAMPLE: if user hovers on a slice in chart2 I'd want to pass '2' to the boomChartTT function so I can access the totals data for that chart (say, totalsData[2]).
I've done this in the past with other chart libraries by simply adding a data attribute to the chart container to give me the index like so...
<div id="chartContainer1" data-index="1"></div>
...and then I'm able to access the chartContainer from the hover function (contentsFunction) and then get that index.
I don't want to add the totals data to the actual chart data because I'd have to add it to each slice which is redundant.
Is there a way to do this?
Please let me know if my post is unclear.
EDITED TO ADD:
I don't think it matters but here is the boomChartTT function:
function boomChartTT(data,slice){
var tt="<div class=\"charttooltip\">";
if(data.name==="Others" || data.name==="Previous"){return tt+=data.name+"</div>";}
//var thisData=dataSearch(totalsData[i],"REFERRINGSITE",data.id);
tt+="<h5 class=\"strong\">"+data.id+"</h5>"+oHoverTable.render(thisData)+"</div>";
return tt;
}
The commented line is where I would need the index (i) to to get the correct totalsData.
SOLVED. I simply added "chartIndex" to the data like so...
for(var i=0;i<r.length;i++){
var thisDataObj ={
id:r[i].REFERRINGSITE,
value:r[i].PCTOFSALES,
name:r[i].REFERRINGSITE,
chartIndex: arguments[1],//<----- arguments[1] is the chart index
style: { expandable: false, fillColor: dataSearch(dataRSList,"REFERRINGSITE",r[i].REFERRINGSITE)[0].COLOR }
};
chartData.preloaded.subvalues.push(thisDataObj);
}
Then in the boomChartTT function...
function boomChartTT(data,slice){
var tt="<div class=\"charttooltip\">";
if(data.name==="Others" || data.name==="Previous"){return tt+=data.name+"</div>";}
var thisData=dataSearch(totalsData[data.chartIndex-1],"REFERRINGSITE",data.id);
tt+="<h5 class=\"strong\">"+data.id+"</h5>"+oHoverTable.render(thisData)+"</div>";
return tt;
}
I feared that adding custom fields to the chart data would break the chart (which I believe I've experienced with other libraries). So, there you go.

how to save page state and css to server? (Drawing app)

I have a web page where the user creates simple drawings using various blocks, e.g. shapes representing furniture are drag and dropped onto a building floor plan. It uses Interact.js.
The blocks themselves can be dragged/moved, resized, added, inserted, deleted, merged, split, recoloured, font etc by the user - JavaScript acting on HTML and CSS.
I plan to save changes locally (for offline if needed) and back to the server for sharing with others who have access to this project. Undo/redo is nice to have too.
How to save modified diagrams (html & CSS)?
Option 1:
document.getElementById('foo').innerHTML for the HTML.
For CSS you'd have to recursively traverse the whole DOM and match selectors on each element to the rules defined in each CSS file.
As described in the answer above, this will (most of the times) work for classes:
var classes = document.styleSheets[0].rules || document.styleSheets[0].cssRules;
for (var x = 0; x < classes.length; x++) {
if (classes[x].selectorText == className) {
(classes[x].cssText) ? alert(classes[x].cssText) : alert(classes[x].style.cssText);
}
}
But this is a bad, error-prone solution.
Option 2:
What you need to do is have a data model that you edit, think of JSON looking like this:
[
{type: 'circle', color: 'blue', x: 10, y: 15, children: [
{type: 'line', color: 'red', x: 100, y: 0, children: []}
]},
{type: 'square', color: 'greed', x: 100, y: 15, children: []}
]
Based on this you'd write a recursive function like this:
var foo = document.getElementById('foo'); // this is where you "draw" stuff
function draw(elements) {
var i;
for(i in elements) {
drawElement(elements[i]);
if(elements[i].children.length > 0) {
draw(elements[i].children);
}
}
}
function drawElement(element) {
var domElement = document.createElement("div");
domElement.className = 'element ' + element.type + ' ' + element.color;
domElement.style.left = element.x + 'px';
domElement.style.top = element.y + 'px';
foo.appendChild(domElement);
}
Now you need to define some CSS:
#foo {
position: relative;
}
#foo .element {
position: absolute;
}
#foo .element.square {
...
}
#foo .element.blue {
background-color: blue;
}
Next, the "interactive" part. Whenever your user adds something to the "canvas" instead of directly manipulating the DOM you only add stuff to that JSON tree and then delete the old DOM and run draw again.
You can go with option 1 but that will be a lot harder. Read the comments in the answer I attached, you'll see there are a lot of browser inconsistencies and limitations.
Option 3:
Working with a <canvas>, not the DOM is more manageable. Try looking into Fabric.js for example, it already handles "saving" and "initializing" from JSON and allows users to "draw" stuff to it.
With jQuery you can use .html() method to retrive inner html of your container. But for css I think you should manually examine all properties of all objects you want to save to get similar approach.
So, for both, if you can modify the code that handles drawing operations, I think the simplest way would be catalog all actions that user can do and store it in a variable that enable you to reproduce all the process another time.
For example:
[
["drawBox", 200, 200, 400, 400, "#ff0000", "#0000ff"],
...
]
This approach will be also useful if you want to impement undo/redo functionalities in the future.
You should store that 'state' in db.
you can use HTML5 SessionState to save rendered Page Content
also you can store that in local storage of browser and sync local storage with db.

how to thoroughly remove JQPlot Chart

Do you know how to thoroughly remove/clean JQPlot chart. Here is my code to clean it:
$j("#reset").click(function() {
$j("#chart").empty();
if (plot1) {
plot1 = null;
}
});
when I click reset button, the chart can be removed from my page, but when I choose new conditions from drop down lists to generate new chart, the old chart will overlap with the new chart, like this image showed
Do you know how to truly clean the old chart? Thanks!
Maybe you want to use redraw method.
http://www.jqplot.com/docs/files/jqplot-core-js.html#jqPlot.redraw
You can redraw it with empty values using :
$.("#reset").click(function(){
if(plot1){
plot1 = $.jqplot('chart', [[[]]], {});
plot1.redraw();
}
});
Please see working example here
My method is "expensive" but works for me. Use .remove():
if ($("#chart").length) {
$("#chart1").remove();
} else {
$(parent).append("<div id=\"chart1\"></div>");
}
// put $.jqplot below here
You can use the destroy method.
e.g.
if (C.chart != null) {
C.chart.destroy();
C.chart = null;
}
C.chart = $.jqplot("chart", chartData, {

Data from json file for use with CHAPS timeline

I'm trying to use the CHAP links library timeline (http://almende.github.io/chap-links-library/timeline.html).
Example17 is using JSON, but it's in the html file itself. I'd like to use an external JSON file sitting on the web server instead.
Here's my example.json:
{"timeline":[
{
"start":"2013,7,26",
"end":"2013,7,26",
"content": "Bleah1"
},
{
"start":"2013,7,26",
"end":"2013,8,2",
"content": "Bleah2"
},
{
"start":"2013,7,26",
"end":"2013,8,2",
"content": "Bleah3"
},
{
"start":"2013,7,26",
"end":"2013,8,2",
"content": "Bleah4"
}
]}
I added this:
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
And here's the modified function:
// Called when the Visualization API is loaded.
function drawVisualization() {
// Create a JSON data table
$.getJSON('example.json', function(jsondata) {
data = jsondata.timeline;
});
// specify options
var options = {
'width': '100%',
'height': '300px',
'editable': true, // enable dragging and editing events
'style': 'box'
};
// Instantiate our timeline object.
timeline = new links.Timeline(document.getElementById('mytimeline'));
function onRangeChanged(properties) {
document.getElementById('info').innerHTML += 'rangechanged ' +
properties.start + ' - ' + properties.end + '<br>';
}
// attach an event listener using the links events handler
links.events.addListener(timeline, 'rangechanged', onRangeChanged);
// Draw our timeline with the created data and options
timeline.draw(data, options);
}
Anyone who can tell me what I'm doing wrong gets a cookie! :-)
Update: I should specify that it's rendering the timeline div correctly, I'm just getting no data showing up.
Your start and end dates need to be parsed as Date objects for use in the timeline
I stumbled on this post as I was implementing similar functionality.
In version 2.6.1 of timeline.js, around line 3439 where the function links.Timeline.Item is declared, you'll notice a comment relating to implementing parseJSONDate.
/* TODO: use parseJSONDate as soon as it is tested and working (in two directions)
this.start = links.Timeline.parseJSONDate(data.start);
this.end = links.Timeline.parseJSONDate(data.end);
*/
I enabled the suggested code and it all works!* (go to the parseJSONDate function to see which formats are accepted)
*(works insofar as dates appear on the timeline.. I'm not using therefore not testing any selection/removal features, images, or anything like that..)

How to update the source of an Image

I'm using the Raphaël Javascript lib (awesome stuff for SVG rendering, by the way) and am currently trying to update the source of an image as the mouse goes over it.
The thing is I can't find anything about it (it's probably not even possible, considering I've read a huge part of the Raphaël's source without finding anything related to that).
Does someone knows a way to do this ?
Maybe it can be done without directly using the Raphaël's API, but as the generated DOM elements doesn't have IDs I don't know how to manually change their properties.
I'm actually doing CoffeeScript, but it's really easy to understand. CoffeeScript is Javascript after all.
This is what I'm doing right know, and I would like the MouseOver and MouseOut methods to change the source of the "bg" attribute.
class Avatar
constructor: (father, pic, posx, posy) ->
#bg = father.container.image "pics/avatar-bg.png", posx, posy, 112, 112
#avatar = father.container.image pic, posx + 10, posy + 10, 92, 92
mouseOver = => #MouseOver()
mouseOut = => #MouseOut()
#bg.mouseover mouseOver
#bg.mouseout mouseOut
MouseOver: ->
#bg.src = "pics/avatar-bg-hovered.png"
alert "Hover"
MouseOut: ->
#bg.src = "pics/avatar-bg.png"
alert "Unhovered"
class Slider
constructor: ->
#container = Raphael "raphael", 320, 200
#sliderTab = new Array()
AddAvatar: (pic) ->
#sliderTab.push new Avatar this, pic, 10, 10
window.onload = ->
avatar = new Slider()
avatar.AddAvatar "pics/daAvatar.png"
This actually works, except for the "#bg.src" part : I wrote it knowing that it wouldn't work, but well...
var paper = Raphael("placeholder", 800, 600);
var c = paper.image("apple.png", 100, 100, 600, 400);
c.node.href.baseVal = "cherry.png"
I hope, you get the idea.
This works for me (and across all browsers):
targetImg.attr({src: "http://newlocation/image.png"})
I was using rmflow's answer until I started testing in IE8 and below which returned undefined for image.node.href.baseVal. IE8 and below did see image.node.src though so I wrote functions getImgSrc, setImgSrc so I can target all browsers.
function getImgSrc(targetImg) {
if (targetImg.node.src) {
return targetImg.node.src;
} else {
return targetImg.node.href.baseVal;
}
}
function setImgSrc(targetImg, newSrc) {
if (targetImg.node.src) {
targetImg.node.src = newSrc;
} else {
targetImg.node.href.baseVal = newSrc;
}
}

Categories