HKFree / UserDB

HKFree.org member managment system
6 stars 6 forks source link

propojeni adres s mapou #18

Open locutus01 opened 7 years ago

locutus01 commented 7 years ago

Ahoj,

navrhuji rozšíření funkčnosti userdb o napojení adresy uživatele na mapu, buď naši mapu mapa.hkfree.org nebo maps.google nebo mapy.cz z vyplněné adresy by se mohl stát odkaz, který by mířil na bod v mapě. Lze tím např. velmi rychle zjistit, kde je uživatel připojen nebo zjisti zda daná adresa vůbec existuje.

Locutus.

pavkriz commented 7 years ago

Krome Google Geocodingu zkusit prohnat i skrz http://www.vugtk.cz/euradin/ruian/rest.py, kde je vice (hlavne novych) cp.

Mapy.cz v geocoding API nedokazi rict, s jakou presnosti polohu urcily - naprd. Muze byt odhad na urovni vesnice a nemame to jak poznat.

bkralik commented 7 years ago

Já bych to prostě bral tak, že místa které google geocoding označí za nepřesné proženeš i tím vugtk.cz...

Mohlo by se ti na to hodit převod z S-JTSK souřadnic na WGS-84, tady je když tak funkce:

function KrowToWgs($X,$Y,$H)
{
  $H=$H+45;

///*Vypocet zemepisnych souradnic z rovinnych souradnic*/
  $a=6377397.15508; $e=0.081696831215303;
  $n=0.97992470462083; $konst_u_ro=12310230.12797036;
  $sinUQ=0.863499969506341; $cosUQ=0.504348889819882;
  $sinVQ=0.420215144586493; $cosVQ=0.907424504992097;
  $alfa=1.000597498371542; $k=1.003419163966575;
  $ro=sqrt($X*$X+$Y*$Y);
  $epsilon=2*atan($Y/($ro+$X));
  $D=$epsilon/$n; $S=2*atan(exp(1/$n*log($konst_u_ro/$ro)))-pi()/2;
  $sinS=sin($S);$cosS=cos($S);
  $sinU=$sinUQ*$sinS-$cosUQ*$cosS*cos($D);$cosU=sqrt(1-$sinU*$sinU);
  $sinDV=sin($D)*$cosS/$cosU; $cosDV=sqrt(1-$sinDV*$sinDV);
  $sinV=$sinVQ*$cosDV-$cosVQ*$sinDV; $cosV=$cosVQ*$cosDV+$sinVQ*$sinDV;
  $Ljtsk=2*atan($sinV/(1+$cosV))/$alfa;
  $t=exp(2/$alfa*log((1+$sinU)/$cosU/$k));
  $pom=($t-1)/($t+1);
  do
  {
    $sinB=$pom;
    $pom=$t*exp($e*log((1+$e*$sinB)/(1-$e*$sinB)));
    $pom=($pom-1)/($pom+1);
  }
  while (abs($pom-$sinB)>1e-15);
  $Bjtsk=atan($pom/sqrt(1-$pom*$pom));

///* Pravoúhlé souřadnice ve S-JTSK */
  $a=6377397.15508; $f_1=299.152812853;
  $e2=1-((1-1/$f_1)*(1-1/$f_1)); $ro=$a/sqrt(1-$e2*sin($Bjtsk)*sin($Bjtsk));
  $x=($ro+$H)*cos($Bjtsk)*cos($Ljtsk);
  $y=($ro+$H)*cos($Bjtsk)*sin($Ljtsk);
  $z=((1-$e2)*$ro+$H)*sin($Bjtsk);

///* Pravoúhlé souřadnice v WGS-84*/
  $dx=570.69; $dy=85.69; $dz=462.84;
  $wz=-5.2611/3600*pi()/180;$wy=-1.58676/3600*pi()/180;$wx=-4.99821/3600*pi()/180; $m=0.000003543;
  $xn=$dx+(1+$m)*($x+$wz*$y-$wy*$z); $yn=$dy+(1+$m)*(-$wz*$x+$y+$wx*$z); $zn=$dz+(1+$m)*($wy*$x-$wx*$y+$z);

///* Geodetické souřadnice v systému WGS-84*/
  $a=6378137; $f_1=298.257223563;
  $a_b=$f_1/($f_1-1); $p=sqrt($xn*$xn+$yn*$yn); $e2=1-(1-1/$f_1)*(1-1/$f_1);
  $theta=atan($zn*$a_b/$p); $st=sin($theta); $ct=cos($theta);
  $t=($zn+$e2*$a_b*$a*$st*$st*$st)/($p-$e2*$a*$ct*$ct*$ct);
  $B=atan($t);  $L=2*atan($yn/($p+$xn));  $H=sqrt(1+$t*$t)*($p-$a/sqrt(1+(1-$e2)*$t*$t));

  $B=$B/pi()*180; //if ($B<0) {$B='S'.-$B;} else {$B='N'.$B;};
  $L=$L/pi()*180; //if ($L<0) {$L='W'.-$L;} else {$L='E'.$L;};

  return(array("y" => $B,"x" => $L,"h" => $H));
}
pavkriz commented 7 years ago

