tutorials.de Buch-Aktion 05/2012
  • How to: HTML Map Area Polygon Koordinaten einer Schaltfläche auf transparentem PNG in C# berechnen

    Anforderung:

    Wir haben eine Landkarte als PNG. Nun wollen wir je nach Einstellung eine Region auf dieser Karte markieren und klickbar machen. Dazu erhalten wir weitere PNGs, auf welchen eine Region eingefärbt und der Rest transparent ist. Dieses PNG ist exakt gleich größ wie die Landkarte und wird im Web über die Landkarte gelegt.

    Aufgabe: Verlinke diese Region.
    Problem: Region hat beliebige Form (kein Quadrat,etc.). Man benötigt eine HTML Map mit einem Polygon als Area. Dazu benötigt man Koordinaten. Wir wollen aber nicht für 100e Regionen Koordinaten händisch finden.
    Eingeschränkte Lösung 1: Kleine C# Klasse, welche das PNG öffnet und von allen vier Rändern weg abtastet und die Koordinaten in der Richtigen Reihenfolge retourniert. Einen Offset für eventuellen Rand kann man auch einstellen. Funktioniert bei einfachen geometrischen Objekten sehr gut (Rechteck, Kreis), scheitert aber bei ausgefallenen Objekten (siehe ANhang).

    Lösung 2:Suche alle Pixel, welche ein transparentes Pixel als Nachbar haben. Danach starte bei beliebigem Pixel und wandere den Rand entlang und sortiere die Pixel. Danach werden Geraden gesucht und unnötige Punkte entfernt. Funktioniert mit allen getesteten Grafiken. Siehe Anhang.

    Einfach dem Konstruktor einen Dateipfad oder einen Filestream übergeben. Auf das Ergebnis kann mit Properties zugegriffen werden. Kann in beliebige .Net Anwendung eingebaut werden. Ein kleines Test-PNG im Anhang.

    Have fun.

    Lösung 1:
    Code csharp:
    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
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.IO;
     
    namespace PNG_Get_HTML_area
    {
     class BitmapZuHtmlKoordinaten
        {
            private string _filename;
            private string _html_area;
            private string _coords;
            private List<pos> _li;
            private Stream _stream;
            private int _offset = 1;
     
            public string HtmlArea { get { return _html_area; } }
            public string Coords { get { return _coords; } }
     
            struct pos
            {
               internal int x;
               internal int y;
            };
     
            //kann exception werfen
            public BitmapZuHtmlKoordinaten(Stream stream)
            {
                _stream = stream;
                _li = new List<pos>();
                this.calculateObjectBorderCoords();
                this.buildHtmlArea();
            }
     
     
            //kann exception werfen
            public BitmapZuHtmlKoordinaten(string filename)
            {
                _filename = filename;
                _li = new List<pos>();
                this.calculateObjectBorderCoords();
                this.buildHtmlArea();
            }
     
            private void buildHtmlArea()
            {
                string temp = "<area shape=\"poly\" coords=\"";
                string end = "\" href=\"{0}\" alt=\"{1}\" title=\"{2}\">";
     
                foreach (pos p in _li)
                {
                    _coords += p.x + "," + p.y + ",";
                }
     
                _coords = _coords.TrimEnd(',');
                temp += _coords;
                temp += end;
                _html_area = temp;
            }
     
            private void calculateObjectBorderCoords()
            {
                Bitmap bitmap = null;
                if (!String.IsNullOrEmpty(_filename) && !String.IsNullOrWhiteSpace(_filename))
                    bitmap = new Bitmap(_filename);
                else if (_stream != null)
                    bitmap = new Bitmap(_stream);
                
                //x ist Breite
                //y ist Höhe
     
                //oben nach unten
                for (int i = _offset; i < bitmap.Size.Width - _offset; i++)
                {
                    for (int j = _offset; j < bitmap.Size.Height - _offset; j++)
                    {
                        if (bitmap.GetPixel(i, j).A > 10)
                        {
                            pos neuespixel;
                            neuespixel.x = i;
                            neuespixel.y = j;
                            if (!_li.Contains(neuespixel))
                                _li.Add(neuespixel);
                            break;
                        }
                    }
                }
     
     
                //rechts nach links 
                for (int i = _offset; i < bitmap.Size.Height - _offset; i++)
                {
                    for (int j = bitmap.Size.Width - 1 - _offset; j >= _offset; j--)
                    {
                        if (bitmap.GetPixel(j, i).A > 10)
                        {
                            pos neuespixel;
                            neuespixel.x = j;
                            neuespixel.y = i;
                            if (!_li.Contains(neuespixel))
                                _li.Add(neuespixel);
                            break;
                        }
                    }
                }
     
                //unten nach oben 
                for (int i = bitmap.Size.Width - 1 - _offset; i >= _offset; i--)
                {
                    for (int j = bitmap.Size.Height - 1 - _offset; j >= _offset; j--)
                    {
                        if (bitmap.GetPixel(i, j).A > 10)
                        {
                            pos neuespixel;
                            neuespixel.x = i;
                            neuespixel.y = j;
                            if (!_li.Contains(neuespixel))
                                _li.Add(neuespixel);
                            break;
                        }
                    }
                }
     
                //links nach rechts
                for (int i = bitmap.Size.Height - 1 - _offset; i >= _offset; i--)
                {
                    for (int j = _offset; j < bitmap.Size.Width - _offset; j++)
                    {
                        if (bitmap.GetPixel(j, i).A > 10)
                        {
                            pos neuespixel;
                            neuespixel.x = j;
                            neuespixel.y = i;
                            if (!_li.Contains(neuespixel))
                                _li.Add(neuespixel);
                            break;
                        }
                    }
                }
            }
        
     
     
            static void Main(string[] args)
            {
                BitmapZuHtmlKoordinaten p1 = new BitmapZuHtmlKoordinaten(@"C:\Unbenannt.png");         
            }
     
        }
    }


    Lösung 2:
    Code csharp:
    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
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.IO;
     
    namespace PNG_Get_HTML_area
    {
        class BitmapZuHtmlKoordinaten
        {
            private string _filename;
            private string _html_area;
            private string _coords;
            private List<pos> _li;
            private Stream _stream;
            private int _offset = 1;
            private int _toleranz = 10;
            private Bitmap _bitmap = null;
     
     
            public string HtmlArea { get { return _html_area; } }
            public string Coords { get { return _coords; } }
     
            struct pos
            {
                internal int x;
                internal int y;
            };
     
            //kann exception werfen
            public BitmapZuHtmlKoordinaten(Stream stream)
            {
                _stream = stream;
                _li = new List<pos>();
                this.calculateObjectBorderCoords();
                this.buildHtmlArea();
            }
     
     
            //kann exception werfen
            public BitmapZuHtmlKoordinaten(string filename)
            {
                _filename = filename;
                _li = new List<pos>();
                this.calculateObjectBorderCoords();
                this.buildHtmlArea();
            }
     
            private void buildHtmlArea()
            {
                string temp = "<area shape=\"poly\" coords=\"";
                string end = "\" href=\"{0}\" alt=\"{1}\" title=\"{2}\">";
     
                foreach (pos p in _li)
                {
                    _coords += p.x + "," + p.y + ",";
                }
     
                _coords = _coords.TrimEnd(',');
                temp += _coords;
                temp += end;
                _html_area = temp;
            }
     
            private void calculateObjectBorderCoords()
            {
     
                if (!String.IsNullOrEmpty(_filename) && !String.IsNullOrWhiteSpace(_filename))
                    _bitmap = new Bitmap(_filename);
                else if (_stream != null)
                    _bitmap = new Bitmap(_stream);
     
    int nachbar = 0;
                bool neustart;
     
                do
                {
                    neustart = false;
                    for (int i = _offset; i < _bitmap.Size.Width - _offset; i++)
                    {
                        for (int j = _offset; j < _bitmap.Size.Height - _offset; j++)
                        {
                            if (_bitmap.GetPixel(i, j).A > _toleranz)
                            {
                                //<7 transparente Nachbarn[def. rechts links oben unten und diagonale pixel]
                                nachbar = 0;
                                if (_bitmap.GetPixel(i - 1, j).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i + 1, j).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i, j - 1).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i, j + 1).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i + 1, j + 1).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i - 1, j + 1).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i + 1, j - 1).A <= _toleranz)
                                    nachbar++;
                                if (_bitmap.GetPixel(i - 1, j - 1).A <= _toleranz)
                                    nachbar++;
     
                                if (nachbar < 5 && nachbar > 0)
                                {
                                    pos pixel;
                                    pixel.x = i;
                                    pixel.y = j;
                                    _li.Add(pixel);
                                }
                                else if (nachbar >= 5)
                                {
                                    //dieses "Sackgasse" pixel löschen von bild und berechnung neu starten (ineffizient gelöst)
                                    _bitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0, 0));
                                    neustart = true;
                                    //_li.Clear();
                                    //break;
                                }
                            }
                        }
                    }
                    if (neustart)
                       _li.Clear();
                } while (neustart);
     
               if(_li.Count == 0)
                   return;
     
                //alle Pixel durchgehen und immer direkt anliegendes nehmen
                List<pos> li = new List<pos>();
                pos start = _li[0];
                _li.Remove(_li[0]);
                pos temp = start;
                bool gefunden;
                do
                {
                    gefunden = false;
                    foreach (pos px in _li)
                    {
                        if (px.x == temp.x - 1 && px.y == temp.y)
                            gefunden = true;
                        else if (px.x == temp.x + 1 && px.y == temp.y)
                            gefunden = true;
                        else if (px.x == temp.x && px.y == temp.y - 1)
                            gefunden = true;
                        else if (px.x == temp.x && px.y == temp.y + 1)
                            gefunden = true;
     
                        if (gefunden)
                        {
                            //System.Console.WriteLine("nn");
                            temp = px;
                            li.Add(temp);
                            _li.Remove(temp);
                            break;
                        }
                    }
                    if (!gefunden) //kann passieren, dass zu früh auf falsches pixel abgebogen wird, dann ist liste nicht leer.
                        break;
                } while (_li.Count > 0);
     
                _li = li;
     
     
                //wenn Koordinaten Gerade bilden werden unnötige Punkte entfernt.
                bool aufraumen;
                do
                {
                    aufraumen = false;
                    if (_li.Count < 3)
                        break;
     
                    for (int i = 2; i < _li.Count; i++)
                    {
                        pos first = _li[i - 2];
                        pos second = _li[i - 1];
                        pos third = _li[i];
     
                        double divisor1 = (second.x - first.x);
                        double divisor2 = (third.x - second.x);
     
                        if (divisor1 != 0 && divisor2 != 0)
                        {
                            double steigung1 = (second.y - first.y) / (second.x - first.x);
                            double steigung2 = (third.y - second.y) / (third.x - second.x);
     
                            double hoeher = steigung1 > steigung2 ? steigung1 : steigung2;
                            double tiefer = hoeher == steigung1 ? steigung2 : steigung1;
     
                            if (hoeher - tiefer < 0.1)
                            {
                                _li.Remove(second);
                                aufraumen = true;
                                break;
                            }
                        }
                        else
                        {
                            if (divisor1 == divisor2)
                            {
                                _li.Remove(second);
                                aufraumen = true;
                                break;
                            }
                        }
                    }
                } while (aufraumen);
     
            static void Main(string[] args)
            {
                BitmapZuHtmlKoordinaten p1 = new BitmapZuHtmlKoordinaten(@"C:\suedsteiermark ot.png");
            }
     
        }
    }


    Schwachstelle: ineffizient, aber da jedes Bild nur einmal berechnet wird und Daten dann in DB gespeichert werden ist das egal.
     
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken content/attachments/58291-oststeiermark-ot.png.html  
    Angehängte Grafiken Angehängte Grafiken  


    Kommentare Kommentar schreiben

    Klicke hier, um dich anzumelden

    Wie nennt man ein vierbeiniges Tier, das bellen kann?