<!DOCTYPE html>
<html ng-app="geocode">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
<title>Address Lookup/Geocode Service</title>
<link href="//code.ionicframework.com/1.1.0/css/ionic.css" rel="stylesheet" />
<link rel="stylesheet" href="style.css" />
<script src="//code.ionicframework.com/1.1.0/js/ionic.bundle.js"></script>
<script src="//maps.googleapis.com/maps/api/js?sensor=false"></script>
<script data-require="lodash.js@3.10.0" data-semver="3.10.0" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.10.0/lodash.js"></script>
<script data-require="angular-google-maps@2.0.11" data-semver="2.0.11" src="//rawgit.com/angular-ui/angular-google-maps/master/dist/angular-google-maps.min.js"></script>
<script data-require="angular-simple-logger@*" data-semver="0.0.2" src="https://rawgit.com/nmccready/angular-simple-logger/master/dist/angular-simple-logger.js"></script>
<script src="script.js"></script>
<script src="modals.service.js"></script>
<script src="geocode.service.js"></script>
</head>
<body ng-controller="GeocodeCtrl as vm">
<ion-header-bar class="bar-positive">
<h1 class="title">Geocode Service</h1>
</ion-header-bar>
<ion-content class="has-header">
<div class="row responsive-sm">
<div class="col col-offset-25 col-50">
<div class="list card">
<div class="item item-divider item-positive">Address Lookup</div>
<div class="item">
<div class="item-input-inset no-padding">
<div class="item-input-wrapper no-padding">
<input type="text" ng-model="vm.address" placeholder="Enter Address" ng-attr-clear-field="vm.isBrowser" />
</div>
<button id="address-lookup-search" ng-click="vm.on.testGeocode( vm.address )" class="button button-balanced button-small no-padding">
<i ng-hide="vm.loading" class="icon ion-map"></i>
<ion-spinner ng-show="vm.loading" class="spinner-light"></ion-spinner>
</button>
</div>
</div>
<div class="item">
<label class="item item-input">
<span class="input-label">Location</span>
<input type="text" ng-model="vm.latlon" placeholder="[lat,lng]" readonly="readonly" />
</label>
<label class="item item-input">
<span class="input-label">Formatted Address</span>
<input type="text" ng-model="vm.addressFormatted" readonly="readonly" />
</label>
</div>
</div>
<div class="card">
<div class="item item-divider item-assertive">console</div>
<div class="item">
<p ng-repeat="msg in vm.console">{{msg}}</p>
</div>
</div>
</div></div>
</ion-content>
</body>
</html>
// condensed John Papa style
(function() {
'use strict';
angular.module('geocode', ['ionic', 'geocode.core']);
angular.module('geocode.core', ['geocode.components']);
angular.module('geocode.components', ['uiGmapgoogle-maps']);
var appRun, ionicConfig, toastrConfig;
appRun = function($rootScope, $ionicPlatform, $ionicHistory, $location, $state) {
$ionicPlatform.ready(function() {
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
}
if (window.StatusBar) {
return StatusBar.styleLightContent();
}
});
};
ionicConfig = function($ionicConfigProvider) {
$ionicConfigProvider.backButton.text('').icon('ion-ios-arrow-back').previousTitleText(false);
};
appRun.$inject = ['$rootScope', '$ionicPlatform', '$ionicHistory', '$location', '$state'];
ionicConfig.$inject = ['$ionicConfigProvider'];
angular.module('geocode.core')
.config(ionicConfig)
.run(appRun);
var GeocodeCtrl;
GeocodeCtrl = function($log, $scope, geocodeSvc) {
var vm;
vm = this;
vm.address = "New York City";
vm.latlon = "";
vm.addressFormatted = "";
vm.isBrowser = ionic.Platform.isWebView() === false
vm.on = {
testGeocode: function(address) {
return geocodeSvc.getLatLon(address)
.then(function(result) {
vm.addressFormatted = result.address;
vm.latlon = result.location
console.log(["testGeocode", result]);
});
},
}
window.console.log = function(msg){
vm.console.unshift(vm.console.length + ":" + JSON.stringify(msg, null, 2));
}
vm.loading = false;
vm.console = [];
console.log("ready")
return vm
};
GeocodeCtrl.$inject = ['$log', '$scope', 'geocodeSvc'];
angular.module('geocode')
.controller('GeocodeCtrl', GeocodeCtrl);
}).call(this);
/* Styles go here */
#address-lookup-modal .item-floating-label input, #address-lookup-modal .item-input-inset input {
width: 100%; }
#map .wrap {
height: 360px; }
@media (max-width: 567px) {
#map .wrap {
height: 200px; } }
#map .wrap .angular-google-map-container {
/*width: 360px;*/
height: 360px; }
@media (max-width: 567px) {
#map .wrap .angular-google-map-container {
height: 200px; } }
input {
width: 100%;
}
# Address Lookup/Geocode Service for ionic Framework
This service uses `angular-google-maps` and the `Google Maps Geocoding API` to geocode an address string, and returns a formatted address and `[lat, lon]` value.
The service presents the geocode result in an $ionicModal and allows the user to
1. customize the formatted address and
1. drag the marker to correct the actual location.
If the geocode results include multiple locations, the top 5 results are displayed and the user can `click` to select the most accurate.
`bower.json` dependencies
```json
{
"devDependencies": {
"angular-google-maps": "~2.1.6",
"ionic": "driftyco/ionic-bower#1.1.0",
"lodash": "~3.10.1",
}
}
```
(function() {
'use strict';
/*
* @description: reusable $ionicModal service
* see: http://forum.ionicframework.com/t/ionic-modal-service-with-extras/15357
* also: http://codepen.io/anon/pen/KdzawK?editors=101
* usage: appModalSvc.show( <templateUrl>, "controller as vm", params )
*/
var ReusableModal;
ReusableModal = function($ionicModal, $rootScope, $q, $injector, $controller) {
var _cleanup, _evalController, show;
show = function(templateUrl, controller, parameters, options) {
var ctrlInstance, defaultOptions, dfd, modalScope, thisScopeId;
dfd = $q.defer();
modalScope = $rootScope.$new();
thisScopeId = modalScope.$id;
ctrlInstance = null;
defaultOptions = {
animation: 'slide-in-up',
focusFirstInput: false,
backdropClickToClose: true,
hardwareBackButtonClose: true,
modalCallback: null
};
options = angular.extend(defaultOptions, options, {
scope: modalScope
});
$ionicModal.fromTemplateUrl(templateUrl, options).then(function(modal) {
var ctrlEval, locals, same;
modalScope.modal = modal;
modalScope.openModal = function() {
return modalScope.modal.show();
};
modalScope.closeModal = function(result) {
dfd.resolve(result);
return modalScope.modal.hide();
};
modalScope.$on('modal.hidden', function(thisModal) {
var modalScopeId;
if (thisModal.currentScope) {
modalScopeId = thisModal.currentScope.$id;
if (thisScopeId === thisModal.currentScope.$id) {
dfd.resolve(null);
return _cleanup(thisModal.currentScope);
}
}
});
if (angular.isObject(controller)) {
modalScope.vm = ctrlInstance = controller;
same = modalScope.vm === modalScope.modal.scope.vm;
ctrlInstance['openModal'] = modalScope.openModal;
ctrlInstance['closeModal'] = modalScope.closeModal;
} else {
locals = {
'$scope': modalScope,
'parameters': parameters
};
ctrlEval = _evalController(controller);
if (ctrlEval.controllerName) {
ctrlInstance = $controller(controller, locals);
}
if (ctrlEval.isControllerAs && ctrlInstance) {
ctrlInstance['openModal'] = modalScope.openModal;
ctrlInstance['closeModal'] = modalScope.closeModal;
}
}
if (parameters != null) {
angular.extend(modalScope, parameters);
}
return modalScope.modal.show().then(function() {
modalScope.$broadcast('modal.afterShow', modalScope.modal);
return typeof options.modalCallback === "function" ? options.modalCallback(modal) : void 0;
});
}, function(err) {
return dfd.reject(err);
});
return dfd.promise;
};
_cleanup = function(scope) {
scope.$destroy();
if (scope.modal) {
scope.modal.remove();
}
};
_evalController = function(ctrlName) {
var fragments, result;
if (ctrlName == null) {
ctrlName = '';
}
result = {
isControllerAs: false,
controllerName: '',
propName: ''
};
fragments = ctrlName.trim().split(/\s+/);
result.isControllerAs = fragments.length === 3 && (fragments[1] || '').toLowerCase() === 'as';
if (result.isControllerAs) {
result.controllerName = fragments[0];
result.propName = ctrlName;
} else {
result.controllerName = ctrlName;
}
return result;
};
return {
show: show
};
};
ReusableModal.$inject = ['$ionicModal', '$rootScope', '$q', '$injector', '$controller'];
angular.module('geocode.components')
.factory('appModalSvc', ReusableModal);
}).call(this);
(function() {
'use strict';
var ClearFieldDirective, GEOCODER, Geocoder, MODAL_VIEW, VerifyLookupCtrl, geocodeSvcConfig;
GEOCODER = {
STATUS: {},
instance: null,
ZERO_RESULT_LOC: [37.77493, -122.419416]
};
MODAL_VIEW = {
DISPLAY_LIMIT: 5,
OFFSET_HEIGHT: 420 + 106, // 420 px for normal usage, +106px for plnkr toolbar
GRID_RESPONSIVE_SM_BREAK: 680,
MARGIN_TOP_BOTTOM: 0.1 + 0.1,
MAP_MIN_HEIGHT: 200,
MESSAGE: {
ZERO_RESULTS_ERROR: "No results found, please try again.",
VERIFY_LABEL: "This is how the location will be displayed",
MULTIPLE_RESULTS: "[multiple results]"
}
};
geocodeSvcConfig = function(uiGmapGoogleMapApiProvider, API_KEY) {
var cfg;
cfg = {};
if (API_KEY) {
cfg.key = API_KEY;
}
uiGmapGoogleMapApiProvider.configure(cfg);
};
geocodeSvcConfig.$inject = ['uiGmapGoogleMapApiProvider', 'API_KEY'];
/*
* @description Google Maps Geocode Service v3
* see https://developers.google.com/maps/documentation/geocoding/intro
*/
Geocoder = function($q, $ionicPlatform, appModalSvc, uiGmapGoogleMapApi) {
var init, mathRound6, self;
init = function(maps) {
GEOCODER.STATUS = maps.GeocoderStatus;
GEOCODER.instance = new maps.Geocoder();
};
mathRound6 = function(v) {
if (_.isNumber(v)) {
return Math.round(v * 1000000) / 1000000;
}
return v;
};
uiGmapGoogleMapApi.then(function(maps) {
console.log("uiGmapGoogleMapApi promise resolved");
init(maps);
});
self = {
/*
@description an Entry Point for this service, returns an object with a geocode location
@return object { address: location: place_id:(options) }
'NOT FOUND', 'CANCELED', 'ERROR'
*/
getLatLon: function(address) {
return self.displayGeocode(address).then(function(result) {
var location, ref, ref1, ref2, resp;
if (!result || result === 'CANCELED') {
return null;
}
if (_.isString(result)) {
return result;
}
if ((ref = result.override) != null ? ref.location : void 0) {
location = (ref1 = result.override) != null ? ref1.location : void 0;
} else {
location = result['geometry']['location'];
location = [location.lat(), location.lng()];
}
location = _.map(location, function(v) {
return mathRound6(v);
});
resp = {
address: ((ref2 = result.override) != null ? ref2.address : void 0) || result['formatted_address'],
location: location
};
if (!result.override) {
resp['place_id'] = result['place_id'];
}
return resp;
})["catch"](function(err) {
return 'ERROR';
});
},
/*
@description launches addressMap modal to allow user to verifiy location of address
@param address String
@return object, one geocode result or CANCELED, ZERO_RESULTS
*/
displayGeocode: function(address) {
return self.geocode(address).then(function(results) {
if (results === GEOCODER.STATUS.ZERO_RESULTS) {
console.log("ZERO_RESULTS FOUND");
results = [self.getPlaceholderDefault()];
}
return self.showResultsAsMap(address, results).then(function(result) {
return result;
});
})["catch"](function(err) {
console.warn(err);
});
},
geocode: function(address) {
var dfd;
if (GEOCODER.instance == null) {
return $q.reject("Geocoder JS lib not ready");
}
dfd = $q.defer();
GEOCODER.instance.geocode({
"address": address
}, function(result, status) {
switch (status) {
case 'OK':
return dfd.resolve(result);
case GEOCODER.STATUS.ZERO_RESULTS:
return dfd.resolve(GEOCODER.STATUS.ZERO_RESULTS);
default:
console.err(['geocodeSvc.geocode()', status]);
return dfd.reject({
status: status,
result: result
});
}
});
return dfd.promise;
},
getPlaceholderDefault: function() {
var geoCodeResult;
return geoCodeResult = {
geometry: {
location: {
lat: function() {
return GEOCODER.ZERO_RESULT_LOC[0];
},
lng: function() {
return GEOCODER.ZERO_RESULT_LOC[1];
}
}
},
status: GEOCODER.STATUS.ZERO_RESULTS,
formatted_address: '[location not found]'
};
},
showResultsAsMap: function(address, geoCodeResults) {
return appModalSvc.show('address-lookup.template.html', 'VerifyLookupCtrl as vm', {
address: address,
geoCodeResults: geoCodeResults
}).then(function(modalResult) {
var geoCodeResult, mm;
if (_.isString(modalResult || !modalResult)) {
return modalResult;
}
mm = modalResult;
geoCodeResult = mm['geoCodeResults'][0];
geoCodeResult.override = {};
if (mm['marker-moved']) {
geoCodeResult.override['location'] = mm.location;
}
if (mm['address-changed']) {
geoCodeResult.override['address'] = mm.addressDisplay;
}
return geoCodeResult;
})["catch"](function(err) {
return $q.reject(err);
});
},
/*
* Utility Methods
*/
/*
* @description get a location array from an object
* @param point object of type
* GEOCODER.instance.geocode() result
* marker from ui-gmap-marker dragend event on map
* model from ui-gmap-markers click event
* {id: latitude: longititde: formatted_address:}
* @return [lat,lon] round to 6 decimals for google Maps API
*/
getLocationFromObj: function(point) {
var ref;
if (point == null) {
point = {};
}
if (((ref = point['geometry']) != null ? ref.location : void 0) != null) {
return [mathRound6(point['geometry']['location'].lat()), mathRound6(point['geometry']['location'].lng())];
}
if (point.getPosition != null) {
return [mathRound6(point.getPosition().lat()), mathRound6(point.getPosition().lng())];
}
if (point.longitude != null) {
return [mathRound6(point.latitude), mathRound6(point.longitude)];
}
return null;
},
/*
* @description add a 'random' offset to latlon to mask exact location
* @param latlon Array, [lat,lon] expressed as decimal
*/
maskLatLon: function(latlon, key) {
var offset;
key = key.slice(0, 11);
offset = {
lat: (self.a2nHash(latlon[0] + key) % 25) / 10000,
lon: (self.a2nHash(latlon[1] + key) % 25) / 10000
};
return [mathRound6(latlon[0] + offset.lat), mathRound6(latlon[1] + offset.lon)];
},
/*
@description: get google Map object for angular-google-maps,
configured map places marker or circle at location
@params: options
id: string optional
location: [lat,lon] or [ [lat,lon], [lat,lon] ], render circle or marker at location
type: [circle, marker]
circleRadius: 500, in meters
draggableMap: true
draggableMarker: true
*/
getMapConfig: function(options) {
var gMapPoint, mapConfig, mapConfigOptions, markers;
_.defaults(options, {
location: [],
markers: [],
type: 'oneMarker',
circleRadius: 500,
draggableMap: true,
draggableMarker: true
});
mapConfigOptions = {
'map': {
options: {
draggable: options.draggableMap
}
}
};
switch (options.type) {
case 'circle':
gMapPoint = {
latitude: mathRound6(options.location[0]),
longitude: mathRound6(options.location[1])
};
mapConfigOptions['circle'] = {
center: gMapPoint,
stroke: {
color: '#FF0000',
weight: 1
},
radius: options.circleRadius,
fill: {
color: '#FF0000',
opacity: '0.2'
}
};
break;
case 'oneMarker':
gMapPoint = {
latitude: mathRound6(options.location[0]),
longitude: mathRound6(options.location[1])
};
mapConfigOptions['oneMarker'] = {
idKey: '1',
coords: gMapPoint,
options: {
draggable: options.draggableMarker
}
};
if (options.draggableMarker) {
mapConfigOptions['oneMarker']['events'] = {
'dragend': options.dragendMarker
};
}
break;
case 'manyMarkers':
markers = _.map(options.markers, function(result, i, l) {
var point;
point = result['geometry']['location'];
return {
'id': i,
'latitude': mathRound6(point.lat()),
'longitude': mathRound6(point.lng()),
'formatted_address': result.formatted_address
};
});
mapConfigOptions['manyMarkers'] = {
models: markers,
options: {
draggable: options.draggableMarker
},
events: {
'click': options.clickMarker
}
};
if (options.draggableMarker) {
mapConfigOptions['manyMarkers']['events']['dragend'] = options.dragendMarker;
}
gMapPoint = markers[0];
}
return mapConfig = {
type: options.type,
center: angular.copy(gMapPoint),
zoom: 14,
scrollwheel: false,
options: mapConfigOptions
};
}
};
return self;
};
Geocoder.$inject = ['$q', '$ionicPlatform', 'appModalSvc', 'uiGmapGoogleMapApi'];
/*
@description Controller for geocodeSvc.showResultsAsMap() Modal
@param parameters.geoCodeResult Array of geocode results
parameters.address String, the original search string
*/
VerifyLookupCtrl = function($scope, parameters, $q, $timeout, $window, geocodeSvc) {
var init, parseLocation, setMapHeight, setupMap, vm;
vm = this;
vm.isBrowser = !ionic.Platform.isWebView();
vm.MESSAGE = MODAL_VIEW.MESSAGE;
vm.isValidMarker = function() {
if (vm['error-address0']) {
return false;
}
if (vm.map.type === 'oneMarker') {
return true;
}
return false;
};
init = function(parameters) {
var stop;
vm['geoCodeResults'] = parameters.geoCodeResults.slice(0, MODAL_VIEW.DISPLAY_LIMIT);
vm['map'] = setupMap(parameters.address, vm['geoCodeResults']);
stop = $scope.$on('modal.afterShow', function(ev) {
var h;
h = setMapHeight();
if (typeof stop === "function") {
stop();
}
});
};
setMapHeight = function() {
var contentH, mapH, styleH;
contentH = $window.innerWidth <= MODAL_VIEW.GRID_RESPONSIVE_SM_BREAK ? $window.innerHeight : $window.innerHeight * (1 - MODAL_VIEW.MARGIN_TOP_BOTTOM);
mapH = contentH - MODAL_VIEW.OFFSET_HEIGHT;
mapH = Math.max(MODAL_VIEW.MAP_MIN_HEIGHT, mapH);
// console.log(["height=", $window.innerHeight, contentH, mapH]);
styleH = "#address-lookup-map .wrap {height: %height%px;}\n#address-lookup-map .angular-google-map-container {height: %height%px;}";
styleH = styleH.replace(/%height%/g, mapH);
angular.element(document.getElementById('address-lookup-style')).append(styleH);
return mapH;
};
parseLocation = function(geoCodeResultOrModel, target) {
var location0, resp;
if (_.isEmpty(geoCodeResultOrModel)) {
return {};
}
location0 = geocodeSvc.getLocationFromObj(geoCodeResultOrModel);
resp = {
'location': location0,
'latlon': location0.join(', '),
'addressFormatted': geoCodeResultOrModel.formatted_address,
'addressDisplay': angular.copy(geoCodeResultOrModel.formatted_address),
'error-address0': null
};
switch (geoCodeResultOrModel.status) {
case GEOCODER.STATUS.ZERO_RESULTS:
resp['error-address0'] = vm.MESSAGE.ZERO_RESULTS_ERROR;
resp['latlon'] = null;
resp['addressDisplay'] = null;
}
_.extend(target, resp);
return resp;
};
setupMap = function(address, geoCodeResults, model) {
var isZeroResult, mapConfig, mapOptions, markerCount, selectedLocation;
if (isZeroResult = geoCodeResults === GEOCODER.STATUS.ZERO_RESULTS) {
geoCodeResults = [geocodeSvc.getPlaceholderDefault()];
}
vm['address0'] = address;
markerCount = model != null ? 1 : geoCodeResults.length;
if (markerCount === 0) {
return;
}
if (markerCount === 1) {
selectedLocation = model || vm['geoCodeResults'][0];
parseLocation(selectedLocation, vm);
vm['marker-moved'] = false;
mapOptions = {
type: isZeroResult ? 'none' : 'oneMarker',
location: vm['location'],
draggableMarker: true,
dragendMarker: function(marker, eventName, args) {
vm['location'] = geocodeSvc.getLocationFromObj(marker);
vm['latlon'] = vm['location'].join(', ');
vm['marker-moved'] = true;
}
};
mapConfig = geocodeSvc.getMapConfig(mapOptions);
return mapConfig;
}
vm['latlon'] = null;
vm['addressFormatted'] = vm.MESSAGE.MULTIPLE_RESULTS;
vm['addressDisplay'] = '';
vm['error-address0'] = null;
mapOptions = {
type: 'manyMarkers',
draggableMarker: true,
markers: geoCodeResults,
clickMarker: function(marker, eventName, model) {
var index, newMapConfig;
index = model.id;
vm['geoCodeResults'] = [vm['geoCodeResults'][index]];
newMapConfig = setupMap(model.formatted_address, null, model);
vm['address-changed'] = true;
vm['marker-moved'] = true;
vm['map'] = newMapConfig;
},
dragendMarker: function(marker, eventName, model) {
mapOptions.clickMarker(marker, eventName, model);
}
};
return geocodeSvc.getMapConfig(mapOptions);
};
vm.updateGeocode = function(address) {
vm.loading = true;
return geocodeSvc.geocode(address).then(function(results) {
if (results === GEOCODER.STATUS.ZERO_RESULTS) {
console.log("ZERO_RESULTS FOUND");
results = [geocodeSvc.getPlaceholderDefault()];
}
return results;
}).then(function(results) {
var newMapConfig;
vm['geoCodeResults'] = results;
newMapConfig = setupMap(address, vm['geoCodeResults']);
vm['address-changed'] = false;
vm['map'] = newMapConfig;
}, function(err) {
return $q.reject(err);
})["finally"](function() {
return $timeout(function() {
return vm.loading = false;
}, 250);
});
};
$scope.$watch('vm.addressDisplay', function(newV) {
vm['address-changed'] = true;
});
init(parameters);
return vm;
};
VerifyLookupCtrl.$inject = ['$scope', 'parameters', '$q', '$timeout', '$window', 'geocodeSvc'];
ClearFieldDirective = function($compile, $timeout) {
var directive;
directive = {
restrict: 'A',
require: 'ngModel',
scope: {},
link: function(scope, element, attrs, ngModel) {
var btnTemplate, inputTypes, template;
inputTypes = /text|search|tel|url|email|password/i;
if (element[0].nodeName !== 'INPUT') {
throw new Error("clearField is limited to input elements");
}
if (!inputTypes.test(attrs.type)) {
throw new Error("Invalid input type for clearField" + attrs.type);
}
btnTemplate = "<i ng-show=\"enabled\" ng-click=\"clear()\" class=\"icon ion-close pull-right\"> </i>";
template = $compile(btnTemplate)(scope);
element.after(template);
scope.clear = function() {
ngModel.$setViewValue(null);
ngModel.$render();
scope.enabled = false;
return $timeout(function() {
return element[0].focus();
}, 150);
};
element.bind('focus', function(e) {
scope.enabled = !ngModel.$isEmpty(element.val());
scope.$apply();
});
}
};
return directive;
};
ClearFieldDirective.$inject = ['$compile', '$timeout'];
angular.module('geocode.components')
.constant('API_KEY', GEOCODER.API_KEY)
.config(geocodeSvcConfig)
.factory('geocodeSvc', Geocoder)
.directive('clearField', ClearFieldDirective)
.controller('VerifyLookupCtrl', VerifyLookupCtrl);
}).call(this);
<style id="address-lookup-style">
@media (min-width: 680px) {
#address-lookup-modal-view.modal { top: 10%; bottom: 10%;}
}
#address-lookup-search { min-width:32.5px; }
#address-lookup-search svg {width: 26px; height: 26px; margin: 5px 0;}
#address-lookup-map .wrap {min-height: 200px;}
#address-lookup-map .angular-google-map-container {min-height: 200px;}
</style>
<ion-modal-view id="address-lookup-modal-view">
<ion-header-bar class="bar-balanced">
<h1 class="title">Address Lookup</h1>
</ion-header-bar>
<ion-content>
<div id="address-lookup-modal" class="list condensed">
<div class="item">
<div class="item-input-inset no-padding">
<div class="item-input-wrapper no-padding">
<input type="text" ng-model="vm.address0" placeholder="Enter Address" ng-attr-clear-field="vm.isBrowser"/>
</div>
<button id="address-lookup-search" ng-click="vm.updateGeocode(vm.address0)" class="button button-balanced button-small no-padding"><i ng-hide="vm.loading" class="icon ion-search"></i>
<ion-spinner ng-show="vm.loading" class="spinner-light"></ion-spinner>
</button>
</div>
<div ng-show="vm['error-address0']" class="error"><span class="assertive">{{vm['error-address0']}}</span></div>
</div>
<label class="item item-floating-label"><span class="label">Location</span>
<input type="text" ng-model="vm.latlon" readonly="readonly"/>
</label>
<label class="item item-floating-label"><span class="label">Formatted Address</span>
<input type="text" ng-model="vm.addressFormatted" readonly="readonly"/>
</label>
<div class="item item-floating-label"><span class="label">Display Address</span>
<div class="item-input-inset no-padding">
<div class="item-input-wrapper no-padding">
<input type="text" ng-model="vm.addressDisplay" ng-attr-clear-field="vm.isBrowser"/>
</div>
</div>
<p class="help"><span ng-show="vm.isValidMarker()" class="positive">{{vm.MESSAGE.VERIFY_LABEL}}</span></p>
</div>
<div id="address-lookup-map" class="item item-complex">
<div ng-if="vm.map" class="wrap">{{opt = vm.map.options;''}}
<ui-gmap-google-map center="vm.map.center" zoom="vm.map.zoom" options="opt.map.options">
<ui-gmap-circle ng-if="vm.map.type=='circle'" center="opt.circle.center" radius="opt.circle.radius" stroke="opt.circle.stroke"></ui-gmap-circle>
<ui-gmap-marker ng-if="vm.isValidMarker()" idKey="opt.oneMarker.idKey" coords="opt.oneMarker.coords" options="opt.oneMarker.options" events="opt.oneMarker.events"></ui-gmap-marker>
<ui-gmap-markers ng-if="vm.map.type=='manyMarkers'" fit="true" idkey="id" coords="'self'" models="opt.manyMarkers.models" options="opt.manyMarkers.options" modelbyref="true" xxclick="'opt.manyMarkers.events.click'" events="opt.manyMarkers.events"></ui-gmap-markers>
</ui-gmap-google-map>
</div>
<p class="padding-horizontal"> <span ng-show="vm.isValidMarker()" class="padding-horizontal positive">Drag the marker to change location</span><span ng-show="vm.map.type=='manyMarkers'" class="padding-horizontal positive">Multiple locations found, click Marker to select</span></p>
</div>
<div class="item">
<div class="button-bar">
<button ng-click="closeModal('CANCELED')" class="button button-balanced button-outline">Cancel</button>
<button ng-click="closeModal(vm)" ng-disabled="vm.isValidMarker()==false" class="button button-balanced">OK</button>
</div>
</div>
</div>
</ion-content>
</ion-modal-view>