Skip to content

Commit

Permalink
- Pievienota JavaScript koordinātu pārveidošanas klase (javascript)
Browse files Browse the repository at this point in the history
- Izveidota projekta dokumentācija README.md
  • Loading branch information
arvislacis committed Dec 21, 2015
1 parent b284284 commit 937f692
Show file tree
Hide file tree
Showing 3 changed files with 283 additions and 2 deletions.
100 changes: 98 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,98 @@
# lks92-wgs84
Neliela, dažādās programmēšanas valodās uzrakstīta ģeogrāfisko koordinātu pārveidošanas klase, kas nodrošina Latvijas koordinātu sistēmā (LKS-92) norādīto koordinātu pārveidošanu vispasaules ģeodēzisko koordinātu sistēmā (WGS-84) un otrādi.
# LKS92-WGS84 #

LKS92-WGS84 ir neliela, dažādās programmēšanas valodās uzrakstīta ģeogrāfisko koordinātu pārveidošanas klase,
kas nodrošina Latvijas koordinātu sistēmā (LKS-92) norādīto koordinātu pārveidošanu vispasaules ģeodēzisko
koordinātu sistēmā (WGS-84) un otrādi.

## Klases izmantošana ##

1. Atkarībā no vēlamās programmēšanas valodas izvēlas pirmkoda direktoriju (piemēram, `javascript`).
2. Izvēlētā pirmkoda direktorija satur divas datnes:
- `lks92-wgs84.*` - atbilstošajā programmēšanas valodā uzrakstītā koordinātu pārveidošanas klase;
- `example.*` - klases izmantošanas paraugs ar Latvijas galējo punktu (skatīt zemāk) koordinātu pārveidojumu testpiemēriem.
3. Vēlamo koordinātu pārveidošanas klasi `lks92-wgs84.*` lejupielādē/saglabā, veicot tās pirmkoda aplūkošanu un noklikšķinot
uz spiedpogas `Raw`, pēc tam izsaucot lapas/datnes saglabāšanas funkcionalitāti no pārlūkprogrammas.
4. Lejupielādēto datni iekļauj un izmanto vēlamajā projektā, vadoties pēc `example.*` parauga; ja nepieciešams, maina datnes nosaukumu.

## Papildu piezīmes ##

- Koordinātu pārveidojumos tiek pielietots LKS-92 variants, kurā punkta koordinātas nobīdītas par `-600 km = -600000 m` ziemeļu virzienā,
mērogojuma faktors - `0.9996`, bet centrālais ass meridiāns - `24 E`.
Šos parametrus iespējams mainīt klases konstantes mainīgajos `OFFSET_Y`, `SCALE` un `CENTRAL_MERIDIAN`; tāpat tiek izmantoti
arī citi konstantie mainīgie, kas var ietekmēt gala rezultātu precizitāti un atbilstību testa datiem.
- Koordinātu pārveidojumos ieteicams izmantot tikai to punktu koordinātas, kuri atrodas Latvijas teritorijā.
- WGS-84 rezultātu kļūda **nav mazāka** par `10^(-8) grāda`, bet LKS-92 rezultātu - `10^(-2) metra`, tādēļ šos pārveidojumus
nav ieteicams izmantot precīzos mērījumos un precīzu aprēķinu veikšanā vai tml.

## Testpiemēru atskaites punkti ##

