using System.IO;
using System.Xml;
using System;
using System.Globalization;

public class GeoMap
{
    const int cIndexTitle = 0;
    const int cIndexIdMap = 1;
    const int cIndexId = 2;
    const int cIndexKfz = 3;
    const int cIndexArea = 4;
    const int cIndexPopulation = 5;

    double[] dRegionData = null;

    enum MapColorMode { WHITE, BLACK, COLOR };
    public string[] MapDetails;
    public double[] MapCoordinates;
    public int[] MapCoordinatesPx;
    public int Width;
    public int Height;
    public double[] Margin = new double[] { 0.0, 0.0, 0.0, 0.0 };
    public double[] ViewBox = new double[] { 0.0, 0.0, 0.0, 0.0 };
    public bool SetTargetSize = false;
    public double[] TargetSize = new double[] { 0.0, 0.0 };
    public double TranslateX;
    public double TranslateY;
    public string svgOutline;
    SvgStyle svgMarginStyle;
    SvgStyle svgRegionStyle;
    SvgStyle svgBorderStyle;
    SvgStyle svgOutlineStyle;
    SvgStyleText svgTextStyle;
    string TransformRegions;
    public GeoMapRegion[] Regions;
    string TransformBorders;
    public GeoMapBorder[] Borders;
    public GeoMapOverlay[] Overlays;
    public GeoMapCoordinates[] TextOffset;
    string[] Colors = new string[]
            {
                "AAAAAA",
                "AACCAA",
                "AAAACC",
                "CCAAAA",
                "CCCCAA",
                "AACCCC",
                "CCAACC",
                "AAAAAA",
                "AACCAA",
                "AAAACC",
                "CCAAAA",
                "CCCCAA",
                "AACCCC",
                "CCAACC",
                "AAAAAA",
                "AACCAA",
                "AAAACC",
                "CCAAAA",
                "CCCCAA",
                "AACCCC",
                "CCAACC",
                "CCCCCC",
                "CCCCCC"        
            };
    public string[] Text;
    public string Grid;
    XmlWriter writer = null;

