I'd like to make a JFreeChart Histogram that maintains a constant number of bins while zooming in and out. For example, maybe zoomed way out you can see 12 years, and there would be 1 bin for each year. When you zoom in a little more you might see 12 months and there would be a bin for each month. Zooming in even further maybe there are 1 bin for each day, each hour, etc. They don't have to be such clean bin sizes, but you get the idea.
I'm using HistogramDataset.addSeries to add my data to the histogram. The bins parameter, is the number of bins for the entire set and doesn't take zooming into consideration.
I've observed that I can use a ChartPanel and override its zoom method. In here I could potentially alter the number of bins. However, I'm having a hard time figuring out where I can alter the number of bins and what it should be altered to.
EDIT:
There are a few parts of this problem that I am interested in that I believe are critical to a solution:
How best to tell that the user has zoomed. My current approach is to override zoom.
How to determine the bounds that the user has zoomed to. I'm still not sure how to do this.
How to change the number of bins in the data model so that zooming maintains the same number of shown bins.
For some context, my end goal is to create a Google finance style control:
Google Finance style control for Java?
Overloading the zoom feature is appealing, but perhaps confusing. As an alternative, consider adding a control that updates the data model, as shown in this example. Instances of an enum are particularly convenient for populating a JComboBox, as shown here.
To answer my individual questions above:
I continued to override zoom on the ChartPanel class.
In order to determine the range that the user zoomed to I used the windowToRatio and ratioToModel functions below, as the zoom function is given window coordinates.
To change the number of bins, I actually kept the number of bins the same and simply changed the dataset passed into the chart. I used a SortedMap for my data model that I could easily get subMap's from.
Functions:
private double windowToRatio(double window) {
Rectangle2D scaledDataArea = getScreenDataArea();
return (window - scaledDataArea.getMinX()) / scaledDataArea.getWidth();
}
private double ratioToModel(double ratio) {
Range domainRange = getChart().getXYPlot().getDomainAxis().getRange();
return domainRange.getLowerBound() + ratio * domainRange.getLength();
}
Related
I have had issues trying to change the order in which data series are layed out in a ScatterChart.
Basically, regarding multiple series, the layout order is given from the order the series are added to the chart. Yet, if I remove one and add it again later, it will appear on top.
I tried :
myChart.getData().add(specificIndex, series)
to make sure that the last one is not on top, but it doesn't work
I also tried:
myChart.getData().add(lastSeries);
myChart.getData().stream().sort((seriesA, seriesB)->myComparator(seriesA, seriesB));
But it does not work either.
Also, I experience similar problems when it comes to displaying data points.
Basically, I am trying to create a scattered heatmap where points are colored depending on their density on the chart. So the data is associated with a Rectangle Node that is colored with a hue proportional to the number of nodes present within a given radius.
This is fine, except that in very dense region, some points are hiding others with higher density, and the end result is not representative of the density function...
The only work around was this (for series):
myChart.getData().clear()
myChart.getData().addAll(myCorrectlyOrderedListOfSeries);
which seems very dirty and computationnaly inneficient...
any other way?
EDIT
I actually solved the problem for data Point, realizing that there was a "toFront" method.
Yet, since I don't have a node for the series, I don't know on which Item I should call the toFront() regarding the series. Do I really need to call in on all data points from all series, in the order I want for the series?
I checked the Node tree generated by a scatter chart, and in fact, all the dataPoints are actually children of the same group, regardless of the Series they belong too.
So there is no other way than the following to put the series in the correct order:
//List<Series<Double, Double>> myOrderedListOfSeries;
myOrderedListOfSeries.forEach(series->
series.getData().forEach(dataPoint->
dataPoint.getNode().toFront()
)
);
I implemented the diamond square algorithm in Java, but i'm not entirely satisfied with the results as a height map. It forms a lot of "lakes" - small areas of low height. The heights are generated using the diamond square algorithm, then normalized. In the example below, white = high, black = low and blue is anything below height 15: a placeholder for oceans.
This image shows the uncolored height map
How can I smooth the terrain to reduce the number of lakes?
I've investigated a simple box blurring function (setting each pixel to the average of its neighbors), but this causes strange artifacts, possibly because of the square step of the diamond square.
Would a different (perhaps gaussian) blur be appropriate, or is this a problem with my implementation? This link says the diamond square has some inherent issues, but these don't seem to be regularly spaced artifacts, and my heightmap is seeded with 16 (not 4) values.
Your threshold algorithm needs to be more logical. You need to actually specify what is to be removed in terms of size, not just height. Basically the simple threshold sets "sea level" and anything below this level will be water. The problem is that because the algorithm used to generate the terrain is does so in a haphazard way, small areas could be filled by water.
To fix this you need to essentially determine the size of regions of water and only allow larger areas.
One simple way to do this is to not allow single "pixels" to represent water. Essentially either do not set them as water(could use a bitmap where each bit represents if there is water or not) or simply raise the level up. This should get most of the single pixels out of your image and clear it up quite a bit.
You can extend this for N pixels(essentially representing area). Basically you have to identify the size of the regions of water by counting connected pixels. The problem is this, is that it allows long thin regions(which could represent rivers).
So it it is better to take it one step further and count the width and length separate.
e.g., to detect a simple single pixel
if map[i,j] < threshold && (map[i-1,j-1] > threshold && ... && map[i+1,j+1] > threshold) then Area = 1
will detect isolated pixels.
You can modify this to detect larger groups and write a generic algorithm to measure any size of potential "oceans"... then it should be simple to get generate any height map with any minimum(and maximum) size oceans you want. The next step is to "fix" up(or use a bitmap) the parts of the map that may be below sea level but did not convert to actual water. i.e., since we generally expect things below sea level to contain water. By using a bitmap you can allow for water in water or water in land, etc.
If you use smoothing, it might work just as well but you still will always run in to such problems. Smoothing reduces the size of the "oceans" but a large ocean might turn in to a small one and a small one eventually in to a single pixel. Depending on the overall average of the map, you might end up with all water or all land after enough iterations. Blurring also reduces the detail of the map.
The good news is, that if you design your algorithm with controllable parameters then you can control things like how many oceans are in the map, or how large they are, how square they are(or how circular if you want), or how much total water can be used, etc).
The more effort you put in to this you more accurate you can simulate reality. Ultimately, if you want to be infinitely complex you can take in to account how terrains are actually formed, etc... but, of course, the whole point of these simple algorithms is to allow them to be computable in reasonable amounts of time.
Question:
In what ways can I improve render-time in scenario described below?
Description:
The task I'm facing is plotting quite a few points on a scatter chart in JavaFX. I'm getting the data from an Oracle DB.
I think my current bottleneck is scatterChart.getData().add(series);
The series contains all the datapoints needed in the chart.
Executing this, and rendering the chart on screen may take anywhere from several seconds to several minutes. During this time the GUI freezes up.
We're running some Lenovo Thinkpads with 4 Cores 4 Threads, Intel HD4000 Graphics.
Some examples of finished charts are below. The latter being the current worst case, but as more data is collected, more data will be displayed.
(due to images being impractically large, only links)
Chart with ~5.5K points
Chart with ~75K points
More info
I followed this tutorial because I'm new to JavaFX, so I'm not sure exactly how rendering happens.
Code sample
This isn't the actual code I'm using, it's just to illustrate how I'm doing things.
//Reference to the chart in the fxml file.
#FXML
private ScatterChart<Date, Number> scatterChart
= new ScatterChart<>(new DateAxis(), new NumberAxis());
private void handleSelectionUpdate(PerfResultSet newValue){
//newValue is an object containing all the data from the database
XYChart.Series<Date, Number> series = new XYChart.Series<>();
/**
* adding all the data from newValue to series as
* XYChart.Data<Date, Number>
*
* this is handled by multiple threads and does not take long
*/
series.setName("myName");
//this statement is where the gui freezes
//can I optimize this in any way?
scatterChart.getData().add(series);
}
I faced similar problem - I was drawing route consisting of many points to google map with Javascript. I eliminated points taking to account current scale. I saw your png files, it seems you really need ~30-50 points for expressing the following message - hey, this vertical/horizontal segment contains so many points you may assume it solid line!
Next problem is max scale - I think, each solid line in 75K chart is actually not solid. I mean, in small scale, I see a lot points like solid line ____ but in max scale I see them as . . . ... . .. But usually, max scale required only for small piece of chart and you may eliminate not by scale but by borders. Again reference to google map - when you zoom the map, you are not seeing whole globe in cool resolution but some small piece of map. Other borders eliminated automatically.
Post here your code, some information about scale, zoom (do you have such feature?). If you haven't and your chart is always as in png files in link, you should iterate over each vertical or horizontal line, look for sequental points and replace, for example, 100 sequantal points to middle point of that 100 points. Instead of 100 experimentally find such X, that replacing sequental X points to one middle-value-point does not affect the resulting chart.
I repeat the main idea - There exist some X, such that drawing X sequental points equal to drawing 1 point in some fixed scale - many points does not give precision but simply "overwrites" each other due to scale and human eye limitations.
Some pseudocode for finding X if your scale is constant;
int MAGIC = 50; //change it on each run of your program! You have to find the best value
int counter=1; // points[0] taken as included in accumulating solid segment
int startIndex=0,endIndex;
ArrayList<Integer> compressedDatesArray = new ArrayList<Integer>();
for(int i=1;i<datesArray.length;i++){
if(deltaBetweenDates(datesArray[i]-datesArray[i-1])==1){
counter++;
endIndex=i;
if(counter==MAGIC){
counter=0;
compressedDatesArray.add(datesArray[{endIndex-startIndex)/2]);
startIndex=endIndex+1;
}
}
}
Render compressedArray, see png. If result is bad - chnage MAGIC and repeat
UPDATE
You may binary search MAGIC - try 5000, if still solid after comperssing 5000 points to one, try bigger value if not, try 2500 and so on... Humans also may binary search something=)))
Also, do not forget to remember Y value of point representing big segment. I mean, also declare compressedY and add correspinding Y of datesArray[(endIndex-startIndex/2)]
I'm looking for a way/algorithm to make a robot balloon fly to a certain altitude. The robot is controlled by a Raspberry Pi and has a propeller. Propeller speed can be set to several values (it uses PWM so technically 1024 different power outputs).
The balloon has a distance sensor pointing down, so it's possible to get the current height several times per second.
Only idea I had so far was to measure the height constantly and set to max speed based on the height left to travel. This doesn't seem like the best option though, but can't figure out how to fit all power outputs in.
Any ideas would be welcome. I'm using Java to code the project but any high-level algorithm/description would be great!
Thx,
Magic
There is a great "game" available that lets you try and play around with exactly that problem: Colobot (seems to be open source now). Create a Winged-Grabber (or shooter if you are more the FPS type of person) and try to get it to fly to a specific destination using only the altitude and motor controls.
in general the Pseudo-Code by MadConan is the way to go, however the main task lies in writing a smart setPower function. In the end you need some smoothing function that reduces the power in relation to how close you are to your desired altitude, however the exact values of that function completely depend on your hardware and the weight of your final system.
Depending on how valuable and/or fragile your setup will be in the end, you might want to develop a learning system, that takes the under-/overshot as a basis to adjust the smoothing function while it runs. Make sure to take factors like up-/down-wind into your calculation.
Pseudo code.
while(true){
val height = getHeight(); // from sensor
// Get the difference between the current height and
// the TARGET height. Positive values mean too low
// while negative values mean too high
val offset = TARGET_VALUE - height;
// Set the power to some direct ratio of the offset
// When the baloon is at 0 height, the offset should
// be relatively high, so the power will be set
// high. If the offset is negative, the power will be
// set negative from the current power.
setPower(offset);// I'll leave it up to you to figure out the ratio
}
I have an app where the user draws pictures and then these pictures are converted to pdf. I need to be able to crop out the whitespace before conversion. Originally I kept track of the highest and lowest x and y values (http://stackoverflow.com/questions/13462088/cropping-out-whitespace-from-a-user-drawn-image). This worked for a while, but now I want to give the user the ability to erase. This is a problem because if for example the user erases the topmost point the bounding box would change, but I wouldn't the new dimensions of the box.
Right now I'm going through the entire image, pixel by pixel, to determine the bounding box. This isn't bad for one image, but I'm going to have ~70, it's way too slow for 70. I also thought about keeping every pixel in an arraylist, but I don't feel like that would work well at all.
Is there an algorithm that would help me solve this? Perhaps something already built in? Speed is more important to me than accuracy. If there is some whitespace left on each side it won't be a tragedy.
Thank you so much.
You mentioned that you are keeping track of the min and max values for X and Y co-ordinates (that also seems the solution you have chosen in the earlier question).
In similar way to this, you should be able to find the min and max X & Y co-ordinates for the erased area, from the erase event...
When the user erases part of the image, you can simply compare the co-ordinates of the erased part with the actual image to find the final co-ordinates.
There is a related problem of trying to see if 2 rectangles overlap:
Determine if two rectangles overlap each other?
You can use similar logic (though slightly different) and figure out the final min/max X & Y values.