Kartenprojektion - Wie funktioniert das?

  • C#
  • .NET (FX) 4.5–4.8

Es gibt 7 Antworten in diesem Thema. Der letzte Beitrag () ist von hal2000.

    Kartenprojektion - Wie funktioniert das?

    Hallo Forum :thumbsup: ,

    stehe vor einem Problem. Einem Verständnisproblem! Und zwar:

    Ich habe gegeben:
    -Punkte mit jeweils einer Koordinate (Längen- und Breitengrad)
    -Ein Canvas (WPF) mit einer variablen Größen
    -Ein Control, welches die Punkte repräsentiert
    -Einen Zoomfaktor, also die Kilometeranzahl vom rechten bis zum linken Rand des Canvas (gewissermaßen ein Maßstab)

    Das will ich erreichen:
    Die Punkte sollen entsprechend geladen werden und unter Berücksichtigung des Zoomfaktors auf dem Canvas in Form der "Punktcontrols" platziert werden.

    Wie will ich das erreichen:
    Ich errechne aus dem Zoomfaktor und der Canvasgröße die rechte untere und die linke obere Ecke des Canvas in Form von Koordinaten, so dass ich zwei Grenzen habe.
    Dann hole ich mir den ersten Punkt aus meiner Sammlung und berechne deren X und Y Punkte in Pixeln mithilfe der zwei Grenzen. Ich platziere das Control mit den entsprechenden X und Y Werten auf dem Canvas. Und so weiter alle Punkte durch.

    Das habe ich verstanden:
    -Die Welt ist rund und darum brauch man ne Projektion um das in 2D abzubilden.
    -Den ganzen WPF Kram
    -Wie ich den Zoomfaktor nutze um die Grenzen zu berechnen

    Das Problem:
    -den X und Y Wert in Pixeln mithilfe der zwei Grenze berechnen
    ->Speziell: Die ganzen Lösungen im Internet wollen keine Grenzen haben und ich weiß nicht wie ich dann den Punkt errechnen soll, da ich ja nicht die komplette Welt darstellen will, sondern nur Ausschnitte. 8| ?( :/

    Habe das im Internet gefunden:
    stackoverflow.com/questions/12…cal-coordinates-to-pixels

    C#-Quellcode

    1. public class GoogleMapsAPIProjection
    2. {
    3. private readonly double PixelTileSize = 256d;
    4. private readonly double DegreesToRadiansRatio = 180d / Math.PI;
    5. private readonly double RadiansToDegreesRatio = Math.PI / 180d;
    6. private readonly PointF PixelGlobeCenter;
    7. private readonly double XPixelsToDegreesRatio;
    8. private readonly double YPixelsToRadiansRatio;
    9. public GoogleMapsAPIProjection(double zoomLevel)
    10. {
    11. var pixelGlobeSize = this.PixelTileSize * Math.Pow(2d, zoomLevel);
    12. this.XPixelsToDegreesRatio = pixelGlobeSize / 360d;
    13. this.YPixelsToRadiansRatio = pixelGlobeSize / (2d * Math.PI);
    14. var halfPixelGlobeSize = Convert.ToSingle(pixelGlobeSize / 2d);
    15. this.PixelGlobeCenter = new PointF(
    16. halfPixelGlobeSize, halfPixelGlobeSize);
    17. }
    18. public PointF FromCoordinatesToPixel(PointF coordinates)
    19. {
    20. var x = Math.Round(this.PixelGlobeCenter.X
    21. + (coordinates.X * this.XPixelsToDegreesRatio));
    22. var f = Math.Min(
    23. Math.Max(
    24. Math.Sin(coordinates.Y * RadiansToDegreesRatio),
    25. -0.9999d),
    26. 0.9999d);
    27. var y = Math.Round(this.PixelGlobeCenter.Y + .5d *
    28. Math.Log((1d + f) / (1d - f)) * -this.YPixelsToRadiansRatio);
    29. return new PointF(Convert.ToSingle(x), Convert.ToSingle(y));
    30. }
    31. public PointF FromPixelToCoordinates(PointF pixel)
    32. {
    33. var longitude = (pixel.X - this.PixelGlobeCenter.X) /
    34. this.XPixelsToDegreesRatio;
    35. var latitude = (2 * Math.Atan(Math.Exp(
    36. (pixel.Y - this.PixelGlobeCenter.Y) / -this.YPixelsToRadiansRatio))
    37. - Math.PI / 2) * DegreesToRadiansRatio;
    38. return new PointF(
    39. Convert.ToSingle(latitude),
    40. Convert.ToSingle(longitude));
    41. }
    42. }

    Sowie eine Bibliothek:
    https://proj4net.codeplex.com/
    Speziell: proj4net.codeplex.com/SourceCo…ion/MercatorProjection.cs


    Hoffe ihr könnt mir helfen und das Problem nachvollziehen :D

    Liebe Grüße
    Julian :thumbup:
    Hmkay. :|

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „jedijaeger“ ()

    Hi,

    aus deinem Post lese ich, dass du schon Koordinaten auf einer vollständigen Weltkarte korrekt setzen / anzeigen kannst. Da ist der Zoomfaktor doch das kleinste Problem: Lade eine komplette Weltkarte und platziere die Koordinaten. Verwende eine ScaleTransform, um den Inhalt des Canvas entsprechend zu vergrößern.
    Ohne Skalierung gilt: (linker Rand) bis (rechter Rand) = Erdumfang. Sind die Grenzen z.B. auf 1000 km eingestellt, skalierst du die Weltkarte einfach auf das 40-fache (= Umfang / 1000). Natürlich wird dann ein Großteil der Karte über den Rand des Canvas hinausgehen, aber das kannst du ja mit ClipToBounds verhindern - das kostet nicht mal zusätzliche Performance.
    Nachteil: Du brauchst eine ziemlich hochauflösende Weltkarte, idealerweise eine Vektorgrafik. Außerdem kannst du mit dieser Variante nur in die Mitte zoomen, also die Karte nicht verschieben wie bei Google Maps. Das müsste aber mit TranslateTransform gehen.
    Gruß
    hal2000
    @hal2000 Erstmal danke fürs antworten :thumbsup: . Ja also das Projizieren kann ich. Nur halt den Zoomfaktor mit einbeziehen nicht. Alle Funktionen wollen keine Größe des Anzeigebereichs. Also frage ich mich woher die Funktion​ public PointF FromCoordinatesToPixel(PointF coordinates) aus dem angegebenen Beispiel wissen will wie groß mein Ausgabebereich ist bzw. auf was sich die zurückgegebenen Pixelwerte beziehen. Wenn ich die Weltkarte z.B. in einem 500*300 Canvas darstelle sollten andere Pixelwerte herauskommen als auf einem 1000*600 Canvas, oder? Kannst du mein Problem bei dem Verständnis verstehen ? :D

    LG Julian
    Hmkay. :|
    Hi,

    eine Sache vorweg: Ich habe keine Ahnung von Kartenprojektion und müsste mich da einlesen. Daher versuche ich aufgrund deiner Informationen eine technische Lösung zu finden.

    Natürlich kommen bei unterschiedlichen Canvas-Größen verschiedene Pixel raus. Die Canvas-Größe konfigurierst du über PixelTileSize - die Funktionen nehmen offenbar an, dass das Canvas quadratisch ist. Macht aber nichts: Zeichne einfach auf eine angenommene Einheitsgröße, z.B. 1000x1000 Pixel. Das verzerrt die Weltkarte natürlich, weil die 2D-Projektion nicht quadratisch, sondern rechteckig ist. Macht aber auch nichts: Eine ScaleTransform rückt das wieder gerade. Danach kannst du mit einer weiteren Transformation zoomen. Ich meine das so:

    XML-Quellcode

    1. <Grid>
    2. <Grid.ColumnDefinitions>
    3. <ColumnDefinition Width="*" />
    4. <ColumnDefinition Width="50" />
    5. </Grid.ColumnDefinitions>
    6. <Border Grid.Column="0" BorderBrush="Black" BorderThickness="1" Margin="50 50 0 50" ClipToBounds="True">
    7. <Canvas>
    8. <Canvas.Background>
    9. <LinearGradientBrush>
    10. <GradientStop Color="Green" Offset="0.4" />
    11. <GradientStop Color="Yellow" Offset="0.9" />
    12. </LinearGradientBrush>
    13. </Canvas.Background>
    14. <Canvas.RenderTransform>
    15. <ScaleTransform CenterX="0" CenterY="0" ScaleX="{Binding Path=Value, ElementName=slider}" ScaleY="{Binding Path=Value, ElementName=slider}" />
    16. </Canvas.RenderTransform>
    17. <Ellipse Fill="Red" Width="10" Height="10" Canvas.Top="100" Canvas.Left="150" />
    18. </Canvas>
    19. </Border>
    20. <Slider Name="slider" Grid.Column="1" Orientation="Vertical" Margin="0 20" HorizontalAlignment="Center">
    21. <Slider.Minimum>1</Slider.Minimum>
    22. <Slider.Maximum>10</Slider.Maximum>
    23. <Slider.SmallChange>0.01</Slider.SmallChange>
    24. <Slider.LargeChange>0.1</Slider.LargeChange>
    25. </Slider>
    26. </Grid>


    Wo du hinzoomst, legst du in CenterX und CenterY fest. Bei mir ist das die linke obere Ecke. Der Einfachheit halber skaliere ich das gesamte Canvas. Du musst natürlich nur den Hintergrund skalieren, damit die Markierungen gleich groß bleiben.
    Gruß
    hal2000
    @hal2000

    Diese Lösung habe ich auf gis.stackexchange.com/question…ial-map-pixel-projections gefunden:

    C#-Quellcode

    1. public static PointF GetPixelCoordinatesInBounds(double width, double height, double top, double bottom, double left, double right, double latitude, double longitude)
    2. {
    3. bottom = bottom * (Math.PI / 180);
    4. top = top * (Math.PI / 180);
    5. left = left * (Math.PI / 180);
    6. right = right * (Math.PI / 180);
    7. latitude = latitude * (Math.PI / 180);
    8. longitude = longitude * (Math.PI / 180);
    9. var ymin = Math.Log(Math.Tan((bottom / 2) + (Math.PI / 4)));
    10. var ymax = Math.Log(Math.Tan((top / 2) + (Math.PI / 4)));
    11. var xfactor = width / (right - left);
    12. var yfactor = height / (ymax - ymin);
    13. var x = (longitude - left) * xfactor;
    14. var y = (ymax - Math.Log(Math.Tan((latitude / 2) + (Math.PI / 4)))) * yfactor;
    15. return new PointF((float)x, (float)y);
    16. }


    Jetzt bleibt nur noch die Frage, wie man das umkehrt...
    Hmkay. :|
    Hi,

    das ist doch schon die Lösung - Du kannst, wie du wolltest, Grenzen und eine Koordinate angeben und bekommst Pixelkoordinaten raus. Oder hast du die Grenzen in Pixelkoordinaten gegeben und brauchst davon jetzt GPS-Koordinaten? Die Lösung dafür müsste die Google-Variante oben sein.
    Gruß
    hal2000