[PHP/Chartdir.] Algoritme om labeloverlapping tegen te gaan*

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Topicstarter
Ik ben een 4 kwadranten grafiek aan het maken. Nu wil het wel eens gebeuren dat punten dicht bij elkaar komen.

Zie voorbeeld grafiek (de layout wordt nog netter ;) ):
Afbeeldingslocatie: http://developer.thuis-online.nl/personeelsbeleid%20voorbeeld.png

Hoe krijg ik het voor elkaar dat als punten dicht bij elkaar liggen, dat de labels niet over elkaar heen vallen? En ook dan ze niet over de randen van de grafieken heen gaan?

Acties:
  • 0 Henk 'm!

  • Luqq
  • Registratie: Juni 2005
  • Nu online
Wat voor taal? In java kan je de breedte en hoogte van een stuk tekst met een bepaalde font opvragen, en ik denk dat je daar wel wat mee kan dan ;)

Acties:
  • 0 Henk 'm!

  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

Misschien Wikipedia: ALGO :?

[ Voor 4% gewijzigd door BtM909 op 24-10-2009 14:42 ]

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.


Acties:
  • 0 Henk 'm!

  • urk_forever
  • Registratie: Juni 2001
  • Laatst online: 17-09 15:08
Tja, als je niet aangeeft in welke taal dit is, en misschien een stukje voorbeeld code zal niemand je hier mee kunnen helpen behalve te zeggen dat je in je code moet controleren of labels elkaar misschien overlappen en ze dan op een andere manier uitlijnen tov het driehoekje.

Hail to the king baby!


Acties:
  • 0 Henk 'm!

  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Topicstarter
Taal: PHP
Library: Chartdirector

