forked from 0x2620/oxjs
better filesystem structure for modules and themes; 'minified' ui if debug option not set; dynamially generated map markers
This commit is contained in:
parent
358ee1bc96
commit
4489e88f44
596 changed files with 115093 additions and 17682 deletions
370
source/Ox.UI/js/Map/Ox.ListMap.js
Normal file
370
source/Ox.UI/js/Map/Ox.ListMap.js
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.ListMap = function(options, self) {
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
addPlace: null,
|
||||
height: 256,
|
||||
labels: false,
|
||||
places: null,
|
||||
selected: [],
|
||||
width: 256
|
||||
})
|
||||
.addClass('OxListMap')
|
||||
.options(options || {})
|
||||
.css({
|
||||
width: self.options.width + 'px',
|
||||
height: self.options.height + 'px'
|
||||
});
|
||||
|
||||
self.columns = [
|
||||
{
|
||||
addable: false, // fixme: implement
|
||||
id: 'id',
|
||||
unique: true,
|
||||
visible: false
|
||||
},
|
||||
{
|
||||
editable: true,
|
||||
id: 'name',
|
||||
operator: '+',
|
||||
removable: false,
|
||||
title: 'Name',
|
||||
visible: true,
|
||||
width: 144
|
||||
},
|
||||
{
|
||||
editable: true,
|
||||
id: 'geoname',
|
||||
removable: false,
|
||||
operator: '+',
|
||||
title: 'Geoname',
|
||||
visible: true,
|
||||
width: 192
|
||||
},
|
||||
{
|
||||
format: function(value) {
|
||||
return $('<img>')
|
||||
.attr({
|
||||
// fixme: not the right place to do this
|
||||
src: Ox.PATH + 'Ox.Geo/png/icons/16/' + (value || 'NTHH') + '.png'
|
||||
})
|
||||
.css({
|
||||
width: '14px',
|
||||
height: '14px'
|
||||
});
|
||||
/*
|
||||
.css({
|
||||
width: '21px',
|
||||
height: '14px'
|
||||
})
|
||||
.load(function() {
|
||||
var $this = $(this);
|
||||
Ox.print($this.width() / $this.height())
|
||||
$this.css({
|
||||
width: Math.round(14 * $this.width() / $this.height()) + 'px',
|
||||
height: '14px',
|
||||
padding: '1px 0 0 1px'
|
||||
});
|
||||
});
|
||||
*/
|
||||
},
|
||||
id: 'countryCode',
|
||||
operator: '+',
|
||||
title: 'Flag',
|
||||
visible: true,
|
||||
width: 48
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: {type: 'area', args: [0]},
|
||||
id: 'size',
|
||||
operator: '-',
|
||||
title: 'Size',
|
||||
visible: true,
|
||||
width: 128
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: toFixed,
|
||||
id: 'lat',
|
||||
operator: '+',
|
||||
title: 'Latitude',
|
||||
visible: true,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: toFixed,
|
||||
id: 'lng',
|
||||
operator: '+',
|
||||
title: 'Longitude',
|
||||
visible: true,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: toFixed,
|
||||
id: 'south',
|
||||
operator: '+',
|
||||
title: 'South',
|
||||
visible: false,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
id: 'west',
|
||||
operator: '+',
|
||||
title: 'West',
|
||||
visible: false,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: toFixed,
|
||||
id: 'north',
|
||||
operator: '+',
|
||||
title: 'North',
|
||||
visible: false,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
format: toFixed,
|
||||
id: 'east',
|
||||
operator: '+',
|
||||
title: 'East',
|
||||
visible: false,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
operator: '+',
|
||||
title: 'User',
|
||||
visible: false,
|
||||
width: 96
|
||||
},
|
||||
{
|
||||
format: 'date',
|
||||
id: 'created',
|
||||
operator: '-',
|
||||
title: 'Date Created',
|
||||
visible: false,
|
||||
width: 96,
|
||||
},
|
||||
{
|
||||
format: 'date',
|
||||
id: 'modified',
|
||||
operator: '-',
|
||||
title: 'Date Modified',
|
||||
visible: false,
|
||||
width: 96,
|
||||
},
|
||||
{
|
||||
align: 'right',
|
||||
id: 'matches',
|
||||
operator: '-',
|
||||
title: 'Matches',
|
||||
visible: false,
|
||||
width: 96,
|
||||
}
|
||||
];
|
||||
|
||||
self.$toolbar = new Ox.Bar({
|
||||
size: 24
|
||||
});
|
||||
|
||||
self.$findElement = new Ox.FormElementGroup({
|
||||
elements: [
|
||||
self.$findSelect = new Ox.Select({
|
||||
items: [
|
||||
{id: 'all', title: 'Find: All'},
|
||||
{id: 'name', title: 'Find: Name'},
|
||||
{id: 'geoname', title: 'Find: Geoname'},
|
||||
{id: 'country', title: 'Find: Country'}
|
||||
],
|
||||
overlap: 'right',
|
||||
width: 128
|
||||
}),
|
||||
self.$findInput = new Ox.Input({
|
||||
clear: true,
|
||||
width: 192
|
||||
})
|
||||
]
|
||||
})
|
||||
.css({float: 'right', margin: '4px'})
|
||||
.appendTo(self.$toolbar)
|
||||
|
||||
self.$list = new Ox.TextList({
|
||||
columns: self.columns,
|
||||
columnsRemovable: true,
|
||||
columnsVisible: true,
|
||||
items: self.options.places,
|
||||
pageLength: 100,
|
||||
scrollbarVisible: true,
|
||||
sort: [
|
||||
{key: 'name', operator: '+'}
|
||||
]
|
||||
})
|
||||
.bindEvent({
|
||||
'delete': removeItem,
|
||||
init: initList,
|
||||
load: function() {
|
||||
that.triggerEvent('loadlist');
|
||||
},
|
||||
open: openItem,
|
||||
select: selectItem
|
||||
});
|
||||
|
||||
self.$statusbar = new Ox.Bar({
|
||||
size: 24
|
||||
});
|
||||
|
||||
self.$status = new Ox.Element()
|
||||
.css({paddingTop: '4px', margin: 'auto', textAlign: 'center'})
|
||||
.appendTo(self.$statusbar);
|
||||
|
||||
self.mapResize = [
|
||||
Math.round(self.options.width * 0.25),
|
||||
Math.round(self.options.width * 0.5),
|
||||
Math.round(self.options.width * 0.75)
|
||||
];
|
||||
|
||||
if (Ox.isArray(self.options.places)) {
|
||||
init(self.options.places)
|
||||
} else {
|
||||
self.options.places({}, function(result) {
|
||||
Ox.print('$$$$', result.data.items)
|
||||
self.options.places({
|
||||
keys: self.columns.map(function(column) {
|
||||
return column.id
|
||||
}),
|
||||
range: [0, result.data.items]
|
||||
}, function(result) {
|
||||
Ox.print('DATA', result)
|
||||
init(result.data.items);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function init(places) {
|
||||
//Ox.print('PLACES', places)
|
||||
self.$map = new Ox.Map({
|
||||
clickable: true,
|
||||
editable: true,
|
||||
findPlaceholder: 'Find on Map',
|
||||
height: self.options.height,
|
||||
places: places,
|
||||
statusbar: true,
|
||||
toolbar: true,
|
||||
width: self.mapResize[1],
|
||||
zoombar: true
|
||||
})
|
||||
.bindEvent({
|
||||
addplace: function(event, data) {
|
||||
that.triggerEvent('addplace', data);
|
||||
},
|
||||
resize: function() {
|
||||
self.$map.resizeMap(); // fixme: don't need event
|
||||
},
|
||||
selectplace: selectPlace
|
||||
});
|
||||
that.$element.replaceWith(
|
||||
that.$element = new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: new Ox.SplitPanel({
|
||||
elements: [
|
||||
{
|
||||
element: self.$toolbar,
|
||||
size: 24
|
||||
},
|
||||
{
|
||||
element: self.$list
|
||||
},
|
||||
{
|
||||
element: self.$statusbar,
|
||||
size: 24
|
||||
}
|
||||
],
|
||||
orientation: 'vertical'
|
||||
})
|
||||
},
|
||||
{
|
||||
element: self.$map,
|
||||
resizable: true,
|
||||
resize: self.mapResize,
|
||||
size: self.mapResize[1]
|
||||
}
|
||||
],
|
||||
orientation: 'horizontal'
|
||||
}).$element
|
||||
);
|
||||
}
|
||||
|
||||
function initList(event, data) {
|
||||
self.$status.html(data.items + ' place' + (data.items == 1 ? '' : 's'))
|
||||
}
|
||||
|
||||
function openItem(event, data) {
|
||||
selectItem(event, data);
|
||||
self.$map.zoomToPlace(data.ids[0]);
|
||||
}
|
||||
|
||||
function removeItem(event, data) {
|
||||
var id = data.ids[0];
|
||||
that.triggerEvent('removeplace', {id: id});
|
||||
self.$map.removePlace(id);
|
||||
}
|
||||
|
||||
function selectItem(event, data) {
|
||||
Ox.print('selectItem', data.ids[0])
|
||||
var id = data.ids.length ? data.ids[0] : null;
|
||||
self.$map.options({selected: id});
|
||||
id && self.$map.panToPlace();
|
||||
}
|
||||
|
||||
function selectPlace(event, data) {
|
||||
Ox.print('selectPlace', data, data.id)
|
||||
data.id && data.id[0] != '_' && self.$list.options({
|
||||
selected: data.id ? [data.id] : []
|
||||
});
|
||||
}
|
||||
|
||||
function toFixed(val) {
|
||||
return val.toFixed(8);
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
Ox.print('ONCHANGE')
|
||||
if (key == 'height' || key == 'width') {
|
||||
Ox.print('ONCHANGE...')
|
||||
self.$map.options({
|
||||
height: self.options.height,
|
||||
width: self.options.width
|
||||
})
|
||||
} else if (key == 'selected') {
|
||||
self.$list.options({selected: value});
|
||||
}
|
||||
}
|
||||
|
||||
that.focusList = function() {
|
||||
self.$list.gainFocus();
|
||||
return that;
|
||||
}
|
||||
|
||||
that.reloadList = function() {
|
||||
self.$list.reloadList();
|
||||
return that;
|
||||
}
|
||||
|
||||
that.resizeMap = function() {
|
||||
Ox.print('Ox.ListMap.resizeMap()')
|
||||
self.$map.resizeMap();
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
954
source/Ox.UI/js/Map/Ox.Map.js
Normal file
954
source/Ox.UI/js/Map/Ox.Map.js
Normal file
|
|
@ -0,0 +1,954 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
|
||||
Ox.Map = function(options, self) {
|
||||
|
||||
self = self || {};
|
||||
var that = new Ox.Element('div', self)
|
||||
.defaults({
|
||||
// fixme: isClickable? hasZoombar?
|
||||
clickable: false,
|
||||
editable: false,
|
||||
findPlaceholder: 'Find',
|
||||
labels: false,
|
||||
markers: 100,
|
||||
places: [],
|
||||
selected: null,
|
||||
statusbar: false,
|
||||
toolbar: false,
|
||||
zoombar: false
|
||||
})
|
||||
.options(options || {})
|
||||
.addClass('OxMap')
|
||||
.click(function(e) {
|
||||
!$(e.target).is('input') && that.gainFocus();
|
||||
})
|
||||
.bindEvent({
|
||||
key_0: function() {
|
||||
that.panToPlace()
|
||||
},
|
||||
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);
|
||||
},
|
||||
key_meta: 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() {
|
||||
that.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
|
||||
});
|
||||
|
||||
self.mapHeight = getMapHeight();
|
||||
self.minZoom = getMinZoom();
|
||||
self.scaleMeters = [
|
||||
50000000, 20000000, 10000000,
|
||||
5000000, 2000000, 1000000,
|
||||
500000, 200000, 100000,
|
||||
50000, 20000, 10000,
|
||||
5000, 2000, 1000,
|
||||
500, 200, 100,
|
||||
50, 20, 10
|
||||
];
|
||||
|
||||
Ox.extend(self, {
|
||||
metaKey: false,
|
||||
resultPlace: null,
|
||||
shiftKey: false
|
||||
});
|
||||
|
||||
if (self.options.toolbar) {
|
||||
self.$toolbar = new Ox.Bar({
|
||||
size: 24
|
||||
})
|
||||
.appendTo(that);
|
||||
self.$labelsButton = new Ox.Button({
|
||||
title: 'Show Labels',
|
||||
width: 96
|
||||
})
|
||||
.css({float: 'left', margin: '4px'})
|
||||
.bindEvent({
|
||||
click: toggleLabels
|
||||
})
|
||||
.appendTo(self.$toolbar)
|
||||
self.$findInput = new Ox.Input({
|
||||
clear: true,
|
||||
placeholder: self.options.findPlaceholder,
|
||||
width: 192
|
||||
})
|
||||
.css({float: 'right', margin: '4px'})
|
||||
.bindEvent({
|
||||
submit: submitFind
|
||||
})
|
||||
.appendTo(self.$toolbar)
|
||||
}
|
||||
|
||||
self.$map = new Ox.Element('div')
|
||||
.css({
|
||||
left: 0,
|
||||
top: self.options.toolbar * 24 + 'px',
|
||||
right: 0,
|
||||
bottom: self.options.zoombar * 16 + self.options.statusbar * 24 + 'px'
|
||||
})
|
||||
.appendTo(that);
|
||||
|
||||
if (self.options.zoombar) {
|
||||
self.$zoombar = new Ox.Bar({
|
||||
size: 16
|
||||
})
|
||||
.css({
|
||||
bottom: self.options.statusbar * 24 + 'px'
|
||||
})
|
||||
.appendTo(that);
|
||||
}
|
||||
|
||||
if (self.options.statusbar) {
|
||||
self.$statusbar = new Ox.Bar({
|
||||
size: 24
|
||||
})
|
||||
.css({
|
||||
bottom: 0
|
||||
})
|
||||
.appendTo(that);
|
||||
self.$placeNameInput = new Ox.Input({
|
||||
placeholder: 'Name',
|
||||
width: 96
|
||||
})
|
||||
//.css({position: 'absolute', left: 4, top: 4})
|
||||
.css({float: 'left', margin: '4px 1px 4px 4px'})
|
||||
.appendTo(self.$statusbar);
|
||||
self.$placeGeonameInput = new Ox.Input({
|
||||
placeholder: 'Geoname',
|
||||
width: 96
|
||||
})
|
||||
//.css({position: 'absolute', left: 104, top: 4})
|
||||
.css({float: 'left', margin: '4px 1px 4px 4px'})
|
||||
.appendTo(self.$statusbar);
|
||||
self.$placeButton = new Ox.Button({
|
||||
title: 'New Place',
|
||||
width: 96
|
||||
})
|
||||
.css({float: 'right', margin: '4px 4px 4px 2px'})
|
||||
.bindEvent({
|
||||
click: clickPlaceButton
|
||||
})
|
||||
.appendTo(self.$statusbar);
|
||||
}
|
||||
|
||||
self.$navigationButtons = {
|
||||
'center': new Ox.Button({
|
||||
title: 'close',
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxMapButton')
|
||||
.css({
|
||||
left: '24px',
|
||||
top: '24px'
|
||||
}),
|
||||
'east': new Ox.Button({
|
||||
title: 'right',
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxMapButton')
|
||||
.css({
|
||||
left: '44px',
|
||||
top: '24px',
|
||||
}),
|
||||
'north': new Ox.Button({
|
||||
title: 'up',
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxMapButton')
|
||||
.css({
|
||||
left: '24px',
|
||||
top: '4px',
|
||||
}),
|
||||
'south': new Ox.Button({
|
||||
title: 'down',
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxMapButton')
|
||||
.css({
|
||||
left: '24px',
|
||||
top: '44px',
|
||||
}),
|
||||
'west': new Ox.Button({
|
||||
title: 'left',
|
||||
type: 'image'
|
||||
})
|
||||
.addClass('OxMapButton')
|
||||
.css({
|
||||
left: '4px',
|
||||
top: '24px',
|
||||
})
|
||||
};
|
||||
|
||||
self.$scaleLabel = new Ox.Label({
|
||||
textAlign: 'center',
|
||||
title: '...'
|
||||
})
|
||||
.addClass('OxMapLabel')
|
||||
.css({
|
||||
right: '4px',
|
||||
top: '4px'
|
||||
});
|
||||
|
||||
if (!window.googleCallback) {
|
||||
window.googleCallback = function() {
|
||||
delete window.googleCallback;
|
||||
initMap();
|
||||
};
|
||||
$.getScript('http://maps.google.com/maps/api/js?callback=googleCallback&sensor=false');
|
||||
} else {
|
||||
(function interval() {
|
||||
window.google ? initMap() : setTimeout(interval, 100);
|
||||
}());
|
||||
}
|
||||
|
||||
function addPlaceToMap(place) {
|
||||
// via find, click, or new place button
|
||||
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({
|
||||
countryCode: '',
|
||||
editable: true,
|
||||
geoname: '',
|
||||
id: '_' + Ox.uid(), // fixme: stupid
|
||||
lat: center.lat(),
|
||||
lng: center.lng(),
|
||||
map: that,
|
||||
name: '',
|
||||
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() {
|
||||
var place = getSelectedPlace();
|
||||
if (self.options.selected == place.id) {
|
||||
self.options.selected = place.id.substr(1);
|
||||
}
|
||||
place.id = place.id.substr(1); // fixme: NOT SAFE!
|
||||
place.name = self.$placeNameInput.value();
|
||||
place.geoname = self.$placeGeonameInput.value();
|
||||
place.countryCode = Ox.getCountryCode(place.geoname);
|
||||
place.marker.update();
|
||||
self.places.push(place);
|
||||
self.resultPlace = null;
|
||||
that.triggerEvent('addplace', place)
|
||||
Ox.print('SSSS', self.options.selected)
|
||||
}
|
||||
|
||||
function boundsChanged() {
|
||||
setScale();
|
||||
self.boundsChanged = true;
|
||||
}
|
||||
|
||||
function canContain(outerBounds, innerBounds) {
|
||||
var outerSpan = outerBounds.toSpan(),
|
||||
innerSpan = innerBounds.toSpan();
|
||||
return outerSpan.lat() > innerSpan.lat() &&
|
||||
outerSpan.lng() > innerSpan.lng();
|
||||
}
|
||||
|
||||
function centerChanged() {
|
||||
self.center = self.map.getCenter();
|
||||
self.centerChanged = true;
|
||||
}
|
||||
|
||||
function changeZoom(event, data) {
|
||||
self.map.setZoom(data.value);
|
||||
}
|
||||
|
||||
function clickMap(event) {
|
||||
Ox.print('Ox.Map clickMap')
|
||||
if (self.options.clickable/* && !editing()*/) {
|
||||
getPlaceByLatLng(event.latLng, self.map.getBounds(), function(place) {
|
||||
if (place) {
|
||||
addPlaceToMap(place);
|
||||
//selectPlace(place.id);
|
||||
} else {
|
||||
selectPlace(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function clickPlaceButton() {
|
||||
var place = getSelectedPlace(),
|
||||
title = self.$placeButton.options('title');
|
||||
if (title == 'New Place') {
|
||||
addPlaceToMap();
|
||||
} else if (title == 'Add Place') {
|
||||
addPlaceToPlaces();
|
||||
} else if (title == 'Remove Place') {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function constructZoomInput() {
|
||||
Ox.print('constructZoomInput', self.minZoom, self.maxZoom)
|
||||
if (self.options.zoombar) {
|
||||
self.$zoomInput && self.$zoomInput.removeElement();
|
||||
self.$zoomInput = new Ox.Range({
|
||||
arrows: 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 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 getMapHeight() {
|
||||
return self.options.height -
|
||||
self.options.statusbar * 24 -
|
||||
self.options.toolbar * 24 -
|
||||
self.options.zoombar * 16;
|
||||
}
|
||||
|
||||
function getMapType() {
|
||||
return self.options.labels ? '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 getMinZoom() {
|
||||
return 0;
|
||||
return Math.ceil(
|
||||
Ox.log(self.mapHeight / Ox.MAP_TILE_SIZE, 2)
|
||||
);
|
||||
}
|
||||
|
||||
function getPlaceById(id) {
|
||||
var place = Ox.getObjectById(self.places, id);
|
||||
if (!place && self.resultPlace && self.resultPlace.id == id) {
|
||||
place = self.resultPlace;
|
||||
}
|
||||
Ox.print('getPlaceById', id, place)
|
||||
return place;
|
||||
}
|
||||
|
||||
function getPlaceByLatLng(latlng, bounds, callback) {
|
||||
Ox.print('ll b', latlng, bounds)
|
||||
var callback = arguments.length == 3 ? callback : bounds,
|
||||
bounds = arguments.length == 3 ? bounds : null;
|
||||
self.geocoder.geocode({
|
||||
latLng: latlng
|
||||
}, function(results, status) {
|
||||
Ox.print('results', results)
|
||||
var length = results.length;
|
||||
if (status == google.maps.GeocoderStatus.OK) {
|
||||
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
|
||||
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])));
|
||||
}
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
} else {
|
||||
//Ox.print('geocode failed:', status);
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getPlaceByName(name, callback) {
|
||||
self.geocoder.geocode({
|
||||
address: name
|
||||
}, function(results, status) {
|
||||
if (status == google.maps.GeocoderStatus.OK) {
|
||||
if (status != google.maps.GeocoderStatus.ZERO_RESULTS) {
|
||||
Ox.print('GEOCODER RESULT', results[0])
|
||||
callback(new Ox.MapPlace(parseGeodata(results[0])));
|
||||
} else {
|
||||
callback(null);
|
||||
}
|
||||
} else {
|
||||
Ox.print('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() {
|
||||
var mapBounds;
|
||||
|
||||
updateFormElements();
|
||||
|
||||
self.elevationService = new google.maps.ElevationService();
|
||||
self.geocoder = new google.maps.Geocoder();
|
||||
self.maxZoomService = new google.maps.MaxZoomService();
|
||||
self.places = [];
|
||||
self.options.places.forEach(function(place, i) {
|
||||
var bounds = new google.maps.LatLngBounds(
|
||||
new google.maps.LatLng(place.south, place.west),
|
||||
new google.maps.LatLng(place.north, place.east)
|
||||
);
|
||||
if (Ox.isUndefined(place.id)) {
|
||||
place.id = Ox.uid();
|
||||
}
|
||||
mapBounds = i == 0 ? bounds : mapBounds.union(bounds);
|
||||
});
|
||||
self.center = mapBounds ? mapBounds.getCenter() : new google.maps.LatLng(0, 0);
|
||||
self.zoom = 1; // fixme: should depend on height
|
||||
that.map = self.map = new google.maps.Map(self.$map.$element[0], {
|
||||
center: self.center,
|
||||
disableDefaultUI: true,
|
||||
disableDoubleClickZoom: true,
|
||||
mapTypeId: google.maps.MapTypeId[getMapType()],
|
||||
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);
|
||||
mapBounds && self.map.fitBounds(mapBounds);
|
||||
/*
|
||||
setTimeout(function() {
|
||||
}, 1000);
|
||||
*/
|
||||
self.options.places.forEach(function(place, i) {
|
||||
self.places[i] = new Ox.MapPlace(Ox.extend({
|
||||
map: that
|
||||
}, place))/*.add()*/;
|
||||
});
|
||||
google.maps.event.trigger(self.map, 'resize');
|
||||
//that.gainFocus();
|
||||
that.triggerEvent('load');
|
||||
function tilesLoaded() {
|
||||
// fixme: can add earlier, use don't replace map contents option
|
||||
Ox.forEach(self.$navigationButtons, function(button) {
|
||||
button.appendTo(self.$map);
|
||||
});
|
||||
self.$scaleLabel.appendTo(self.$map);
|
||||
}
|
||||
}
|
||||
|
||||
function mapChanged() {
|
||||
// gets called after panning or zooming
|
||||
Ox.print('mapChanged');
|
||||
var bounds;
|
||||
if (self.boundsChanged) {
|
||||
bounds = self.map.getBounds();
|
||||
self.places.sort(function(a, b) {
|
||||
var sort = {
|
||||
a: a.selected ? Infinity :
|
||||
bounds.contains(a.center) ? a.size : -Infinity,
|
||||
b: b.selected ? Infinity :
|
||||
bounds.contains(b.center) ? b.size : -Infinity,
|
||||
};
|
||||
return sort.b - sort.a;
|
||||
}).forEach(function(place, i) {
|
||||
if (i < self.options.markers && !place.visible) {
|
||||
place.add();
|
||||
} else if (i >= self.options.markers && place.visible) {
|
||||
place.remove();
|
||||
}
|
||||
});
|
||||
self.boundsChanged = false;
|
||||
}
|
||||
if (self.centerChanged) {
|
||||
getMaxZoom(function(zoom) {
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
function pan(x, y) {
|
||||
self.map.panBy(x * self.$map.width() / 2, y * self.$map.height() / 2);
|
||||
};
|
||||
|
||||
function parseGeodata(data) {
|
||||
var bounds = data.geometry.bounds || data.geometry.viewport,
|
||||
place = {
|
||||
components: data.address_components,
|
||||
countryCode: getCountryCode(data.address_components),
|
||||
east: bounds.getNorthEast().lng(),
|
||||
editable: self.options.editable,
|
||||
fullGeoname: getFullGeoname(data.address_components),
|
||||
geoname: data.formatted_address,
|
||||
id: '_' + Ox.uid(),
|
||||
map: that,
|
||||
name: data.formatted_address.split(', ')[0],
|
||||
north: bounds.getNorthEast().lat(),
|
||||
south: bounds.getSouthWest().lat(),
|
||||
types: data.types.map(function(type) {
|
||||
return Ox.toTitleCase(type.replace(/_/g, ' '));
|
||||
}),
|
||||
west: bounds.getSouthWest().lng()
|
||||
};
|
||||
function getCountryCode(components) {
|
||||
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(', ')
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
function removePlace(id) {
|
||||
|
||||
}
|
||||
|
||||
function reset() {
|
||||
//Ox.print(self.map.getZoom(), self.zoom);
|
||||
self.map.getZoom() == self.zoom ?
|
||||
self.map.panTo(self.center) :
|
||||
self.map.fitBounds(self.bounds);
|
||||
}
|
||||
|
||||
function resizeMap() {
|
||||
/*
|
||||
Ox.print('resizeMap', self.options.width, self.options.height);
|
||||
var center = self.map.getCenter();
|
||||
self.mapHeight = getMapHeight();
|
||||
self.minZoom = getMinZoom();
|
||||
that.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.$map.css({
|
||||
height: self.mapHeight + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
google.maps.event.trigger(self.map, 'resize');
|
||||
self.map.setCenter(center);
|
||||
*/
|
||||
}
|
||||
|
||||
function selectPlace(id) {
|
||||
var place,
|
||||
selected = getSelectedMarker();
|
||||
Ox.print('Ox.Map selectPlace()', id, selected);
|
||||
if (id != selected) {
|
||||
place = getPlaceById(selected);
|
||||
place && place.deselect();
|
||||
place = getPlaceById(id);
|
||||
place && place.select();
|
||||
self.options.selected = id;
|
||||
setStatus();
|
||||
that.triggerEvent('selectplace', place);
|
||||
}
|
||||
};
|
||||
|
||||
function getMetersPerPixel() {
|
||||
var mapWidth = self.$map.width(),
|
||||
span = self.map.getBounds().toSpan().lng();
|
||||
if (span >= 360) {
|
||||
span = 360 * mapWidth / Ox.MAP_TILE_SIZE;
|
||||
}
|
||||
return span * Ox.getMetersPerDegree(self.map.getCenter().lat()) / mapWidth;
|
||||
}
|
||||
|
||||
function setScale() {
|
||||
var metersPerPixel = getMetersPerPixel();
|
||||
Ox.forEach(self.scaleMeters, function(meters) {
|
||||
var scaleWidth = Math.round(meters / metersPerPixel);
|
||||
if (scaleWidth <= 256) {
|
||||
self.$scaleLabel
|
||||
.options({
|
||||
title: '\u2190 ' + (
|
||||
meters > 1000 ? Ox.formatNumber(meters / 1000) + ' k' : meters + ' '
|
||||
) + 'm \u2192'
|
||||
})
|
||||
.css({
|
||||
width: (scaleWidth - 10) + 'px'
|
||||
})
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setStatus() {
|
||||
Ox.print('setStatus()', self.options.selected)
|
||||
var disabled, place, title;
|
||||
if (self.options.statusbar) {
|
||||
place = getSelectedPlace();
|
||||
if (place) {
|
||||
title = place.id[0] == '_' ? 'Add Place' : 'Remove Place';
|
||||
} else {
|
||||
title = 'New Place';
|
||||
}
|
||||
disabled = place && !place.editable;
|
||||
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.print('STATUS DONE');
|
||||
}
|
||||
|
||||
function submitFind(event, data) {
|
||||
that.findPlace(data.value, function(place) {
|
||||
setStatus(place);
|
||||
});
|
||||
}
|
||||
|
||||
function toggleLabels() {
|
||||
self.options.labels = !self.options.labels
|
||||
self.map.setMapTypeId(google.maps.MapTypeId[getMapType()]);
|
||||
self.$labelsButton.options({
|
||||
title: self.$labelsButton.options('title') == 'Show Labels' ?
|
||||
'Hide Labels' : 'Show Labels'
|
||||
});
|
||||
}
|
||||
|
||||
function undo() {
|
||||
Ox.print('Map undo')
|
||||
var place = getSelectedPlace();
|
||||
place.editing && place.undo();
|
||||
}
|
||||
|
||||
function updateFormElements() {
|
||||
var width = that.width();
|
||||
self.$zoomInput && constructZoomInput();
|
||||
self.$placeNameInput.options({
|
||||
width: Math.floor((width - 112) / 2)
|
||||
});
|
||||
self.$placeGeonameInput.options({
|
||||
width: Math.ceil((width - 112) / 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.options({value: zoom});
|
||||
that.triggerEvent('zoom', {
|
||||
value: zoom
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function zoomToPlace() {
|
||||
Ox.print('zoomToPlace')
|
||||
if (self.options.selected !== null) {
|
||||
self.map.fitBounds(getPlaceById(self.options.selected).bounds);
|
||||
}
|
||||
}
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
/*if (key == 'height' || key == 'width') {
|
||||
resizeMap();
|
||||
} else */if (key == 'places') {
|
||||
loadPlaces();
|
||||
} else if (key == 'selected') {
|
||||
selectPlace(value);
|
||||
} else if (key == 'type') {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
that.getKey = function() {
|
||||
var key = null;
|
||||
if (self.shiftKey) {
|
||||
key = 'shift'
|
||||
} else if (self.metaKey) {
|
||||
key = 'meta'
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
that.editPlace = function() {
|
||||
getPlaceById(self.options.selected).edit();
|
||||
return that;
|
||||
}
|
||||
|
||||
that.findPlace = function(name, callback) {
|
||||
getPlaceByName(name, function(place) {
|
||||
if (place) {
|
||||
addPlaceToMap(place);
|
||||
self.map.fitBounds(place.bounds);
|
||||
} else {
|
||||
self.$findInput.addClass('OxError');
|
||||
}
|
||||
callback(place);
|
||||
});
|
||||
};
|
||||
|
||||
that.panToPlace = function() {
|
||||
Ox.print('panToPlace:', self.options.selected)
|
||||
var place = getSelectedPlace();
|
||||
place && self.map.panTo(place.center);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.removePlace = function(id) {
|
||||
return that;
|
||||
};
|
||||
|
||||
that.resizeMap = function() {
|
||||
|
||||
/*
|
||||
Ox.print('resizeMap', self.options.width, self.options.height);
|
||||
var center = self.map.getCenter();
|
||||
self.mapHeight = getMapHeight();
|
||||
self.minZoom = getMinZoom();
|
||||
that.css({
|
||||
height: self.options.height + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
self.$map.css({
|
||||
height: self.mapHeight + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
google.maps.event.trigger(self.map, 'resize');
|
||||
self.map.setCenter(center);
|
||||
*/
|
||||
|
||||
/*
|
||||
Ox.print('Ox.Map.resizeMap()');
|
||||
var center = self.map.getCenter();
|
||||
self.options.height = that.$element.height();
|
||||
self.options.width = that.$element.width();
|
||||
Ox.print(self.options.width, self.options.height)
|
||||
self.$map.css({
|
||||
height: self.mapHeight + 'px',
|
||||
width: self.options.width + 'px'
|
||||
});
|
||||
google.maps.event.trigger(self.map, 'resize');
|
||||
self.map.setCenter(center);
|
||||
self.options.zoombar && self.$zoomInput.options({
|
||||
size: self.options.width
|
||||
});
|
||||
*/
|
||||
updateFormElements();
|
||||
google.maps.event.trigger(self.map, 'resize');
|
||||
return that;
|
||||
}
|
||||
|
||||
that.zoomToPlace = function() {
|
||||
Ox.print('zoomToPlace')
|
||||
var place = getSelectedPlace();
|
||||
place && self.map.fitBounds(place.bounds);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.zoom = function(value) {
|
||||
self.map.setZoom(value);
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
68
source/Ox.UI/js/Map/Ox.MapImage.js
Normal file
68
source/Ox.UI/js/Map/Ox.MapImage.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.MapImage = function(options, self) {
|
||||
|
||||
/**
|
||||
options
|
||||
height image height (px)
|
||||
places array of either names (''), points ([0, 0]),
|
||||
or objects ({name, point, highlight})
|
||||
type map type ('hybrid', 'roadmap', 'satellite', 'terrain')
|
||||
width image width (px)
|
||||
*/
|
||||
|
||||
var self = self || {},
|
||||
that = new Ox.Element('img', self)
|
||||
.defaults({
|
||||
height: 360,
|
||||
markerColorHighlight: 'yellow',
|
||||
markerColorNormal: 'blue',
|
||||
places: [],
|
||||
type: 'satellite',
|
||||
width: 640
|
||||
})
|
||||
.options(options || {})
|
||||
|
||||
$.extend(self, {
|
||||
markers: {
|
||||
highlight: [],
|
||||
normal: []
|
||||
},
|
||||
src: 'http://maps.google.com/maps/api/staticmap?sensor=false' +
|
||||
'&size=' + self.options.width + 'x' + self.options.height +
|
||||
'&maptype=' + self.options.type
|
||||
});
|
||||
|
||||
if (self.options.places.length) {
|
||||
self.options.places.forEach(function(place) {
|
||||
if (Ox.isString(place)) {
|
||||
self.markers.normal.push(place);
|
||||
} else if (Ox.isArray(place)) {
|
||||
self.markers.normal.push(place.join(','));
|
||||
} else {
|
||||
self.markers[place.highlight ? 'highlight' : 'normal']
|
||||
.push('point' in place ? place.point.join(',') : place.name)
|
||||
}
|
||||
});
|
||||
Ox.forEach(self.markers, function(markers, k) {
|
||||
if (markers.length) {
|
||||
self.src += '&markers=icon:' + 'http://dev.pan.do:8000' +
|
||||
Ox.PATH + 'png/Ox.UI/marker' +
|
||||
Ox.toTitleCase(self.options['markerColor' + Ox.toTitleCase(k)]) +
|
||||
'.png|' + markers.join('|')
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.src += '¢er=0,0&zoom=2'
|
||||
}
|
||||
|
||||
that.attr({
|
||||
src: self.src
|
||||
});
|
||||
|
||||
self.onChange = function(key, value) {
|
||||
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
184
source/Ox.UI/js/Map/Ox.MapMarker.js
Normal file
184
source/Ox.UI/js/Map/Ox.MapMarker.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
|
||||
Ox.MapMarker = function(options) {
|
||||
|
||||
options = Ox.extend({
|
||||
color: [255, 0, 0],
|
||||
map: null,
|
||||
place: null,
|
||||
size: 16
|
||||
}, options);
|
||||
|
||||
var that = this;
|
||||
|
||||
Ox.forEach(options, function(val, key) {
|
||||
that[key] = val;
|
||||
});
|
||||
that.marker = new google.maps.Marker({
|
||||
raiseOnDrag: false,
|
||||
shape: {coords: [8, 8, 8], type: 'circle'},
|
||||
title: that.place.name,
|
||||
//zIndex: 1000
|
||||
});
|
||||
|
||||
setOptions();
|
||||
|
||||
function click() {
|
||||
if (!that.place.selected) {
|
||||
that.map.options({selected: that.place.id});
|
||||
} else if (that.map.getKey() == 'meta') {
|
||||
that.map.options({selected: null});
|
||||
} else if (that.map.getKey() == 'shift') {
|
||||
that.map.zoomToPlace();
|
||||
} else {
|
||||
that.map.panToPlace();
|
||||
}
|
||||
}
|
||||
|
||||
function dragstart(e) {
|
||||
|
||||
}
|
||||
|
||||
function drag(e) {
|
||||
var northSouth = (that.place.north - that.place.south) / 2,
|
||||
lat = Ox.limit(
|
||||
e.latLng.lat(),
|
||||
Ox.MIN_LATITUDE + northSouth,
|
||||
Ox.MAX_LATITUDE - northSouth
|
||||
),
|
||||
lng = e.latLng.lng(),
|
||||
span = Math.min(
|
||||
that.place.sizeEastWest * Ox.getDegreesPerMeter(lat) / 2, 179.99999999
|
||||
);
|
||||
degreesPerMeter = Ox.getDegreesPerMeter(lat);
|
||||
that.place.south += lat - that.place.lat;
|
||||
that.place.north += lat - that.place.lat;
|
||||
that.place.west = lng - span;
|
||||
that.place.east = lng + span;
|
||||
if (that.place.west < -180) {
|
||||
that.place.west += 360;
|
||||
} else if (that.place.east > 180) {
|
||||
that.place.east -= 360;
|
||||
}
|
||||
Ox.print('west', that.place.west, 'east', that.place.east, 'span', span);
|
||||
that.place.update();
|
||||
that.marker.setOptions({
|
||||
position: that.place.center
|
||||
})
|
||||
that.place.rectangle.update();
|
||||
}
|
||||
|
||||
function dragend(e) {
|
||||
|
||||
}
|
||||
|
||||
function getMarkerImage(options, callback) {
|
||||
// unused
|
||||
options = Ox.extend({
|
||||
background: [255, 0, 0],
|
||||
editing: false,
|
||||
result: false,
|
||||
selected: false,
|
||||
size: 16
|
||||
}, options);
|
||||
var background = options.result ? [255, 255, 0] : [255, 0, 0],
|
||||
border = options.editing ? [128, 128, 255] :
|
||||
options.selected ? [255, 255, 255] : [0, 0, 0],
|
||||
c = Ox.canvas(options.size, options.size),
|
||||
image,
|
||||
r = options.size / 2;
|
||||
if (Ox.isArray(background)) {
|
||||
c.context.fillStyle = 'rgba(' + background.join(', ') + ', 0.5)';
|
||||
c.context.arc(r, r, r - 2, 0, 360);
|
||||
c.context.fill();
|
||||
renderImage();
|
||||
} else {
|
||||
image = new Image();
|
||||
image.onload = renderImage;
|
||||
image.src = background;
|
||||
}
|
||||
function renderImage() {
|
||||
//var i;
|
||||
if (Ox.isString(background)) {
|
||||
c.context.drawImage(image, 1, 1, options.size - 2, options.size - 2);
|
||||
/*
|
||||
c.imageData = c.context.getImageData(0, 0, options.size, options.size);
|
||||
c.data = c.imageData.data;
|
||||
for (i = 3; i < c.data.length; i += 1) {
|
||||
c.data[i] = Math.round(c.data[i] * 0.5);
|
||||
}
|
||||
c.context.putImageData(c.imageData, 0, 0);
|
||||
*/
|
||||
}
|
||||
c.context.beginPath();
|
||||
c.context.lineWidth = 2;
|
||||
c.context.strokeStyle = 'rgb(' + border.join(', ') + ')';
|
||||
c.context.arc(r, r, r - 1, 0, 360);
|
||||
c.context.stroke();
|
||||
callback(new google.maps.MarkerImage(
|
||||
c.canvas.toDataURL(),
|
||||
new google.maps.Size(options.size, options.size),
|
||||
new google.maps.Point(0, 0),
|
||||
new google.maps.Point(r, r)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function setOptions() {
|
||||
// workaround to prevent marker from appearing twice
|
||||
// after setting draggable from true to false (google maps bug)
|
||||
var fix = that.marker.getDraggable() && !that.place.editing;
|
||||
//Ox.print('setOptions, that.color', that.color)
|
||||
that.marker.setOptions({
|
||||
// fixme: cursor remains pointer
|
||||
cursor: that.place.editing ? 'move' : 'pointer',
|
||||
draggable: that.place.editing,
|
||||
icon: Ox.MapMarkerImage({
|
||||
color: that.color,
|
||||
mode: that.place.editing ? 'editing' :
|
||||
that.place.selected ? 'selected' : 'normal',
|
||||
size: that.size,
|
||||
type: that.place.id[0] == '_' ? 'result' : 'place'
|
||||
}),
|
||||
position: that.place.center
|
||||
});
|
||||
if (fix) {
|
||||
that.marker.setVisible(false);
|
||||
setTimeout(function() {
|
||||
that.marker.setVisible(true);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
that.add = function() {
|
||||
that.marker.setMap(that.map.map);
|
||||
google.maps.event.addListener(that.marker, 'click', click);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.edit = function() {
|
||||
setOptions();
|
||||
google.maps.event.addListener(that.marker, 'dragstart', dragstart);
|
||||
google.maps.event.addListener(that.marker, 'drag', drag);
|
||||
google.maps.event.addListener(that.marker, 'dragend', dragend);
|
||||
};
|
||||
|
||||
that.remove = function() {
|
||||
that.marker.setMap(null);
|
||||
google.maps.event.clearListeners(that.marker);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.submit = function() {
|
||||
google.maps.event.clearListeners(that.marker, 'dragstart');
|
||||
google.maps.event.clearListeners(that.marker, 'drag');
|
||||
google.maps.event.clearListeners(that.marker, 'dragend');
|
||||
}
|
||||
|
||||
that.update = function() {
|
||||
setOptions();
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
49
source/Ox.UI/js/Map/Ox.MapMarkerImage.js
Normal file
49
source/Ox.UI/js/Map/Ox.MapMarkerImage.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
|
||||
Ox.MapMarkerImage = (function() {
|
||||
|
||||
var cache = {};
|
||||
|
||||
return function(options) {
|
||||
|
||||
options = Ox.extend({
|
||||
color: [255, 0, 0],
|
||||
mode: 'normal', // normal, selected, editing
|
||||
size: 16,
|
||||
type: 'place', // place, result, rectangle
|
||||
}, options);
|
||||
|
||||
var index = [
|
||||
options.type, options.mode, options.size, options.color.join(',')
|
||||
].join(';');
|
||||
|
||||
if (!cache[index]) {
|
||||
var color = options.type == 'place' ?
|
||||
Ox.merge(Ox.clone(options.color), [0.5]) : [0, 0, 0, 0],
|
||||
border = options.mode == 'normal' ? [0, 0, 0] :
|
||||
options.mode == 'selected' ? [255, 255, 255] : [128, 128, 255],
|
||||
c = Ox.canvas(options.size, options.size),
|
||||
image,
|
||||
r = options.size / 2;
|
||||
c.context.fillStyle = 'rgba(' + color.join(', ') + ')';
|
||||
c.context.arc(r, r, r - 2, 0, 360);
|
||||
c.context.fill();
|
||||
c.context.beginPath();
|
||||
c.context.lineWidth = 2;
|
||||
c.context.strokeStyle = 'rgb(' + border.join(', ') + ')';
|
||||
c.context.arc(r, r, r - 1, 0, 360);
|
||||
c.context.stroke();
|
||||
cache[index] = new google.maps.MarkerImage(
|
||||
c.canvas.toDataURL(),
|
||||
new google.maps.Size(options.size, options.size),
|
||||
new google.maps.Point(0, 0),
|
||||
new google.maps.Point(r, r)
|
||||
);
|
||||
//Ox.print(options, 'index', index)
|
||||
}
|
||||
|
||||
return cache[index];
|
||||
|
||||
}
|
||||
|
||||
}());
|
||||
160
source/Ox.UI/js/Map/Ox.MapPlace.js
Normal file
160
source/Ox.UI/js/Map/Ox.MapPlace.js
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
|
||||
Ox.MapPlace = function(options) {
|
||||
|
||||
options = Ox.extend({
|
||||
east: 0,
|
||||
editing: false,
|
||||
geoname: '',
|
||||
map: null,
|
||||
markerColor: [255, 0, 0],
|
||||
markerSize: 16,
|
||||
name: '',
|
||||
north: 0,
|
||||
selected: false,
|
||||
south: 0,
|
||||
type: [],
|
||||
visible: false,
|
||||
west: 0
|
||||
}, options);
|
||||
|
||||
var that = this;
|
||||
|
||||
Ox.forEach(options, function(val, key) {
|
||||
that[key] = val;
|
||||
});
|
||||
|
||||
update();
|
||||
|
||||
function update() {
|
||||
that.points = {
|
||||
ne: new google.maps.LatLng(that.north, that.east),
|
||||
sw: new google.maps.LatLng(that.south, that.west)
|
||||
};
|
||||
that.bounds = new google.maps.LatLngBounds(that.points.sw, that.points.ne);
|
||||
that.center = that.bounds.getCenter();
|
||||
that.lat = that.center.lat();
|
||||
that.lng = that.center.lng();
|
||||
Ox.extend(that.points, {
|
||||
e: new google.maps.LatLng(that.lat, that.east),
|
||||
s: new google.maps.LatLng(that.south, that.lng),
|
||||
se: new google.maps.LatLng(that.south, that.east),
|
||||
n: new google.maps.LatLng(that.north, that.lng),
|
||||
nw: new google.maps.LatLng(that.north, that.west),
|
||||
w: new google.maps.LatLng(that.lat, that.west),
|
||||
});
|
||||
// fixme: use bounds.toSpan()
|
||||
that.sizeNorthSouth = (that.north - that.south) *
|
||||
Ox.EARTH_CIRCUMFERENCE / 360;
|
||||
that.sizeEastWest = (that.east + (that.west > that.east ? 360 : 0) - that.west) *
|
||||
Ox.getMetersPerDegree(that.lat);
|
||||
that.size = Ox.getArea(
|
||||
{lat: that.south, lng: that.west},
|
||||
{lat: that.north, lng: that.east}
|
||||
);
|
||||
if (!that.marker) {
|
||||
that.marker = new Ox.MapMarker({
|
||||
color: that.markerColor,
|
||||
map: that.map,
|
||||
place: that,
|
||||
size: that.markerSize
|
||||
});
|
||||
that.rectangle = new Ox.MapRectangle({
|
||||
map: that.map,
|
||||
place: that
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function editable() {
|
||||
return that.map.options('editable') && that.editable;
|
||||
}
|
||||
|
||||
that.add = function() {
|
||||
that.visible = true;
|
||||
that.marker.add();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.cancel = function() {
|
||||
if (editable()) {
|
||||
that.undo();
|
||||
that.editing = false;
|
||||
that.marker.update();
|
||||
that.rectangle.deselect();
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
that.crossesDateline = function() {
|
||||
return that.west > that.east;
|
||||
}
|
||||
|
||||
that.deselect = function() {
|
||||
that.editing && that.submit();
|
||||
that.selected = false;
|
||||
that.marker.update();
|
||||
that.rectangle.remove();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.edit = function() {
|
||||
if (editable()) {
|
||||
that.editing = true;
|
||||
that.original = {
|
||||
south: that.south,
|
||||
west: that.west,
|
||||
north: that.north,
|
||||
east: that.east
|
||||
};
|
||||
that.marker.edit();
|
||||
that.rectangle.select();
|
||||
}
|
||||
return that;
|
||||
}
|
||||
|
||||
that.remove = function() {
|
||||
that.editing && that.submit();
|
||||
that.selected && that.deselect();
|
||||
that.visible = false;
|
||||
that.marker.remove();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.select = function() {
|
||||
that.selected = true;
|
||||
!that.visible && that.add();
|
||||
that.marker.update();
|
||||
that.rectangle.add();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.submit = function() {
|
||||
if (editable()) {
|
||||
that.editing = false;
|
||||
that.marker.update();
|
||||
that.rectangle.deselect();
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
that.update = function() {
|
||||
update();
|
||||
return that;
|
||||
};
|
||||
|
||||
that.undo = function() {
|
||||
if (editable()) {
|
||||
Ox.forEach(that.original, function(v, k) {
|
||||
that[k] = v;
|
||||
});
|
||||
that.update();
|
||||
that.marker.update();
|
||||
that.rectangle.update();
|
||||
}
|
||||
return that;
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
89
source/Ox.UI/js/Map/Ox.MapRectangle.js
Normal file
89
source/Ox.UI/js/Map/Ox.MapRectangle.js
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
Ox.MapRectangle = function(options, self) {
|
||||
|
||||
var options = Ox.extend({
|
||||
map: null,
|
||||
place: null
|
||||
}, options),
|
||||
that = this;
|
||||
|
||||
Ox.forEach(options, function(val, key) {
|
||||
that[key] = val;
|
||||
});
|
||||
|
||||
that.rectangle = new google.maps.Rectangle({
|
||||
clickable: true,
|
||||
bounds: that.place.bounds,
|
||||
});
|
||||
that.markers = Ox.map(that.place.points, function(point, position) {
|
||||
return new Ox.MapRectangleMarker({
|
||||
map: that.map,
|
||||
place: that.place,
|
||||
position: position
|
||||
});
|
||||
});
|
||||
|
||||
setOptions();
|
||||
|
||||
function click() {
|
||||
if (that.map.options('editable') && that.place.editable && !that.place.editing) {
|
||||
that.place.edit();
|
||||
} else if (that.map.getKey() == 'meta') {
|
||||
that.place.submit();
|
||||
} else if (that.map.getKey() == 'shift') {
|
||||
that.map.zoomToPlace();
|
||||
} else {
|
||||
that.map.panToPlace();
|
||||
}
|
||||
}
|
||||
|
||||
function setOptions() {
|
||||
var color = that.place.editing ? '#8080FF' : '#FFFFFF';
|
||||
that.rectangle.setOptions({
|
||||
fillColor: color,
|
||||
fillOpacity: that.place.editing ? 0.1 : 0,
|
||||
strokeColor: color,
|
||||
strokeOpacity: 1,
|
||||
strokeWeight: 2
|
||||
})
|
||||
}
|
||||
|
||||
that.add = function() {
|
||||
that.rectangle.setMap(that.map.map);
|
||||
google.maps.event.addListener(that.rectangle, 'click', click);
|
||||
return that;
|
||||
};
|
||||
|
||||
that.deselect = function() {
|
||||
setOptions();
|
||||
Ox.print('MARKERS', that.markers)
|
||||
Ox.forEach(that.markers, function(marker) {
|
||||
marker.remove();
|
||||
});
|
||||
};
|
||||
|
||||
that.remove = function() {
|
||||
that.rectangle.setMap(null);
|
||||
google.maps.event.clearListeners(that.rectangle);
|
||||
return that
|
||||
}
|
||||
|
||||
that.select = function() {
|
||||
setOptions();
|
||||
Ox.forEach(that.markers, function(marker) {
|
||||
marker.add();
|
||||
});
|
||||
};
|
||||
|
||||
that.update = function() {
|
||||
that.rectangle.setOptions({
|
||||
bounds: that.place.bounds
|
||||
});
|
||||
Ox.forEach(that.markers, function(marker) {
|
||||
marker.update();
|
||||
});
|
||||
}
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
96
source/Ox.UI/js/Map/Ox.MapRectangleMarker.js
Normal file
96
source/Ox.UI/js/Map/Ox.MapRectangleMarker.js
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// vim: et:ts=4:sw=4:sts=4:ft=js
|
||||
|
||||
Ox.MapRectangleMarker = function(options, self) {
|
||||
|
||||
options = Ox.extend({
|
||||
map: null,
|
||||
place: null,
|
||||
position: ''
|
||||
}, options);
|
||||
|
||||
var that = this;
|
||||
|
||||
Ox.forEach(options, function(val, key) {
|
||||
that[key] = val;
|
||||
});
|
||||
|
||||
that.markerImage = new google.maps.MarkerImage
|
||||
that.marker = new google.maps.Marker({
|
||||
cursor: that.position + '-resize',
|
||||
draggable: true,
|
||||
icon: Ox.MapMarkerImage({
|
||||
mode: 'editing',
|
||||
type: 'rectangle'
|
||||
}),
|
||||
position: that.place.points[that.position],
|
||||
raiseOnDrag: false
|
||||
});
|
||||
|
||||
function dragstart(e) {
|
||||
that.drag = {
|
||||
lat: e.latLng.lat(),
|
||||
lng: e.latLng.lng()
|
||||
};
|
||||
}
|
||||
|
||||
function drag(e) {
|
||||
// fixme: implement shift+drag (center stays the same)
|
||||
Ox.print(e.pixel.x, e.pixel.y)
|
||||
var lat = Ox.limit(e.latLng.lat(), Ox.MIN_LATITUDE, Ox.MAX_LATITUDE),
|
||||
lng = e.latLng.lng();
|
||||
that.drag = {
|
||||
lat: lat,
|
||||
lng: lng
|
||||
};
|
||||
if (that.position.indexOf('s') > -1) {
|
||||
that.place.south = lat;
|
||||
}
|
||||
if (that.position.indexOf('n') > -1) {
|
||||
that.place.north = lat;
|
||||
}
|
||||
if (that.position.indexOf('w') > -1) {
|
||||
that.place.west = lng;
|
||||
}
|
||||
if (that.position.indexOf('e') > -1) {
|
||||
that.place.east = lng;
|
||||
}
|
||||
Ox.print('west', that.place.west, 'east', that.place.east);
|
||||
Ox.print('south', that.place.south, 'north', that.place.north);
|
||||
that.place.update();
|
||||
that.place.marker.update();
|
||||
that.place.rectangle.update();
|
||||
}
|
||||
|
||||
function dragend(e) {
|
||||
var south;
|
||||
if (that.place.south > that.place.north) {
|
||||
south = that.place.south;
|
||||
that.place.south = that.place.north;
|
||||
that.place.north = south;
|
||||
that.place.update();
|
||||
that.place.marker.update();
|
||||
that.place.rectangle.update();
|
||||
}
|
||||
}
|
||||
|
||||
that.add = function() {
|
||||
that.marker.setMap(that.map.map);
|
||||
google.maps.event.addListener(that.marker, 'dragstart', dragstart);
|
||||
google.maps.event.addListener(that.marker, 'drag', drag);
|
||||
google.maps.event.addListener(that.marker, 'dragend', dragend);
|
||||
};
|
||||
|
||||
that.remove = function() {
|
||||
that.marker.setMap(null);
|
||||
google.maps.event.clearListeners(that.marker);
|
||||
};
|
||||
|
||||
that.update = function() {
|
||||
that.marker.setOptions({
|
||||
position: that.place.points[that.position]
|
||||
});
|
||||
};
|
||||
|
||||
return that;
|
||||
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue