Instructions for the Places Mobile App Project
In this project you are going to create a mobile app like this that reads your geolocation and displays nearby places.
We will be using the following Application Programmer Interfaces (APIs) which augment Google's Maps and Places APIs. The source code for these APIs is provided if you would like to implement the same or modified interface on your own server.
APIs
API URL Source Code |
---|
Geocoding (latitude/longitude lookup) Reverse Geocoding (address lookup) https://secrdir.com/api/geocode/?address=... https://secrdir.com/api/geocode/?latlng=... /geocode/ |
Place Search https://secrdir.com/api/place/nearbysearch/?location=... /place/nearbysearch/ |
Place Details https://secrdir.com/api/place/details/?placeid=... /place/details/ |
Place Types https://secrdir.com/api/place/types/ /place/types/ |
Rating Icons
https://secrdir.com/api/rating_icons/?stars=
https://secrdir.com/api/rating_icons/?dollars= /rating_icons/ |
Static Maps https://secrdir.com/api/staticmap/?center=... /staticmap/ |
In this exercise we use Ionic Creator to create the screens. If you don't have an account yet you can sign up at Creator.Ionic.io. Then you can decide whether to upgrade to Creator Pro. With Creator Pro you will continue to use Creator to add the Angular directives and services necessary for the app to work. If you choose not to upgrade, then you can export your app to your desktop to complete.
Step 1. Add Ionic Components
Create Pages
Create the two pages used for this app.- Page 1
- Title = Places
- Routing URL = /page1
- Page 2 - Click +Add Page above the left Pages panel
- Click to create a Blank page
- Set Title = Details
- Routing URL = /page2
Page 1 Components
In the Pages panel in the upper left, click the Places page to edit the first page. Add the following components- Container - First row
- Tag = div
- Classes = row
- Container
- Tag = div
- Classes = col-50 col-bottom
- Paragraph
- Content = Enter Address or tap **Where Am I?**
- Container - "Where Am I?" button
Hint - Duplicate the left container, then edit the right container.- Tag = div
- Classes = col-50
- Button
- Link = Page (first icon)
- Type = Not Linked
- Text = Where Am I?
Page 1 - Places (continued)
Continue by adding these components on the first page under the row container containing the "Where Am I?" button.- Input
- Title =
- Type = Text
- Placeholder = Enter Street Address
- Paragraph
- Align = Center
- Content = **{{ data.latlng }}**
- Select
- Title = Place Type
- Paragraph
- Align = Center
- Content = {{ data.num_places }}
- Avatar List Item
- Link = Page (first icon)
- Type = Details - /page2
- Style = Avatar
- Switch to text input (not upload mode)
- Avatar Image = {{ place.icon }}
- Text Content = {{ place.name }}
- Text Line #2 = {{ place.vicinity }}
- Right Side Icon = ion-ios-arrow-right
Page 2 - Place Details
In the Pages panel in the upper left, click the Details page to edit the second page. Add the following components- Heading
- Text = {{ post.result.name }}
- Size = H3
- Align = Center
- Paragraph
- Align = Center
- Content = **{{ post.result.vicinity }}**
- Container - Star Rating and Dollar Sign Price Level
- Tag = div
- Classes = row
- Container
- Tag = div
- Classes = col-50
- Paragraph
- Content = **Rating**
- Container - below paragraph
- Tag = div
- Classes = row
- Container
- Tag = div
- Classes = col-25
- Paragraph - inside col-25 container
- Color = #E42112
- Align = Center
- Content = {{ post.result.rating }}
- Container - Star Icons
Hint - Duplicate the col-25 container containing the paragraph on the left, then edit the duplicated container right container.- Tag = div
- Classes = col-75
- Image - inside the col-75 container
- Link = Page (first icon)
- Type = Not Linked
- Source = Switch to text input (not upload mode)
- Image Source = {{ data.star_rating_icon }}
- Style Size = 100% x auto
- Style Position= Center
- Container
Hint - Duplicate the col-50 container containing the rating and rating icons, then edit the right container.- Tag = div
- Classes = col-50
- Paragraph
- Content = **Price Level**
- Container - Price Level
- Tag = div
- Classes = row
- Container
- Tag = div
- Classes = col-25
- Paragraph
- Color = #E42112
- Align = Center
- Content = {{ post.result.price_level }}
- Container - Dollar Sign Icons
- Tag = div
- Classes = col-75
- Image
- Link = Page (first icon)
- Type = Not Linked
- Source = Switch to text input (not upload mode)
- Image Source = {{ data.price_level_icon }}
- Style Size = 100% x auto
- Style Position= Center
- Spacer
- Height = 50px - When adding the rest of the components, always keep this spacer component at the very bottom
Page 2 - Place Details (continued)
Continue on the second page. Add the following components below the Rating and Price Level icons and above the 50px spacer component. Keep the 50px spacer at the very bottom.- Container
- Tag = div
- Classes = row
- We will make the More... button first, then duplicate it for the Phone button.
- Container - More ... Button to Google Places Page
- Tag = div
- Classes = col-34
- Button
- Link = Link (second icon)
- Type = {{ post.result.url }}
- Text = More ...
- Align = Center
- Icon = ion-ios-arrow-right
- Icon Position = align right
- Container - Phone Number Button (left of More... button)
Hint: Duplicate the col-34 container containing the button, then edit the left col-34 container.- Tag = div
- Classes = col-66
- Button
- Link = Phone (last icon)
- Type = {{ post.result.formatted_phone_number }}
- Text = {{ post.result.formatted_phone_number }}
- Align = Center
- Icon = No Icon
- Container - Hours, Open, Closed Row
- Tag = div
- Classes = row
- Container
- Tag = div
- Classes = col-50
- Paragraph
- Content = **Hours**
- Container
- Tag = div
- Classes = col-50
- Paragraph
- Color = #E42112 (red)
- Align = Right
- Content = **Open**
- Paragraph
- Color = #969292 (gray)
- Align = Right
- Content = **Closed**
- Container - Open Hours Daily
- Tag = div
- Classes =
- Html
- <span ng-repeat="hour in post.result.opening_hours.weekday_text">
{{ hour }}<br>
</span>
- <span ng-repeat="hour in post.result.opening_hours.weekday_text">
- Paragraph
- Content = _____
- Slider - Static Maps (4 at different zoom levels)
- Style Size = 100% x 300px
- Click Add New Slide (Slide 4)
- Click on and delete the Slide 4 Heading (on the slide)
- Switch to text input (not upload mode)
- Enter {{ data.map4 }}
- Edit Slide = Slide 1
- Title = Slide 1
- Switch to text input (not upload mode)
- Enter {{ data.map1 }}
- Edit Slide = Slide 2
- Title = Slide 2
- Switch to text input (not upload mode)
- Enter {{ data.map2 }}
- Edit Slide = Slide 3
- Title = Slide 3
- Switch to text input (not upload mode)
- Enter {{ data.map3 }}
- Paragraph
- Align = Center
- Content = Swipe to Zoom
- Paragraph
- Content = _____
Step 2. Export Your Mobile App from Ionic Creator to your Website
To export your app to your server,
|
Step 3. Add Angular Directives
Page 1 - Places
- Button - "Where Am I"
- ng-click="readGeolocation()"
- Input - "Enter Street Address"
- ng-model="data.address"
- ng-blur="getLocation()"
- Select - Place Type
- ng-model="data.type"
- ng-options="type.TypeID as type.TypeName for type in types"
- ng-change="getPlaces()"
- Avatar List - List of nearby places to geolocation
- ng-repeat="place in places"
- Avatar List Item
- (Pro) place_id="{{ place.place_id }}"
- (Pro) price_level="{{ place.price_level }}"
- (Free) ui-sref="details({"place_id":""+ place.place_id +"","price_level":""+ place.price_level +""})"
Page 2 - Place Details
- Paragraph - "Rating"
- ng-hide="hideRating"
- Container class="row" containing the rating number and icons
- ng-hide="hideRating"
- Container class="col-50" containing the Price Level paragraph, price level number and icons
- ng-hide="hidePriceLevel"
- Container class="col-66" containing phone Button
- style="padding-right:20px"
- Button - Phone
- ng-hide="hidePhone"
- Paragraph - "Hours"
- ng-hide="hideHours"
- Paragraph - "Open"
- ng-hide="hideOpen"
- Paragraph - "Closed"
- ng-hide="hideClosed"
- Container - HTML Hours
- style="padding-left:10px;"
- ng-hide="hideHours"
With the Creator.Ionic.io development platform, follow the instructions to create a mobile app that uses this API.
Step 4. Add Page Controller Code
.controller('placesCtrl'- Locate the line that begins with .controller('placesCtrl', ['$scope', '$stateParams', and
add'GetPlaces', 'GetAddress', 'GetLocation', 'GetTypes', 'Globals',
(including the comma) just after "'$stateParams',"
- Replace line: function ($scope, $stateParams) { with :
function ($scope, $stateParams, GetPlaces, GetAddress, GetLocation, GetTypes, GetPlace, Globals) { var key = Globals.key; $scope.data = { "type": "restaurant" }; GetTypes.getPost() .then(function(response) { $scope.post = response; $scope.types = $scope.post.PlaceTypes; }); var geo_options = { enableHighAccuracy: true, timeout: 10000, maximumAge: 0 }; function error_alert(err) { var msg='Geolocation ERROR(' + err.code + '): ' + err.message; alert(msg); } $scope.readGeolocation = function() { navigator.geolocation.getCurrentPosition(savePosition, error_alert, geo_options); setTimeout(readGeolocationAgain,1000); } function readGeolocationAgain() { navigator.geolocation.getCurrentPosition(savePosition, error_alert, geo_options); $scope.getAddress(); } function savePosition(location) { var latitude = location.coords.latitude.toFixed(6); var longitude = location.coords.longitude.toFixed(6); $scope.data.latlng = latitude+','+longitude; } $scope.readGeolocation(); $scope.getAddress = function() { GetAddress.getPost($scope.data.latlng, key) .then(function(response) { $scope.post = response; $scope.data.address = $scope.post.results[0].formatted_address; }); $scope.getPlaces(); } $scope.getLocation = function() { GetLocation.getPost($scope.data.address, key) .then(function(response) { $scope.post = response; $scope.data.address = $scope.post.results[0].formatted_address; var latitude = $scope.post.results[0].geometry.location.lat; var longitude = $scope.post.results[0].geometry.location.lng; $scope.data.latlng = latitude+','+longitude; $scope.getPlaces(); }) } $scope.getPlaces = function() { GetPlaces.getPost($scope.data.latlng, $scope.data.type, key) .then(function(response) { $scope.post = response; $scope.places = $scope.post.results; $scope.data.num_places = $scope.post.results.length + " places returned"; }) }
- Locate the line that begins with .controller('detailsCtrl', ['$scope', '$stateParams', and
add'GetPlace', 'Globals',
(including the comma) just after "'$stateParams',"
- Replace line: function ($scope, $stateParams) {
with :function ($scope, $stateParams, GetPlace, Globals) { var key = Globals.key; $scope.data = { "placeid" : $stateParams.place_id, "price_level" : $stateParams.price_level, }; GetPlace.getPost($scope.data.placeid, key) .then(function(response) { $scope.post = response; if(!$scope.post.result.formatted_phone_number) $scope.hidePhone = true; if($scope.post.result.opening_hours) { if($scope.post.result.opening_hours.open_now) { $scope.hideClosed = true; } else $scope.hideOpen = true; } else { $scope.hideClosed = true; $scope.hideOpen = true; } if($scope.post.result.rating) { $scope.data.star_rating_icon = "https://secrdir.com/api/rating_icons?stars=" + $scope.post.result.rating; } else $scope.hideRating = true; if($scope.post.result.price_level) { $scope.data.price_level_icon = "https://secrdir.com/api/rating_icons?dollars=" + $scope.post.result.price_level; } else $scope.hidePriceLevel = true; var latlng = $scope.post.result.geometry.location.lat; latlng += ',' + $scope.post.result.geometry.location.lng; var map_url = "https://secrdir.com/api/staticmap?key=" + key; map_url += "¢er="+latlng; $scope.data.map1 = map_url + "&zoom=12"; $scope.data.map2 = map_url + "&zoom=15"; $scope.data.map3 = map_url + "&zoom=18"; });
Step 5. Add Parameter Passing to Page Routing
Locate and open the routes.js file in the js folder. Just after "url: '/page2',", add a new line: params: { place_id: "", price_level: "" }, |
Step 6. Add Services
Copy the following two services just above the with : .service('Globals',[function(){ var ret = { "key": "MY_GOOGLE_API_KEY" } return ret; }]) .service('GetPlaces', function($http){ return { getPost: function(location, type, key) { var query = "?location="+location; query += "&key=" + key; query += "&type=" + type; query += "&rankby=distance"; return $http.get("https://secrdir.com/api/place/nearbysearch/"+query) .then(function (response) { return response.data; }) } } }) .service('GetPlace', function($http){ return { getPost: function(placeid, key) { var query = "?placeid="+placeid; query += "&key=" + key; return $http.get("https://secrdir.com/api/place/details/"+query) .then(function (response) { return response.data; }) } } }) .service('GetAddress', function($http){ return { getPost: function(latlng, key) { var query = "?latlng="+latlng; query += "&key=" + key; return $http.get("https://secrdir.com/api/geocode/"+query) .then(function (response) { return response.data; }) } } }) .service('GetLocation', function($http){ return { getPost: function(address, key) { var query = "?address="+address; query += "&key=" + key; return $http.get("https://secrdir.com/api/geocode/"+query) .then(function (response) { return response.data; }) } } }) .service('GetTypes', function($http){ return { getPost: function() { return $http.get("https://secrdir.com/api/place/types/") .then(function (response) { return response.data; }) } } }) |
Richard Halverson, Jr., Ph.D. • University of Hawaii
SECRDIR.COM HOME
| IONIC PRO
| IONIC FREE