I'm trying to write a method in GWT to override the series.show function for all Highcharts series, on show I want to essentially copy the series, remove the series from the chart, and readd it so that showing the series will redraw the line (instead of the default behavior where the line appears and the chart redraws). I have this modeled in a Jsfiddle: http://jsfiddle.net/ax3o8uf3/16/
I used Moxie's highcharts wrapper for GWT and used the setSeriesPlotOptions() method to set the series show event handler and call this native method from inside the onShow().
public static native void showSeries(JavaScriptObject series, JavaScriptObject chart) /*-{
var options = series.options;
options.color = series.color;
options.index = series.index;
options.marker.symbol = series.symbol;
series.remove();
options.visible = true;
chart.addSeries(options);
}-*/;
and everything worked fine. Then we updated the project's highcharts and highstock.js files and it broke everything. Now hiding and showing a series in the legend will cause it to redraw fine for the first series you do it for, but as soon as you try and show another series it breaks and goes back to the default functionality for all series. Any ideas on what I'm doing wrong or what might be causing it to not work after showing a second series on the graph?
I'm not sure why but for some reason using the seriesShowEventHandler() caused it to break after trying to show more than one series, the solution I came up with was something like this:
setSeriesPlotOptions(new SeriesPlotOptions().setSeriesLegendItemClickEventHandler(new SeriesLegendItemClickEventHandler() {
#Override
public boolean onClick(SeriesLegendItemClickEvent seriesLegendItemClickEvent) {
Series series = getSeries(seriesLegendItemClickEvent.getSeriesId());
if(seriesLegendItemClickEvent.isVisible()) {
series.hide();
} else if((series.getOptions().get("type").toString()).equals("\"line\"")){
removeAndReAddSeries(getNativeChart(), series.getNativeSeries());
} else {
series.show();
}
return false;
}
}));
Related
In my application, I have to render Projects, Tasks and Milestones. Projects and Tasks are differently coloured bars, and the Milestone is a Diamond (I'm using BaseRectangle and BaseDiamond respectively).
Since some items in my hierarchy are Projects, Some Tasks and Some Milestones, how can I render differing shapes on each row?
My first thought was to use the common "visible" property, but shapes don't have that, conversely "opacity" makes things invisible, but they still respond to mouse position.
I then tried using an Aggregation factory function, but although my chart renders correctly on first display, it doesn't recalculate the shapes on expanding or collapsing branches.
It seems to me that the factory function should work, but something is breaking in the chart that doesn't throw errors to console.
At the moment in my XML template, I have the following:
rowSettingTemplate has shapes1={path: factory:} and no shapes1 element.
Each of my BaseShapes is in a different fragment which are attached to my TreeTable as dependents.
Example Shape Fragment - Project.fragment.xml
<core:FragmentDefinition xmlns:core="sap.ui.core" xmlns="sap.m" xmlns:gnt2="sap.gantt.simple">
<gnt2:BaseRectangle id="shapeProject"
shapeId="{plandata>id}" countInBirdEye="true"
time="{plandata>start_date}" endTime="{plandata>end_date}"
resizable="true" selectable="true" draggable= "true" connectable="true"
title="{plandata>text}" showTitle="true"
tooltip=""
fill="#0c1" />
</core:FragmentDefinition>
Factory function:
shapeFactory: function(sId, oContext) {
var parentId = (/(.*)-\d+$/.exec(sId))[1];
var rowSettings = sap.ui.getCore().byId(parentId);
var node: Project.Node = oContext.getProperty();
if (String(node.id) == rowSettings.getProperty("rowId")) {
switch (node.type) {
case "project":
return this.byId('shapeProject').clone(sId);
case "task":
return this.byId('shapeTask').clone(sId);
case "milestone":
return this.byId('shapeMilestone').clone(sId);
default:
return this.byId('shapeErr').clone(sId);
}
} else {
return this.byId('shapeEmpty').clone(sId);
}
}
My empty shape is a BaseGroup - note that SAPUI5 crashes if I return a null from factory, so something has to be returned when I actually want nothing.
I also tried wrapping all my shapes in BaseGroup so that the chart always sees the same control type, but that doesn't work. Note also that if I return a clone of Empty each time without any special logic, then the chart works correctly.
I'm hoping that this is a settings or something to ensure that the aggregation works properly each time. My SAPUI5 version is 1.61.2 — I'll try 1.63.1 when I get some time, but I think that this issue is fairly deep down.
If anybody has any ideas or sample code, it would be greatly appreciated.
I have come up with a workaround for this, that may save somebody several hours. Basically instead of defining the shapes1 aggregation via a factory function, I have used the <shapes1> tag instead. My Shapes1 tag contains a reference to my own custom shape which derives from BaseRectangle. My custom shape can then render whatever SVG it requires based on the bound object context. Now my tree can expand and collapse whilst rendering whatever shapes are required.
My renderer now looks like this:
CustomChartShape.prototype.renderElementRectangle = BaseRectangle.prototype.renderElement;
CustomChartShape.prototype.renderElementDiamond = BaseDiamond.prototype.renderElement;
CustomChartShape.prototype.renderElement = function(oRm, oElement) {
// There is possibilities that x is invalid number.
// for instance wrong timestamp binded to time property
if (this.bHasInvalidPropValue) { return; }
var Node = this.getBindingInfo('endTime').binding.getContext().getProperty();
if (Node.type == "milestone") {
this.renderElementDiamond(oRm, oElement);
} else {
this.renderElementRectangle(oRm, oElement);
}
}
I had to provide a 'getD' function that has a fixed width, and I'll have o go through and rewrite several functions, but I think that this will work for me.
Another developer created our original map but I'm tasked with making some changes. One of these is making sure the activated marker is brought to the front when clicked on (where it is partially overlapped by other markers).
The developers have used mapbox 2.2.2.
I have looked at leafletjs's docs, have followed some instructions on other posted solutions (e.g. solution one and solution two). Neither of these makes any difference.
Examining the marker in Chrome's console I can see the value of options.zIndexOffset is being set (10000 in my test case). I've even set _zIndex to an artificially high value and can see that reflected in the marker's data structure. But visually nothing is changing.
This is how the map is set up initially. All features are from a single geojson feed:
L.mapbox.accessToken = '<access token here>';
var map = L.mapbox.map('map', 'map.id', {
}).setView([37.8, -96], 3);
var jsonFeed, jsonFeedURL;
var featureLayer = L.mapbox.featureLayer()
.addTo(map)
.setFilter(function (f) {
return false;
});
$.getJSON(jsonFeedURL, function (json) {
jsonFeed = json;
jsonFeedOld = json;
// Load all the map features from our json file
featureLayer.setGeoJSON(jsonFeed);
}).done(function(e) {
// Once the json feed has loaded via AJAX, check to see if
// we should show a default view
mapControl.activateInitialItem();
});
Below is a snippet of how I had tried setting values to change the z-index. When a visual marker on the featureLayer is clicked, 'activateMarker' is called:
featureLayer.on('click', function (e) {
mapControl.activateMarker(e);
});
The GEOjson feed has urls for the icons to show, and the active marker icon is switched to an alternative version (which is also larger). When the active feature is a single Point I've tried to set values for the marker (lines commented out, some of the various things I've tried!)
activateMarker: function (e) {
var marker = e.layer;
var feature = e.layer.feature;
this.resetMarkers();
if (feature.properties.hasOwnProperty('icon')) {
feature.properties.icon['oldIcon'] = feature.properties.icon['iconUrl'];
feature.properties.icon['iconUrl'] = feature.properties.icon['iconActive'];
feature.properties.icon['oldIconSize'] = feature.properties.icon['iconSize'];
feature.properties.icon['iconSize'] = feature.properties.icon['iconSizeActive'];
}
if (feature.geometry.type == 'Point') {
marker.setZIndexOffset(10001);
marker.addTo(featureLayer);
}
//featureLayer.setGeoJSON(jsonFeed);
}
Any advice would be greatly appreciated! I'm at the point where I don't know what else to try (and that's saying something).
What probably happens is that you just flush your markers with the last call to .setGeoJSON():
If the layer already has features, they are replaced with the new features.
You correctly adjust the GeoJSON data related to your icon, so that when re-created, your featureLayer can use the new values to show a new icon (depending on how you configured featureLayer).
But anything you changed directly on the marker is lost, as the marker is removed and replaced by a new one, re-built from the GeoJSON data.
The "cleanest" way would probably be to avoid re-creating all features at every click.
Another way could be to also change something else in your GeoJSON data that tells featureLayer to build your new marker (through the pointToLayer option) with a different zIndexOffset option.
I just started using AmCharts and have setup two line plots, one on top of the other, with their respective scrollbars.
Now, I want to "link" the scrollbars of both plots, so that if I move the scrollbar on the chart1, I'll get the same date range on the chart2. I imagine this shouldn't be too difficult with a listener, a get value function and a set value function, but I'm unable to find how to get the start/end values of the scrollbar so that I can play with them.
Any help would be appreciated.
thanks
There is a demo for this in the AmCharts Knowledge Base
https://www.amcharts.com/kbase/share-scrollbar-across-several-charts/
This is the code that is syncing the scrollbars, (I have added the annotations):
Create an array to populate with your charts
var charts = [];
Create how ever many charts you need
charts.push(AmCharts.makeChart("chartdiv", chartConfig));
charts.push(AmCharts.makeChart("chartdiv2", chartConfig2));
charts.push(AmCharts.makeChart("chartdiv3", chartConfig3));
Iterate over the charts, adding an event listener for "zoomed" which will share the event handler
for (var x in charts) {
charts[x].addListener("zoomed", syncZoom);
}
The event handler
function syncZoom(event) {
for (x in charts) {
if (charts[x].ignoreZoom) {
charts[x].ignoreZoom = false;
}
if (event.chart != charts[x]) {
charts[x].ignoreZoom = true;
charts[x].zoomToDates(event.startDate, event.endDate);
}
}
}
I am trying to add series to my chart application using gwt-highchart (using the latest gwt-highchart 1.6.0 and Highstock 2.3.4 versions). Everything seems fine until the third series. When I try to add the third one I got this error:
com.google.gwt.core.client.JavaScriptException: (String)
#org.moxieapps.gwt.highcharts.client.BaseChart::nativeAddSeries(Lcom/google/gwt/core
/client/JavaScriptObject;Lcom/google/gwt/core/client/JavaScriptObject;ZZ)([JavaScript
object(4953), JavaScript object(5135), bool: true, bool: true]): Highcharts error #18:
www.highcharts.com/errors/18
And here is my code (runs within a loop):
// Create a new serie with a new yAxis
Series newSeries = chart.createSeries().setYAxis(index).setPlotOptions(new LinePlotOptions().setColor(tag.getColor()));
// Set new yAxis options
chart.getYAxis(index).setPlotLines(chart.getYAxis(index).createPlotLine().setValue(0).setWidth(1).setColor(tag.getColor())).setLabels(new YAxisLabels().setEnabled(false)).setTickLength(0).setOffset(60).setStartOnTick(false)
.setEndOnTick(false).setGridLineWidth(0).setMaxPadding(DEFAULT_YAXIS_MAX_PADDING).setMinPadding(DEFAULT_YAXIS_MIN_PADDING)
.setAxisTitle(new AxisTitle().setText(null).setStyle(new Style().setColor(tag.getColor())));
// Add the serie to the chart
chart.addSeries(newSeries.setName("Test " + index));
First two series are OK as I said before but third one throws the above exception (when I debug the application, I can see the newly created yAxis references).
Here is the line which throws the exception:
chart.addSeries(newSeries.setName("Test " + index));
Thanks
I've figured it out finally!
GWT-HighCharts seems to be the problem. It does not add the new YAxis to the Chart at all. So you must add YAxis via native calls like this;
private static native void nativeAddAxis(JavaScriptObject chart, JavaScriptObject axisOptions, boolean isX, boolean redraw, boolean animationFlag) /*-{
chart.addAxis(axisOptions, isX, redraw, animationFlag);
}-*/;
Just call this native method before adding the new series.
// Create new series
Series newSeries = chart.createSeries().setYAxis(index);
newSeries.setPlotOptions(new LinePlotOptions().setColor(tag.getColor()));
newSeries.setName(index + 1 + ") ");
// Create a new YAxis
YAxis yAxis = chart.getYAxis(index).setPlotLines(chart.getYAxis(index).createPlotLine().setValue(0).setWidth(1).setColor(tag.getColor())).setLabels(new YAxisLabels().setEnabled(false)).setTickLength(0).setOffset(60)
.setStartOnTick(false).setEndOnTick(false).setGridLineWidth(0).setPlotLines().setMaxPadding(DEFAULT_YAXIS_MAX_PADDING).setMinPadding(DEFAULT_YAXIS_MIN_PADDING)
.setAxisTitle(new AxisTitle().setText(null).setStyle(new Style().setColor(tag.getColor())));
// IMPORTANT!: New YAxis must be added to the chart via native calls since gwt-highcharts wrapper doesn't do that properly!
nativeAddAxis(chart.getNativeChart(), yAxis.getOptions().getJavaScriptObject(), false, false, false);
// Physical attach
chart.addSeries(newSeries);
Here is the cause of this type of errors:
If you are using GWT-HighCharts wrapper, you must make the configuration before adding chart to DOM! It seems that after adding it to the DOM, any configuration changes does not seem to work at all!
Happy coding!
please check the index value.
if index is more than the axis count this error may occur
highcharts error #18 indicates that the axis trying to access does not exist.
here is the link http://www.highcharts.com/errors/18
Hope that will help you
I want to set a minimum and a maximum zoom level in my map.
My first idea was to listen to 'zoomstart' events, but the org.gwtopenmaps.openlayers.client.Map class doesn't implement any listener with such event type. Then I tried to listen to 'zoomend' events. My idea was to check the zoomlevel after the zoom event and if it is higher/lower than a threshold value than i zoom to that threshold value. Example code:
#Override
public void onMapZoom(MapZoomEvent eventObject) {
if (eventObject.getSource().getZoom() > 18) {
eventObject.getSource().zoomTo(18);
}
}
But i found, the zoomTo event doesn't fire in this case. Has anybody got a solution to this problem?
Great idea Imreking.
I have added this to the GWT-Openlayers library.
So if you download the latest version from github now you can do :
map.setMinMaxZoomLevel(6, 8);
And you no longer need some javascript method in your own code.
I actually also added a showcase but having difficulties uploading it to our website.
Uploading the new showcase has now succeeded.
See http://demo.gwt-openlayers.org/gwt_ol_showcase/GwtOpenLayersShowcase.html?example=Min%20max%20zoom%20example to see an example of newly added Map.setMinMaxZoomLevel(minZoom, maxZoom).
I don't think this is possible in OpenLayers (normal and GWT).
According to me two solutions are available.
Option 1
This is ugly for the user. As he sees the map getting zoomed, and just after this going back to the previous zoomlevel.
The Timer is needed to give OL the chance to animate the zoom.
map.addMapZoomListener(new MapZoomListener()
{
#Override
public void onMapZoom(final MapZoomEvent eventObject)
{
Timer t = new Timer()
{
#Override
public void run()
{
if (eventObject.getSource().getZoom() > 15)
{
map.zoomTo(15);
}
else if (eventObject.getSource().getZoom() < 10)
{
map.zoomTo(10);
}
}
};
t.schedule(500);
}
});
Option 2
Don't use the zoom default zoom control but create your own zoom buttons (using normal GWT), and putting these on top of the map. If you want you can style these buttons in the same way as the normal buttons. The trick 'create in normal GWT, and make it look like OL' is a trick I use a lot (for example to create a much more advanced layer switcher).
Note : I am one of the developers of GWT-OpenLayers, if you want I can add an example to our showcase displaying how to do 'Option 2'.
Knarf, thank you for your reply. I tried the 'Option 1' and it worked, but i found another solution which is maybe more acceptable for the users.
My solution is:
map.isValidZoomLevel = function(zoomLevel) {
return ((zoomLevel != null) &&
(zoomLevel >= minZoomLevel) &&
(zoomLevel <= maxZoomLevel) &&
(zoomLevel < this.getNumZoomLevels()));
}
I overrode the isValidZoomLevel method. The minZoomLevel and maxZoomLevel variables were set when the application started. I don't like calling javascript from GWT code, but here i didn't have any other opportunity.