code om dit te genereren:
PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
<?php
public function fourquadrant ($data) {
    # XY points for the scatter chart
    $dataX = array();
    $dataY = array();
    
    if (is_array($data['data'][0]['data'][0]))
        foreach ($data['data'][0]['data'] as $data_point)
        {
            $dataX[] = $data_point[0];
            $dataY[] = $data_point[1];
        }
    else
        foreach ($data['data'][0]['data'] as $data_id => $data_point)
        {
            $dataX[] = $data_point;
            $dataY[] = isset($data['data'][1]['data'][$data_id]) ? $data['data'][1]['data'][$data_id] : 0;
        }
    
    //$c = new XYChart(intval($data["graph"]["width"]), intval($data["graph"]["width"]));
    $c = new XYChart(530, 530);
    
    # Add a title box to the chart using 16 pts Arial Bold Italic font, with white text
    # on deep blue background
    if (!empty($data["title"]) && $data["options"]["showtitle"])
        $textBoxObj = $c->addTitle($data['title'],"arialbd.ttf", 12, 0xf26522);
    
    # Set the plotarea at (20, 60) and of size 560 x 360 pixels, with grey (808080)
    # border, and light grey (c0c0c0) horizontal and vertical grid lines. Set 4 quadrant
    # coloring, where the colors of the quadrants alternate between lighter and deeper
    # grey (dddddd/eeeeee)
    //$plotAreaObj = $c->setPlotArea(50, 60, $data["graph"]["width"]-100, $data["graph"]["width"]-100);
    $plotAreaObj = $c->setPlotArea(50, 60, 530-100, 530-100);

    // Set axes
    $xMin = min($dataX) - 0.2;
    $xMax = max($dataX) + 0.2;
    
    $xMid = 0;
    if (!empty($data['data'][0]['normvalue']))
        $xMid = floatval($data['data'][0]['normvalue']);
    else {
        foreach ($dataX as $d) {
            $xMid += floatval($d);
        }
        
        $xMid = $xMid / count($dataX);
    }
    
    $xMid = ($xMin + $xMax)/2;
    $c->xAxis->setLinearScale2($xMin, $xMax, array($xMin,$xMid,$xMax));
    $xMark1 = $c->xAxis->addMark($xMid, 0xff0000);
    $xMark1->setLineWidth(1);
    
    $yMin = min($dataY) - 0.2;
    $yMax = max($dataY) + 0.2;
    
    $yMid = 0;
    if (!empty($data['data'][1]['normvalue']))
        $yMid = floatval($data['data'][1]['normvalue']);
    else {
        foreach ($dataY as $d) {
            $yMid += floatval($d);
        }
        
        $yMid = $yMid / count($dataY);
    }
    
    $yMid = ($yMin + $yMax)/2;
    $c->yAxis->setLinearScale2($yMin, $yMax, array($yMin,$yMid,$yMax));
    $yMark1 = $c->yAxis->addMark($yMid, 0xff0000);
    $yMark1->setLineWidth(1);
    
    if (isset($data['data'][0]['name']))
        $c->xAxis->setTitle($data['data'][0]['name']);
    if (isset($data['data'][1]['name']))
        $c->yAxis->setTitle($data['data'][1]['name']);
    
    # Set axes width to 2 pixels
    $c->xAxis->setWidth(0);
    $c->yAxis->setWidth(0);
    
    # Add scatter layer, using 15 pixels red (ff33333) X shape symbols
    $layer = $c->addScatterLayer($dataX, $dataY, "Group A", TriangleSymbol, 8, $this->chartColors[0]);
    $layer->addExtraField($data['labels']);
    $layer->setDataLabelFormat("{field0}");
    
    # Display the trend line parameters as a text table formatted using CDML
    $textbox = $c->addText('', '', $data['quadrant_labels'][0], "arialbd.ttf", 10);
    $textbox->setPos($plotAreaObj->getLeftX(),$plotAreaObj->getTopY());
    $textbox = $c->addText('', '', $data['quadrant_labels'][1], "arialbd.ttf", 10);
    $textbox->setPos($plotAreaObj->getLeftX()+$plotAreaObj->getWidth()-$textbox->getWidth(),$plotAreaObj->getTopY());
    $textbox = $c->addText('', '', $data['quadrant_labels'][2], "arialbd.ttf", 10);
    $textbox->setPos($plotAreaObj->getLeftX()+$plotAreaObj->getWidth()-$textbox->getWidth(),$plotAreaObj->getTopY()+$plotAreaObj->getHeight()-$textbox->getHeight());
    $textbox = $c->addText('', '', $data['quadrant_labels'][3], "arialbd.ttf", 10);
    $textbox->setPos($plotAreaObj->getLeftX(),$plotAreaObj->getTopY()+$plotAreaObj->getHeight()-$textbox->getHeight());
    
    # Output the chart
    header("Content-type: image/png");
    print($c->makeChart2(PNG));
    exit;
}
?>


Documentatie: http://download2.advsofteng.com/cdphpdoc_html.zip

$xMid en $yMid zijn even op de helft gezet, maar dat weet ik, dat is namelijk even om te testen.

Acties:
  • 0 Henk 'm!

  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Topicstarter
Ow, ik bedoelde algoritme. ;)

Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Ik zou zoietsdoen met mouseovers, of in Silverlight. Krijg je mooiere effecten en is het interessanter voor de gebruiker.

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Snake schreef op zaterdag 24 oktober 2009 @ 15:07:
Ik zou zoietsdoen met mouseovers, of in Silverlight. Krijg je mooiere effecten en is het interessanter voor de gebruiker.
En dan moet de gebruiker dus maar over alle stipjes gaan hoveren voordat 'ie een beetje een idee heeft wat waar staat? Het lijkt me juist belangrijk dat alles in 1 oogopslag duidelijk wordt en je idee klinkt dan ook aardig ongebruiksvriendelijk ;)

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


Acties:
  • 0 Henk 'm!

  • Matis
  • Registratie: Januari 2007
  • Laatst online: 17-09 18:39

Matis

Rubber Rocket

Ik weet niet of PHP nog steeds een redelijke optie is, maar met http://www.php.net/manual/en/function.imagettfbbox.php kun je de afmetingen van je string/lettertype opvragen. Daarmee zou je dus kunnen uitrekenen of er overlap komt en zoja waar ;)

