Konwersja współrzędnych geograficznych do współrzędnych ekranu

Chciałbym poprosić Was o pomoc w problemie z tematu. Wybrałem sobie taki obszar mapy:

ściąłnąłem dla niego plik osm, przetworzyłem go i chciałem go narysować.

Przyjąłem następujący algorytm:

Wziąłem pierwszy punkt z pliku o wsółrzędnych geograficznych (O_lon, O_lat). Na ekranie przyznałem mu punkt o współrzędnych (O_x, O_y). Teraz dla dowolnego punktu z terenu o współrzędnych geograficznych (lon, lat) wyliczam dla niego współrzędne ekranowe jako:

(O_lon + ratio * (lon - O_lon) / 2, O_lat - ratio * (lat - O_lat))

Ratio jest czymś w rodzaju skali. Podałem 30000.

W ten sposób narysowałem kilka ulic:

Problem w tym, że jak mój wynik nałoży się na kafelki to widać, że bardzo się rozjeżdża:

Czego mi brakuje? Czy trzeba użyć jeszcze jakieś współczynniki z tego powodu, że Ziemia jest elipsoidą?

Rozjazd jest duży ponieważ teren to około 1 km kwadratowy.

Tutaj jest chyba Twój błąd. Sprawdź sobie, czy współrzędne prawego dolnego rogu obrazka przeliczają Ci się tak, jak trzeba (zakładam, że O_x, O_y masz w lewym górnym rogu, bo tam jest najmniejsze przesunięcie). W praktyce ma być tak, że masz prostokąt [O_lon,Max_lon]x[[O_lat,Max_lat] i chcesz go przeliczyć na wymiary obrazka podkładu (lub ekranu) Max_X, Max_y. Powinno być jakoś tak, że punkt x_lon, y_lat przelicza się na

x_lon → (x_lon-O_lon)*Max_X / (Max_lon-O_lon)
y_lat → Max_Y - ( (y_lat-O_lat)*Max_Y / (Max_lat-O_lat) )

czyli Ratio zależy tak naprawdę od stosunku wymiaru w stopniach na wymiar w pikselach. Przy bardzo dużych obszarach być może dojdzie problem zniekształceń/projekcji, ale na pewno jeszcze nie na takim przykładzie, jak załączony.

Ale czy szerokość geograficzna przelicza się liniowo? Mam wrażenie ze nie i to stąd może być błąd, algorytmy obliczeń są tutaj: http://wiki.openstreetmap.org/wiki/Mercator

A tu o liczeniu współrzędnych kafelka:
Http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames

Szerokość tak (zakładając, że ziemia jest kulą, a to założenie jest dość sensowne). Długość oczywiście nie, bo 1 stopień długości geograficznej na równiku i na powiedzmy naszej szerokości geograficznej to nie jest tyle samo kilometrów. Ale Cetusek ma problem przy przeliczeniach na bardzo małym obszarze (osiedla?), gdzie ta różnica nie ma jeszcze żadnego znaczenia.

Podejście Domissa zakłada w obliczeniach wykorzystanie maksymalnych rozmiarów ekranu. Chciałem tego uniknąć i zastosowałem na inne podejście.
Wybrany punkt geograficzny na mapie traktuję jaką obserwatora (zmienna observerOnEarthPosition typu Coordinates), umieszczam go na ekranie (zmienna observerOnScreenPosition typu Point2D.Double)
i w opraciu o ten jeden punkt wyznaczam położenie pozostałych punktów. Aby to zrobić wykorzysuję wzór wskazany przez WiktorN w metodzie getLatitudeAfterMercatorCorr:


public class Coordinates {

	double latitude;
	double longitue;
	
	public Coordinates() {
		latitude = 0;
		longitue = 0;
	}
	
	public Coordinates (double latitude, double longitude) {
		this.latitude = latitude;
		this.longitue = longitude;
	}

	public double getLatitudeAfterMercatorCorr() {
		return Math.toDegrees(Math.log(Math.tan(Math.PI / 4 + Math.toRadians(latitude) / 2)));
	}
}


Następnie wyliczam punkt na ekranie dla dowolnych współrzędnych geograficznych:


	public Point2D.Double coordinatesToScreen(Coordinates coordinates) {
		double ratio = getRatio();
		double latitudeDiff = coordinates.getLatitudeAfterMercatorCorr() - observerOnEarthPosition.getLatitudeAfterMercatorCorr();
		double longitudeDiff = coordinates.longitue - observerOnEarthPosition.longitue;
		Point2D.Double screenPoint = new Point2D.Double(observerOnScreenPosition.x + longitudeDiff * ratio , observerOnScreenPosition.y - latitudeDiff * ratio);
		return screenPoint;
	}

Wartość ratio jest do ustalenia eksperymentalnie. Wynik idelany:

Dziękuję Wam za pomoc.

Kalfelki masz ściągnięte z którego zoom-a? 15? To może wartość 34123.67334159654 jest przypadkiem słuszna?

Więcej współczynników znajdziesz w tabeli E.4:
http://portal.opengeospatial.org/files/?artifact_id=35326

A tak generalnie, jak chcesz coś rysować w Java’ie, to polecam:
http://trac.osgeo.org/proj4j/

Pozwoli Ci to w miarę swobodnie wybrać rodzaj odwzorowania i niekoniecznie będziesz musiał się przypinać do Pseudo-Merkatora

Kafelki są z zoomu 15. Przy wartości 34123.67334159654 rysowane linie zajmują większy obszar niż kafelki.