comparison flys-artifacts/src/main/java/de/intevation/flys/jfree/StableXYDifferenceRenderer.java @ 1884:4ae9c92feb8c

StableXYDifferenceRenderer: Make rendering work with definition holes. flys-artifacts/trunk@3243 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Sun, 13 Nov 2011 14:02:35 +0000
parents eb671699fbc2
children 110dd067bb8f
comparison
equal deleted inserted replaced
1883:eb671699fbc2 1884:4ae9c92feb8c
114 import org.jfree.chart.renderer.xy.XYItemRenderer; 114 import org.jfree.chart.renderer.xy.XYItemRenderer;
115 import org.jfree.chart.renderer.xy.XYItemRendererState; 115 import org.jfree.chart.renderer.xy.XYItemRendererState;
116 116
117 import gnu.trove.TDoubleArrayList; 117 import gnu.trove.TDoubleArrayList;
118 118
119 import de.intevation.flys.artifacts.math.Linear;
120
119 /** 121 /**
120 * A renderer for an {@link XYPlot} that highlights the differences between two 122 * A renderer for an {@link XYPlot} that highlights the differences between two
121 * series. The example shown here is generated by the 123 * series. The example shown here is generated by the
122 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart 124 * <code>DifferenceChartDemo1.java</code> program included in the JFreeChart
123 * demo collection: 125 * demo collection:
362 */ 364 */
363 public int getPassCount() { 365 public int getPassCount() {
364 return 2; 366 return 2;
365 } 367 }
366 368
367 public static List<XYDataset> splitByNaNs(XYDataset dataset) { 369 private static final void addSeries(
370 DefaultXYDataset ds,
371 Comparable key,
372 TDoubleArrayList xs,
373 TDoubleArrayList ys
374 ) {
375 ds.addSeries(
376 key,
377 new double [][] {
378 xs.toNativeArray(),
379 ys.toNativeArray()
380 });
381 }
382
383 protected static List<XYDataset> splitByNaNsOneSeries(
384 XYDataset dataset
385 ) {
368 List<XYDataset> datasets = new ArrayList<XYDataset>(); 386 List<XYDataset> datasets = new ArrayList<XYDataset>();
369 387
370 switch (dataset.getSeriesCount()) { 388 int N = dataset.getItemCount(0);
371 case 0: 389 TDoubleArrayList xs = new TDoubleArrayList(N);
372 return datasets; 390 TDoubleArrayList ys = new TDoubleArrayList(N);
373 case 1: 391 for (int i = 0; i < N; ++i) {
374 int N = dataset.getItemCount(0); 392 double x = dataset.getXValue(0, i);
375 TDoubleArrayList xs = new TDoubleArrayList(N); 393 double y = dataset.getYValue(0, i);
376 TDoubleArrayList ys = new TDoubleArrayList(N); 394 if (Double.isNaN(x) || Double.isNaN(y)) {
377 for (int i = 0; i < N; ++i) { 395 if (!xs.isEmpty()) {
396 DefaultXYDataset ds = new DefaultXYDataset();
397 addSeries(ds, dataset.getSeriesKey(0), xs, ys);
398 datasets.add(ds);
399 xs.reset();
400 ys.reset();
401 }
402 }
403 else {
404 xs.add(x);
405 ys.add(y);
406 }
407 }
408 if (!xs.isEmpty()) {
409 DefaultXYDataset ds = new DefaultXYDataset();
410 addSeries(ds, dataset.getSeriesKey(0), xs, ys);
411 datasets.add(ds);
412 }
413
414 return datasets;
415 }
416
417 private static final boolean add(TDoubleArrayList xs, double x) {
418 int N = xs.size();
419 if (N == 0 || xs.getQuick(N-1) < x) {
420 xs.add(x);
421 return true;
422 }
423 System.err.println("pushed smaller");
424 return false;
425 }
426
427 protected static List<XYDataset> splitByNaNsTwoSeries(
428 XYDataset dataset
429 ) {
430 List<XYDataset> datasets = new ArrayList<XYDataset>();
431
432 int N = dataset.getItemCount(0);
433 int M = dataset.getItemCount(1);
434
435 int i = 0, j = 0;
436 // ignore leading NaNs
437 for (; i < N; ++i) {
438 double x = dataset.getXValue(0, i);
439 double y = dataset.getYValue(0, i);
440 if (!Double.isNaN(x) && !Double.isNaN(y)) {
441 break;
442 }
443 }
444
445 for (; j < M; ++j) {
446 double x = dataset.getXValue(1, j);
447 double y = dataset.getYValue(1, j);
448 if (!Double.isNaN(x) && !Double.isNaN(y)) {
449 break;
450 }
451 }
452
453 TDoubleArrayList six = new TDoubleArrayList();
454 TDoubleArrayList siy = new TDoubleArrayList();
455 TDoubleArrayList sjx = new TDoubleArrayList();
456 TDoubleArrayList sjy = new TDoubleArrayList();
457
458 while (i < N && j < M) {
459 int ni = i+1;
460 for (; ni < N && !Double.isNaN(dataset.getXValue(0, ni)); ++ni);
461 for (; ni < N && Double.isNaN(dataset.getXValue(0, ni)); ++ni);
462
463 int nj = j+1;
464 for (; nj < M && !Double.isNaN(dataset.getXValue(1, nj)); ++nj);
465 for (; nj < M && Double.isNaN(dataset.getXValue(1, nj)); ++nj);
466
467 if (ni == N && nj == M) { // no more splits
468 System.err.println("no more splits ....");
469 for (; i < ni; ++i) {
378 double x = dataset.getXValue(0, i); 470 double x = dataset.getXValue(0, i);
379 double y = dataset.getYValue(0, i); 471 double y = dataset.getYValue(0, i);
380 if (Double.isNaN(x) || Double.isNaN(y)) { 472 if (!Double.isNaN(x)
381 if (!xs.isEmpty()) { 473 && !Double.isNaN(y)
382 DefaultXYDataset ds = new DefaultXYDataset(); 474 && add(six, x)) {
383 datasets.add(ds); 475 siy.add(y);
384 ds.addSeries( 476 }
385 dataset.getSeriesKey(0), 477 }
386 new double [][] { 478 for (; j < nj; ++j) {
387 xs.toNativeArray(), 479 double x = dataset.getXValue(1, j);
388 ys.toNativeArray() }); 480 double y = dataset.getYValue(1, j);
389 xs.reset(); 481 if (!Double.isNaN(x)
390 ys.reset(); 482 && !Double.isNaN(y)
483 && add(sjx, x)) {
484 sjy.add(y);
485 }
486 }
487 if (!six.isEmpty() && !sjx.isEmpty()) {
488 DefaultXYDataset ds = new DefaultXYDataset();
489 addSeries(ds, dataset.getSeriesKey(0), six, siy);
490 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
491 datasets.add(ds);
492 }
493 break;
494 }
495
496 System.err.println("ni: " + ni + " " + N);
497 System.err.println("nj: " + nj + " " + M);
498
499 double xni = ni < N
500 ? dataset.getXValue(0, ni)
501 : Double.MAX_VALUE;
502
503 double xnj = nj < M
504 ? dataset.getXValue(1, nj)
505 : Double.MAX_VALUE;
506
507 double xns = Math.min(xni, xnj);
508
509 double pushxi = Double.NaN;
510 double pushyi = Double.NaN;
511 double pushxj = Double.NaN;
512 double pushyj = Double.NaN;
513
514 for (; i < ni; ++i) {
515 double x = dataset.getXValue(0, i);
516 double y = dataset.getYValue(0, i);
517 if (Double.isNaN(x) || Double.isNaN(y)) {
518 continue;
519 }
520 if (x < xns) {
521 if (add(six, x)) {
522 siy.add(y);
523 }
524 continue;
525 }
526 if (x == xns) { // exact match
527 if (add(six, x)) {
528 siy.add(y);
529 }
530 pushxi = x; pushyi = y;
531 }
532 else { // x > xns: intersection
533 System.err.println("xns: " + xns);
534 System.err.println("x/y: " + x + " / " + y);
535 int SIX = six.size();
536 if (SIX > 0) { // should always be true
537 double yns = Linear.linear(
538 xns,
539 six.getQuick(SIX-1), x,
540 siy.getQuick(SIX-1), y);
541 System.err.println("intersection at: " + yns);
542 if (add(six, xns)) {
543 siy.add(yns);
391 } 544 }
545 pushxi = xns;
546 pushyi = yns;
392 } 547 }
393 else { 548 }
394 xs.add(x); 549 break; // Split point reached.
395 ys.add(y); 550 }
551
552 for (; j < nj; ++j) {
553 double x = dataset.getXValue(1, j);
554 double y = dataset.getYValue(1, j);
555 if (Double.isNaN(x) || Double.isNaN(y)) {
556 continue;
557 }
558 if (x < xns) {
559 if (add(sjx, x)) {
560 sjy.add(y);
396 } 561 }
397 } 562 continue;
398 if (!xs.isEmpty()) { 563 }
399 DefaultXYDataset ds = new DefaultXYDataset(); 564 if (x == xns) { // exact match
400 datasets.add(ds); 565 if (add(sjx, x)) {
401 ds.addSeries( 566 sjy.add(y);
402 dataset.getSeriesKey(0), 567 }
403 new double [][] { 568 pushxj = x; pushyj = y;
404 xs.toNativeArray(), 569 }
405 ys.toNativeArray() }); 570 else { // x > xns: intersection
406 } 571 int SJX = sjx.size();
407 break; 572 if (SJX > 0) { // should always be true
573 double yns = Linear.linear(
574 xns,
575 sjx.getQuick(SJX-1), x,
576 sjy.getQuick(SJX-1), y);
577 System.err.println("intersection at: " + yns);
578 if (add(sjx, xns)) {
579 sjy.add(yns);
580 }
581 pushxj = xns; pushyj = yns;
582 }
583 }
584 break; // Split point reached.
585 }
586
587 if (!six.isEmpty() && !sjx.isEmpty()) {
588 DefaultXYDataset ds = new DefaultXYDataset();
589 addSeries(ds, dataset.getSeriesKey(0), six, siy);
590 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
591 datasets.add(ds);
592 }
593
594 six.reset(); siy.reset();
595 sjx.reset(); sjy.reset();
596
597 // Push split points.
598 if (!Double.isNaN(pushxi)) {
599 six.add(pushxi);
600 siy.add(pushyi);
601 }
602
603 if (!Double.isNaN(pushxj)) {
604 sjx.add(pushxj);
605 sjy.add(pushyj);
606 }
607 }
608
609 // Copy the rest.
610 for (; i < N; ++i) {
611 double x = dataset.getXValue(0, i);
612 double y = dataset.getXValue(0, i);
613 if (!Double.isNaN(x)
614 && !Double.isNaN(y)
615 && add(six, x)) {
616 siy.add(y);
617 }
618 }
619
620 for (; j < M; ++j) {
621 double x = dataset.getXValue(1, j);
622 double y = dataset.getXValue(1, j);
623 if (!Double.isNaN(x)
624 && !Double.isNaN(y)
625 && add(sjx, x)) {
626 sjy.add(y);
627 }
628 }
629
630 // Build final dataset.
631 if (!six.isEmpty() && !sjx.isEmpty()) {
632 DefaultXYDataset ds = new DefaultXYDataset();
633 addSeries(ds, dataset.getSeriesKey(0), six, siy);
634 addSeries(ds, dataset.getSeriesKey(1), sjx, sjy);
635 datasets.add(ds);
636 }
637
638 System.err.println("datasets after split: " + datasets.size());
639
640 return datasets;
641 }
642
643 public static List<XYDataset> splitByNaNs(XYDataset dataset) {
644
645 switch (dataset.getSeriesCount()) {
646 case 0:
647 return Collections.emptyList();
648 case 1:
649 return splitByNaNsOneSeries(dataset);
408 default: // two or more 650 default: // two or more
409 // TODO: split two parts 651 return splitByNaNsTwoSeries(dataset);
410 datasets.add(dataset); 652 }
411 break;
412 }
413
414 return datasets;
415 } 653 }
416 654
417 /** 655 /**
418 * Draws the visual representation of a single data item. 656 * Draws the visual representation of a single data item.
419 * 657 *
447 switch (pass) { 685 switch (pass) {
448 case 0: 686 case 0:
449 for (XYDataset ds: splitByNaNs(dataset)) { 687 for (XYDataset ds: splitByNaNs(dataset)) {
450 drawItemPass0(g2, dataArea, info, 688 drawItemPass0(g2, dataArea, info,
451 plot, domainAxis, rangeAxis, 689 plot, domainAxis, rangeAxis,
452 dataset, series, item, crosshairState); 690 ds, series, item, crosshairState);
453 } 691 }
454 break; 692 break;
455 case 1: 693 case 1:
456 drawItemPass1(g2, dataArea, info, 694 drawItemPass1(g2, dataArea, info,
457 plot, domainAxis, rangeAxis, 695 plot, domainAxis, rangeAxis,

http://dive4elements.wald.intevation.org