    public GeoMap(string[] MapDetails, double[] MapCoordinates, 
        int Width, int Height, double TranslateX, double TranslateY, string svgOutline,
        string TransformRegions, GeoMapRegion[] Regions,
        string TransformBorders, GeoMapBorder[] Borders,
        GeoMapOverlay[] Overlays)
    {
        this.MapDetails = MapDetails;
        this.MapCoordinates = MapCoordinates;
        this.Width = Width;
        this.Height = Height;
        this.TranslateX = TranslateX;
        this.TranslateY = TranslateY;
        this.svgOutline = svgOutline;
        this.TransformRegions = TransformRegions;
        this.Regions = Regions;
        this.TransformBorders = TransformBorders;
        this.Borders = Borders;
        this.Overlays = Overlays;
        this.TextOffset = new GeoMapCoordinates[Regions.Length];
        this.Text = new string[Regions.Length];
        for (int i = 0; i < Regions.Length; i++)
        {
            TextOffset[i] = new GeoMapCoordinates(0, 0);
            Text[i] = "";
        }
        for (int i = 0; i < Margin.Length; i++)
        {
            Margin[i] = 0.0;
        }
        for (int i = 0; i < ViewBox.Length; i++)
        {
            ViewBox[i] = 0.0;
        }
    }
    private bool ShowCenter = false;
    private bool ShowFlag = false;
    private bool ShowName = false;
    private bool ShowRegions = true;
    private void SetMap(string[] Map)
    {
        if (Map == null)
        {
            return;
        }
        else
        {
            switch (Map.Length)
            {
                case 1:
                    SetMargin("0,0,0,0");
                    SetViewBox("0,0,0,0");
                    SizeTo("1.0");
                    break;
                case 2:
                    SetMargin(Map[1]);
                    SetViewBox("0,0,0,0");
                    SizeTo("1.0");
                    break;
                case 3:
                    SetMargin(Map[1]);
                    SetViewBox(Map[2]);
                    SizeTo("1.0");
                    break;
                case 4:
                    SetMargin(Map[1]);
                    SetViewBox(Map[2]);
                    SizeTo(Map[3]);
                    break;
            }
        }
    }
    private void SetMargin(string QsPart)
    {
        string[] MarginComponents = QsPart.Split(new char[] { ',' });
        switch (MarginComponents.Length)
        {
            case 0:
                break;
            case 1: // Margin
                if (MarginComponents.Length == 1)
                {
                    double MarginWidth = 0.0;
                    try
                    {
                        MarginWidth = double.Parse(MarginComponents[0]);
                    }
                    catch
                    {
                        MarginWidth = 0.0;
                    }
                    for (int i = 0; i < Margin.Length; i++)
                        Margin[i] = MarginWidth;
                }
                break;
            default:
                for (int side = 0; side < MarginComponents.Length; side++)
                {
                    double MarginWidth = 0.0;
                    try
                    {
                        MarginWidth = double.Parse(MarginComponents[side]);
                    }
                    catch
                    {
                        MarginWidth = 0.0;
                    }
                    Margin[side] = MarginWidth;
                }
                break;
        }
    }
    private void SetViewBox(string QsPart)
    {
        string[] ViewBoxComponents = QsPart.Split(new char[] { ',' });
        if (ViewBoxComponents.Length == 4)
        {
            for (int vb = 0; vb < ViewBoxComponents.Length; vb++)
            {
                double dVB = 0.0;
                try
                {
                    dVB = double.Parse(ViewBoxComponents[vb]);
                }
                catch
                {
                    dVB = 0.0;
                }
                ViewBox[vb] = dVB;
            }
        }
    }
    private void SizeTo(string QsPart)
    {
        string[] SizeComponents = QsPart.Split(new char[] { ',' });
        if (SizeComponents.Length == 2)
        {
            SetTargetSize = true;
            for (int vb = 0; vb < SizeComponents.Length; vb++)
            {
                double dVB = 0.0;
                try
                {
                    dVB = double.Parse(SizeComponents[vb]);
                }
                catch
                {
                    dVB = 0.0;
                }
                TargetSize[vb] = dVB;
            }
        }
    }
    private void SetStyle(string[] Style)
    {
        if (Style != null)
        {
            for (int i = 0; i < Style.Length; i++)
            {
                if (Style[i] != "")
                {
                    string[] StyleComponents = Style[i].Split(new char[] { ',' });
                    switch (i)
                    {
                        case 0: // Margin
                            svgMarginStyle.stroke_width = StyleComponents[0];
                            svgMarginStyle.stroke = StyleComponents[1];
                            svgMarginStyle.fill_opacity = StyleComponents[2];
                            svgMarginStyle.fill = StyleComponents[3];
                            break;
                        case 1: // Outline Map
                            svgOutlineStyle.stroke_width = StyleComponents[0];
                            svgOutlineStyle.stroke = StyleComponents[1];
                            svgOutlineStyle.fill_opacity = StyleComponents[2];
                            svgOutlineStyle.fill = StyleComponents[3];
                            break;
                        case 2: // Borders
                            svgBorderStyle.stroke_width = StyleComponents[0];
                            svgBorderStyle.stroke = StyleComponents[1];
                            svgBorderStyle.fill_opacity = StyleComponents[2];
                            svgBorderStyle.fill = StyleComponents[3];
                            break;
                        case 3: // Region
                            svgRegionStyle.stroke_width = StyleComponents[0];
                            svgRegionStyle.stroke = StyleComponents[1];
                            svgRegionStyle.fill_opacity = StyleComponents[2];
                            svgRegionStyle.fill = StyleComponents[3];
                            for (int c = 0; c < Regions.Length; c++)
                            {
                                Colors[c] = svgRegionStyle.fill;
                            }
                            break;
                        case 4: // Text
                            svgTextStyle.Set(StyleComponents);
                            break;
                    }
                }
            }
        }
    }
    private void SetData(string[] RegionData, string[] RegionColors, string Inverse)
    {
        if (RegionData == null) // Wenn keine Daten angegeben sind, kann man die Farben frei steuern
        {
            if (RegionColors != null)
            {
                // die übergebenen Farben sind die Farben für die einzelnen Bezirke
                for (int rc = 0; rc < RegionColors.Length; rc++)
                {
                    if (RegionColors[rc].StartsWith("no"))
                    {
                        ShowRegions = false;
                    }
                    else if (RegionColors[rc] == "random")
                    {
                        Random rnd1 = new Random();
                        for (int i = 0; i < Colors.Length; i++)
                        {
                            double d = rnd1.NextDouble();
                            long lColor = (long)(16777216 * d);
                            Colors[i] = ColorModel.DEZ2RGB(lColor);
                        }
                    }
                    else if (RegionColors[rc].StartsWith("all,"))
                        for (int i = 0; i < Colors.Length; i++)
                            Colors[i] = RegionColors[rc].Substring(4);
                    else if (RegionColors[rc].Contains(",")) // mit Bezirksangabe
                    {
                        string[] RegionColor = RegionColors[rc].Split(new char[] { ',' });
                        int Region = int.Parse(RegionColor[0]) - 1;
                        Colors[Region] = RegionColor[1];
                    }
                    else
                        if (RegionColors[rc] != "") Colors[rc] = RegionColors[rc];
                }
            }
        }
        else // Daten werden angegeben
        {
            MapColorMode colorMode = MapColorMode.WHITE;
            string ColorMax = "CCCCCC";
            string ColorMin = "FFFFFF";
            string ColorNeutral = "FFFFFF";
            double HSBsat = 100;
            double HSBval = 100;
            if (RegionColors != null) // Wenn Daten angegeben sind, bestimmen die Daten die Farbe
            {
                switch (RegionColors.Length)
                {
                    case 2: // nur NeutraleFarbe und Maximalfarbe
                        ColorNeutral = RegionColors[0];
                        ColorMax = RegionColors[1];
                        if (ColorMax[0] == '-') // Zielfarbe ist Schwarz
                        {
                            ColorMax = ColorMax.Substring(1);
                            colorMode = MapColorMode.BLACK;
                        }
                        else
                            colorMode = MapColorMode.WHITE;
                        break;
                    case 3: // Farbübergang
                        colorMode = MapColorMode.COLOR;
                        ColorNeutral = RegionColors[0];
                        ColorMax = RegionColors[1];
                        ColorMin = RegionColors[2];
                        break;
                }
            }
            double Min = double.MaxValue;
            double Max = double.MinValue;
            dRegionData = new double[Regions.Length];
            for (int i = 0; i < dRegionData.Length; i++)
            {
                dRegionData[i] = double.MinValue;
            }
            double Datum;
            string strMaxMin = "";
            string RegionDataToUse = "";
            int RegionDataLength = 0;
            switch (RegionData[0])
            {
                case "area":
                case "population":
                case "density":
                    RegionDataToUse = RegionData[0];
                    if (RegionData.Length > 1) // entweder MaxMin oder auch Daten
                    {
                        if (RegionData[1][0] == 'm')
                        {
                            strMaxMin = RegionData[1].Substring(1);
                            RegionDataLength = RegionData.Length - 2;
                            for (int regd = 0; regd < RegionDataLength; regd++) // Verschieben der Daten
                            {
                                RegionData[regd] = RegionData[regd + 2];
                            }
                        }
                        else
                        {
                            RegionDataLength = RegionData.Length - 1;
                            for (int regd = 0; regd < RegionDataLength; regd++) // Verschieben der Daten
                            {
                                RegionData[regd] = RegionData[regd + 1];
                            }
                        }
                    }
                    break;
                default:
                    if (RegionData[0][0] == 'm')
                    {
                        strMaxMin = RegionData[0].Substring(1);
                        RegionDataLength = RegionData.Length - 1;
                        for (int regd = 0; regd < RegionDataLength; regd++)
                        {
                            RegionData[regd] = RegionData[regd + 1];
                        }
                    }
                    else
                        RegionDataLength = RegionData.Length;
                    break;
            }
            if (RegionDataLength == 0) // no data provided
            {
                for (int reg = 0; reg < Regions.Length; reg++)
                {
                    double Area = double.Parse(GeoMapStatic.CheckFloat(Regions[reg].MapRegionDetails[cIndexArea]), new CultureInfo("en-US"));
                    double Population = double.Parse(GeoMapStatic.CheckFloat(Regions[reg].MapRegionDetails[cIndexPopulation]), new CultureInfo("en-US"));
                    switch (RegionDataToUse)
                    {
                        case "area":
                            Datum = Area;
                            break;
                        case "population":
                            Datum = Population;
                            break;
                        case "density":
                            Datum = Population / Area;
                            break;
                        default:
                            Datum = double.MinValue;
                            break;
                    }
                    dRegionData[reg] = Datum;
                    if (Datum > double.MinValue)
                        if (Datum < Min) Min = Datum;
                    if (Datum > Max) Max = Datum;
                }
            }
            else // Data provided
            {
                int Region = 0;
                for (int i = 0; i < RegionDataLength; i++)
                {
                    if (RegionData[i].Contains(",")) // mit Bezirksangabe
                    {
                        string[] RegionDataRecord = RegionData[i].Split(new char[] { ',' });
                        try
                        {
                            Region = int.Parse(RegionDataRecord[0]) - 1;
                        }
                        catch
                        {
                        }
                        try
                        {
                            Datum = double.Parse(GeoMapStatic.CheckFloat(RegionDataRecord[1]), new CultureInfo("en-US"));
                        }
                        catch
                        {
                            Datum = double.MinValue;
                        }
                        if (RegionDataRecord.Length > 2) // Beschriftung, Beschritungsposition
                        {
                            GeoMapCoordinates textOffset = new GeoMapCoordinates(0, 0);
                            if (RegionDataRecord[2] != "")
                                Text[Region] = RegionDataRecord[2];
                            double Coordinate = 0.0;
                            try
                            {
                                Coordinate = double.Parse(RegionDataRecord[3]);
                            }
                            catch
                            {
                                Coordinate = 0.0;
                            }
                            TextOffset[Region].x = Coordinate;
                            try
                            {
                                Coordinate = double.Parse(RegionDataRecord[4]);
                            }
                            catch
                            {
                                Coordinate = 0.0;
                            }
                            TextOffset[Region].y = Coordinate;
                        }
                    }
                    else
                    {
                        try
                        {
                            Datum = double.Parse(RegionData[i], new CultureInfo("en-US"));
                        }
                        catch
                        {
                            Datum = double.MinValue;
                        }
                    }
                    if (RegionDataToUse != "")
                        if (Datum != double.MinValue)
                        {
                            double Area = double.Parse(GeoMapStatic.CheckFloat(Regions[Region].MapRegionDetails[cIndexArea]), new CultureInfo("en-US"));
                            double Population = double.Parse(GeoMapStatic.CheckFloat(Regions[Region].MapRegionDetails[cIndexPopulation]), new CultureInfo("en-US"));
                            switch (RegionDataToUse)
                            {
                                case "area":
                                    Datum /= Area;
                                    break;
                                case "population":
                                    Datum /= Population;
                                    break;
                                case "density":
                                    Datum /= (Population / Area);
                                    break;
                                default:
                                    Datum = double.MinValue;
                                    break;
                            }
                        }
                    dRegionData[Region] = Datum;
                    Region++;
                    if (Datum > double.MinValue)
                        if (Datum < Min) Min = Datum;
                    if (Datum > Max) Max = Datum;
                }
            }
            if (strMaxMin != "")
            {
                string[] MaxMin = strMaxMin.Split(new char[] { ',' });
                if (MaxMin.Length == 2)
                {
                    bool Error = false;
                    try
                    {
                        Min = double.Parse(MaxMin[0], new CultureInfo("en-US"));
                    }
                    catch
                    {
                        Error = true;
                    }
                    try
                    {
                        Max = double.Parse(MaxMin[1], new CultureInfo("en-US"));
                    }
                    catch
                    {
                        Error = true;
                    }
                    if (!Error)
                        for (int i = 0; i < dRegionData.Length; i++)
                        {
                            if (dRegionData[i] != double.MinValue)
                            {
                                if (dRegionData[i] < Min) dRegionData[i] = Min;
                                if (dRegionData[i] > Max) dRegionData[i] = Max;
                            }
                        }
                }
            }
            // Farbwerte zuordnen
            RGBobject RGBStart = ColorModel.hex2RGB(ColorMax);
            RGBobject RGBEnd = ColorModel.hex2RGB(ColorMin);
            HSVobject HSVStart = ColorModel.RGB2HSV(RGBStart);
            HSVobject HSVEnd = ColorModel.RGB2HSV(RGBEnd);
            for (int i = 0; i < Regions.Length; i++)
            {
                RGBobject MyColorRGB = null;
                if (dRegionData[i] == double.MinValue)
                    MyColorRGB = ColorModel.hex2RGB(ColorNeutral);
                else
                    switch (colorMode)
                    {
                        case MapColorMode.BLACK:
                        case MapColorMode.WHITE:
                            double MyColorSat;
                            switch (Inverse)
                            {
                                default:
                                case "0":
                                    MyColorSat = ((dRegionData[i] - Min) / (Max - Min)) * 100.0;
                                    break;
                                case "1":
                                    MyColorSat = ((Max - dRegionData[i]) / (Max - Min)) * 100.0;
                                    break;
                            }
                            switch (colorMode)
                            {
                                case MapColorMode.BLACK:
                                    MyColorRGB = ColorModel.HSV2RGB(new HSVobject(HSVStart.h, 100, (int)MyColorSat));
                                    break;
                                case MapColorMode.WHITE:
                                    MyColorRGB = ColorModel.HSV2RGB(new HSVobject(HSVStart.h, (int)MyColorSat, 100));
                                    break;
                            }
                            break;
                        case MapColorMode.COLOR:
                            double MyColorHue;
                            switch (Inverse)
                            {
                                default:
                                case "0":
                                    MyColorHue = HSVStart.h + ((dRegionData[i] - Min) / (Max - Min)) * (HSVEnd.h - HSVStart.h);
                                    break;
                                case "1":
                                    MyColorHue = HSVStart.h + ((Max - dRegionData[i]) / (Max - Min)) * (HSVEnd.h - HSVStart.h);
                                    break;
                            }
                            MyColorRGB = ColorModel.HSV2RGB(new HSVobject((int)MyColorHue, 100, 100)); // HSVStart.s, HSVStart.v));
                            break;
                    }
                if (MyColorRGB != null)
                    Colors[i] = ColorModel.RGB2hex(MyColorRGB).Substring(1); // ColorModel.DEZ2RGB(i * 10 * 256 * 256); heller werdende Farben
            }
        }
    }
    private void SetOverlay(string[] ShowOverlay)
    {
        if (ShowOverlay != null)
        {
            string OvlCaption = "";
            string OvlUrl = "";
            string OvlImg = "";
            string OvlX = "";
            string OvlY = "";
            string OvlWidth = "";
            string OvlHeight = "";
            string OvlOpacity = "";
            bool OvlResize = false;
            writer.WriteStartElement("g");
            for (int i = 0; i < ShowOverlay.Length; i++)
            {
                string[] OverlayComponents = ShowOverlay[i].Split(new char[] { ',' });
                OvlCaption = "Ovl_" + OverlayComponents[0];
                if (OverlayComponents[1] != "") OvlUrl = OverlayComponents[1];
                OvlImg = OverlayComponents[2];
                OvlX = OverlayComponents[3];
                OvlY = OverlayComponents[4];
                OvlWidth = OverlayComponents[5];
                OvlHeight = OverlayComponents[6];
                OvlOpacity = OverlayComponents[7];
                OvlResize = OverlayComponents[8] == "1";
                WriteSvgOverlay(OvlCaption, OvlUrl, OvlImg, OvlX, OvlY, OvlWidth, OvlHeight, OvlOpacity, OvlResize);
            }
            writer.WriteEndElement();
        }
    }
    private int Coo2Pix(double coordinate, double minC, double maxC, int deltaP)
    {
        return (int)Math.Round((coordinate - minC) * (double)deltaP / (maxC - minC));
    }
    private void SetGrid(string Grid)
    {
        if (Grid != null)
        {
            if (IsInteger(Grid)) 
            { // Pixel-Grid
                int iGrid = (int)double.Parse(Grid);
                for (int x = (int)-Margin[0]; x < Margin[0] + Width; x += iGrid)
                {
                    DrawLineY(x);
                }
                for (int y = (int)-Margin[1]; y < Margin[1] + Height; y += iGrid)
                {
                    DrawLineX(y);
                }
            }
            else
            { // Geo-Grid
                double dGrid = double.Parse(Grid, new CultureInfo("en-US"));
                double xStart = dGrid * Math.Truncate(MapCoordinates[1] / dGrid);
                double yStart = dGrid * Math.Truncate(MapCoordinates[0] / dGrid);
                double xStop = dGrid * Math.Truncate(MapCoordinates[3] / dGrid);
                double yStop = dGrid * Math.Truncate(MapCoordinates[2] / dGrid);
                // int gridP = Coo2Pix(MapCoordinates[1] + dGrid, MapCoordinates[1], MapCoordinates[3], Width);
                for (double x = xStart; x < xStop; x += dGrid)
                {
                    double ix = dGrid * Math.Round(x / dGrid);
                    string number = Math.Round(ix / dGrid).ToString();
                    DrawLineY((int)Margin[0]+Coo2Pix(x, MapCoordinates[1], MapCoordinates[3], Width), number[number.Length-1]=='0');
                }
                for (double y = yStart; y < yStop; y += dGrid)
                {
                    double iy = dGrid * Math.Round(y / dGrid);
                    string number = Math.Round(iy / dGrid).ToString();
                    DrawLineX((int)Margin[1] + Coo2Pix(y, MapCoordinates[0], MapCoordinates[2], Height), number[number.Length - 1] == '0');
                }
            }
        }
    }
    public MemoryStream GetSvgMap(string[] Map, string[] Show, string[] RegionColors, string Inverse, string[] RegionData, string[] ShowOverlay, string[] Style, bool ShowData, string Grid)
    {
        double[] Data = new double[Regions.Length];

        // Outline
        svgOutlineStyle = new SvgStyle();
        svgOutlineStyle.fill = "fefee9";
        svgOutlineStyle.fill_opacity = "1";
        svgOutlineStyle.stroke = "888888";
        svgOutlineStyle.stroke_width = "5";
        // Border
        svgBorderStyle = new SvgStyle();
        svgBorderStyle.fill = "none";
        svgBorderStyle.stroke = "646464";
        svgBorderStyle.stroke_width = "0.8";
        // Region
        svgRegionStyle = new SvgStyle();
        svgRegionStyle.fill = "aa8800";
        svgRegionStyle.fill_opacity = "1";
        svgRegionStyle.stroke = "000000";
        svgRegionStyle.stroke_width = "0.1";
        // Margin
        svgMarginStyle = new SvgStyle();
        svgMarginStyle.fill = "ffffff";
        svgMarginStyle.fill_opacity = "1";
        svgMarginStyle.stroke = "ffffff";
        svgMarginStyle.stroke_width = "0.0";
        // Text
        svgTextStyle = new SvgStyleText();
        SetMap(Map);
        SetStyle(Style);
        SetData(RegionData, RegionColors, Inverse);
        ShowCenter = false;
        ShowFlag = false;
        ShowName = false;
        if (Show != null)
            for (int i = 0; i < Show.Length; i++)
            {
                switch (Show[i])
                {
                    case "center":
                        ShowCenter = true;
                        break;
                    case "flag":
                        ShowFlag = true;
                        break;
                    case "name":
                        ShowName = true;
                        break;
                }
            }
        string strDocType = "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">";

        XmlWriterSettings xws = new XmlWriterSettings();
        xws.Indent = true;
        xws.IndentChars = "\t";
        xws.NewLineChars = "\n";
        xws.ConformanceLevel = ConformanceLevel.Document;
        xws.CheckCharacters = false;
        xws.Encoding = System.Text.Encoding.UTF8;

        MemoryStream strm = new MemoryStream();

        writer = XmlWriter.Create(strm, xws);
        writer.WriteStartDocument();

        switch (ShowData)
        {
            case true:
                writer.WriteStartElement("html");
                writer.WriteStartElement("body");
                WriteSvgData();

                double SumValue = 0;
                int SumCount = 0;
                if (dRegionData != null)
                {
                    for (int reg = 0; reg < Regions.Length; reg++)
                    {
                        if (dRegionData[reg] != double.MinValue)
                        {
                            SumCount++;
                            SumValue += dRegionData[reg];
                        }
                    }
                }
                WriteRegionData(MapDetails, SumCount == 0 ? double.MinValue : SumValue / SumCount);
                for (int reg = 0; reg < Regions.Length; reg++)
                {
                    WriteRegionData(Regions[reg].MapRegionDetails, dRegionData == null ? 0 : dRegionData[reg]);
                }
                writer.WriteEndElement(); // "body"
                writer.WriteEndElement(); // "html"
                writer.WriteEndDocument();
                writer.Flush();
                writer.Close();
                return strm;
            case false: // show map
                writer.WriteStartElement("svg", "http://www.w3.org/2000/svg");
                writer.WriteAttributeString("xmlns", "xlink", null, "http://www.w3.org/1999/xlink");
                writer.WriteAttributeString("version", "1.1");
                writer.WriteAttributeString("width", GeoMapStatic.CheckFloat(Width + 2 * (Margin[0] + Margin[2])));
                writer.WriteAttributeString("height", GeoMapStatic.CheckFloat(Height + 2 * (Margin[1] + Margin[3])));
                writer.WriteAttributeString("id", "svg_" + MapDetails[1]);
                writer.WriteAttributeString("viewBox", (ViewBox[0] + Margin[0]) + "," + (ViewBox[1] + Margin[1]) + "," + ViewBox[2] + "," + ViewBox[3]);
                // writer.WriteAttributeString("preserveAspectRatio", "none");

                if (SetTargetSize)
                {
                    double ScaleFactor = 1.0;
                    writer.WriteStartElement("g");
                    double ScaleX = TargetSize[0] / (Width + 2 * (Margin[0] + Margin[2]));
                    double ScaleY = TargetSize[1] / (Height + 2 * (Margin[1] + Margin[3]));
                    if (ScaleX < ScaleY)
                    {
                        ScaleFactor = ScaleX;
                    }
                    else
                    {
                        ScaleFactor = ScaleY;
                    }
                    writer.WriteAttributeString("transform", "scale(" + GeoMapStatic.CheckFloat(ScaleFactor) + ")");
                }
                writer.WriteStartElement("g");
                writer.WriteAttributeString("id", "Map_" + MapDetails[1]);
                writer.WriteAttributeString("transform", "translate(" + GeoMapStatic.CheckFloat(TranslateX + Margin[0]) + "," + GeoMapStatic.CheckFloat(TranslateY + Margin[1]) + ")");
                // Area
                WriteSvgRect("area",
                    svgMarginStyle.GetSvgStyle(),
                    -(TranslateX + Margin[0]), -(TranslateY + Margin[1]),
                    Width + (Margin[0] + Margin[2]), Height + (Margin[1] + Margin[3]), // MarginWidth - TranslateX, MarginWidth - TranslateY, Width, Height,
                    "");
                // Outline
                WriteSvgPath("Outline", svgOutlineStyle.GetSvgStyle(), svgOutline);
                // Border
                writer.WriteStartElement("g");
                WriteTransformAttributeString(TransformBorders);
                for (int i = 0; i < Borders.Length; i++)
                    WriteSvgPath("Border_" + Borders[i].IdSuffix, svgBorderStyle.GetSvgStyle(), Borders[i].svgPath);
                writer.WriteEndElement();
                // Regions
                if (ShowRegions)
                {
                    writer.WriteStartElement("g");
                    WriteTransformAttributeString(TransformRegions);
                    for (int i = 0; i < Regions.Length; i++)
                    {
                        svgRegionStyle.fill = Colors[i];
                        if (Regions[i].svgPath.StartsWith("#"))
                        {
                            string[] temp = Regions[i].svgPath.Substring(1).Split(new char[] { '#' });
                            for (int part = 0; part < temp.Length; part++)
                            {
                                WriteSvgPath(Regions[i].MapRegionDetails[1] + "-" + part, svgRegionStyle.GetSvgStyle(), temp[part]);
                            }
                        }
                        else
                            WriteSvgPath(Regions[i].MapRegionDetails[1], svgRegionStyle.GetSvgStyle(), Regions[i].svgPath);
                    }
                    writer.WriteEndElement();
                }
                writer.WriteStartElement("g");
                for (int i = 0; i < Regions.Length; i++)
                {
                    if (ShowCenter)
                        WriteSvgCircle(
                        "Center_" + Regions[i].MapRegionDetails[1],
                        "fill:#56d500;fill-opacity:0.58823529;stroke:#6a0000;stroke-width:0.17299999;", //fill:#56d500;fill-opacity:0.58823529;stroke:#6a0000;stroke-width:0.17299999;stroke-miterlimit:3.97446823;stroke-opacity:1;stroke-dasharray:none",
                        "-319", "81", "5", // "m -318,81.362183 a 6,6 0 1 1 -12,0 6,6 0 1 1 12,0 z",
                        "matrix(1.3285961,0,0,1.3285961," + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")" // "translate(" + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")"    // "matrix(1.3285961,0,0,1.3285961,765.46517,270.26471)"
                        );
                    if (ShowName)
                        WriteSvgText(
                        "Text_" + Regions[i].MapRegionDetails[1],
                        Regions[i].MapRegionDetails[0],
                            // "fill:#000000;fill-opacity:0.58823529;font-family:Tahoma;font-size=12;font-weight:bold;text-align:center;text-anchor:middle;",
                        svgTextStyle.GetSvgStyle(),
                            // "font-weight:bold;font-stretch:normal;text-align:center;text-anchor:middle;fill:#000000;fill-opacity:1;font-family:Tahoma;",
                        -319 + TextOffset[i].x, 81 + TextOffset[i].y, //(Regions[i].Center.x), //765 + 
                            // (Regions[i].Center.y), //270 + 
                            // "-319", "81", "5", // "m -318,81.362183 a 6,6 0 1 1 -12,0 6,6 0 1 1 12,0 z",
                         "matrix(1.3285961,0,0,1.3285961," + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")" // "translate(" + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")"    // "matrix(1.3285961,0,0,1.3285961,765.46517,270.26471)"
                        );
                    if (ShowFlag)
                        WriteSvgImage(
                        "Flag_" + Regions[i].MapRegionDetails[1],
                        "",
                        Regions[i].Center.x.ToString(),
                        Regions[i].Center.y.ToString(),
                        "17.7",
                        "23",
                        "translate(330,370)",
                        GeoMapStatic.PathToPictures + Regions[i].MapRegionDetails[1] + "-kl.jpg"
                    );
                    // Caption
                    WriteSvgText(
                    "Text_" + Regions[i].MapRegionDetails[1],
                    Text[i],
                    svgTextStyle.GetSvgStyle(),
                    -319 + TextOffset[i].x, 81 + TextOffset[i].y, //(Regions[i].Center.x), //765 + 
                     "matrix(1.3285961,0,0,1.3285961," + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")" // "translate(" + (765 + Regions[i].Center.x) + "," + (270 + Regions[i].Center.y) + ")"    // "matrix(1.3285961,0,0,1.3285961,765.46517,270.26471)"
                    );
                }
                writer.WriteEndElement();
                writer.WriteEndElement(); // g
                // Overlays
                SetOverlay(ShowOverlay);
                // Grid
                SetGrid(Grid);
                if (SetTargetSize)
                {
                    writer.WriteEndElement(); // g SizeToTagert
                }
                writer.WriteEndElement(); // svg
                break;
        }
        writer.WriteEndDocument();
        writer.Flush();
        writer.Close();
        return strm;
    }
    void DrawLineX(int y)
    {
        if ((y - Margin[1]) % 100 == 0)
            DrawLineX(y, true);
        else
            DrawLineX(y, false);
    }
    void DrawLineX(int y, bool bold)
    {
        writer.WriteStartElement("line");
        writer.WriteAttributeString("x1", "0");
        writer.WriteAttributeString("y1", GeoMapStatic.CheckFloat(y));
        writer.WriteAttributeString("x2", GeoMapStatic.CheckFloat(Width + Margin[0]));
        writer.WriteAttributeString("y2", GeoMapStatic.CheckFloat(y));
        if (bold)
            writer.WriteAttributeString("stroke-width", "0.5");
        else
            writer.WriteAttributeString("stroke-width", "0.1");
        writer.WriteAttributeString("stroke", "#ff00ff");
        writer.WriteEndElement(); // path
    }
    void DrawLineY(int x)
    {
        if ((x - Margin[0]) % 100 == 0)
            DrawLineX(x, true);
        else
            DrawLineX(x, false);
    }
    void DrawLineY(int x, bool bold)
    {
        writer.WriteStartElement("line");
        writer.WriteAttributeString("x1", GeoMapStatic.CheckFloat(x));
        writer.WriteAttributeString("y1", "0");
        writer.WriteAttributeString("x2", GeoMapStatic.CheckFloat(x));
        writer.WriteAttributeString("y2", GeoMapStatic.CheckFloat(Height + Margin[1]));
        if (bold)
            writer.WriteAttributeString("stroke-width", "0.5");
        else
            writer.WriteAttributeString("stroke-width", "0.1");
        writer.WriteAttributeString("stroke", "#ff00ff");
        writer.WriteEndElement(); // path
    }
    void WriteTransformAttributeString(string TransformParameter)
    {
        if (TransformParameter == null)
            return;
        if (TransformParameter == "")
            return;
        writer.WriteAttributeString("transform", TransformParameter);
    }
    void WriteSvgData()
    {
        string c = "";
        writer.WriteElementString("h3", "Drawing");
        writer.WriteStartElement("table");
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Width");
        writer.WriteElementString("td", GeoMapStatic.CheckFloat(Width).ToString());
        writer.WriteEndElement();
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Height");
        writer.WriteElementString("td", GeoMapStatic.CheckFloat(Height).ToString());
        writer.WriteEndElement();
        c = "lt up rt lo ";
        for (int i = 0; i < Margin.Length; i++)
            c += GeoMapStatic.CheckFloat(Margin[i]).ToString() + " ";
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Margin Left");
        writer.WriteElementString("td", c);
        writer.WriteEndElement();
        c = "x y width height ";
        for (int i = 0; i < ViewBox.Length; i++)
            c += GeoMapStatic.CheckFloat(ViewBox[i]).ToString() + " ";
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "ViewBox");
        writer.WriteElementString("td", c);
        writer.WriteEndElement();
        c = "lat_lt lng_lo lat_rt lng_up ";
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Coordinates");
        for (int i=0; i<MapCoordinates.Length; i++)
            c += GeoMapStatic.CheckFloat(MapCoordinates[i]).ToString()+" ";
        writer.WriteElementString("td", c);
        writer.WriteEndElement();
        writer.WriteEndElement();
    }
    void WriteRegionData(string[] MapRegionDetails, double dRegionData)
    {
        writer.WriteElementString("h3", MapRegionDetails[cIndexTitle]);
        writer.WriteStartElement("table");
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Map-Id");
        writer.WriteElementString("td", MapRegionDetails[cIndexIdMap]);
        writer.WriteEndElement();
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Id");
        writer.WriteElementString("td", MapRegionDetails[cIndexId]);
        writer.WriteEndElement();
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Kfz");
        writer.WriteElementString("td", MapRegionDetails[cIndexKfz]);
        writer.WriteEndElement();
        double Area = double.Parse(GeoMapStatic.CheckFloat(MapRegionDetails[cIndexArea]), new CultureInfo("en-US"));
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Fläche");
        writer.WriteElementString("td", Area.ToString());
        writer.WriteEndElement();
        double Population = double.Parse(GeoMapStatic.CheckFloat(MapRegionDetails[cIndexPopulation]), new CultureInfo("en-US"));
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Einwohner");
        writer.WriteElementString("td", Population.ToString());
        writer.WriteEndElement();
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Dichte");
        writer.WriteElementString("td", (Population / Area).ToString());
        writer.WriteEndElement();
        string TextToWrite = "";
        if (dRegionData == double.MinValue)
            TextToWrite = "none";
        else
            TextToWrite = dRegionData.ToString();
        writer.WriteStartElement("tr");
        writer.WriteElementString("td", "Datum");
        writer.WriteElementString("td", TextToWrite);
        writer.WriteEndElement();
        writer.WriteEndElement();
    }
    bool IsInteger(string Number)
    {
        if (Number.Contains(",") || Number.Contains("."))
            return false;
        else
            return true;
    }
    void WriteSvgOverlay(string OvlCaption, string OvlUrl, string OvlImg, string OvlX, string OvlY, string OvlWidth, string OvlHeight, string OvlOpacity, bool resize)
    {
        // Overlay wird pixelgenau positioniert, wenn die Parameter OvlX und OvlY ganze Zahlen sind
        if (!((IsInteger(OvlX) && IsInteger(OvlY))))
        {
            // sonst wird angenommen, dass es sich um Geo-Daten handelt: OvlX=Lat und OvlY=Lng
            int PixX = Coo2Pix(
                double.Parse(OvlY, new CultureInfo("en-US")), 
                MapCoordinates[1], MapCoordinates[3],
                Width);
            int PixY = Height - Coo2Pix(
                double.Parse(OvlX, new CultureInfo("en-US")),
                MapCoordinates[0], MapCoordinates[2],
                Height);
            OvlX = PixX.ToString();
            OvlY = (PixY - int.Parse(OvlHeight)).ToString();
        }
        string ox = GeoMapStatic.CheckFloat(double.Parse(OvlX, new CultureInfo("en-US")) + Margin[0]);
        string oy = GeoMapStatic.CheckFloat(double.Parse(OvlY, new CultureInfo("en-US")) + Margin[1]);
        if (resize)
        {
            WriteSvgImage(OvlCaption, "opacity:" + OvlOpacity + ";",
                ox, oy, OvlWidth, OvlHeight,
                "", OvlUrl + OvlImg);
        }
        else // resize
        {
            writer.WriteStartElement("g");
            writer.WriteAttributeString("transform", "translate(" + ox + "," + oy + ")");
            writer.WriteStartElement("g");
            writer.WriteAttributeString("transform", "scale(0.1)");
            WriteSvgImage(OvlCaption, "opacity:" + OvlOpacity + ";",
                "0", "0", GeoMapStatic.CheckFloat(double.Parse(OvlWidth) * 10), GeoMapStatic.CheckFloat(double.Parse(OvlHeight) * 10),
                "", OvlUrl + OvlImg);
            writer.WriteEndElement();
            writer.WriteEndElement();
        }
    }
    void WriteSvgPath(string id, string style, string d)
    {
        WriteSvgPath(id, style, d, null);
    }
    void WriteSvgPath(string id, string style, string d, string transform)
    {
        writer.WriteStartElement("path");
        writer.WriteAttributeString("id", id);
        writer.WriteAttributeString("d", d);
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("style", style);
        writer.WriteEndElement(); // path
    }
    void WriteSvgCircle(string id, string style, string cx, string cy, string r, string transform)
    {
        writer.WriteStartElement("circle");
        writer.WriteAttributeString("id", id);
        writer.WriteAttributeString("cx", cx);
        writer.WriteAttributeString("cy", cy);
        writer.WriteAttributeString("r", r);
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("style", style);
        writer.WriteEndElement(); // path
    }
    void WriteSvgRect(string id, string style, double x, double y, double width, double height, string transform)
    {
        writer.WriteStartElement("rect");
        writer.WriteAttributeString("id", id);
        writer.WriteAttributeString("x", GeoMapStatic.CheckFloat(x));
        writer.WriteAttributeString("y", GeoMapStatic.CheckFloat(y));
        writer.WriteAttributeString("width", GeoMapStatic.CheckFloat(width));
        writer.WriteAttributeString("height", GeoMapStatic.CheckFloat(height));
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("style", style);
        writer.WriteEndElement(); // path
    }
    void WriteSvgText(string id, string Text, string style, double x, double y, string transform)
    {
        writer.WriteStartElement("text");
        writer.WriteAttributeString("id", id);
        writer.WriteAttributeString("x", GeoMapStatic.CheckFloat(x));
        writer.WriteAttributeString("y", GeoMapStatic.CheckFloat(y));
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("style", style);
        writer.WriteString(Text);
        writer.WriteEndElement(); // text
    }
    void WriteSvgImage(string id, string style, string x, string y, string width, string height, string transform, string href)
    {
        writer.WriteStartElement("image");
        writer.WriteAttributeString("id", id);
        writer.WriteAttributeString("x", x);
        writer.WriteAttributeString("y", y);
        writer.WriteAttributeString("width", width);
        writer.WriteAttributeString("height", height);
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("style", style);
        writer.WriteAttributeString("xlink", "href", null, href);
        writer.WriteEndElement(); // path
    }
    void WriteSvgClone(string id, string transform)
    {
        writer.WriteStartElement("use");
        writer.WriteAttributeString("id", "Bezirksmitte_" + id);
        writer.WriteAttributeString("x", "0");
        writer.WriteAttributeString("y", "0");
        writer.WriteAttributeString("width", "1");
        writer.WriteAttributeString("height", "1");
        if (transform != null) writer.WriteAttributeString("transform", transform);
        writer.WriteAttributeString("xlink:href", "#Bezirksmitte");
        writer.WriteEndElement(); // path
    }
}
