{"id":24732707,"url":"https://github.com/gonzalo123/locator","last_synced_at":"2025-10-10T01:32:48.201Z","repository":{"id":66582109,"uuid":"121873208","full_name":"gonzalo123/locator","owner":"gonzalo123","description":"Playing with Ionic, Lumen, Firebase, Google maps, Raspberry Pi and background geolocation.","archived":false,"fork":false,"pushed_at":"2018-04-08T15:18:46.000Z","size":1405,"stargazers_count":9,"open_issues_count":1,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-03T12:12:32.186Z","etag":null,"topics":["firebase","google-maps","ionic","ionic3","laravel","lumen","php","raspberry-pi"],"latest_commit_sha":null,"homepage":"https://gonzalo123.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gonzalo123.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-02-17T16:53:06.000Z","updated_at":"2022-04-30T05:58:14.000Z","dependencies_parsed_at":null,"dependency_job_id":"0c84f506-5c84-4503-9abf-38e90c12e892","html_url":"https://github.com/gonzalo123/locator","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/gonzalo123/locator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Flocator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Flocator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Flocator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Flocator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gonzalo123","download_url":"https://codeload.github.com/gonzalo123/locator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gonzalo123%2Flocator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002406,"owners_count":26083374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["firebase","google-maps","ionic","ionic3","laravel","lumen","php","raspberry-pi"],"created_at":"2025-01-27T17:53:47.374Z","updated_at":"2025-10-10T01:32:48.194Z","avatar_url":"https://github.com/gonzalo123.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Playing with Ionic, Lumen, Firebase, Google maps, Raspberry Pi and background geolocation.\n\nI wanna do a simple pet project. The idea is to build a mobile application. This application will track my gps location and send this information to a Firebase database. I've never play with Firebase and I want to learn a little bit. With this information I will build a simple web application hosted in my Raspberry Pi. This web application will show a Google map with my last location. I will put this web application in my TV and anyone in my house will see where I am every time.\n\nThat's the idea. I want a MVP. First the mobile application. I will use ionic framework. \nThe mobile application is very simple it only has a toggle to activate-deactivate the background geolocation (sometimes I don't want to be tracked :).\n\n```html\n\u003cion-header\u003e\n    \u003cion-navbar\u003e\n        \u003cion-title\u003e\n            Ionic Blank\n        \u003c/ion-title\u003e\n    \u003c/ion-navbar\u003e\n\u003c/ion-header\u003e\n\n\u003cion-header\u003e\n    \u003cion-toolbar [color]=\"toolbarColor\"\u003e\n        \u003cion-title\u003e{{title}}\u003c/ion-title\u003e\n        \u003cion-buttons end\u003e\n            \u003cion-toggle color=\"light\"\n                        checked=\"{{isBgEnabled}}\"\n                        (ionChange)=\"changeWorkingStatus($event)\"\u003e\n            \u003c/ion-toggle\u003e\n        \u003c/ion-buttons\u003e\n    \u003c/ion-toolbar\u003e\n\u003c/ion-header\u003e\n\n\u003cion-content padding\u003e\n\u003c/ion-content\u003e\n```\n\nAnd the controller:\n```typescript\nimport {Component} from '@angular/core';\nimport {Platform} from 'ionic-angular';\nimport {LocationTracker} from \"../../providers/location-tracker/location-tracker\";\n\n@Component({\n    selector: 'page-home',\n    templateUrl: 'home.html'\n})\nexport class HomePage {\n    public status: string = localStorage.getItem('status') || \"-\";\n    public title: string = \"\";\n    public isBgEnabled: boolean = false;\n    public toolbarColor: string;\n\n    constructor(platform: Platform,\n                public locationTracker: LocationTracker) {\n\n        platform.ready().then(() =\u003e {\n\n                if (localStorage.getItem('isBgEnabled') === 'on') {\n                    this.isBgEnabled = true;\n                    this.title = \"Working ...\";\n                    this.toolbarColor = 'secondary';\n                } else {\n                    this.isBgEnabled = false;\n                    this.title = \"Idle\";\n                    this.toolbarColor = 'light';\n                }\n        });\n    }\n\n    public changeWorkingStatus(event) {\n        if (event.checked) {\n            localStorage.setItem('isBgEnabled', \"on\");\n            this.title = \"Working ...\";\n            this.toolbarColor = 'secondary';\n            this.locationTracker.startTracking();\n        } else {\n            localStorage.setItem('isBgEnabled', \"off\");\n            this.title = \"Idle\";\n            this.toolbarColor = 'light';\n            this.locationTracker.stopTracking();\n        }\n    }\n}\n```\n\nAs you can see, the toggle button will activate-deactivate the background geolocation and it also changes de background color of the toolbar.\n\nFor background geolocation I will use one cordova plugin available as ionic native plugin: https://ionicframework.com/docs/native/background-geolocation/\n\nHere you can see read a very nice article explaining how to use the plugin with ionic: https://www.joshmorony.com/adding-background-geolocation-to-an-ionic-2-application/\n\nAs the article explains I've created a provider \n```typescript\nimport {Injectable, NgZone} from '@angular/core';\nimport {BackgroundGeolocation} from '@ionic-native/background-geolocation';\nimport {CONF} from \"../conf/conf\";\n\n@Injectable()\nexport class LocationTracker {\n    constructor(public zone: NgZone,\n                private backgroundGeolocation: BackgroundGeolocation) {\n    }\n\n    showAppSettings() {\n        return this.backgroundGeolocation.showAppSettings();\n    }\n\n    startTracking() {\n        this.startBackgroundGeolocation();\n    }\n\n    stopTracking() {\n        this.backgroundGeolocation.stop();\n    }\n\n    private startBackgroundGeolocation() {\n        this.backgroundGeolocation.configure(CONF.BG_GPS);\n        this.backgroundGeolocation.start();\n    }\n}\n```\n\nThe idea of the plugin is send a POST request to a url with the gps data in the body of the request. So, I will create a web api server to handle this request. I will use my Raspberry Pi3 (one of mines :) to serve the application. I will create a simple PHP/Lumen application. This application will handle the POST request of the mobile application and also will serve a html page with the map (using google maps). \n\nMobile requests will be authenticated with a token in the header and web application will use a basic http authentication. Because of that I will create two middlewares to handle the the different ways to authenticate.\n\n```php\n\u003c?php\nrequire __DIR__ . '/../vendor/autoload.php';\n\nuse App\\Http\\Middleware;\nuse App\\Model\\Gps;\nuse Illuminate\\Contracts\\Debug\\ExceptionHandler;\nuse Illuminate\\Http\\Request;\nuse Laravel\\Lumen\\Application;\nuse Laravel\\Lumen\\Routing\\Router;\n\n(new Dotenv\\Dotenv(__DIR__ . '/../env/'))-\u003eload();\n\n$app = new Application(__DIR__ . '/..');\n$app-\u003esingleton(ExceptionHandler::class, App\\Exceptions\\Handler::class);\n$app-\u003erouteMiddleware([\n    'auth'  =\u003e Middleware\\AuthMiddleware::class,\n    'basic' =\u003e Middleware\\BasicAuthMiddleware::class,\n]);\n\n$app-\u003erouter-\u003egroup(['middleware' =\u003e 'auth', 'prefix' =\u003e '/locator'], function (Router $route) {\n    $route-\u003epost('/gps', function (Gps $gps, Request $request) {\n        $requestData = $request-\u003eall();\n        foreach ($requestData as $poi) {\n            $gps-\u003epersistsData([\n                'date'             =\u003e date('YmdHis'),\n                'serverTime'       =\u003e time(),\n                'time'             =\u003e $poi['time'],\n                'latitude'         =\u003e $poi['latitude'],\n                'longitude'        =\u003e $poi['longitude'],\n                'accuracy'         =\u003e $poi['accuracy'],\n                'speed'            =\u003e $poi['speed'],\n                'altitude'         =\u003e $poi['altitude'],\n                'locationProvider' =\u003e $poi['locationProvider'],\n            ]);\n        }\n\n        return 'OK';\n    });\n});\n\nreturn $app;\n```\n\nAs we can see the route /locator/gps will handle the post request. I've created a model to persists gps data in the firebase database:\n\n```php\n\u003c?php\n\nnamespace App\\Model;\n\nuse Kreait\\Firebase\\Factory;\nuse Kreait\\Firebase\\ServiceAccount;\n\nclass Gps\n{\n    private $database;\n\n    private const FIREBASE_CONF = __DIR__ . '/../../conf/firebase.json';\n\n    public function __construct()\n    {\n        $serviceAccount = ServiceAccount::fromJsonFile(self::FIREBASE_CONF);\n        $firebase       = (new Factory)\n            -\u003ewithServiceAccount($serviceAccount)\n            -\u003ecreate();\n\n        $this-\u003edatabase = $firebase-\u003egetDatabase();\n    }\n\n    public function getLast()\n    {\n        $value = $this-\u003edatabase-\u003egetReference('gps/poi')\n            -\u003eorderByKey()\n            -\u003elimitToLast(1)\n            -\u003egetValue();\n\n        $out                 = array_values($value)[0];\n        $out['formatedDate'] = \\DateTimeImmutable::createFromFormat('YmdHis', $out['date'])-\u003eformat('d/m/Y H:i:s');\n\n        return $out;\n    }\n\n    public function persistsData(array $data)\n    {\n        return $this-\u003edatabase\n            -\u003egetReference('gps/poi')\n            -\u003epush($data);\n    }\n}\n```\n\nThe project is almost finished. Now we only need to create the google map.\n\nThat's the api\n```php\n\u003c?php\n$app-\u003erouter-\u003egroup(['middleware' =\u003e 'basic', 'prefix' =\u003e '/map'], function (Router $route) {\n    $route-\u003eget('/', function (Gps $gps) {\n        return view(\"index\", $gps-\u003egetLast());\n    });\n\n    $route-\u003eget('/last', function (Gps $gps) {\n        return $gps-\u003egetLast();\n    });\n});\n```\n\nAnd the HTML\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003cmeta name=\"viewport\" content=\"initial-scale=1.0, user-scalable=no\"\u003e\n    \u003cmeta charset=\"utf-8\"\u003e\n    \u003ctitle\u003eLocator\u003c/title\u003e\n    \u003cstyle\u003e\n        #map {\n            height: 100%;\n        }\n\n        html, body {\n            height: 100%;\n            margin: 0;\n            padding: 0;\n        }\n    \u003c/style\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n\u003cdiv id=\"map\"\u003e\u003c/div\u003e\n\u003cscript\u003e\n\n    var lastDate;\n    var DELAY = 60;\n\n    function drawMap(lat, long, text) {\n        var CENTER = {lat: lat, lng: long};\n        var contentString = '\u003cdiv id=\"content\"\u003e' + text + '\u003c/div\u003e';\n        var infowindow = new google.maps.InfoWindow({\n            content: contentString\n        });\n        var map = new google.maps.Map(document.getElementById('map'), {\n            zoom: 11,\n            center: CENTER,\n            disableDefaultUI: true\n        });\n\n        var marker = new google.maps.Marker({\n            position: CENTER,\n            map: map\n        });\n        var trafficLayer = new google.maps.TrafficLayer();\n\n        trafficLayer.setMap(map);\n        infowindow.open(map, marker);\n    }\n\n    function initMap() {\n        lastDate = '{{ $formatedDate }}';\n        drawMap({{ $latitude }}, {{ $longitude }}, lastDate);\n    }\n\n    setInterval(function () {\n        fetch('/map/last', {credentials: \"same-origin\"}).then(function (response) {\n            response.json().then(function (data) {\n                if (lastDate !== data.formatedDate) {\n                    drawMap(data.latitude, data.longitude, data.formatedDate);\n                }\n            });\n        });\n    }, DELAY * 1000);\n\u003c/script\u003e\n\u003cscript async defer src=\"https://maps.googleapis.com/maps/api/js?key=my_google_maps_key\u0026callback=initMap\"\u003e\n\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nAnd that's all just enough for a weekend.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgonzalo123%2Flocator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgonzalo123%2Flocator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgonzalo123%2Flocator/lists"}