Lai salīdzinātu koordinātu pārveidojumu rezultātus, īpaši veicot jaunu programmēšanas valodu pievienošanu, tiek izmantotas
[Latvijas galējo punktu koordinātas](https://www.vietas.lv/index.php?p=34&gid=15):

| Punkta nosaukums | Virziens | WGS-84 platums | WGS-84 garums | LKS-92 X | LKS-92 Y |
|:----------------:|:--------:|:----------------:|:---------------:|:----------:|:----------:|
| "Baltās naktis" | Z | 58.079501574948 | 25.189986971284 | 570181.000 | 438180.000 |
| "Austras koks" | A | 56.172282784562 | 28.095216442873 | 754190.003 | 232806.000 |
| "Saules puķe" | D | 55.675228242509 | 26.580528487143 | 662269.000 | 172953.000 |
| "Zaļais stars" | R | 56.377008455189 | 20.979185882058 | 313470.000 | 252137.000 |

## Programmēšanas valodu pieejamība ##

Projektā pašlaik pieejamās programmēšanas valodas - valodas, kurām izstrādāta koordinātu pārveidošanas klase `lks92-wgs84.*`:

| Programmēšanas valoda | Klases autors | Pēdējo izmaiņu datums |
|:---------------------:|:--------------------------------------------:|:---------------------:|
| JavaScript | [Arvis Lācis](https://github.com/arvislacis) | 22.12.2015. |

Laika gaitā plānots projektu papildināt arī ar citām, mazāk vai vairāk, populārām programmēšanas valodām gan no projekta autora,
gan no citu interesentu puses.

Jebkuram interesentam ir iespējams iesniegt - gan izmantojot GitHub *Pull requests* sistēmu, gan rakstot personīgi -
jaunu koordinātu pārveidošanas klasi citā, viņam labi zināmā, programmēšanas valodā, ievērojot sekojošus nosacījumus:
- **Nedublējiet jau esošās programmēšanas valodas.** Ja esošajos risinājumos pamanāt kļūdu, tad izveidojiet jaunu problēmas
ziņojumu *(Issues)*, nevis pārstrādājiet vai veidojiet jaunu esoša risinājuma variantu.
- **Stingri ievērojiet projekta autora veidoto klašu pierakstu** - komentāri, funkciju secība, funkciju ieejas un izejas
parametri, vērtības utt. Atkāpes no iepriekš uzskaitītajām normām pieļaujamas tikai tad, ja izvēlētajā programmēšanas valodā nav iespējams
izmantot tāda paša veida risinājumu, kas ir apšaubāmi.
- **Atļauts izmantot izvēlētajā programmēšanas valodā unikālos operatorus un iebūvētās funkcijas** - gan kā alternatīvu, gan atkārtojošā,
liekā pirmkoda aizvietošanas nolūkiem -, piemēram, izmantojot valodā iebūvēto funkciju grādu pārveidošanai par radiāniem un otrādi, kas
JavaScript valodā nav pieejama utml. Šādu operatoru, funkciju izmantojums nedrīkst pārlieku sarežģīt koordinātu pārveidošanas klasi un
visiem funkciju ieejas, izejas parametriem jāpaliek nemainīgiem.
- **Klases realizācijā stingri jāizvairās no papildus bibliotēku vai klašu izmantošanas**, ja tas nav iespējams, tad pieļaujama
standarta bibliotēku iekļaušana.
- Ja vien to pieļauj programmēšanas valoda, **pirmkods jāstrukturē klases veidā ar statiski izsaucamām funkcijām**, kas nodrošina
koordinātu pārveidošanas klases vienkāršu izmantošanu un atjaunināšanu, tāpat pārveidošanas funkcionalitātes nodrošināšanai
nav nepieciešams veidot jaunu klases objektu.
- **Iesniegtajam pirmkodam jāsatur gan klases datne `lks92-wgs84.*`, gan klases izmantošanas parauga un testpiemēru datne `example.*`.**
Neskaidrību gadījumā ieteicams vadīties pēc projektā esošo datņu paraugiem.

Ieteikumu, uzlabojumu vai cita veida kļūdu atklāšanas gadījumā ieteicams izveidot jaunu problēmas ziņojumu *(Issues)*.

## Izmantotie avoti ##

Lai izveidotu projekta sākotnējo koordinātu pārveidošanas klasi (JavaScript), tika izmantoti šādi informācijas avoti:

1. [Koordinātu pārrēķinātājs - NeoGeo.lv](https://neogeo.lv/ekartes/koord2/)
2. [Transverse Mercator: Redfearn series - Wikipedia](https://en.wikipedia.org/wiki/Transverse_Mercator:_Redfearn_series)

## MIT licence ##

Copyright (c) 2015-2016 Arvis Lācis

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
32 changes: 32 additions & 0 deletions javascript/example.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="lks92-wgs84.js"></script>
</head>
<body>
<script>
function TestCase(coordinates) {
var converted = LKS92WGS84.convertXYToLatLon(LKS92WGS84.convertLatLonToXY(coordinates)),
result;

if (Math.round(converted[0] * Math.pow(10, 8)) / Math.pow(10, 8) == Math.round(coordinates[0] * Math.pow(10, 8)) / Math.pow(10, 8) && Math.round(converted[1] * Math.pow(10, 8)) / Math.pow(10, 8) == Math.round(coordinates[1] * Math.pow(10, 8)) / Math.pow(10, 8)) {
result = "izpildās";
} else {
result = "neizpildās";
}

return result;
}

var coordinates = [58.079501574948, 25.189986971284];
console.log("\"Baltās naktis\" - Latvijas tālākais ziemeļu punkts [" + coordinates + "] => " + TestCase(coordinates));
coordinates = [56.172282784562, 28.095216442873];
console.log("\"Austras koks\" - Latvijas tālākais austrumu punkts [" + coordinates + "] => " + TestCase(coordinates));
coordinates = [55.675228242509, 26.580528487143];
console.log("\"Saules puķe\" - Latvijas tālākais dienvidu punkts [" + coordinates + "] => " + TestCase(coordinates));
coordinates = [56.377008455189, 20.979185882058];
console.log("\"Zaļais stars\" - Latvijas galējais rietumu punkts [" + coordinates + "] => " + TestCase(coordinates));
</script>
</body>
</html>
153 changes: 153 additions & 0 deletions javascript/lks92-wgs84.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
var LKS92WGS84 = {
// Koordinātu pārveidojumos izmantotās konstantes
PI: Math.PI, // Skaitlis pi
A_AXIS: 6378137, // Elipses modeļa lielā ass (a)
B_AXIS: 6356752.31414, // Elipses modeļa mazā ass (b)
CENTRAL_MERIDIAN: 24 / 180 * Math.PI, // Centrālais meridiāns
OFFSET_X: 500000, // Koordinātu nobīde horizontālās (x) ass virzienā
OFFSET_Y: -6000000, // Koordinātu nobīde vertikālās (y) ass virzienā
SCALE: 0.9996, // Kartes mērogojuma faktors (reizinātājs)

// Aprēķina loka garumu no ekvatora līdz dotā punkta ģeogrāfiskajam platumam
getArcLengthOfMeridian: function(phi) {
var alpha, beta, gamma, delta, epsilon, n;

n = (this.A_AXIS - this.B_AXIS) / (this.A_AXIS + this.B_AXIS);
alpha = ((this.A_AXIS + this.B_AXIS) / 2) * (1 + (Math.pow(n, 2) / 4) + (Math.pow(n, 4) / 64));
beta = (-3 * n / 2) + (9 * Math.pow(n, 3) / 16) + (-3 * Math.pow(n, 5) / 32);
gamma = (15 * Math.pow(n, 2) / 16) + (-15 * Math.pow(n, 4) / 32);
delta = (-35 * Math.pow(n, 3) / 48) + (105 * Math.pow(n, 5) / 256);
epsilon = (315 * Math.pow(n, 4) / 512);

return alpha * (phi + (beta * Math.sin(2 * phi)) + (gamma * Math.sin(4 * phi)) + (delta * Math.sin(6 * phi)) + (epsilon * Math.sin(8 * phi)));
},

// Aprēķina ģeogrāfisko platumu centrālā meridiāna punktam
getFootpointLatitude: function(y) {
var y_, alpha_, beta_, gamma_, delta_, epsilon_, n;

n = (this.A_AXIS - this.B_AXIS) / (this.A_AXIS + this.B_AXIS);
alpha_ = ((this.A_AXIS + this.B_AXIS) / 2) * (1 + (Math.pow(n, 2) / 4) + (Math.pow(n, 4) / 64));
y_ = y / alpha_;
beta_ = (3 * n / 2) + (-27 * Math.pow(n, 3) / 32) + (269 * Math.pow(n, 5) / 512);
gamma_ = (21 * Math.pow(n, 2) / 16) + (-55 * Math.pow(n, 4) / 32);
delta_ = (151 * Math.pow(n, 3) / 96) + (-417 * Math.pow(n, 5) / 128);
epsilon_ = (1097 * Math.pow(n, 4) / 512);

return y_ + (beta_ * Math.sin(2 * y_)) + (gamma_ * Math.sin(4 * y_)) + (delta_ * Math.sin(6 * y_)) + (epsilon_ * Math.sin(8 * y_));
},

// Pārveido punkta ģeogrāfiskā platuma, garuma koordinātas par x, y koordinātām (bez pārvietojuma un mērogojuma)
convertMapLatLngToXY: function(phi, lambda, lambda0) {
var N, nu2, ep2, t, t2, l,
l3coef, l4coef, l5coef, l6coef, l7coef, l8coef,
xy = [0, 0];

ep2 = (Math.pow(this.A_AXIS, 2) - Math.pow(this.B_AXIS, 2)) / Math.pow(this.B_AXIS, 2);
nu2 = ep2 * Math.pow(Math.cos(phi), 2);
N = Math.pow(this.A_AXIS, 2) / (this.B_AXIS * Math.sqrt(1 + nu2));
t = Math.tan(phi);
t2 = t * t;

l = lambda - lambda0;
l3coef = 1 - t2 + nu2;
l4coef = 5 - t2 + 9 * nu2 + 4 * (nu2 * nu2);
l5coef = 5 - 18 * t2 + (t2 * t2) + 14 * nu2 - 58 * t2 * nu2;
l6coef = 61 - 58 * t2 + (t2 * t2) + 270 * nu2 - 330 * t2 * nu2;
l7coef = 61 - 479 * t2 + 179 * (t2 * t2) - (t2 * t2 * t2);
l8coef = 1385 - 3111 * t2 + 543 * (t2 * t2) - (t2 * t2 * t2);

// x koordināta
xy[0] = N * Math.cos(phi) * l + (N / 6 * Math.pow(Math.cos(phi), 3) * l3coef * Math.pow(l, 3)) + (N / 120 * Math.pow(Math.cos(phi), 5) * l5coef * Math.pow(l, 5)) + (N / 5040 * Math.pow(Math.cos(phi), 7) * l7coef * Math.pow(l, 7));

// y koordināta
xy[1] = this.getArcLengthOfMeridian(phi) + (t / 2 * N * Math.pow(Math.cos(phi), 2) * Math.pow(l, 2)) + (t / 24 * N * Math.pow(Math.cos(phi), 4) * l4coef * Math.pow(l, 4)) + (t / 720 * N * Math.pow(Math.cos(phi), 6) * l6coef * Math.pow(l, 6)) + (t / 40320 * N * Math.pow(Math.cos(phi), 8) * l8coef * Math.pow(l, 8));
return xy;
},

// Pārveido punkta x, y koordinātas par ģeogrāfiskā platuma, garuma koordinātām (bez pārvietojuma un mērogojuma)
convertMapXYToLatLon: function(x, y, lambda0) {
var phif, Nf, Nfpow, nuf2, ep2, tf, tf2, tf4, cf,
x1frac, x2frac, x3frac, x4frac, x5frac, x6frac, x7frac, x8frac,
x2poly, x3poly, x4poly, x5poly, x6poly, x7poly, x8poly,
latLng = [0, 0];

phif = this.getFootpointLatitude(y);
ep2 = (Math.pow(this.A_AXIS, 2) - Math.pow(this.B_AXIS, 2)) / Math.pow(this.B_AXIS, 2);
cf = Math.cos(phif);
nuf2 = ep2 * Math.pow(cf, 2);
Nf = Math.pow(this.A_AXIS, 2) / (this.B_AXIS * Math.sqrt(1 + nuf2));
Nfpow = Nf;

tf = Math.tan(phif);
tf2 = tf * tf;
tf4 = tf2 * tf2;

x1frac = 1 / (Nfpow * cf);

Nfpow *= Nf; // Nf^2
x2frac = tf / (2 * Nfpow);

Nfpow *= Nf; // Nf^3
x3frac = 1 / (6 * Nfpow * cf);

Nfpow *= Nf; // Nf^4
x4frac = tf / (24 * Nfpow);

Nfpow *= Nf; // Nf^5
x5frac = 1 / (120 * Nfpow * cf);

Nfpow *= Nf; // Nf^6
x6frac = tf / (720 * Nfpow);

Nfpow *= Nf; // Nf^7
x7frac = 1 / (5040 * Nfpow * cf);

Nfpow *= Nf; // Nf^8
x8frac = tf / (40320 * Nfpow);

x2poly = -1 - nuf2;
x3poly = -1 - 2 * tf2 - nuf2;
x4poly = 5 + 3 * tf2 + 6 * nuf2 - 6 * tf2 * nuf2 - 3 * (nuf2 *nuf2) - 9 * tf2 * (nuf2 * nuf2);
x5poly = 5 + 28 * tf2 + 24 * tf4 + 6 * nuf2 + 8 * tf2 * nuf2;
x6poly = -61 - 90 * tf2 - 45 * tf4 - 107 * nuf2 + 162 * tf2 * nuf2;
x7poly = -61 - 662 * tf2 - 1320 * tf4 - 720 * (tf4 * tf2);
x8poly = 1385 + 3633 * tf2 + 4095 * tf4 + 1575 * (tf4 * tf2);

// Ģeogrāfiskais platums
latLng[0] = phif + x2frac * x2poly * (x * x) + x4frac * x4poly * Math.pow(x, 4) + x6frac * x6poly * Math.pow(x, 6) + x8frac * x8poly * Math.pow(x, 8);

// Ģeogrāfiskais garums
latLng[1] = lambda0 + x1frac * x + x3frac * x3poly * Math.pow(x, 3) + x5frac * x5poly * Math.pow(x, 5) + x7frac * x7poly * Math.pow(x, 7);

return latLng;
},

// Pārveido punkta ģeogrāfiskā platuma, garuma koordinātas par x, y koordinātām (ar pārvietojumu un mērogojumu)
convertLatLonToXY: function(coordinates) {
var lat = coordinates[0] * this.PI / 180,
lng = coordinates[1] * this.PI / 180,
xy = this.convertMapLatLngToXY(lat, lng, this.CENTRAL_MERIDIAN);

xy[0] = xy[0] * this.SCALE + this.OFFSET_X;
xy[1] = xy[1] * this.SCALE + this.OFFSET_Y;

if (xy[1] < 0) {
xy[1] = xy[1] + 10000000;
}

return xy;
},

// Pārveido punkta x, y koordinātas par ģeogrāfiskā platuma, garuma koordinātām (ar pārvietojumu un mērogojumu)
convertXYToLatLon: function(coordinates) {
var x = (coordinates[0] - this.OFFSET_X) / this.SCALE,
y = (coordinates[1] - this.OFFSET_Y) / this.SCALE,
latLng = this.convertMapXYToLatLon(x, y, this.CENTRAL_MERIDIAN);

latLng[0] = latLng[0] / this.PI * 180;
latLng[1] = latLng[1] / this.PI * 180;

return latLng;
}
};

0 comments on commit 937f692

Please sign in to comment.