If money talks then I'm a mime
If time is money then I'm out of time


Acties:
  • 0 Henk 'm!

  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Topicstarter
Snake schreef op zaterdag 24 oktober 2009 @ 15:07:
Ik zou zoietsdoen met mouseovers, of in Silverlight. Krijg je mooiere effecten en is het interessanter voor de gebruiker.
Dit moet in een PDF document komen. Dus er is geen vorm van interactie met gebruiker mogelijk.


Wat ik in ieder geval zou kunnen doen, is de labels aan de kant van het centrum van zo'n kwadrant kunnen alignen. Dan staat hij iig altijd van de randen af. Maar wat als er 2 punten te dicht bij elkaar staan?

[ Voor 9% gewijzigd door eghie op 24-10-2009 15:28 ]


Acties:
  • 0 Henk 'm!

  • XiniX88
  • Registratie: December 2006
  • Laatst online: 17-09 19:30
Nee er bestaat geen magische functie om dit op te lossen... Aangezien je wel een magische classe gebruikt waarvan geen inzicht wordt gegeven kan ik moeilijk een oplossing geven (ik ga de code niet opzoeken), ik kan je wel een aantal (lees: 1) functies geven waardoor je het wel kan laten werken:

Tekst print je via de GD Lib (aannemende dat chartdirector dat ook gebruikt)

http://nl.php.net/manual/en/function.imageftbbox.php

Met deze functie kan je de punten (x1, y1, x2, y2) krijgen waar de tekst begint, en waar deze zou eindigen. Kijk of elke volgende toegevoegde box in deze reeks zit, zo ja, verlaag deze naar de dichtstbijzeinde x1 of y1 (dus links van de tekst) of x2 of y2 (beneden of rechts van de box).

Ja hierbij moet je wel je interne classe waarschijnlijk aanpassen, aangezien deze labels direct boven het driehoekje plaatst.

Succes

Okey een klein voorbeeldje:

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Coordinaten array:
$array = array();

// Een paar gegeven coordinaten: (zijn 2 regels onder elkaar... x1, y1 representateren linkerboven hoek,
// x2, y2 de rechteronder hoek
$array[] = array("x1" => 100, "y1" => 100, "x2" => 200, "y2" => 120);
$array[] = array("x1" => 100, "y1" => 120, "x2" => 200, "y2" => 140);

// Nu wil ik het volgende punt toevoegen, ik moet dus gaan kijken of de punten
// niet conflicteren:

$toAdd = array("x1" => 110, "y1" => 100, "x2" => 210, "y2" => 120);

// volgende ga ik vergelijken:

foreach($array as $textItem) {
    // x1 moet verder staan dan x2, als deze binnen y1 en y2 staat
    // Zelf moet je nog kijken of x2 niet binnen een x1 van textItem valt
    if($toAdd['x1'] > $textItem['x2'] || !($toAdd['y1'] >= $textItem['y1'] && $toAdd['y1'] <= $textItem['y2']))
    {
           // Mja plaatsbaar, niets doen!
    }
    else
    {
          // Pas de x1 en x2 aan naar eigen wens, hij moet nu links of rechts  van het zojuist geteste
          // element staan. Dus geef voor x1 (van toAdd) b.v. x2 (zo zet je m dus rechts van t element neer, waarmee het conflicteerd) mee van $textItem als deze afstand kleiner is,
          // plaats m anders op de volgende regel door y2 mee te geven van $textItem aan de y1 van toAdd.
          // Laat de loop draaien om volgende conflicten op te lossen
          // Kijk dus in beide gevallen vanaf het centrum wat het dichstbijzijnde punt is.
    }
}

Alle handelingen mag je zelf verzinnen verder

Afbeeldingslocatie: http://tweakers.denhaas.info/example.png

Zie hierboven het voorbeeld, daaronder met de rode lijnen is aangegeven hoe je kan kijken wat je moet doen.

Staat hij boven de helft van het tekstelement, verschuif m naar boven, staat hij onder de helft van het tekstelement, verschuif naar onder.