Jj, tak jsem to myslel.

pavkriz commented 7 years ago
cermis commented 3 years ago

Neumím s GITem (promiňte), ale protože soubor v app/Console/UpdateLocationsCommand.php obsahuje VUGTK.cz, které je už neaktuální (proběhly tam asi 3 synchronizace s RUIAN a konec, tak jsem upravil ten soubor na RUIAN API. Snad by to mohlo fungovat. Zkuste to nějak sloučit a vyzkoušet (nemám běhové prostředí k dispozici). `<?php

namespace App\Console;

use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface;

class UpdateLocationsCommand extends Command { private $googleMapsApiKey;

protected function configure()
{
    $this->setName('app:update_locations')
        ->setDescription('Aktualizovat hromadne zemepis. souradnice podle adres u uzivatelu (kde nejsou ve stavu "valid")');
    $this->addArgument('mode', InputArgument::OPTIONAL, 'normal nebo retry, retry zkusi krome pending adres znovu geokodovat adresy ve stavu approx nebo uknown', 'normal');
}

protected function googleMapsGeocode($adresa) {
    $client = new \GuzzleHttp\Client(['verify' => false]);
    return $client->request('GET', 'https://maps.googleapis.com/maps/api/geocode/json?key='.
        urlencode($this->googleMapsApiKey).
        '&address='.urlencode($adresa));
}

protected function vugtkGeocode($adresa) {
    $client = new \GuzzleHttp\Client(['verify' => false]);
    return $client->request('GET', 'https://ags.cuzk.cz/arcgis/rest/services/RUIAN/Vyhledavaci_sluzba_nad_daty_RUIAN/MapServer/find?layers=AdresniMisto&returnGeometry=true&f=json&searchText='.
        urlencode($adresa));
}

private function krowToWgs($X,$Y,$H)
{
    $H=$H+45;

    ///*Vypocet zemepisnych souradnic z rovinnych souradnic*/
    $a=6377397.15508; $e=0.081696831215303;
    $n=0.97992470462083; $konst_u_ro=12310230.12797036;
    $sinUQ=0.863499969506341; $cosUQ=0.504348889819882;
    $sinVQ=0.420215144586493; $cosVQ=0.907424504992097;
    $alfa=1.000597498371542; $k=1.003419163966575;
    $ro=sqrt($X*$X+$Y*$Y);
    $epsilon=2*atan($Y/($ro+$X));
    $D=$epsilon/$n; $S=2*atan(exp(1/$n*log($konst_u_ro/$ro)))-pi()/2;
    $sinS=sin($S);$cosS=cos($S);
    $sinU=$sinUQ*$sinS-$cosUQ*$cosS*cos($D);$cosU=sqrt(1-$sinU*$sinU);
    $sinDV=sin($D)*$cosS/$cosU; $cosDV=sqrt(1-$sinDV*$sinDV);
    $sinV=$sinVQ*$cosDV-$cosVQ*$sinDV; $cosV=$cosVQ*$cosDV+$sinVQ*$sinDV;
    $Ljtsk=2*atan($sinV/(1+$cosV))/$alfa;
    $t=exp(2/$alfa*log((1+$sinU)/$cosU/$k));
    $pom=($t-1)/($t+1);
    do
    {
        $sinB=$pom;
        $pom=$t*exp($e*log((1+$e*$sinB)/(1-$e*$sinB)));
        $pom=($pom-1)/($pom+1);
    }
    while (abs($pom-$sinB)>1e-15);
    $Bjtsk=atan($pom/sqrt(1-$pom*$pom));

    ///* Pravoúhlé souřadnice ve S-JTSK */
    $a=6377397.15508; $f_1=299.152812853;
    $e2=1-((1-1/$f_1)*(1-1/$f_1)); $ro=$a/sqrt(1-$e2*sin($Bjtsk)*sin($Bjtsk));
    $x=($ro+$H)*cos($Bjtsk)*cos($Ljtsk);
    $y=($ro+$H)*cos($Bjtsk)*sin($Ljtsk);
    $z=((1-$e2)*$ro+$H)*sin($Bjtsk);

    ///* Pravoúhlé souřadnice v WGS-84*/
    $dx=570.69; $dy=85.69; $dz=462.84;
    $wz=-5.2611/3600*pi()/180;$wy=-1.58676/3600*pi()/180;$wx=-4.99821/3600*pi()/180; $m=0.000003543;
    $xn=$dx+(1+$m)*($x+$wz*$y-$wy*$z); $yn=$dy+(1+$m)*(-$wz*$x+$y+$wx*$z); $zn=$dz+(1+$m)*($wy*$x-$wx*$y+$z);

    ///* Geodetické souřadnice v systému WGS-84*/
    $a=6378137; $f_1=298.257223563;
    $a_b=$f_1/($f_1-1); $p=sqrt($xn*$xn+$yn*$yn); $e2=1-(1-1/$f_1)*(1-1/$f_1);
    $theta=atan($zn*$a_b/$p); $st=sin($theta); $ct=cos($theta);
    $t=($zn+$e2*$a_b*$a*$st*$st*$st)/($p-$e2*$a*$ct*$ct*$ct);
    $B=atan($t);  $L=2*atan($yn/($p+$xn));  $H=sqrt(1+$t*$t)*($p-$a/sqrt(1+(1-$e2)*$t*$t));

    $B=$B/pi()*180; //if ($B<0) {$B='S'.-$B;} else {$B='N'.$B;};
    $L=$L/pi()*180; //if ($L<0) {$L='W'.-$L;} else {$L='E'.$L;};

    return(array("lat" => $B,"lon" => $L,"h" => $H));
}

protected function execute(InputInterface $input, OutputInterface $output)
{
    $this->googleMapsApiKey = $this->getHelper('container')->getParameter('googleMapsApiKey');
    $mode = $input->getArgument('mode');
    echo "update_locations started, mode $mode, googleMapsApiKey: $this->googleMapsApiKey\n";
    /** @var \App\Model\Uzivatel $uzivatelModel */
    $uzivatelModel = $this->getHelper('container')->getByType('\App\Model\Uzivatel');
    $statuses = ($mode === 'retry') ? ['pending', 'approx', 'unknown'] : ['pending'];
    $uzivatele = $uzivatelModel->findAll()->where('location_status IN ?', $statuses)->limit(500)->fetchAll();
    //$uzivatele = $uzivatelModel->findAll()->where('id IN ?', [1016])->limit(500)->fetchAll();
    foreach($uzivatele as $uzivatel) {
        $adresa = "{$uzivatel->ulice_cp}, {$uzivatel->mesto}";
        $uid = $uzivatel->id;
        echo "[$uid] $adresa\n";
        $lat = null;
        $lon = null;
        $status = 'error';
        $res = $this->googleMapsGeocode($adresa);
        if ($res->getStatusCode() === 200) {
            //echo $res->getBody()."\n";
            $data = json_decode($res->getBody(), true);
            //print_r($data);
            if ($data['status'] === 'OK') {
                // nalezen vysledek (s ruznou presnosti)
                $geometry = $data['results'][0]['geometry'];
                $lat = $geometry['location']['lat'];
                $lon = $geometry['location']['lng'];
                $precision = $geometry['location_type'];
                echo "  found $lat,$lon $precision\n";
                $status = ($precision === 'ROOFTOP') ? 'valid' : 'approx';
            } else if ($data['status'] === 'ZERO_RESULTS') {
                // vysledek nenalezen
                echo "  unknown\n";
                $status = 'uknown';
            } else {
                // prekrocen denni limit volani API, anebo byl request odmitnut, anebo jina chyba
                echo "  API ERROR ".$data['status']."\n";
                break; // dal uz to ted zkouset nebudeme
            }
        } else {
            echo "  HTTP ERROR ".$res->getStatusCode()."\n";
        }

        if ($status === 'valid') {
            // google nasel adresu presne, ulozit
            $uzivatelModel->update($uid, [
                'location_status' => $status,
                'latitude' => $lat,
                'longitude' => $lon
            ]);
        } else if ($status === 'error') {
            // pri chybe ignorovat, pri pristim behu to zkusime znovu (zustava status 'pending')
        } else {
            // google adresu nenasel (uknown) nebo nasel, ale nepresne (approx);
            // zkusit jeste jine geocoding api
            $res = $this->vugtkGeocode($adresa);
            if ($res->getStatusCode() === 200) {
                $data = json_decode($res->getBody(), true);
                if (isset($data['results']) && (count($data['results']) === 1)) {
                    // adresa nalezena (presne 1 misto)
                    $jtskx = abs($data['results'][0]['geometry']['x']);
                    $jtsky = abs($data['results'][0]['geometry']['y']);
                    // prevedeme Krovaka na WGS
                    $wgs = $this->krowToWgs($jtskx, $jtsky, 240);
                    $status = 'valid';
                    $lat = $wgs['lat'];
                    $lon = $wgs['lon'];
                    echo "  vugtk found $lat,$lon\n";
                } else {
                    // adresa nenalezena nebo nalezeno vetsi uzemi, ulozime tedy aspon to, co nalezly google mapy
                    echo "  vugtk not found\n";
                }
                // ulozime stav a souradnice (muze byt uknown+null nebo approx+lat,lon z google nebo valid+lat,lon z vugtk;
                //                              valid+lan,lon z google se uklada vyse)
                $uzivatelModel->update($uid, [
                    'location_status' => $status,
                    'latitude' => $lat,
                    'longitude' => $lon
                ]);
            } else {
                // vugtk API nefunguje, vysledek (i z google map) ignorujeme a zkusime za chvili znovu (zustava status 'pending')
            }
        }

        sleep(2);
    }
    echo "update_locations finished\n";
}

} ?> `