1596 lines
52 KiB
JavaScript
1596 lines
52 KiB
JavaScript
'use strict';
|
|
|
|
/*@
|
|
Ox.Map <function> Basic map object
|
|
# DESCRIPTION --------------------------------------------------------------
|
|
<code>Ox.Map</code> is a wrapper around the
|
|
<a href="http://code.google.com/apis/maps/documentation/javascript/">Google
|
|
Maps API</a>.
|
|
# USAGE --------------------------------------------------------------------
|
|
() -> <f> Map object
|
|
(options) -> <f> Map object
|
|
(options, self) -> <f> Map object
|
|
# ARGUMENTS ----------------------------------------------------------------
|
|
options <o|{}> options
|
|
clickable <b|false> If true, clicking on the map finds a place
|
|
editable <b|false> If true, places are editable
|
|
find <s|""> Initial query
|
|
findPlaceholder <s|"Find"> Placeholder text for the find input element
|
|
keys <a|[]> Additional place properties to be requested
|
|
markerColor <[n]|f|s|'auto'> Color of place markers ([r, g, b])
|
|
markerSize <n|f||s|'auto'> Size of place markers in px
|
|
maxMarkers <n|100> Maximum number of markers to be displayed
|
|
places <[o]|f|null> Array of, or function that returns, place objects
|
|
countryCode <s> ISO 3166 country code
|
|
east <n> Longitude of the eastern boundary in degrees
|
|
editable <b|false> If true, the place is editable
|
|
geoname <s> Geoname (like "Paris, Île-de-France, France")
|
|
lat <n> Latitude in degrees
|
|
lng <n> Longitude in degrees
|
|
name <s> Name (like "Paris")
|
|
north <n> Latitude of the northern boundary in degrees
|
|
south <n> Latitude of the southern boundary in degrees
|
|
type <s> Type (like "city" or "country")
|
|
west <n> Longitude of the western boundary in degrees
|
|
selected <s|""> Id of the selected place
|
|
showControls <b|false> If true, show controls
|
|
showLabels <b|false> If true, show labels on the map
|
|
showStatusbar <b|false> If true, the map has a statusbar
|
|
showToolbar <b|false> If true, the map has a toolbar
|
|
showZoombar <b|false> If true, the map has a zoombar
|
|
zoomOnlyWhenFocused <b|false> If true, scroll-zoom only when focused
|
|
self <o|{}> Shared private variable
|
|
# EVENTS -------------------------------------------------------------------
|
|
addplace <!> Fires when a place has been added
|
|
place <o> Place object
|
|
editplace <!> Fires when a place has been edited
|
|
place <o> Place object
|
|
geocode <!> Fires when a google geocode request returns
|
|
latLng <o|u> Query coordinates, or undefined
|
|
lat <n> latitude
|
|
lng <n> longitude
|
|
address <s|u> Query string, or undefined
|
|
results <[o]> Google Maps geocode results
|
|
address_components <[o]> Address components
|
|
long_name <s> Long name
|
|
short_name <s> Short name
|
|
types <[s]> Types (like "country" or "political")
|
|
formatted_address <s> Formatted address
|
|
geometry <o> Geometry
|
|
bounds <o> Bounds
|
|
northEast <o> North-east corner
|
|
lat <n> Latitude
|
|
lng <n> Longitude
|
|
southWest <o> South-west corner
|
|
lat <n> Latitude
|
|
lng <n> Longitude
|
|
location <o> Location
|
|
lat <n> Latitude
|
|
lng <n> Longitude
|
|
location_type <s> Location type (like "APPROXIMATE")
|
|
viewport <o> Viewport
|
|
northEast <o> North-east corner
|
|
lat <n> Latitude
|
|
lng <n> Longitude
|
|
southWest <o> South-west corner
|
|
lat <n> Latitude
|
|
lng <n> Longitude
|
|
types <[s]> Types (like "country" or "political")
|
|
selectplace <!> Fires when a place has been selected or deselected
|
|
place <o> Place object
|
|
# EXAMPLES -----------------------------------------------------------------
|
|
<script>
|
|
// simple
|
|
Ox.load('UI', function() {
|
|
Ox.Map().appendTo(Ox.UI.$body);
|
|
});
|
|
</script>
|
|
> Ox.Map() === true
|
|
false
|
|
> Ox.Map() === false
|
|
false
|
|
@*/
|
|
|
|
Ox.Map = function(options, self) {
|
|
|
|
self = self || {};
|
|
var that = Ox.Element({}, self)
|
|
.defaults({
|
|
// fixme: isClickable?
|
|
clickable: false,
|
|
editable: false,
|
|
find: '',
|
|
findPlaceholder: 'Find',
|
|
keys: [],
|
|
markerColor: 'auto',
|
|
markerSize: 'auto',
|
|
maxMarkers: 100,
|
|
places: null,
|
|
selected: '',
|
|
showControls: false,
|
|
showLabels: false,
|
|
showStatusbar: false,
|
|
showToolbar: false,
|
|
showZoombar: false,
|
|
zoomOnlyWhenFocused: false
|
|
// fixme: width, height
|
|
})
|
|
.options(options || {})
|
|
.addClass('OxMap')
|
|
.bindEvent({
|
|
gainfocus: function() {
|
|
self.options.zoomOnlyWhenFocused && self.map.setOptions({scrollwheel: true});
|
|
},
|
|
losefocus: function() {
|
|
self.options.zoomOnlyWhenFocused && self.map.setOptions({scrollwheel: false});
|
|
},
|
|
key_0: function() {
|
|
panToPlace()
|
|
},
|
|
key_c: toggleControls,
|
|
key_down: function() {
|
|
pan(0, 1);
|
|
},
|
|
key_enter: pressEnter,
|
|
key_escape: pressEscape,
|
|
key_equal: function() {
|
|
zoom(1);
|
|
},
|
|
key_l: toggleLabels,
|
|
key_left: function() {
|
|
pan(-1, 0);
|
|
},
|
|
// FIXME:
|
|
'key_meta.left': function() {
|
|
self.metaKey = true;
|
|
$(document).one({
|
|
keyup: function() {
|
|
self.metaKey = false;
|
|
}
|
|
});
|
|
},
|
|
'key_meta.right': function() {
|
|
self.metaKey = true;
|
|
$(document).one({
|
|
keyup: function() {
|
|
self.metaKey = false;
|
|
}
|
|
});
|
|
},
|
|
key_minus: function() {
|
|
zoom(-1);
|
|
},
|
|
key_right: function() {
|
|
pan(1, 0);
|
|
},
|
|
key_shift: function() {
|
|
self.shiftKey = true;
|
|
$(document).one({
|
|
keyup: function() {
|
|
self.shiftKey = false;
|
|
}
|
|
});
|
|
},
|
|
key_shift_down: function() {
|
|
pan(0, 2);
|
|
},
|
|
key_shift_0: function() {
|
|
zoomToPlace();
|
|
},
|
|
key_shift_equal: function() {
|
|
zoom(2);
|
|
},
|
|
key_shift_left: function() {
|
|
pan(-2, 0);
|
|
},
|
|
key_shift_minus: function() {
|
|
zoom(-2);
|
|
},
|
|
key_shift_right: function() {
|
|
pan(2, 0);
|
|
},
|
|
key_shift_up: function() {
|
|
pan(0, -2);
|
|
},
|
|
key_up: function() {
|
|
pan(0, -1);
|
|
},
|
|
key_z: undo,
|
|
mousedown: function(e) {
|
|
!$(e.target).is('input') && that.gainFocus();
|
|
}
|
|
});
|
|
|
|
// HANDLE DEPRECATED OPTIONS
|
|
['statusbar', 'toolbar', 'zoombar'].forEach(function(key) {
|
|
if (options[key]) {
|
|
self.options['show' + Ox.toTitleCase(key)] = options[key];
|
|
}
|
|
});
|
|
|
|
if (Ox.isArray(self.options.places)) {
|
|
self.options.places.forEach(function(place) {
|
|
if (Ox.isUndefined(place.id)) {
|
|
place.id = Ox.encodeBase32(Ox.uid());
|
|
}
|
|
});
|
|
self.options.places = Ox.api(self.options.places, {
|
|
geo: true,
|
|
sort: '-area'
|
|
});
|
|
}
|
|
|
|
self.mapHeight = getMapHeight();
|
|
self.metaKey = false;
|
|
self.minZoom = getMinZoom();
|
|
self.placeKeys = Ox.merge([
|
|
'id', 'name', 'alternativeNames', 'geoname', 'countryCode', 'type',
|
|
'lat', 'lng', 'south', 'west', 'north', 'east', 'area',
|
|
'editable'
|
|
], self.options.keys);
|
|
self.resultPlace = null;
|
|
self.scaleMeters = [
|
|
50000000, 20000000, 10000000,
|
|
5000000, 2000000, 1000000,
|
|
500000, 200000, 100000,
|
|
50000, 20000, 10000,
|
|
5000, 2000, 1000,
|
|
500, 200, 100,
|
|
50, 20, 10
|
|
];
|
|
self.shiftKey = false;
|
|
|
|
if (self.options.showToolbar) {
|
|
self.$toolbar = Ox.Bar({
|
|
size: 24
|
|
})
|
|
.appendTo(that);
|
|
self.$select = Ox.MenuButton({
|
|
items: [
|
|
{
|
|
id: 'toggleLabels',
|
|
title: self.options.showLabels
|
|
? ['Hide Labels', 'Show Labels']
|
|
: ['Show Labels', 'Hide Labels'],
|
|
keyboard: 'l'
|
|
},
|
|
{
|
|
id: 'toggleControls',
|
|
title: self.options.showControls
|
|
? ['Hide Controls', 'Show Controls']
|
|
: ['Show Controls', 'Hide Controls'],
|
|
keyboard: 'c'
|
|
}
|
|
],
|
|
title: 'set',
|
|
tooltip: 'Map Options',
|
|
type: 'image'
|
|
})
|
|
.css({float: 'left', margin: '4px'})
|
|
.bindEvent({
|
|
click: function(data) {
|
|
if (data.id == 'toggleLabels') {
|
|
toggleLabels();
|
|
} else if (data.id == 'toggleControls') {
|
|
toggleControls();
|
|
}
|
|
}
|
|
})
|
|
.appendTo(self.$toolbar);
|
|
/*
|
|
self.$labelsButton = Ox.Checkbox({
|
|
title: 'Labels',
|
|
width: 64
|
|
})
|
|
.css({float: 'left', margin: '4px'})
|
|
.bindEvent({
|
|
change: toggleLabels
|
|
})
|
|
.appendTo(self.$toolbar)
|
|
*/
|
|
self.$findInput = Ox.Input({
|
|
clear: true,
|
|
placeholder: self.options.findPlaceholder,
|
|
width: 192
|
|
})
|
|
.css({float: 'right', margin: '4px'})
|
|
.bindEvent({
|
|
submit: submitFind
|
|
})
|
|
.appendTo(self.$toolbar)
|
|
}
|
|
|
|
self.$map = Ox.Element()
|
|
.css({
|
|
left: 0,
|
|
top: self.options.showToolbar * 24 + 'px',
|
|
right: 0,
|
|
bottom: self.options.showZoombar * 16 + self.options.showStatusbar * 24 + 'px'
|
|
})
|
|
.appendTo(that);
|
|
|
|
if (self.options.showZoombar) {
|
|
self.$zoombar = Ox.Bar({
|
|
size: 16
|
|
})
|
|
.css({
|
|
bottom: self.options.showStatusbar * 24 + 'px'
|
|
})
|
|
.appendTo(that);
|
|
}
|
|
|
|
if (self.options.showStatusbar) {
|
|
self.$statusbar = Ox.Bar({
|
|
size: 24
|
|
})
|
|
.css({
|
|
bottom: 0
|
|
})
|
|
.appendTo(that);
|
|
self.$placeFlag = $('<img>')
|
|
.addClass('OxFlag')
|
|
.attr({
|
|
src: Ox.PATH + 'Ox.Geo/png/icons/16/NTHH.png'
|
|
})
|
|
.css({float: 'left', margin: '4px 2px 4px 4px'})
|
|
.appendTo(self.$statusbar.$element);
|
|
self.$placeNameInput = Ox.Input({
|
|
placeholder: 'Name',
|
|
width: 96
|
|
})
|
|
//.css({position: 'absolute', left: 4, top: 4})
|
|
.css({float: 'left', margin: '4px 2px 4px 2px'})
|
|
.appendTo(self.$statusbar);
|
|
self.$placeGeonameInput = Ox.Input({
|
|
placeholder: 'Geoname',
|
|
width: 96
|
|
})
|
|
//.css({position: 'absolute', left: 104, top: 4})
|
|
.css({float: 'left', margin: '4px 2px 4px 2px'})
|
|
.appendTo(self.$statusbar);
|
|
self.$placeButton = Ox.Button({
|
|
title: 'New Place',
|
|
width: 96
|
|
})
|
|
.css({float: 'right', margin: '4px 4px 4px 2px'})
|
|
.bindEvent({
|
|
click: clickPlaceButton
|
|
})
|
|
.appendTo(self.$statusbar);
|
|
}
|
|
|
|
self.$controls = {
|
|
center: Ox.Button({
|
|
title: 'center',
|
|
type: 'image'
|
|
})
|
|
.addClass('OxMapControl OxMapButtonCenter')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
panToPlace();
|
|
},
|
|
doubleclick: function() {
|
|
zoomToPlace();
|
|
}
|
|
}),
|
|
east: Ox.Button({
|
|
title: 'right',
|
|
type: 'image'
|
|
})
|
|
.addClass('OxMapControl OxMapButtonEast')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
pan(1, 0);
|
|
},
|
|
doubleclick: function() {
|
|
pan(2, 0);
|
|
}
|
|
}),
|
|
north: Ox.Button({
|
|
title: 'up',
|
|
type: 'image'
|
|
})
|
|
.addClass('OxMapControl OxMapButtonNorth')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
pan(0, -1);
|
|
},
|
|
doubleclick: function() {
|
|
pan(0, -2);
|
|
}
|
|
}),
|
|
south: Ox.Button({
|
|
title: 'down',
|
|
type: 'image'
|
|
})
|
|
.addClass('OxMapControl OxMapButtonSouth')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
pan(0, 1);
|
|
},
|
|
doubleclick: function() {
|
|
pan(0, 2);
|
|
}
|
|
}),
|
|
west: Ox.Button({
|
|
title: 'left',
|
|
type: 'image'
|
|
})
|
|
.addClass('OxMapControl OxMapButtonWest')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
pan(-1, 0);
|
|
},
|
|
doubleclick: function() {
|
|
pan(-2, 0);
|
|
}
|
|
}),
|
|
scale: Ox.Label({
|
|
textAlign: 'center'
|
|
})
|
|
.addClass('OxMapControl OxMapScale')
|
|
};
|
|
!self.options.showControls && Ox.forEach(self.$controls, function($control) {
|
|
$control.css({opacity: 0}).hide();
|
|
});
|
|
|
|
self.$placeControls = {
|
|
flag: Ox.Element()
|
|
.addClass('OxPlaceControl OxPlaceFlag')
|
|
.bindEvent({
|
|
anyclick: function() {
|
|
// FIXME: doesn't work for 'Georgia', use Ox.Geo data!
|
|
var country = this.data('country');
|
|
country && getPlaceByName(country, function(place) {
|
|
place && self.map.fitBounds(place.bounds);
|
|
});
|
|
}
|
|
}),
|
|
name: Ox.Label({
|
|
textAlign: 'center',
|
|
tooltip: 'Click to pan, doubleclick to zoom'
|
|
})
|
|
.addClass('OxPlaceControl OxPlaceName')
|
|
.bindEvent({
|
|
singleclick: function() {
|
|
panToPlace();
|
|
},
|
|
doubleclick: function() {
|
|
zoomToPlace();
|
|
}
|
|
}),
|
|
deselectButton: Ox.Button({
|
|
title: 'close',
|
|
tooltip: 'Deselect',
|
|
type: 'image',
|
|
})
|
|
.addClass('OxPlaceControl OxPlaceDeselectButton')
|
|
.bindEvent({
|
|
click: function() {
|
|
selectPlace(null);
|
|
}
|
|
})
|
|
}
|
|
Ox.forEach(self.$placeControls, function($placeControl) {
|
|
$placeControl.css({opacity: 0}).hide();
|
|
});
|
|
|
|
if (window.google) {
|
|
initMap();
|
|
} else if (window.googleCallback) {
|
|
(function interval() {
|
|
window.google ? initMap() : setTimeout(interval, 100);
|
|
}());
|
|
} else {
|
|
window.googleCallback = function() {
|
|
delete window.googleCallback;
|
|
initMap();
|
|
};
|
|
$.getScript(
|
|
document.location.protocol
|
|
+ '//maps.google.com/maps/api/js?callback=googleCallback&sensor=false'
|
|
);
|
|
}
|
|
|
|
function addPlaces() {
|
|
Ox.forEach(self.$placeControls, function($placeControl) {
|
|
$placeControl.hide();
|
|
});
|
|
self.places && self.places.forEach(function(place) {
|
|
place.remove();
|
|
});
|
|
self.places = [];
|
|
/*
|
|
if (!self.isAsync) {
|
|
self.options.places.forEach(function(place) {
|
|
self.places.push(new Ox.MapPlace(Ox.extend({
|
|
map: that
|
|
}, place)));
|
|
});
|
|
}
|
|
*/
|
|
}
|
|
|
|
function addPlaceToMap(place) {
|
|
// via find, click, shift-click, or new place button
|
|
Ox.Log('Map', 'addPlaceToMap', place)
|
|
var exists = false;
|
|
if (!place) {
|
|
var bounds = self.map.getBounds(),
|
|
center = self.map.getCenter(),
|
|
southwest = new google.maps.LatLngBounds(
|
|
bounds.getSouthWest(), center
|
|
).getCenter(),
|
|
northeast = new google.maps.LatLngBounds(
|
|
center, bounds.getNorthEast()
|
|
).getCenter(),
|
|
place = new Ox.MapPlace({
|
|
alternativeNames: [],
|
|
countryCode: '',
|
|
editable: true,
|
|
geoname: '',
|
|
id: '_' + Ox.encodeBase32(Ox.uid()), // fixme: stupid
|
|
map: that,
|
|
name: '',
|
|
type: 'feature',
|
|
south: southwest.lat(),
|
|
west: southwest.lng(),
|
|
north: northeast.lat(),
|
|
east: northeast.lng()
|
|
});
|
|
}
|
|
Ox.forEach(self.places, function(p, i) {
|
|
if (place.bounds.equals(p.bounds)) {
|
|
place = p;
|
|
exists = true;
|
|
return false;
|
|
}
|
|
});
|
|
if (!exists) {
|
|
self.resultPlace && self.resultPlace.remove();
|
|
self.resultPlace = place;
|
|
place.add();
|
|
}
|
|
selectPlace(place.id);
|
|
}
|
|
|
|
function addPlaceToPlaces(data) {
|
|
var place = getSelectedPlace(),
|
|
country = Ox.getCountryByGeoname(place.geoname);
|
|
Ox.extend(place, data);
|
|
//Ox.print('addPlaceToPlaces, place name:', place.name)
|
|
self.options.selected = place.id;
|
|
place.countryCode = country ? country.code : '';
|
|
Ox.Log('Map', 'addP2P', data, place);
|
|
setPlaceControls(place);
|
|
place.marker.update();
|
|
place.rectangle.update();
|
|
self.places.push(place);
|
|
self.resultPlace = null;
|
|
that.triggerEvent('addplace', place)
|
|
}
|
|
|
|
function boundsChanged() {
|
|
setScale();
|
|
self.boundsChanged = true;
|
|
}
|
|
|
|
function canContain(outerBounds, innerBounds) {
|
|
// checks if outerBounds _can_ contain innerBounds
|
|
var outerSpan = outerBounds.toSpan(),
|
|
innerSpan = innerBounds.toSpan();
|
|
return outerSpan.lat() > innerSpan.lat() &&
|
|
outerSpan.lng() > innerSpan.lng();
|
|
}
|
|
|
|
function centerChanged() {
|
|
var tooltip = $('.OxMapMarkerTooltip');
|
|
tooltip.length && Ox.UI.elements[$(tooltip[0]).data('oxid')].hide();
|
|
self.center = self.map.getCenter();
|
|
self.centerChanged = true;
|
|
}
|
|
|
|
function changeZoom(data) {
|
|
self.map.setZoom(data.value);
|
|
}
|
|
|
|
function clickMap(event) {
|
|
var place = getSelectedPlace();
|
|
if (self.options.clickable/* && !editing()*/) {
|
|
getPlaceByLatLng(event.latLng, self.map.getBounds(), function(place) {
|
|
if (place) {
|
|
addPlaceToMap(place);
|
|
//selectPlace(place.id);
|
|
} else {
|
|
selectPlace(null);
|
|
}
|
|
});
|
|
} else {
|
|
pressEscape();
|
|
}
|
|
}
|
|
|
|
function clickPlaceButton() {
|
|
var place = getSelectedPlace(),
|
|
title = self.$placeButton.options('title');
|
|
if (title == 'New Place') {
|
|
addPlaceToMap();
|
|
} else if (title == 'Add Place') {
|
|
addPlaceToPlaces();
|
|
}
|
|
}
|
|
|
|
function constructZoomInput() {
|
|
if (self.options.showZoombar) {
|
|
self.$zoomInput && self.$zoomInput.remove();
|
|
self.$zoomInput = Ox.Range({
|
|
arrows: true,
|
|
changeOnDrag: true,
|
|
max: self.maxZoom,
|
|
min: self.minZoom,
|
|
size: that.width(),
|
|
thumbSize: 32,
|
|
thumbValue: true,
|
|
value: self.map.getZoom()
|
|
})
|
|
.bindEvent({
|
|
change: changeZoom
|
|
})
|
|
.appendTo(self.$zoombar);
|
|
}
|
|
}
|
|
|
|
function crossesDateline() {
|
|
var bounds = self.map.getBounds();
|
|
return bounds.getSouthWest().lng() > bounds.getNorthEast().lng();
|
|
}
|
|
|
|
function editing() {
|
|
var place = getSelectedPlace();
|
|
return place && place.editing;
|
|
}
|
|
|
|
function getElevation(point, callback) {
|
|
// fixme: unused
|
|
if (arguments.length == 1) {
|
|
callback = point;
|
|
point = self.map.getCenter();
|
|
}
|
|
self.elevationService.getElevationForLocations({
|
|
locations: [point]
|
|
}, function(data) {
|
|
callback(data.elevation);
|
|
});
|
|
}
|
|
|
|
function getMapBounds(callback) {
|
|
// get initial map bounds
|
|
self.options.places({}, function(result) {
|
|
var area = result.data.area;
|
|
callback(new google.maps.LatLngBounds(
|
|
new google.maps.LatLng(area.south, area.west),
|
|
new google.maps.LatLng(area.north, area.east)
|
|
));
|
|
});
|
|
}
|
|
|
|
function getMapHeight() {
|
|
return self.options.height
|
|
- self.options.showStatusbar * 24
|
|
- self.options.showToolbar * 24
|
|
- self.options.showZoombar * 16;
|
|
}
|
|
|
|
function getMapType() {
|
|
return self.options.showLabels ? 'HYBRID' : 'SATELLITE'
|
|
}
|
|
|
|
function getMaxZoom(point, callback) {
|
|
if (arguments.length == 1) {
|
|
callback = point;
|
|
point = self.map.getCenter();
|
|
}
|
|
self.maxZoomService.getMaxZoomAtLatLng(point, function(data) {
|
|
callback(data.status == 'OK' ? data.zoom : null);
|
|
});
|
|
}
|
|
|
|
function getMetersPerPixel() {
|
|
// m/px = m/deg * deg/px
|
|
var degreesPerPixel = 360 / (Ox.MAP_TILE_SIZE * Math.pow(2, self.map.getZoom()));
|
|
return Ox.getMetersPerDegree(self.map.getCenter().lat()) * degreesPerPixel;
|
|
}
|
|
|
|
function getMinZoom() {
|
|
return self.mapHeight > 1024 ? 3
|
|
: self.mapHeight > 512 ? 2
|
|
: self.mapHeight > 256 ? 1
|
|
: 0;
|
|
// fixme: there must be a function for this...
|
|
/*
|
|
return Math.ceil(
|
|
Ox.log(self.mapHeight / Ox.MAP_TILE_SIZE, 2)
|
|
);
|
|
*/
|
|
}
|
|
|
|
function getPlaceById(id) {
|
|
return self.resultPlace && self.resultPlace.id == id
|
|
? self.resultPlace
|
|
: Ox.getObjectById(self.places, id);
|
|
}
|
|
|
|
function getPlaceByLatLng(latlng, bounds, callback) {
|
|
// gets the largest place at latlng that would fit in bounds
|
|
var callback = arguments.length == 3 ? callback : bounds,
|
|
bounds = arguments.length == 3 ? bounds : null;
|
|
self.geocoder.geocode({
|
|
latLng: latlng
|
|
}, function(results, status) {
|
|
//Ox.Log('Map', 'results', results)
|
|
var length = results.length;
|
|
if (status == google.maps.GeocoderStatus.OK) {
|
|
if (bounds) {
|
|
Ox.forEach(results.reverse(), function(result, i) {
|
|
if (
|
|
i == length - 1 ||
|
|
canContain(bounds, result.geometry.bounds || result.geometry.viewport)
|
|
) {
|
|
callback(new Ox.MapPlace(parseGeodata(results[i])));
|
|
return false;
|
|
}
|
|
});
|
|
} else {
|
|
callback(new Ox.MapPlace(parseGeodata(results[0])));
|
|
}
|
|
}
|
|
if (
|
|
status == google.maps.GeocoderStatus.OK ||
|
|
status == google.maps.GeocoderStatus.ZERO_RESULTS
|
|
) {
|
|
triggerGeocodeEvent({
|
|
latLng: latlng,
|
|
results: results
|
|
});
|
|
} else {
|
|
Ox.Log('Map', 'geocode failed:', status);
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
function getPlaceByName(name, callback) {
|
|
self.geocoder.geocode({
|
|
address: name
|
|
}, function(results, status) {
|
|
if (status == google.maps.GeocoderStatus.OK) {
|
|
callback(new Ox.MapPlace(parseGeodata(results[0])));
|
|
}
|
|
if (
|
|
status == google.maps.GeocoderStatus.OK
|
|
&& status != google.maps.GeocoderStatus.ZERO_RESULTS
|
|
) {
|
|
triggerGeocodeEvent({
|
|
address: name,
|
|
results: results
|
|
});
|
|
} else {
|
|
Ox.Log('Map', 'geocode failed:', status);
|
|
callback(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
function getPositionByName(name) {
|
|
var position = -1;
|
|
Ox.forEach(self.options.places, function(place, i) {
|
|
if (place.name == name) {
|
|
position = i;
|
|
return false;
|
|
}
|
|
});
|
|
return position;
|
|
}
|
|
|
|
function getSelectedMarker() {
|
|
// needed in case self.options.selected
|
|
// is changed from outside
|
|
var id = null;
|
|
if (self.resultPlace && self.resultPlace.selected) {
|
|
id = self.resultPlace.id;
|
|
} else {
|
|
Ox.forEach(self.places, function(place) {
|
|
if (place.selected) {
|
|
id = place.id;
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
return id;
|
|
}
|
|
|
|
function getSelectedPlace() {
|
|
return self.options.selected
|
|
? getPlaceById(self.options.selected)
|
|
: null;
|
|
}
|
|
|
|
function initMap() {
|
|
|
|
getMapBounds(function(mapBounds) {
|
|
|
|
//Ox.Log('Map', 'init', mapBounds.getSouthWest(), mapBounds.getNorthEast(), mapBounds.getCenter())
|
|
|
|
self.elevationService = new google.maps.ElevationService();
|
|
self.geocoder = new google.maps.Geocoder();
|
|
self.maxZoomService = new google.maps.MaxZoomService();
|
|
|
|
self.center = mapBounds ? mapBounds.getCenter() : new google.maps.LatLng(0, 0);
|
|
self.zoom = self.minZoom;
|
|
that.map = self.map = new google.maps.Map(self.$map.$element[0], {
|
|
center: self.center,
|
|
disableDefaultUI: true,
|
|
disableDoubleClickZoom: true,
|
|
mapTypeId: google.maps.MapTypeId[getMapType()],
|
|
noClear: true,
|
|
scrollwheel: !self.options.zoomOnlyWhenFocused,
|
|
zoom: self.zoom
|
|
});
|
|
google.maps.event.addListener(self.map, 'bounds_changed', boundsChanged);
|
|
google.maps.event.addListener(self.map, 'center_changed', centerChanged);
|
|
google.maps.event.addListener(self.map, 'click', clickMap);
|
|
google.maps.event.addListener(self.map, 'idle', mapChanged);
|
|
google.maps.event.addListener(self.map, 'zoom_changed', zoomChanged);
|
|
google.maps.event.addListenerOnce(self.map, 'tilesloaded', tilesLoaded);
|
|
google.maps.event.trigger(self.map, 'resize');
|
|
|
|
// needed to get mouse x/y coordinates on marker mouseover,
|
|
// see http://code.google.com/p/gmaps-api-issues/issues/detail?id=2342
|
|
that.overlayView = new google.maps.OverlayView();
|
|
that.overlayView.setMap(self.map);
|
|
that.overlayView.draw = function () {
|
|
if (!this.ready) {
|
|
this.ready = true;
|
|
google.maps.event.trigger(this, 'ready');
|
|
}
|
|
}
|
|
that.overlayView.draw();
|
|
|
|
addPlaces();
|
|
|
|
Ox.forEach(self.$controls, function($control) {
|
|
$control.appendTo(self.$map);
|
|
});
|
|
Ox.forEach(self.$placeControls, function($placeControl) {
|
|
$placeControl.appendTo(self.$map);
|
|
});
|
|
|
|
if (self.options.find) {
|
|
self.$findInput
|
|
.value(self.options.find)
|
|
.triggerEvent('submit', {value: self.options.find});
|
|
} else {
|
|
if (self.options.selected) {
|
|
selectPlace(self.options.selected, true);
|
|
}
|
|
if (mapBounds) {
|
|
if (isEmpty(mapBounds)) {
|
|
self.map.setZoom(self.minZoom);
|
|
} else {
|
|
self.map.fitBounds(mapBounds);
|
|
}
|
|
}
|
|
if (self.map.getZoom() < self.minZoom) {
|
|
self.map.setZoom(self.minZoom);
|
|
}
|
|
}
|
|
updateFormElements();
|
|
|
|
//that.gainFocus();
|
|
self.loaded = true;
|
|
that.triggerEvent('load');
|
|
|
|
function tilesLoaded() {
|
|
setTimeout(formatTerms, 250);
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
function isEmpty(bounds) {
|
|
// Google's bounds.isEmpty() is not reliable
|
|
var southWest = bounds.getSouthWest(),
|
|
northEast = bounds.getNorthEast();
|
|
return southWest.lat() == northEast.lat()
|
|
&& southWest.lng() == northEast.lng();
|
|
}
|
|
|
|
function formatTerms() {
|
|
setTimeout(function() {
|
|
var $element;
|
|
try {
|
|
$element = $(self.$map.find('a[href$="terms_maps.html"]').parent()).css({
|
|
color: 'rgb(192, 192, 192)',
|
|
textShadow: '1px 1px 0 rgb(64, 64, 64)'
|
|
});
|
|
['moz', 'o', 'webkit'].forEach(function(browser) {
|
|
$element.css({
|
|
backgroundImage: '-' + browser
|
|
+ '-linear-gradient(left, rgba(255, 255, 255, 0) 0px, rgba(255, 255, 255, 0.1) 50px)',
|
|
});
|
|
});
|
|
$element.children().css({color: 'rgb(192, 192, 192)'});
|
|
} catch (e) {}
|
|
}, 0);
|
|
}
|
|
|
|
function mapChanged() {
|
|
// gets called after panning or zooming
|
|
//Ox.print('Map', 'mapChanged');
|
|
if (self.boundsChanged) {
|
|
var bounds = self.map.getBounds(),
|
|
southWest = bounds.getSouthWest(),
|
|
northEast = bounds.getNorthEast(),
|
|
south = southWest.lat(),
|
|
west = southWest.lng(),
|
|
north = northEast.lat(),
|
|
east = northEast.lng();
|
|
self.options.places({
|
|
keys: self.placeKeys,
|
|
query: {
|
|
conditions: Ox.merge([
|
|
{key: 'lat', value: [south, north], operator: '='}
|
|
], spansGlobe() ? [
|
|
{key: 'lng', value: [-180, 180], operator: '='}
|
|
] : crossesDateline() ? [
|
|
{key: 'lng', value: [east, west], operator: '!='}
|
|
] : [
|
|
{key: 'lng', value: [west, east], operator: '='}
|
|
]),
|
|
operator: '&'
|
|
},
|
|
range: [0, self.options.maxMarkers],
|
|
sort: [{key: 'area', operator: '-'}]
|
|
}, function(result) {
|
|
Ox.Log('Map', 'RESULT', result)
|
|
var ids = [];
|
|
result.data.items.forEach(function(item, i) {
|
|
var place = getPlaceById(item.id);
|
|
if (!place) {
|
|
self.places.push(
|
|
new Ox.MapPlace(Ox.extend({
|
|
map: that
|
|
}, item)).add()
|
|
);
|
|
} else if (!place.visible) {
|
|
place.add();
|
|
}
|
|
ids.push(item.id);
|
|
});
|
|
self.places.forEach(function(place) {
|
|
if (
|
|
!place.selected && place.visible
|
|
&& ids.indexOf(place.id) == -1
|
|
) {
|
|
place.remove();
|
|
}
|
|
});
|
|
});
|
|
self.boundsChanged = false;
|
|
}
|
|
if (self.centerChanged) {
|
|
getMaxZoom(function(zoom) {
|
|
if (zoom && zoom != self.maxZoom) {
|
|
self.maxZoom = zoom;
|
|
if (self.map.getZoom() > zoom) {
|
|
self.map.setZoom(zoom);
|
|
}
|
|
constructZoomInput();
|
|
}
|
|
});
|
|
self.centerChanged = false;
|
|
}
|
|
if (self.zoomChanged) {
|
|
self.zoomChanged = false;
|
|
}
|
|
formatTerms();
|
|
}
|
|
|
|
function pan(x, y) {
|
|
self.map.panBy(x * self.$map.width() / 2, y * self.$map.height() / 2);
|
|
};
|
|
|
|
function panToPlace() {
|
|
var place = getSelectedPlace();
|
|
place && self.map.panTo(place.center);
|
|
}
|
|
|
|
function parseGeodata(data) {
|
|
var bounds = data.geometry.bounds || data.geometry.viewport,
|
|
northEast = bounds.getNorthEast(),
|
|
southWest = bounds.getSouthWest(),
|
|
place = {
|
|
alternativeNames: [],
|
|
components: data.address_components,
|
|
countryCode: getCountryCode(data.address_components),
|
|
east: northEast.lng(),
|
|
editable: self.options.editable,
|
|
fullGeoname: getFullGeoname(data.address_components),
|
|
id: '_' + Ox.encodeBase32(Ox.uid()),
|
|
map: that,
|
|
north: northEast.lat(),
|
|
south: southWest.lat(),
|
|
type: getType(data.address_components[0].types),
|
|
west: southWest.lng()
|
|
};
|
|
place.geoname = data.formatted_address || place.fullGeoname;
|
|
place.name = (place.geoname || place.fullGeoname).split(', ')[0];
|
|
if (Math.abs(place.west) == 180 && Math.abs(place.east) == 180) {
|
|
place.west = -179.99999999;
|
|
place.east = 179.99999999;
|
|
}
|
|
place.south = Ox.limit(place.south, Ox.MIN_LATITUDE, Ox.MAX_LATITUDE - 0.00000001);
|
|
place.north = Ox.limit(place.north, Ox.MIN_LATITUDE + 0.00000001, Ox.MAX_LATITUDE);
|
|
function getCountryCode(components) {
|
|
var countryCode = '';
|
|
Ox.forEach(components, function(component) {
|
|
if (component.types.indexOf('country') > -1) {
|
|
countryCode = component.short_name;
|
|
return false;
|
|
}
|
|
});
|
|
return countryCode;
|
|
}
|
|
function getFullGeoname(components) {
|
|
var country = false;
|
|
return components.map(function(component, i) {
|
|
var name = component.long_name;
|
|
if (i && components[i - 1].types.indexOf('country') > -1) {
|
|
country = true;
|
|
}
|
|
return !country && (
|
|
i == 0 || name != components[i - 1].long_name
|
|
) ? name : null;
|
|
}).join(', ');
|
|
}
|
|
function getType(types) {
|
|
Ox.Log('Map', 'getType', types)
|
|
// see http://code.google.com/apis/maps/documentation/javascript/services.html#GeocodingAddressTypes
|
|
var strings = {
|
|
'country': ['country'],
|
|
'region': ['administrative_area', 'colloquial_area'],
|
|
'city': ['locality'],
|
|
'borough': ['neighborhood', 'postal_code', 'sublocality'],
|
|
'street': [
|
|
'intersection', 'route',
|
|
'street_address', 'street_number'
|
|
],
|
|
'building': [
|
|
'airport', 'establishment', 'floor',
|
|
'premise', 'room', 'subpremise'
|
|
]
|
|
},
|
|
type = 'feature';
|
|
function find(type) {
|
|
var ret;
|
|
Ox.forEach(types, function(v) {
|
|
ret = Ox.startsWith(v, type);
|
|
return !ret;
|
|
});
|
|
return ret;
|
|
}
|
|
Ox.forEach(strings, function(values, key) {
|
|
Ox.forEach(values, function(value) {
|
|
if (find(value)) {
|
|
type = key;
|
|
return false;
|
|
}
|
|
});
|
|
return type == 'feature';
|
|
});
|
|
return type;
|
|
}
|
|
return place;
|
|
}
|
|
|
|
function pressEnter() {
|
|
var place = getSelectedPlace();
|
|
if (place) {
|
|
if (place.editing) {
|
|
place.submit();
|
|
} else {
|
|
place.edit();
|
|
}
|
|
} else if (self.resultPlace) {
|
|
selectPlace(self.resultPlace.id)
|
|
}
|
|
}
|
|
|
|
function pressEscape() {
|
|
var place = getSelectedPlace();
|
|
if (place) {
|
|
if (place.editing) {
|
|
place.cancel();
|
|
} else {
|
|
selectPlace(null);
|
|
}
|
|
} else if (self.resultPlace) {
|
|
self.resultPlace.remove();
|
|
self.resultPlace = null;
|
|
}
|
|
}
|
|
|
|
// fixme: removePlacefromMap?
|
|
function removePlace() {
|
|
var place = getSelectedPlace();
|
|
place.id = '_' + place.id;
|
|
self.options.selected = place.id;
|
|
self.places.splice(Ox.getIndexById(self.places, place.id), 1);
|
|
self.resultPlace && self.resultPlace.remove();
|
|
self.resultPlace = place;
|
|
//Ox.print('removed place, place is now', place);
|
|
place.marker.update();
|
|
place.rectangle.update();
|
|
}
|
|
|
|
function reset() {
|
|
self.map.getZoom() == self.zoom
|
|
? self.map.panTo(self.center)
|
|
: self.map.fitBounds(self.bounds);
|
|
}
|
|
|
|
function selectPlace(id, zoom) {
|
|
//Ox.print('SELECT PLACE', id)
|
|
// id can be null (deselect)
|
|
var place, selected = getSelectedMarker();
|
|
if (!self.loaded) {
|
|
setTimeout(function() {
|
|
selectPlace(id, zoom)
|
|
}, 1000);
|
|
} else {
|
|
Ox.Log('Map', 'Ox.Map selectPlace()', id, selected);
|
|
if (id != selected) {
|
|
place = getPlaceById(selected);
|
|
place && place.deselect();
|
|
if (id !== null) {
|
|
place = getPlaceById(id);
|
|
if (place) {
|
|
select();
|
|
} else {
|
|
// async && place doesn't exist yet
|
|
self.options.places({
|
|
keys: self.placeKeys,
|
|
query: {
|
|
conditions: [
|
|
{key: 'id', value: id, operator: '=='}
|
|
],
|
|
operator: '&'
|
|
}
|
|
}, function(result) {
|
|
if (result.data.items.length) {
|
|
place = new Ox.MapPlace(Ox.extend({
|
|
map: that
|
|
}, result.data.items[0])).add();
|
|
self.places.push(place);
|
|
select();
|
|
if (zoom) {
|
|
zoomToPlace();
|
|
} else {
|
|
panToPlace();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
place = null;
|
|
select();
|
|
}
|
|
}
|
|
}
|
|
function select() {
|
|
place && place.select();
|
|
self.options.selected = id;
|
|
setPlaceControls(place);
|
|
setStatus();
|
|
that.triggerEvent('selectplace', place); // DEPRECATED
|
|
that.triggerEvent('select', {place: place});
|
|
}
|
|
};
|
|
|
|
function setPlaceControls(place) {
|
|
var $placeControls = that.$element.find('.OxPlaceControl'),
|
|
country,
|
|
isVisible = self.$placeControls.name.is(':visible');
|
|
if (place) {
|
|
country = place.geoname.indexOf(', ') > -1
|
|
? place.geoname.split(', ').pop()
|
|
: '';
|
|
self.$placeControls.flag.options({
|
|
tooltip: country ? 'Zoom to ' + country : ''
|
|
})
|
|
.data({country: country})
|
|
.empty()
|
|
.append(
|
|
$('<img>').attr({
|
|
src: Ox.getFlagByGeoname(place.geoname, 16)
|
|
})
|
|
)
|
|
.show();
|
|
self.$placeControls.name.options({
|
|
title: place.name ||'<span class="OxLight">Unnamed</span>'
|
|
});
|
|
!isVisible && $placeControls.show().animate({opacity: 1}, 250);
|
|
} else {
|
|
isVisible && $placeControls.animate({opacity: 0}, 250, function() {
|
|
$placeControls.hide();
|
|
});
|
|
}
|
|
}
|
|
|
|
function setScale() {
|
|
var metersPerPixel = getMetersPerPixel();
|
|
Ox.forEach(self.scaleMeters, function(meters) {
|
|
var mapWidth = self.options.width || that.width(),
|
|
scaleWidth = Math.round(meters / metersPerPixel);
|
|
if (scaleWidth <= mapWidth / 2 - 4) {
|
|
self.$controls.scale
|
|
.options({
|
|
title: '\u2190 ' + (
|
|
meters > 1000 ? Ox.formatNumber(meters / 1000) + ' k' : meters + ' '
|
|
) + 'm \u2192'
|
|
})
|
|
.css({
|
|
width: (scaleWidth - 16) + 'px'
|
|
})
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
function setStatus() {
|
|
//Ox.Log('Map', 'setStatus()', self.options.selected)
|
|
var code, country, disabled, place, title;
|
|
if (self.options.showStatusbar) {
|
|
place = getSelectedPlace();
|
|
country = place ? Ox.getCountryByGeoname(place.geoname) : '';
|
|
code = country ? country.code : 'NTHH';
|
|
disabled = place && !place.editable;
|
|
if (place) {
|
|
title = place.id[0] == '_' ? 'Add Place' : 'Remove Place';
|
|
} else {
|
|
title = 'New Place';
|
|
}
|
|
self.$placeFlag.attr({
|
|
src: Ox.PATH + 'Ox.Geo/png/icons/16/' + code + '.png'
|
|
});
|
|
self.$placeNameInput.options({
|
|
disabled: disabled,
|
|
value: place ? place.name : ''
|
|
});
|
|
self.$placeGeonameInput.options({
|
|
disabled: disabled,
|
|
value: place ? place.geoname : ''
|
|
});
|
|
self.$placeButton.options({
|
|
disabled: disabled,
|
|
title: title
|
|
});
|
|
}
|
|
//Ox.Log('Map', 'STATUS DONE');
|
|
}
|
|
|
|
function spansGlobe() {
|
|
// fixme: or self.options.width ??
|
|
return self.$map.width() > Ox.MAP_TILE_SIZE * Math.pow(2, self.map.getZoom());
|
|
};
|
|
|
|
function submitFind(data) {
|
|
self.options.find = data.value;
|
|
if (data.value === '') {
|
|
if (self.options.selected && self.options.selected[0] == '_') {
|
|
selectPlace(null);
|
|
}
|
|
} else {
|
|
that.findPlace(data.value, function(place) {
|
|
setStatus(place);
|
|
});
|
|
}
|
|
}
|
|
|
|
function toggleControls() {
|
|
// fixme: that.find() doesn't work here
|
|
var $controls = that.$element.find('.OxMapControl');
|
|
self.options.showControls = !self.options.showControls;
|
|
if (self.options.showControls) {
|
|
$controls.show().animate({opacity: 1}, 250);
|
|
} else {
|
|
$controls.animate({opacity: 0}, 250, function() {
|
|
$controls.hide();
|
|
});
|
|
}
|
|
that.triggerEvent('togglecontrols', {
|
|
visible: self.options.showControls
|
|
});
|
|
}
|
|
|
|
function toggleLabels() {
|
|
self.options.showLabels = !self.options.showLabels;
|
|
self.map.setMapTypeId(google.maps.MapTypeId[getMapType()]);
|
|
that.triggerEvent('togglelabels', {
|
|
visible: self.options.showLabels
|
|
});
|
|
}
|
|
|
|
function triggerGeocodeEvent(data) {
|
|
// someone may want to cache google geocode data, so we fire an event.
|
|
// google puts functions like lat or lng on the objects' prototypes,
|
|
// so we create properly named properties, for json encoding
|
|
if (data.latLng) {
|
|
data.latLng = {
|
|
lat: data.latLng.lat(),
|
|
lng: data.latLng.lng()
|
|
}
|
|
}
|
|
data.results.forEach(function(result) {
|
|
['bounds', 'viewport'].forEach(function(key) {
|
|
if (result.geometry[key]) {
|
|
result.geometry[key] = {
|
|
northEast: {
|
|
lat: result.geometry[key].getNorthEast().lat(),
|
|
lng: result.geometry[key].getNorthEast().lng()
|
|
},
|
|
southWest: {
|
|
lat: result.geometry[key].getSouthWest().lat(),
|
|
lng: result.geometry[key].getSouthWest().lng()
|
|
}
|
|
}
|
|
}
|
|
});
|
|
if (result.geometry.location) {
|
|
result.geometry.location = {
|
|
lat: result.geometry.location.lat(),
|
|
lng: result.geometry.location.lng()
|
|
}
|
|
}
|
|
});
|
|
that.triggerEvent('geocode', data);
|
|
}
|
|
|
|
function undo() {
|
|
Ox.Log('Map', 'Map undo')
|
|
var place = getSelectedPlace();
|
|
place.editing && place.undo();
|
|
}
|
|
|
|
function updateFormElements() {
|
|
var width = that.width();
|
|
if (self.options.showZoombar) {
|
|
getMaxZoom(function(zoom) {
|
|
self.maxZoom = zoom;
|
|
constructZoomInput();
|
|
});
|
|
}
|
|
if (self.options.showStatusbar) {
|
|
self.$placeNameInput.options({
|
|
width: Math.floor((width - 132) / 2)
|
|
});
|
|
self.$placeGeonameInput.options({
|
|
width: Math.ceil((width - 132) / 2)
|
|
});
|
|
}
|
|
}
|
|
|
|
function zoom(z) {
|
|
self.map.setZoom(self.map.getZoom() + z);
|
|
}
|
|
|
|
function zoomChanged() {
|
|
var zoom = self.map.getZoom();
|
|
if (zoom < self.minZoom) {
|
|
self.map.setZoom(self.minZoom);
|
|
} else if (self.maxZoom && zoom > self.maxZoom) {
|
|
self.map.setZoom(self.maxZoom);
|
|
} else {
|
|
self.zoomChanged = true;
|
|
self.$zoomInput && self.$zoomInput.value(zoom);
|
|
that.triggerEvent('zoom', {
|
|
value: zoom
|
|
});
|
|
}
|
|
}
|
|
|
|
function zoomToPlace() {
|
|
var place = getSelectedPlace();
|
|
place && self.map.fitBounds(place.bounds);
|
|
}
|
|
|
|
self.setOption = function(key, value) {
|
|
if (key == 'find') {
|
|
self.$findInput
|
|
.value(self.options.find)
|
|
.triggerEvent('submit', {value: self.options.find});
|
|
} else if (key == 'height' || key == 'width') {
|
|
that.$element.css(key, value + 'px');
|
|
that.resizeMap();
|
|
} else if (key == 'places') {
|
|
addPlaces();
|
|
getMapBounds(function(mapBounds) {
|
|
if (mapBounds) {
|
|
self.map.fitBounds(mapBounds);
|
|
} else {
|
|
self.map.setZoom(self.minZoom);
|
|
self.map.setCenter(new google.maps.LatLng(0, 0));
|
|
}
|
|
// fixme: the following is just a guess
|
|
self.boundsChanged = true;
|
|
mapChanged();
|
|
});
|
|
if (self.options.selected) {
|
|
if (getSelectedPlace()) {
|
|
selectPlace(self.options.selected);
|
|
} else {
|
|
self.options.selected = '';
|
|
}
|
|
}
|
|
} else if (key == 'selected') {
|
|
selectPlace(value || null);
|
|
} else if (key == 'type') {
|
|
|
|
}
|
|
};
|
|
|
|
/*@
|
|
addPlace <f> addPlace
|
|
(data) -> <u> add place to places
|
|
@*/
|
|
that.addPlace = function(data) {
|
|
addPlaceToPlaces(data);
|
|
};
|
|
|
|
/*@
|
|
getKey <f> getKey
|
|
() -> <o> get key
|
|
@*/
|
|
that.getKey = function() {
|
|
return self.shiftKey ? 'shift'
|
|
: self.metaKey ? 'meta'
|
|
: null;
|
|
};
|
|
|
|
/*@
|
|
getSelectedPlace <f> getSelectedPlace
|
|
() -> <o> get selected place
|
|
@*/
|
|
that.getSelectedPlace = function() {
|
|
return getSelectedPlace();
|
|
}
|
|
|
|
/*@
|
|
editPlace <f> editPlace
|
|
() -> <o> edit selected place
|
|
@*/
|
|
that.editPlace = function() {
|
|
getSelectedPlace().edit();
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
findPlace <f> findPlace
|
|
(name, callback) -> <o> find place and pass to callback
|
|
@*/
|
|
that.findPlace = function(name, callback) {
|
|
getPlaceByName(name, function(place) {
|
|
if (place) {
|
|
addPlaceToMap(place);
|
|
self.map.fitBounds(place.bounds);
|
|
} else {
|
|
name && self.$findInput.addClass('OxError');
|
|
}
|
|
callback(place);
|
|
});
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
newPlace <f> newPlace
|
|
(place) -> <o> add place to map
|
|
@*/
|
|
that.newPlace = function(place) {
|
|
addPlaceToMap(place);
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
panToPlace <f> panToPlace
|
|
() -> <o> pan to place
|
|
@*/
|
|
that.panToPlace = function() {
|
|
panToPlace();
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
removePlace <f> removePlace
|
|
() -> <o> remove selected place from places
|
|
@*/
|
|
that.removePlace = function() {
|
|
// fixme: removePlaceFromPlaces() ?
|
|
removePlace();
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
resizeMap <f> resizeMap
|
|
() -> <o> resize map
|
|
@*/
|
|
that.resizeMap = function() {
|
|
// keep center on resize has been commented out
|
|
// var center = self.map.getCenter();
|
|
self.options.height = that.height();
|
|
self.options.width = that.width();
|
|
// check if map has initialized
|
|
if (self.map) {
|
|
self.mapHeight = getMapHeight();
|
|
self.minZoom = getMinZoom();
|
|
if (self.minZoom > self.map.getZoom()) {
|
|
self.map.setZoom(self.minZoom);
|
|
}
|
|
self.$map.css({
|
|
height: self.mapHeight + 'px',
|
|
width: self.options.width + 'px'
|
|
});
|
|
self.options.$zoomInput && self.$zoomInput.options({
|
|
size: self.options.width
|
|
});
|
|
updateFormElements();
|
|
Ox.Log('Map', 'triggering google maps resize event, height', self.options.height)
|
|
google.maps.event.trigger(self.map, 'resize');
|
|
// self.map.setCenter(center);
|
|
}
|
|
return that;
|
|
}
|
|
|
|
/*@
|
|
value <f> value
|
|
(id, key, value) -> <o> set id, key to value
|
|
@*/
|
|
that.value = function(id, key, value) {
|
|
// fixme: should be like the corresponding List/TextList/etc value function
|
|
Ox.Log('Map', 'Map.value', id, key, value);
|
|
getPlaceById(id).options(key, value);
|
|
if (id == self.options.selected) {
|
|
if (key == 'name') {
|
|
self.$placeControls.name.options({title: value});
|
|
} else if (key == 'geoname') {
|
|
self.$placeControls.flag.empty().append(
|
|
$('<img>').attr({
|
|
src: Ox.getFlagByGeoname(value, 16)
|
|
})
|
|
);
|
|
}
|
|
}
|
|
return that;
|
|
}
|
|
|
|
/*@
|
|
zoomToPlace <f> zoomToPlace
|
|
() -> <o> zoom to selected place
|
|
@*/
|
|
that.zoomToPlace = function() {
|
|
zoomToPlace();
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
zoom <f> zoom
|
|
(value) -> <o> zoom to value
|
|
@*/
|
|
that.zoom = function(value) {
|
|
zoom(value);
|
|
return that;
|
|
};
|
|
|
|
return that;
|
|
|
|
};
|