Staat hij bijna recht op het tekstelement, maar dicht bij het einde van x1, verschuif m naar links.
In donkergroen is aangegeven waar hij dan komt te staan (na herberekening). De x en y punten kan je makkelijk terug berekenen.

De 2 roze lijnen geven aan tot waar hij hem naar links of rechts moet schuiven van het tekstelement waar het al stond. Neem hier b.v. 25% voor het einde, en 25% voor het begin voor. Schuif m anders naar boven of naar beneden.

Ik zie net wat in je code:
PHP:
1
2
3
4
$x1 = $plotAreaObj->getLeftX();
$x2 = $plotAreaObj->getLeftX()+$plotAreaObj->getWidth();
$y1 = $plotAreaObj->getLeftY();
$y2 = $plotAreaObj->getLeftY()+$plotAreaObj->getHeight();


zo kan je aan je variabelen komen om bovenstaande functie te doen laten werken.

[ Voor 69% gewijzigd door XiniX88 op 24-10-2009 16:38 ]


Acties:
  • 0 Henk 'm!

  • eghie
  • Registratie: Februari 2002
  • Niet online

eghie

Spoken words!

Topicstarter
XiniX88 schreef op zaterdag 24 oktober 2009 @ 16:04:
Nee er bestaat geen magische functie om dit op te lossen... Aangezien je wel een magische classe gebruikt waarvan geen inzicht wordt gegeven kan ik moeilijk een oplossing geven (ik ga de code niet opzoeken), ik kan je wel een aantal (lees: 1) functies geven waardoor je het wel kan laten werken:

Tekst print je via de GD Lib (aannemende dat chartdirector dat ook gebruikt)

http://nl.php.net/manual/en/function.imageftbbox.php

Met deze functie kan je de punten (x1, y1, x2, y2) krijgen waar de tekst begint, en waar deze zou eindigen. Kijk of elke volgende toegevoegde box in deze reeks zit, zo ja, verlaag deze naar de dichtstbijzeinde x1 of y1 (dus links van de tekst) of x2 of y2 (beneden of rechts van de box).

Ja hierbij moet je wel je interne classe waarschijnlijk aanpassen, aangezien deze labels direct boven het driehoekje plaatst.

Succes

Okey een klein voorbeeldje:
...
Bedankt. Dat is inderdaad wel een goed uitgangspunt. Ik ga eens met die methode. (by the way dan bedoel ik dat positioneren, niet de PHP functie). Ik weet dat er geen magical formula is, maar ik zocht meer naar een algoritme/gedachtegang om het op te lossen.

X1,X2,Y1,Y2 zijn op te vragen. Dan moest ik ze op andere manieren tekenen. Nu heb ik dit, waarbij ik de labels zo positioneer, dat ze niet buiten de grafiek vallen, omdat dat ook een punt was. Daarbij gebruik ik de 4 rode punten als "zwaartekracht" punten, waarnaar de labels zich naar toe trekken.

Voorbeeld plaatje:
Afbeeldingslocatie: http://developer.thuis-online.nl/personeelsbeleid%20voorbeeld1.png

PHP Code, hoe ik dat heb opgelost: http://pastie.org/668053 (te lang om hier te posten)

Acties:
  • 0 Henk 'm!

  • Voutloos
  • Registratie: Januari 2002
  • Niet online
Probeer het eens met handje. :) Photshop idee: zet je grafiek en de punten in de background, en vervolgens een layer voor elk tekstje. Verplaats nu elke layer totdat het past.

Als je niet passend krijgt, zal het met een algo ook niet lukken en zal je dus naar legenda's (of kortere labels ;) ) moeten grijpen. Als het wel past, kan je misschien afleiden waarom wel etc.

Zwaartekracht op 4 vaste locaties gaat in ieder geval niet optimaal zijn, je moet die punten in het midden van de open ruimtes hebben.

[ Voor 18% gewijzigd door Voutloos op 24-10-2009 18:40 ]

{signature}

Pagina: 1