350 lines
11 KiB
JavaScript
350 lines
11 KiB
JavaScript
'use strict';
|
|
|
|
/*@
|
|
Ox.MapMarker <f> MapMarker
|
|
(options) -> <o> MapMarker object
|
|
options <o> Options object
|
|
color <a|[255, 0, 0]> marker color
|
|
map <o|null> map
|
|
place <o|null> place
|
|
size <n|16> size
|
|
@*/
|
|
|
|
Ox.MapMarker = function(options) {
|
|
|
|
options = Ox.extend({
|
|
map: null,
|
|
place: null
|
|
}, options);
|
|
|
|
var that = this,
|
|
areaSize = {
|
|
100: 10, // 10 x 10 m
|
|
10000: 12, // 100 x 100 m
|
|
1000000: 14, // 1 x 1 km
|
|
100000000: 16, // 10 x 10 km
|
|
10000000000: 18, // 100 x 100 km
|
|
1000000000000: 20, // 1,000 x 1,000 km
|
|
100000000000000: 22 // 10,000 x 10,000 km
|
|
},
|
|
themeData = Ox.Theme.getThemeData(),
|
|
typeColor = {};
|
|
|
|
[
|
|
'country', 'region', 'city', 'borough',
|
|
'street', 'building', 'feature'
|
|
].forEach(function(type) {
|
|
typeColor[type] = themeData[
|
|
'mapPlace' + Ox.toTitleCase(type) + 'Color'
|
|
];
|
|
});
|
|
|
|
Ox.forEach(options, function(val, key) {
|
|
that[key] = val;
|
|
});
|
|
setColor()
|
|
setSize()
|
|
const element = document.createElement('div')
|
|
element.style.border = '2px solid black'
|
|
element.style.borderRadius = '50px'
|
|
element.style.backgroundColor = '#' + Ox.toHex(that.color)
|
|
element.style.width = element.style.height = that.size + 'px'
|
|
that.marker = new maplibregl.Marker({
|
|
raiseOnDrag: false,
|
|
element: element,
|
|
//shape: {coords: [8, 8, 8], type: 'circle'},
|
|
//title: that.place.name,
|
|
//zIndex: 1000
|
|
});
|
|
that.tooltip = new maplibregl.Popup({
|
|
closeButton: false,
|
|
closeOnClick: false,
|
|
className: 'tooltip'
|
|
});
|
|
that.tooltip.addClass
|
|
|
|
setOptions();
|
|
|
|
function click(event) {
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
var key = that.map.getKey(),
|
|
place, bounds, southWest, northEast;
|
|
if (!that.place.selected) {
|
|
if (
|
|
that.map.options('editable')
|
|
&& (key == 'meta' || key == 'shift')
|
|
) {
|
|
place = that.map.getSelectedPlace();
|
|
}
|
|
if (place) {
|
|
bounds = new maplibregl.LngLatBounds(
|
|
new maplibregl.LngLatBounds(place.west, place.south),
|
|
new maplibregl.LngLatBounds(place.east, place.north)
|
|
);
|
|
bounds = bounds.union(that.place.bounds);
|
|
southWest = bounds.getSouthWest();
|
|
northEast = bounds.getNorthEast();
|
|
that.map.newPlace(new Ox.MapPlace({
|
|
// fixme: duplicated, see Ox.Map.js
|
|
alternativeNames: [],
|
|
countryCode: '',
|
|
editable: true,
|
|
geoname: '',
|
|
id: '_' + Ox.encodeBase32(Ox.uid()), // fixme: stupid
|
|
map: that.map,
|
|
name: '',
|
|
type: 'feature',
|
|
south: southWest.lat,
|
|
west: southWest.lng,
|
|
north: northEast.lat,
|
|
east: northEast.lng
|
|
}));
|
|
} else {
|
|
that.map.options({selected: that.place.id});
|
|
}
|
|
} else {
|
|
if (key == 'meta') {
|
|
that.map.options({selected: null});
|
|
} else {
|
|
that.map.panToPlace();
|
|
}
|
|
}
|
|
}
|
|
|
|
function dblclick() {
|
|
that.place.selected && that.map.zoomToPlace();
|
|
}
|
|
|
|
function dragstart(e) {
|
|
Ox.$body.addClass('OxDragging');
|
|
}
|
|
|
|
function drag(e) {
|
|
var northSouth = (that.place.north - that.place.south) / 2,
|
|
lat = Ox.limit(
|
|
e.lngLat.lat,
|
|
Ox.MIN_LATITUDE + northSouth,
|
|
Ox.MAX_LATITUDE - northSouth
|
|
),
|
|
lng = e.lngLat.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.Log('Map', 'west', that.place.west, 'east', that.place.east, 'span', span);
|
|
that.place.update();
|
|
that.marker.setLngLat(that.place.center)
|
|
that.place.rectangle.update();
|
|
}
|
|
|
|
function dragend(e) {
|
|
Ox.$body.removeClass('OxDragging');
|
|
that.map.triggerEvent('changeplaceend', that.place);
|
|
}
|
|
|
|
function getMarkerImage(options, callback) {
|
|
// fixme: 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 maplibregl.MarkerImage(
|
|
c.canvas.toDataURL(),
|
|
new maplibregl.Size(options.size, options.size),
|
|
new maplibregl.Point(0, 0),
|
|
new maplibregl.Point(r, r)
|
|
));
|
|
}
|
|
}
|
|
|
|
function mouseover(e) {
|
|
that.tooltip.setLngLat(that.place.center).setHTML(
|
|
'<div style="display: flex; gap: 8px; margin: 2px 2px"><img src="' + Ox.getFlagByGeoname(that.place.geoname, 16)
|
|
+ '" style="border-radius: 4px;margin: auto"/>'
|
|
+ '<div style="font-size: 9px;">'
|
|
+ that.map.options('markerTooltip')(that.place) + '</div></div>'
|
|
).addTo(that.map.map);
|
|
}
|
|
|
|
function mouseout() {
|
|
that.tooltip.remove();
|
|
}
|
|
|
|
function setColor() {
|
|
var color = that.map.options('markerColor');
|
|
//Ox.Log('Map', 'setOptions, that.map: ', that.map)
|
|
if (color == 'auto') {
|
|
that.color = typeColor[that.place.type] || typeColor['mapPlaceFeatureColor'];
|
|
} else if (Ox.isArray(color)) {
|
|
that.color = color;
|
|
} else {
|
|
that.color = color(that.place);
|
|
}
|
|
}
|
|
|
|
function setSize() {
|
|
var size = that.map.options('markerSize');
|
|
if (size == 'auto') {
|
|
that.size = 8;
|
|
Ox.forEach(areaSize, function(size, area) {
|
|
if (that.place.area >= area) {
|
|
that.size = size;
|
|
} else {
|
|
return false; // break
|
|
}
|
|
});
|
|
} else if (Ox.isNumber(size)) {
|
|
that.size = size;
|
|
} else {
|
|
that.size = size(that.place);
|
|
}
|
|
}
|
|
|
|
function setOptions() {
|
|
// workaround to prevent marker from appearing twice
|
|
// after setting draggable from true to false (google maps bug)
|
|
var fix = false, // that.marker.getDraggable() && !that.place.editing,
|
|
size = that.map.options('markerSize');
|
|
//Ox.Log('Map', 'setOptions, that.map: ', that.map)
|
|
setColor()
|
|
setSize()
|
|
/* fixme, some of those can be set some not
|
|
that.marker.setOptions({
|
|
// fixme: cursor remains pointer
|
|
cursor: that.place.editing ? 'move' : 'pointer',
|
|
draggable: that.place.editing,
|
|
element: 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'
|
|
}),
|
|
})
|
|
*/
|
|
//that.marker._color = that.color;
|
|
that.marker._element.style.cursor = that.place.editing ? 'move' : 'pointer';
|
|
that.marker._element.height = that.marker._element.width = that.size + 'px'
|
|
that.marker.setDraggable(that.place.editing);
|
|
that.marker.setLngLat(that.place.center);
|
|
if (fix) {
|
|
that.marker.setVisible(false);
|
|
setTimeout(function() {
|
|
that.marker.setVisible(true);
|
|
}, 0);
|
|
}
|
|
}
|
|
|
|
|
|
/*@
|
|
add <f> add to map
|
|
() -> <f> add to map, returns MapMarker
|
|
@*/
|
|
that.add = function() {
|
|
that.marker.addTo(that.map.map);
|
|
const element = that.marker.getElement()
|
|
if(element) {
|
|
element.addEventListener('click', click)
|
|
element.addEventListener('dblclick', dblclick)
|
|
element.addEventListener('mouseover', mouseover)
|
|
element.addEventListener('mouseout', mouseout)
|
|
}
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
edit <f> edit marker
|
|
() -> <f> edit marker, returns MapMarker
|
|
@*/
|
|
that.edit = function() {
|
|
setOptions();
|
|
that.marker.on('dragstart', dragstart);
|
|
that.marker.on('drag', drag);
|
|
that.marker.on('dragend', dragend);
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
remove <f> remove marker
|
|
() -> <f> remove marker from map, returns MapMarker
|
|
@*/
|
|
that.remove = function() {
|
|
that.marker.remove();
|
|
//that.marker.off('dragstart');
|
|
//that.marker.off('drag');
|
|
//that.marker.off('dragend');
|
|
//fixme does this work to remove all events?
|
|
that.marker.off();
|
|
return that;
|
|
};
|
|
|
|
/*@
|
|
submit <f> submit marker
|
|
() -> <f> clear edit listeners, returns MapMarker
|
|
@*/
|
|
that.submit = function() {
|
|
that.marker.off('dragstart');
|
|
that.marker.off('drag');
|
|
that.marker.off('dragend');
|
|
return that;
|
|
}
|
|
|
|
/*@
|
|
update <f> update marker
|
|
() -> <f> update marker, returns MapMarker
|
|
@*/
|
|
that.update = function() {
|
|
setOptions();
|
|
return that;
|
|
}
|
|
|
|
return that;
|
|
|
|
};
|