Compare commits
26 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29df9f1760 | |||
| ef24247a36 | |||
| 529277eb12 | |||
| 95b3a4be6e | |||
| 3c482989d1 | |||
| e274d88c2b | |||
| bbc8debf46 | |||
| f5d15ce75f | |||
| c4591c3a0e | |||
|
|
905258c962 | ||
| d737817528 | |||
|
|
a0b1e0eab4 | ||
| 80d3919b46 | |||
| 1bac02d24f | |||
| 56123e4575 | |||
| bc174c49dd | |||
| f3e0632f0e | |||
| bcade5f2d7 | |||
| 8dbfcc0ece | |||
| 6a59ff5193 | |||
| e917bce40d | |||
| 66fca1fc02 | |||
| 8cebad9fb4 | |||
| f3b8025e8e | |||
| 25cd3f6bb1 | |||
| 76b9517a78 |
16 changed files with 1180 additions and 274 deletions
13
examples/maps/map_editor/index.html
Normal file
13
examples/maps/map_editor/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>World Map with Countries</title>
|
||||||
|
<meta http-equiv="Keywords" content="Lists"/>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
|
<link rel="shortcut icon" type="image/png" href="../../../source/UI/themes/oxlight/png/icon16.png"/>
|
||||||
|
<script type="text/javascript" src="../../../dev/Ox.js"></script>
|
||||||
|
<script type="text/javascript" src="js/example.js"></script>
|
||||||
|
<script>window.addEventListener('message', function(e) { e.origin == window.location.origin && eval('(' + e.data + ')'); });</script>
|
||||||
|
</head>
|
||||||
|
<body></body>
|
||||||
|
</html>
|
||||||
88
examples/maps/map_editor/js/example.js
Normal file
88
examples/maps/map_editor/js/example.js
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
In this example, we use Ox.MapEditor
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
Ox.load(['UI', 'Geo'], function() {
|
||||||
|
var $storage = Ox.localStorage("map_editor")
|
||||||
|
var places = $storage('places') || []
|
||||||
|
var placesAPI = Ox.api(places, {
|
||||||
|
geo: true,
|
||||||
|
sort: '-area',
|
||||||
|
cache: true,
|
||||||
|
})
|
||||||
|
var $map = Ox.MapEditor({
|
||||||
|
addPlace: function(place, callback) {
|
||||||
|
place = Ox.clone(place)
|
||||||
|
place.id = Ox.encodeBase26((places.length ? Ox.max(places.map(p => Ox.decodeBase26(p.id))) : 0) + 1)
|
||||||
|
place.editable = true;
|
||||||
|
console.log("addPlace", place.id, place)
|
||||||
|
places.push(place)
|
||||||
|
$storage("places", places)
|
||||||
|
placesAPI.update(places)
|
||||||
|
setTimeout(() => {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
callback({
|
||||||
|
status: {
|
||||||
|
code: 200
|
||||||
|
},
|
||||||
|
data: place
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
editPlace: function(place, callback) {
|
||||||
|
place = Ox.clone(place)
|
||||||
|
places.forEach(p => {
|
||||||
|
if (p.id == place.id) {
|
||||||
|
Object.assign(p, place);
|
||||||
|
place = p
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$storage("places", places)
|
||||||
|
placesAPI.update(places)
|
||||||
|
setTimeout(() => {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
callback({
|
||||||
|
status: {
|
||||||
|
code: 200
|
||||||
|
},
|
||||||
|
data: place
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
getMatches: function(names, callback) {
|
||||||
|
console.log("getMatches", names)
|
||||||
|
callback(23);
|
||||||
|
},
|
||||||
|
hasMatches: true, // FIXME: getMatches is enough
|
||||||
|
height: 800,
|
||||||
|
mode: 'add', // 'define',
|
||||||
|
names: null,
|
||||||
|
places: placesAPI,
|
||||||
|
removePlace: function(place, callback) {
|
||||||
|
console.log("removePlace", place.id, places)
|
||||||
|
places = places.filter(p => { return p.id != place.id })
|
||||||
|
console.log("new places", places)
|
||||||
|
$storage("places", places)
|
||||||
|
placesAPI.update(places)
|
||||||
|
setTimeout(() => {
|
||||||
|
Ox.Request.clearCache();
|
||||||
|
callback({
|
||||||
|
status: {
|
||||||
|
code: 200
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}, 200)
|
||||||
|
},
|
||||||
|
selected: '',
|
||||||
|
showControls: false,
|
||||||
|
showLabels: false,
|
||||||
|
showTypes: true,
|
||||||
|
width: 600
|
||||||
|
})
|
||||||
|
.appendTo(Ox.$body);
|
||||||
|
|
||||||
|
Ox.$window.bind({resize: $map.resizeMap});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
<meta http-equiv="Keywords" content="Lists"/>
|
<meta http-equiv="Keywords" content="Lists"/>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
|
||||||
<link rel="shortcut icon" type="image/png" href="../../../source/UI/themes/oxlight/png/icon16.png"/>
|
<link rel="shortcut icon" type="image/png" href="../../../source/UI/themes/oxlight/png/icon16.png"/>
|
||||||
<script type="text/javascript" src="../../../min/Ox.js"></script>
|
<script type="text/javascript" src="../../../dev/Ox.js"></script>
|
||||||
<script type="text/javascript" src="js/example.js"></script>
|
<script type="text/javascript" src="js/example.js"></script>
|
||||||
<script>window.addEventListener('message', function(e) { e.origin == window.location.origin && eval('(' + e.data + ')'); });</script>
|
<script>window.addEventListener('message', function(e) { e.origin == window.location.origin && eval('(' + e.data + ')'); });</script>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body></body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1800,6 +1800,7 @@ Maps
|
||||||
.OxMap .OxLabel.OxMapControl.OxMapScale {
|
.OxMap .OxLabel.OxMapControl.OxMapScale {
|
||||||
right: 4px;
|
right: 4px;
|
||||||
bottom: 19px;
|
bottom: 19px;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.OxMap .OxPlaceControl.OxPlaceFlag {
|
.OxMap .OxPlaceControl.OxPlaceFlag {
|
||||||
|
|
@ -1821,6 +1822,7 @@ Maps
|
||||||
top: 4px;
|
top: 4px;
|
||||||
width: 136px;
|
width: 136px;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
line-height: 1;
|
||||||
}
|
}
|
||||||
.OxMap .OxPlaceControl.OxPlaceDeselectButton {
|
.OxMap .OxPlaceControl.OxPlaceDeselectButton {
|
||||||
right: 4px;
|
right: 4px;
|
||||||
|
|
@ -2969,3 +2971,20 @@ Miscellaneous
|
||||||
.OxTooltip > div {
|
.OxTooltip > div {
|
||||||
font-size: 9px;
|
font-size: 9px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
================================================================================
|
||||||
|
MapLibre GL cleanups
|
||||||
|
================================================================================
|
||||||
|
*/
|
||||||
|
|
||||||
|
.maplibregl-popup-anchor-bottom .maplibregl-popup-tip {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-popup-content {
|
||||||
|
padding: 0px !important;
|
||||||
|
border-radius: 5px;
|
||||||
|
top: 50px;
|
||||||
|
left: -4px;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ Ox.Map = function(options, self) {
|
||||||
return place.name || '<span class="OxLight">Unnamed</span>';
|
return place.name || '<span class="OxLight">Unnamed</span>';
|
||||||
},
|
},
|
||||||
maxMarkers: 100,
|
maxMarkers: 100,
|
||||||
|
nominatim: 'https://nominatim.openstreetmap.org',
|
||||||
places: null,
|
places: null,
|
||||||
selected: '',
|
selected: '',
|
||||||
showControls: false,
|
showControls: false,
|
||||||
|
|
@ -106,6 +107,7 @@ Ox.Map = function(options, self) {
|
||||||
showStatusbar: false,
|
showStatusbar: false,
|
||||||
showToolbar: false,
|
showToolbar: false,
|
||||||
showZoombar: false,
|
showZoombar: false,
|
||||||
|
style: 'https://tiles.openfreemap.org/styles/liberty',
|
||||||
zoomOnlyWhenFocused: false
|
zoomOnlyWhenFocused: false
|
||||||
// fixme: width, height
|
// fixme: width, height
|
||||||
})
|
})
|
||||||
|
|
@ -143,7 +145,7 @@ Ox.Map = function(options, self) {
|
||||||
self.map.fitBounds(mapBounds);
|
self.map.fitBounds(mapBounds);
|
||||||
} else {
|
} else {
|
||||||
self.map.setZoom(self.minZoom);
|
self.map.setZoom(self.minZoom);
|
||||||
self.map.setCenter(new google.maps.LatLng(0, 0));
|
self.map.setCenter(new maplibregl.LngLat(0, 0));
|
||||||
}
|
}
|
||||||
// fixme: the following is just a guess
|
// fixme: the following is just a guess
|
||||||
self.boundsChanged = true;
|
self.boundsChanged = true;
|
||||||
|
|
@ -509,23 +511,27 @@ Ox.Map = function(options, self) {
|
||||||
$placeControl.css({opacity: 0}).hide();
|
$placeControl.css({opacity: 0}).hide();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (window.google) {
|
if (window.maplibregl) {
|
||||||
// timeout needed so that the map is in the DOM
|
// timeout needed so that the map is in the DOM
|
||||||
setTimeout(initMap);
|
setTimeout(initMap);
|
||||||
} else if (window.googleCallback) {
|
|
||||||
(function interval() {
|
|
||||||
isLoaded() ? initMap() : setTimeout(interval, 100);
|
|
||||||
}());
|
|
||||||
} else {
|
} else {
|
||||||
window.googleCallback = function() {
|
Ox.getStylesheet([
|
||||||
delete window.googleCallback;
|
Ox.PATH + 'UI/maplibre-gl/maplibre-gl.css',
|
||||||
initMap();
|
Ox.PATH + 'UI/maplibre-gl/maplibre-gl-geocoder.css'
|
||||||
};
|
], () => {})
|
||||||
$.getScript(
|
$.getScript([
|
||||||
document.location.protocol
|
Ox.PATH + 'UI/maplibre-gl/maplibre-gl.js',
|
||||||
+ '//maps.google.com/maps/api/js?callback=googleCallback&sensor=false'
|
Ox.PATH + 'UI/maplibre-gl/maplibre-gl-geocoder.min.js',
|
||||||
+ (Ox.Map.GoogleApiKey ? '&key=' + Ox.Map.GoogleApiKey : '')
|
], initMap)
|
||||||
);
|
}
|
||||||
|
|
||||||
|
function equalBonds(a, b) {
|
||||||
|
return (
|
||||||
|
a._sw.lat == b._sw.lat &&
|
||||||
|
a._sw.lng == b._sw.lng &&
|
||||||
|
a._ne.lat == b._ne.lat &&
|
||||||
|
a._ne.lng == b._ne.lng
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function addPlaceToMap(place) {
|
function addPlaceToMap(place) {
|
||||||
|
|
@ -535,11 +541,11 @@ Ox.Map = function(options, self) {
|
||||||
if (!place) {
|
if (!place) {
|
||||||
var bounds = self.map.getBounds(),
|
var bounds = self.map.getBounds(),
|
||||||
center = self.map.getCenter(),
|
center = self.map.getCenter(),
|
||||||
southwest = new google.maps.LatLngBounds(
|
southwest = new maplibregl.LngLatBounds(
|
||||||
bounds.getSouthWest(), center
|
bounds._sw, center
|
||||||
).getCenter(),
|
).getCenter(),
|
||||||
northeast = new google.maps.LatLngBounds(
|
northeast = new maplibregl.LngLatBounds(
|
||||||
center, bounds.getNorthEast()
|
center, bounds._ne
|
||||||
).getCenter(),
|
).getCenter(),
|
||||||
place = new Ox.MapPlace({
|
place = new Ox.MapPlace({
|
||||||
alternativeNames: [],
|
alternativeNames: [],
|
||||||
|
|
@ -550,14 +556,14 @@ Ox.Map = function(options, self) {
|
||||||
map: that,
|
map: that,
|
||||||
name: '',
|
name: '',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
south: southwest.lat(),
|
south: southwest.lat,
|
||||||
west: southwest.lng(),
|
west: southwest.lng,
|
||||||
north: northeast.lat(),
|
north: northeast.lat,
|
||||||
east: northeast.lng()
|
east: northeast.lng
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ox.forEach(self.places, function(p, i) {
|
Ox.forEach(self.places, function(p, i) {
|
||||||
if (place.bounds.equals(p.bounds)) {
|
if (equalBonds(place.bounds, p.bounds)) {
|
||||||
place = p;
|
place = p;
|
||||||
exists = true;
|
exists = true;
|
||||||
return false; // break
|
return false; // break
|
||||||
|
|
@ -589,17 +595,23 @@ Ox.Map = function(options, self) {
|
||||||
self.boundsChanged = true;
|
self.boundsChanged = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toSpan(bounds) {
|
||||||
|
return {
|
||||||
|
lat: bounds._ne.lat - bounds._sw.lat,
|
||||||
|
lng: bounds._ne.lng - bounds._sw.lng,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function canContain(outerBounds, innerBounds) {
|
function canContain(outerBounds, innerBounds) {
|
||||||
// checks if outerBounds _can_ contain innerBounds
|
// checks if outerBounds _can_ contain innerBounds
|
||||||
var outerSpan = outerBounds.toSpan(),
|
var outerSpan = toSpan(outerBounds),
|
||||||
innerSpan = innerBounds.toSpan();
|
innerSpan = toSpan(innerBounds);
|
||||||
return outerSpan.lat() > innerSpan.lat() &&
|
return outerSpan.lat > innerSpan.lat &&
|
||||||
outerSpan.lng() > innerSpan.lng();
|
outerSpan.lng > innerSpan.lng;
|
||||||
}
|
}
|
||||||
|
|
||||||
function centerChanged() {
|
function centerChanged() {
|
||||||
var tooltip = $('.OxMapMarkerTooltip');
|
that.tooltip.remove()
|
||||||
tooltip.length && Ox.$elements[$(tooltip[0]).data('oxid')].hide();
|
|
||||||
self.center = self.map.getCenter();
|
self.center = self.map.getCenter();
|
||||||
self.centerChanged = true;
|
self.centerChanged = true;
|
||||||
}
|
}
|
||||||
|
|
@ -609,9 +621,21 @@ Ox.Map = function(options, self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function clickMap(event) {
|
function clickMap(event) {
|
||||||
|
// Check if click hit any rectangle fill layers (which have rectangle click handlers)
|
||||||
|
var features = self.map.queryRenderedFeatures(event.point, {
|
||||||
|
layers: self.map.getStyle().layers
|
||||||
|
.filter(layer => layer.id.includes('-fill'))
|
||||||
|
.map(layer => layer.id)
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we clicked on a rectangle, don't process map click
|
||||||
|
if (features.length > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var place = getSelectedPlace();
|
var place = getSelectedPlace();
|
||||||
if (self.options.clickable/* && !editing()*/) {
|
if (self.options.clickable/* && !editing()*/) {
|
||||||
getPlaceByLatLng(event.latLng, self.map.getBounds(), function(place) {
|
getPlaceByLatLng(event.lngLat, self.map.getBounds(), function(place) {
|
||||||
if (place) {
|
if (place) {
|
||||||
addPlaceToMap(place);
|
addPlaceToMap(place);
|
||||||
//selectPlace(place.id);
|
//selectPlace(place.id);
|
||||||
|
|
@ -656,7 +680,7 @@ Ox.Map = function(options, self) {
|
||||||
|
|
||||||
function crossesDateline() {
|
function crossesDateline() {
|
||||||
var bounds = self.map.getBounds();
|
var bounds = self.map.getBounds();
|
||||||
return bounds.getSouthWest().lng() > bounds.getNorthEast().lng();
|
return bounds._sw.lng > bounds._ne.lng;
|
||||||
}
|
}
|
||||||
|
|
||||||
function editing() {
|
function editing() {
|
||||||
|
|
@ -681,9 +705,9 @@ Ox.Map = function(options, self) {
|
||||||
// get initial map bounds
|
// get initial map bounds
|
||||||
self.options.places({}, function(result) {
|
self.options.places({}, function(result) {
|
||||||
var area = result.data.area;
|
var area = result.data.area;
|
||||||
callback(new google.maps.LatLngBounds(
|
callback(new maplibregl.LngLatBounds(
|
||||||
new google.maps.LatLng(area.south, area.west),
|
new maplibregl.LngLat(area.west, area.south),
|
||||||
new google.maps.LatLng(area.north, area.east)
|
new maplibregl.LngLat(area.east, area.north)
|
||||||
));
|
));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -695,24 +719,28 @@ Ox.Map = function(options, self) {
|
||||||
- self.options.showZoombar * 16;
|
- self.options.showZoombar * 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMapType() {
|
|
||||||
return self.options.showLabels ? 'HYBRID' : 'SATELLITE'
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMaxZoom(point, callback) {
|
function getMaxZoom(point, callback) {
|
||||||
if (arguments.length == 1) {
|
if (arguments.length == 1) {
|
||||||
callback = point;
|
callback = point;
|
||||||
point = self.map.getCenter();
|
point = self.map.getCenter();
|
||||||
}
|
}
|
||||||
|
// fixme, why is getMaxZoom off by one?
|
||||||
|
let maxZoom = self.map.getMaxZoom()
|
||||||
|
setTimeout(() => {
|
||||||
|
callback(maxZoom)
|
||||||
|
})
|
||||||
|
/*
|
||||||
self.maxZoomService.getMaxZoomAtLatLng(point, function(data) {
|
self.maxZoomService.getMaxZoomAtLatLng(point, function(data) {
|
||||||
callback(data.status == 'OK' ? data.zoom : null);
|
callback(data.status == 'OK' ? data.zoom : null);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetersPerPixel() {
|
function getMetersPerPixel() {
|
||||||
// m/px = m/deg * deg/px
|
// m/px = m/deg * deg/px
|
||||||
var degreesPerPixel = 360 / (self.tileSize * Math.pow(2, self.map.getZoom()));
|
const degreesPerPixel = 360 / (self.tileSize * Math.pow(2, self.map.getZoom()));
|
||||||
return Ox.getMetersPerDegree(self.map.getCenter().lat()) * degreesPerPixel;
|
const center = self.map.getCenter();
|
||||||
|
return Ox.getMetersPerDegree(center.lat) * degreesPerPixel;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMinZoom() {
|
function getMinZoom() {
|
||||||
|
|
@ -734,61 +762,40 @@ Ox.Map = function(options, self) {
|
||||||
: Ox.getObjectById(self.places, id);
|
: Ox.getObjectById(self.places, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlaceByLatLng(latlng, bounds, callback) {
|
async function getPlaceByLatLng(latlng, bounds, callback) {
|
||||||
// gets the largest place at latlng that would fit in bounds
|
// gets the place at latlng appropriate for current zoom level
|
||||||
var callback = arguments.length == 3 ? callback : bounds,
|
var callback = arguments.length == 3 ? callback : bounds,
|
||||||
bounds = arguments.length == 3 ? bounds : null;
|
bounds = arguments.length == 3 ? bounds : null;
|
||||||
self.$loadingIcon && self.$loadingIcon.start();
|
self.$loadingIcon && self.$loadingIcon.start();
|
||||||
self.geocoder.geocode({
|
var results = await reverseGeocode(latlng);
|
||||||
latLng: latlng
|
self.$loadingIcon && self.$loadingIcon.stop();
|
||||||
}, function(results, status) {
|
|
||||||
self.$loadingIcon && self.$loadingIcon.stop();
|
if (results.features.length) {
|
||||||
if (status == google.maps.GeocoderStatus.OK) {
|
// We only have one result based on current zoom level
|
||||||
if (bounds) {
|
var feature = results.features[0];
|
||||||
Ox.forEach(results.reverse(), function(result, i) {
|
callback(new Ox.MapPlace(parseGeodata(feature)));
|
||||||
if (
|
|
||||||
i == results.length - 1 ||
|
triggerGeocodeEvent({
|
||||||
canContain(bounds, result.geometry.bounds || result.geometry.viewport)
|
latLng: latlng,
|
||||||
) {
|
results: results.features
|
||||||
callback(new Ox.MapPlace(parseGeodata(results[i])));
|
});
|
||||||
return false; // break
|
} else {
|
||||||
}
|
Ox.Log('Map', 'geocode failed: no results');
|
||||||
});
|
callback(null);
|
||||||
} 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) {
|
function getPlaceByName(name, callback) {
|
||||||
self.$loadingIcon && self.$loadingIcon.start();
|
self.$loadingIcon && self.$loadingIcon.start();
|
||||||
self.geocoder.geocode({
|
forwardGeocode({
|
||||||
address: name
|
query: name
|
||||||
}, function(results, status) {
|
}).then(function(results) {
|
||||||
self.$loadingIcon && self.$loadingIcon.stop();
|
self.$loadingIcon && self.$loadingIcon.stop();
|
||||||
if (status == google.maps.GeocoderStatus.OK) {
|
if (results.features.length) {
|
||||||
callback(new Ox.MapPlace(parseGeodata(results[0])));
|
callback(new Ox.MapPlace(parseGeodata(results.features[0])));
|
||||||
}
|
|
||||||
if (
|
|
||||||
status == google.maps.GeocoderStatus.OK
|
|
||||||
&& status != google.maps.GeocoderStatus.ZERO_RESULTS
|
|
||||||
) {
|
|
||||||
triggerGeocodeEvent({
|
triggerGeocodeEvent({
|
||||||
address: name,
|
address: name,
|
||||||
results: results
|
results: results.features
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Ox.Log('Map', 'geocode failed:', status);
|
Ox.Log('Map', 'geocode failed:', status);
|
||||||
|
|
@ -831,36 +838,192 @@ Ox.Map = function(options, self) {
|
||||||
: null;
|
: null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function bbox2bounds(bbox) {
|
||||||
|
return new maplibregl.LngLatBounds(
|
||||||
|
new maplibregl.LngLat(bbox[0], bbox[1]),
|
||||||
|
new maplibregl.LngLat(bbox[2], bbox[3])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function converNominatimFeature(feature) {
|
||||||
|
const center = [
|
||||||
|
feature.bbox[0] +
|
||||||
|
(feature.bbox[2] - feature.bbox[0]) / 2,
|
||||||
|
feature.bbox[1] +
|
||||||
|
(feature.bbox[3] - feature.bbox[1]) / 2
|
||||||
|
];
|
||||||
|
const polygon = {
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [
|
||||||
|
[feature.bbox[0], feature.bbox[1]],
|
||||||
|
[feature.bbox[1], feature.bbox[2]],
|
||||||
|
[feature.bbox[2], feature.bbox[3]],
|
||||||
|
[feature.bbox[0], feature.bbox[3]],
|
||||||
|
]
|
||||||
|
},
|
||||||
|
bounds: bbox2bounds(feature.bbox),
|
||||||
|
place_name: feature.properties.display_name,
|
||||||
|
properties: feature.properties,
|
||||||
|
text: feature.properties.display_name,
|
||||||
|
address_components: [
|
||||||
|
{
|
||||||
|
long_name: feature.properties.display_name,
|
||||||
|
short_name: feature.properties.display_name,
|
||||||
|
types: ['place']
|
||||||
|
}
|
||||||
|
],
|
||||||
|
place_type: ['place'],
|
||||||
|
};
|
||||||
|
return polygon
|
||||||
|
}
|
||||||
|
|
||||||
|
async function reverseGeocode(config) {
|
||||||
|
const features = [];
|
||||||
|
|
||||||
|
// Use current map zoom level directly as Nominatim zoom level
|
||||||
|
const currentMapZoom = Math.round(self.map.getZoom());
|
||||||
|
// FIXME: map to nominatim zoom levels:
|
||||||
|
// https://nominatim.org/release-docs/develop/api/Reverse/
|
||||||
|
// zoom address detail
|
||||||
|
// 3 country
|
||||||
|
// 5 state
|
||||||
|
// 8 county
|
||||||
|
// 10 city
|
||||||
|
// 12 town / borough
|
||||||
|
// 13 village / suburb
|
||||||
|
// 14 neighbourhood
|
||||||
|
// 15 any settlement
|
||||||
|
// 16 major streets
|
||||||
|
// 17 major and minor streets
|
||||||
|
// 18 building
|
||||||
|
//
|
||||||
|
console.log('Reverse geocoding: using map zoom =', currentMapZoom, 'for Nominatim zoom');
|
||||||
|
let request = `${self.options.nominatim}/reverse?lat=${
|
||||||
|
config.lat
|
||||||
|
}&lon=${
|
||||||
|
config.lng
|
||||||
|
}&format=geojson&polygon_geojson=0&addressdetails=1&extratags=1&accept-language=en`;
|
||||||
|
request += `&zoom=${currentMapZoom}`
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(request);
|
||||||
|
const geojson = await response.json();
|
||||||
|
|
||||||
|
if (geojson.features && geojson.features.length > 0) {
|
||||||
|
const feature = converNominatimFeature(geojson.features[0]);
|
||||||
|
feature.zoom = currentMapZoom;
|
||||||
|
features.push(feature);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to reverseGeocode with error: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
features
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async function forwardGeocode(config) {
|
||||||
|
const features = [];
|
||||||
|
const request = `${self.options.nominatim}/search?q=${
|
||||||
|
config.query
|
||||||
|
}&format=geojson&polygon_geojson=0&addressdetails=1&accept-language=en`;
|
||||||
|
try {
|
||||||
|
const response = await fetch(request);
|
||||||
|
const geojson = await response.json();
|
||||||
|
for (const feature of geojson.features) {
|
||||||
|
features.push(converNominatimFeature(feature));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to forwardGeocode with error: ${e}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
features
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function initMap() {
|
function initMap() {
|
||||||
|
|
||||||
getMapBounds(function(mapBounds) {
|
getMapBounds(function(mapBounds) {
|
||||||
|
|
||||||
//Ox.Log('Map', 'init', mapBounds.getSouthWest(), mapBounds.getNorthEast(), mapBounds.getCenter())
|
//Ox.Log('Map', 'init', mapBounds._sw, mapBounds._ne, mapBounds.getCenter())
|
||||||
|
|
||||||
self.elevationService = new google.maps.ElevationService();
|
//self.elevationService = new google.maps.ElevationService();
|
||||||
self.geocoder = new google.maps.Geocoder();
|
//self.maxZoomService = new google.maps.MaxZoomService();
|
||||||
self.maxZoomService = new google.maps.MaxZoomService();
|
//
|
||||||
|
|
||||||
self.center = mapBounds ? mapBounds.getCenter() : new google.maps.LatLng(0, 0);
|
self.center = mapBounds ? mapBounds.getCenter() : new maplibregl.LngLat(0, 0);
|
||||||
self.zoom = self.minZoom;
|
self.zoom = self.minZoom;
|
||||||
that.map = self.map = new google.maps.Map(self.$map[0], {
|
|
||||||
|
self.sateliteStyle = {
|
||||||
|
'version': 8,
|
||||||
|
'sources': {
|
||||||
|
'raster-tiles': {
|
||||||
|
'type': 'raster',
|
||||||
|
'tiles': [
|
||||||
|
'https://mt0.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
|
'https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
|
'https://mt2.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
|
'https://mt3.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',
|
||||||
|
],
|
||||||
|
'tileSize': 256,
|
||||||
|
'attribution':
|
||||||
|
'FIXME',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'layers': [
|
||||||
|
{
|
||||||
|
'id': 'simple-tiles',
|
||||||
|
'type': 'raster',
|
||||||
|
'source': 'raster-tiles',
|
||||||
|
'roundZoom': true,
|
||||||
|
'minzoom': 0,
|
||||||
|
'maxzoom': 22
|
||||||
|
},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
window.map = that.map = self.map = new maplibregl.Map({
|
||||||
|
container: self.$map[0],
|
||||||
center: self.center,
|
center: self.center,
|
||||||
disableDefaultUI: true,
|
style: self.options.showLabels ? self.options.style : self.sateliteStyle,
|
||||||
disableDoubleClickZoom: true,
|
//noClear: true,
|
||||||
mapTypeId: google.maps.MapTypeId[getMapType()],
|
//scrollwheel: !self.options.zoomOnlyWhenFocused,
|
||||||
noClear: true,
|
|
||||||
scrollwheel: !self.options.zoomOnlyWhenFocused,
|
|
||||||
zoom: self.zoom
|
zoom: self.zoom
|
||||||
|
})
|
||||||
|
self.geocoder = new MaplibreGeocoder({
|
||||||
|
forwardGeocode: forwardGeocode,
|
||||||
|
reverseGeocode: reverseGeocode,
|
||||||
|
})
|
||||||
|
/*
|
||||||
|
map.addControl(self.geocoder, {
|
||||||
|
maplibregl
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
that.map.on('click', clickMap)
|
||||||
|
that.map.on('zoom', zoomChanged)
|
||||||
|
that.map.on('idle', mapChanged)
|
||||||
|
that.map.on('moveend', boundsChanged)
|
||||||
|
that.map.on('dragend', boundsChanged)
|
||||||
|
that.map.on('zoomend', boundsChanged)
|
||||||
|
/*
|
||||||
|
that.map.on('resize', () => {
|
||||||
|
that.resizeMap()
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
/*
|
||||||
google.maps.event.addListener(self.map, 'bounds_changed', boundsChanged);
|
google.maps.event.addListener(self.map, 'bounds_changed', boundsChanged);
|
||||||
google.maps.event.addListener(self.map, 'center_changed', centerChanged);
|
google.maps.event.addListener(self.map, 'center_changed', centerChanged);
|
||||||
google.maps.event.addListener(self.map, 'click', clickMap);
|
google.maps.event.addListener(self.map, 'click', clickMap);
|
||||||
google.maps.event.addListener(self.map, 'idle', mapChanged);
|
google.maps.event.addListener(self.map, 'idle', mapChanged);
|
||||||
google.maps.event.addListener(self.map, 'zoom_changed', zoomChanged);
|
google.maps.event.addListener(self.map, 'zoom_changed', zoomChanged);
|
||||||
google.maps.event.trigger(self.map, 'resize');
|
google.maps.event.trigger(self.map, 'resize');
|
||||||
|
*/
|
||||||
|
|
||||||
// needed to get mouse x/y coordinates on marker mouseover,
|
// needed to get mouse x/y coordinates on marker mouseover,
|
||||||
// see http://code.google.com/p/gmaps-api-issues/issues/detail?id=2342
|
// see http://code.google.com/p/gmaps-api-issues/issues/detail?id=2342
|
||||||
|
/*
|
||||||
that.overlayView = new google.maps.OverlayView();
|
that.overlayView = new google.maps.OverlayView();
|
||||||
that.overlayView.setMap(self.map);
|
that.overlayView.setMap(self.map);
|
||||||
that.overlayView.draw = function () {
|
that.overlayView.draw = function () {
|
||||||
|
|
@ -870,52 +1033,55 @@ Ox.Map = function(options, self) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
that.overlayView.draw();
|
that.overlayView.draw();
|
||||||
|
*/
|
||||||
|
|
||||||
Ox.forEach(self.$controls, function($control) {
|
that.map.on('load', () => {
|
||||||
$control.appendTo(self.$map);
|
Ox.forEach(self.$controls, function($control) {
|
||||||
});
|
$control.appendTo(self.$map);
|
||||||
Ox.forEach(self.$placeControls, function($placeControl) {
|
});
|
||||||
$placeControl.appendTo(self.$map);
|
Ox.forEach(self.$placeControls, function($placeControl) {
|
||||||
});
|
$placeControl.appendTo(self.$map);
|
||||||
|
});
|
||||||
|
|
||||||
if (self.options.find) {
|
if (self.options.find) {
|
||||||
self.$findInput
|
self.$findInput
|
||||||
.value(self.options.find)
|
.value(self.options.find)
|
||||||
.triggerEvent('submit', {value: self.options.find});
|
.triggerEvent('submit', {value: self.options.find});
|
||||||
} else {
|
} else {
|
||||||
if (self.options.selected) {
|
if (self.options.selected) {
|
||||||
selectPlace(self.options.selected, true);
|
selectPlace(self.options.selected, true);
|
||||||
}
|
}
|
||||||
if (mapBounds) {
|
if (mapBounds) {
|
||||||
if (isEmpty(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);
|
self.map.setZoom(self.minZoom);
|
||||||
} else {
|
|
||||||
self.map.fitBounds(mapBounds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self.map.getZoom() < self.minZoom) {
|
updateFormElements();
|
||||||
self.map.setZoom(self.minZoom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
updateFormElements();
|
|
||||||
|
|
||||||
self.loaded = true;
|
that.resizeMap()
|
||||||
that.triggerEvent('load');
|
self.loaded = true;
|
||||||
|
self.boundsChanged = true;
|
||||||
|
that.triggerEvent('load');
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmpty(bounds) {
|
function isEmpty(bounds) {
|
||||||
// Google's bounds.isEmpty() is not reliable
|
return bounds._sw.lat == bounds._ne.lat
|
||||||
var southWest = bounds.getSouthWest(),
|
&& bounds._sw.lng == bounds._ne.lng;
|
||||||
northEast = bounds.getNorthEast();
|
|
||||||
return southWest.lat() == northEast.lat()
|
|
||||||
&& southWest.lng() == northEast.lng();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLoaded() {
|
function isLoaded() {
|
||||||
return window.google && window.google.maps && window.google.maps.LatLng;
|
return window.maplibregl && window.maplibregl.maps && window.maplibregl.LngLat;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapChanged() {
|
function mapChanged() {
|
||||||
|
|
@ -927,12 +1093,11 @@ Ox.Map = function(options, self) {
|
||||||
self.boundsChanged = false;
|
self.boundsChanged = false;
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var southWest = bounds.getSouthWest(),
|
var south = bounds._sw.lat,
|
||||||
northEast = bounds.getNorthEast(),
|
west = bounds._sw.lng,
|
||||||
south = southWest.lat(),
|
north = bounds._ne.lat,
|
||||||
west = southWest.lng(),
|
east = bounds._ne.lng;
|
||||||
north = northEast.lat(),
|
|
||||||
east = northEast.lng();
|
|
||||||
self.options.places({
|
self.options.places({
|
||||||
keys: self.placeKeys,
|
keys: self.placeKeys,
|
||||||
query: {
|
query: {
|
||||||
|
|
@ -999,7 +1164,8 @@ Ox.Map = function(options, self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function pan(x, y) {
|
function pan(x, y) {
|
||||||
self.map.panBy(x * self.$map.width() / 2, y * self.$map.height() / 2);
|
console.log(x, y, self.$map.width())
|
||||||
|
self.map.panBy([x * self.$map.width() / 2, y * self.$map.height() / 2]);
|
||||||
};
|
};
|
||||||
|
|
||||||
function panToPlace() {
|
function panToPlace() {
|
||||||
|
|
@ -1015,22 +1181,24 @@ Ox.Map = function(options, self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseGeodata(data) {
|
function parseGeodata(data) {
|
||||||
var bounds = data.geometry.bounds || data.geometry.viewport,
|
console.log("parseGeodata", data)
|
||||||
northEast = bounds.getNorthEast(),
|
// FIXME: data is geojson Feature with Polygon geometry now
|
||||||
southWest = bounds.getSouthWest(),
|
var bounds = data.bounds,
|
||||||
|
northEast = bounds._ne,
|
||||||
|
southWest = bounds._sw,
|
||||||
place = {
|
place = {
|
||||||
alternativeNames: [],
|
alternativeNames: [],
|
||||||
components: data.address_components,
|
components: data.address_components,
|
||||||
countryCode: getCountryCode(data.address_components),
|
countryCode: getCountryCode(data.address_components),
|
||||||
east: northEast.lng(),
|
east: northEast.lng,
|
||||||
editable: self.options.editable,
|
editable: self.options.editable,
|
||||||
fullGeoname: getFullGeoname(data.address_components),
|
fullGeoname: getFullGeoname(data.address_components),
|
||||||
id: '_' + Ox.encodeBase32(Ox.uid()),
|
id: '_' + Ox.encodeBase32(Ox.uid()),
|
||||||
map: that,
|
map: that,
|
||||||
north: northEast.lat(),
|
north: northEast.lat,
|
||||||
south: southWest.lat(),
|
south: southWest.lat,
|
||||||
type: getType(data.address_components[0].types),
|
type: getType(data.address_components[0].types, data),
|
||||||
west: southWest.lng()
|
west: southWest.lng
|
||||||
};
|
};
|
||||||
place.geoname = data.formatted_address || place.fullGeoname;
|
place.geoname = data.formatted_address || place.fullGeoname;
|
||||||
place.name = (place.geoname || place.fullGeoname).split(', ')[0];
|
place.name = (place.geoname || place.fullGeoname).split(', ')[0];
|
||||||
|
|
@ -1062,19 +1230,34 @@ Ox.Map = function(options, self) {
|
||||||
) ? name : null;
|
) ? name : null;
|
||||||
}).join(', ');
|
}).join(', ');
|
||||||
}
|
}
|
||||||
function getType(types) {
|
function getType(types, data) {
|
||||||
// see https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes
|
// see https://developers.google.com/maps/documentation/javascript/geocoding#GeocodingAddressTypes
|
||||||
|
types.unshift(data.properties.addresstype)
|
||||||
|
console.log(types)
|
||||||
|
|
||||||
|
|
||||||
var strings = {
|
var strings = {
|
||||||
'country': ['country'],
|
'country': ['country'],
|
||||||
'region': ['administrative_area', 'colloquial_area'],
|
'region': [
|
||||||
'city': ['locality'],
|
'administrative_area', 'colloquial_area',
|
||||||
'borough': ['neighborhood', 'postal_code', 'sublocality'],
|
'state', 'county', 'region'
|
||||||
|
],
|
||||||
|
'city': [
|
||||||
|
'locality', 'city', 'town', 'village'
|
||||||
|
],
|
||||||
|
'borough': [
|
||||||
|
'neighborhood', 'postal_code', 'sublocality', 'suburb', 'borough',
|
||||||
|
'neighbourhood',
|
||||||
|
|
||||||
|
],
|
||||||
'street': [
|
'street': [
|
||||||
'intersection', 'route',
|
'intersection', 'route',
|
||||||
'street_address', 'street_number'
|
'street_address', 'street_number',
|
||||||
|
'road',
|
||||||
],
|
],
|
||||||
'building': [
|
'building': [
|
||||||
'airport', 'floor', 'premise', 'room', 'subpremise'
|
'airport', 'floor', 'premise', 'room', 'subpremise',
|
||||||
|
'building',
|
||||||
],
|
],
|
||||||
'feature': ['natural_feature', 'park']
|
'feature': ['natural_feature', 'park']
|
||||||
},
|
},
|
||||||
|
|
@ -1326,7 +1509,7 @@ Ox.Map = function(options, self) {
|
||||||
|
|
||||||
function toggleLabels() {
|
function toggleLabels() {
|
||||||
self.options.showLabels = !self.options.showLabels;
|
self.options.showLabels = !self.options.showLabels;
|
||||||
self.map.setMapTypeId(google.maps.MapTypeId[getMapType()]);
|
self.map.setStyle(self.options.showLabels ? self.options.style : self.sateliteStyle)
|
||||||
that.triggerEvent('togglelabels', {
|
that.triggerEvent('togglelabels', {
|
||||||
visible: self.options.showLabels
|
visible: self.options.showLabels
|
||||||
});
|
});
|
||||||
|
|
@ -1336,10 +1519,12 @@ Ox.Map = function(options, self) {
|
||||||
// someone may want to cache google geocode data, so we fire an event.
|
// someone may want to cache google geocode data, so we fire an event.
|
||||||
// google puts functions like lat or lng on the objects' prototypes,
|
// google puts functions like lat or lng on the objects' prototypes,
|
||||||
// so we create properly named properties, for json encoding
|
// so we create properly named properties, for json encoding
|
||||||
|
console.log(data)
|
||||||
|
/*
|
||||||
if (data.latLng) {
|
if (data.latLng) {
|
||||||
data.latLng = {
|
data.latLng = {
|
||||||
lat: data.latLng.lat(),
|
lat: data.latLng.lat,
|
||||||
lng: data.latLng.lng()
|
lng: data.latLng.lng
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.results.forEach(function(result) {
|
data.results.forEach(function(result) {
|
||||||
|
|
@ -1347,23 +1532,24 @@ Ox.Map = function(options, self) {
|
||||||
if (result.geometry[key]) {
|
if (result.geometry[key]) {
|
||||||
result.geometry[key] = {
|
result.geometry[key] = {
|
||||||
northEast: {
|
northEast: {
|
||||||
lat: result.geometry[key].getNorthEast().lat(),
|
lat: result.geometry[key]._ne.lat,
|
||||||
lng: result.geometry[key].getNorthEast().lng()
|
lng: result.geometry[key]._ne.lng
|
||||||
},
|
},
|
||||||
southWest: {
|
southWest: {
|
||||||
lat: result.geometry[key].getSouthWest().lat(),
|
lat: result.geometry[key]._sw.lat,
|
||||||
lng: result.geometry[key].getSouthWest().lng()
|
lng: result.geometry[key]._sw.lng
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (result.geometry.location) {
|
if (result.geometry.location) {
|
||||||
result.geometry.location = {
|
result.geometry.location = {
|
||||||
lat: result.geometry.location.lat(),
|
lat: result.geometry.location.lat,
|
||||||
lng: result.geometry.location.lng()
|
lng: result.geometry.location.lng
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
that.triggerEvent('geocode', data);
|
that.triggerEvent('geocode', data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1402,7 +1588,7 @@ Ox.Map = function(options, self) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function zoomChanged() {
|
function zoomChanged() {
|
||||||
var zoom = self.map.getZoom();
|
var zoom = parseInt(self.map.getZoom());
|
||||||
if (zoom < self.minZoom) {
|
if (zoom < self.minZoom) {
|
||||||
self.map.setZoom(self.minZoom);
|
self.map.setZoom(self.minZoom);
|
||||||
} else if (self.maxZoom && zoom > self.maxZoom) {
|
} else if (self.maxZoom && zoom > self.maxZoom) {
|
||||||
|
|
@ -1443,8 +1629,7 @@ Ox.Map = function(options, self) {
|
||||||
lng <n> Longitude
|
lng <n> Longitude
|
||||||
@*/
|
@*/
|
||||||
that.getCenter = function() {
|
that.getCenter = function() {
|
||||||
var center = self.map.getCenter();
|
return self.map.getCenter();
|
||||||
return {lat: center.lat(), lng: center.lng()};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
|
|
@ -1544,7 +1729,7 @@ Ox.Map = function(options, self) {
|
||||||
});
|
});
|
||||||
updateFormElements();
|
updateFormElements();
|
||||||
Ox.Log('Map', 'triggering google maps resize event, height', self.options.height)
|
Ox.Log('Map', 'triggering google maps resize event, height', self.options.height)
|
||||||
google.maps.event.trigger(self.map, 'resize');
|
self.map.triggerRepaint()
|
||||||
// self.map.setCenter(center);
|
// self.map.setCenter(center);
|
||||||
}
|
}
|
||||||
return that;
|
return that;
|
||||||
|
|
@ -1558,7 +1743,7 @@ Ox.Map = function(options, self) {
|
||||||
lng <n> Longitude
|
lng <n> Longitude
|
||||||
@*/
|
@*/
|
||||||
that.setCenter = function(center) {
|
that.setCenter = function(center) {
|
||||||
self.map.setCenter(new google.maps.LatLng(center.lat, center.lng));
|
self.map.setCenter(new maplibregl.LngLat(center.lng, center.lat));
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,15 @@ Ox.MapEditor = function(options, self) {
|
||||||
self.$list.size();
|
self.$list.size();
|
||||||
self.$map.resizeMap();
|
self.$map.resizeMap();
|
||||||
},
|
},
|
||||||
|
places: function() {
|
||||||
|
self.isAsync = Ox.isFunction(self.options.places);
|
||||||
|
self.$list.options({
|
||||||
|
items: self.isAsync ? self.options.places : Ox.clone(self.options.places)
|
||||||
|
})
|
||||||
|
self.$map.options({
|
||||||
|
places: self.options.places
|
||||||
|
})
|
||||||
|
},
|
||||||
selected: function() {
|
selected: function() {
|
||||||
self.$list.options({selected: self.options.selected});
|
self.$list.options({selected: self.options.selected});
|
||||||
},
|
},
|
||||||
|
|
@ -328,7 +337,7 @@ Ox.MapEditor = function(options, self) {
|
||||||
columns: self.columns,
|
columns: self.columns,
|
||||||
columnsRemovable: true,
|
columnsRemovable: true,
|
||||||
columnsVisible: true,
|
columnsVisible: true,
|
||||||
items: Ox.clone(self.options.places),
|
items: self.isAsync ? self.options.places : Ox.clone(self.options.places),
|
||||||
//items: self.options.places,
|
//items: self.options.places,
|
||||||
// area needed for icon, geoname needed for flag
|
// area needed for icon, geoname needed for flag
|
||||||
keys: ['area', 'geoname', 'matches'],
|
keys: ['area', 'geoname', 'matches'],
|
||||||
|
|
|
||||||
|
|
@ -42,16 +42,32 @@ Ox.MapMarker = function(options) {
|
||||||
Ox.forEach(options, function(val, key) {
|
Ox.forEach(options, function(val, key) {
|
||||||
that[key] = val;
|
that[key] = val;
|
||||||
});
|
});
|
||||||
that.marker = new google.maps.Marker({
|
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,
|
raiseOnDrag: false,
|
||||||
shape: {coords: [8, 8, 8], type: 'circle'}
|
element: element,
|
||||||
|
//shape: {coords: [8, 8, 8], type: 'circle'},
|
||||||
//title: that.place.name,
|
//title: that.place.name,
|
||||||
//zIndex: 1000
|
//zIndex: 1000
|
||||||
});
|
});
|
||||||
|
that.tooltip = new maplibregl.Popup({
|
||||||
|
closeButton: false,
|
||||||
|
closeOnClick: false,
|
||||||
|
className: 'tooltip'
|
||||||
|
});
|
||||||
|
that.tooltip.addClass
|
||||||
|
|
||||||
setOptions();
|
setOptions();
|
||||||
|
|
||||||
function click() {
|
function click(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
event.stopPropagation()
|
||||||
var key = that.map.getKey(),
|
var key = that.map.getKey(),
|
||||||
place, bounds, southWest, northEast;
|
place, bounds, southWest, northEast;
|
||||||
if (!that.place.selected) {
|
if (!that.place.selected) {
|
||||||
|
|
@ -62,9 +78,9 @@ Ox.MapMarker = function(options) {
|
||||||
place = that.map.getSelectedPlace();
|
place = that.map.getSelectedPlace();
|
||||||
}
|
}
|
||||||
if (place) {
|
if (place) {
|
||||||
bounds = new google.maps.LatLngBounds(
|
bounds = new maplibregl.LngLatBounds(
|
||||||
new google.maps.LatLng(place.south, place.west),
|
new maplibregl.LngLatBounds(place.west, place.south),
|
||||||
new google.maps.LatLng(place.north, place.east)
|
new maplibregl.LngLatBounds(place.east, place.north)
|
||||||
);
|
);
|
||||||
bounds = bounds.union(that.place.bounds);
|
bounds = bounds.union(that.place.bounds);
|
||||||
southWest = bounds.getSouthWest();
|
southWest = bounds.getSouthWest();
|
||||||
|
|
@ -79,10 +95,10 @@ Ox.MapMarker = function(options) {
|
||||||
map: that.map,
|
map: that.map,
|
||||||
name: '',
|
name: '',
|
||||||
type: 'feature',
|
type: 'feature',
|
||||||
south: southWest.lat(),
|
south: southWest.lat,
|
||||||
west: southWest.lng(),
|
west: southWest.lng,
|
||||||
north: northEast.lat(),
|
north: northEast.lat,
|
||||||
east: northEast.lng()
|
east: northEast.lng
|
||||||
}));
|
}));
|
||||||
} else {
|
} else {
|
||||||
that.map.options({selected: that.place.id});
|
that.map.options({selected: that.place.id});
|
||||||
|
|
@ -105,13 +121,15 @@ Ox.MapMarker = function(options) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag(e) {
|
function drag(e) {
|
||||||
|
// In MapLibre GL, get current position from marker directly
|
||||||
|
var lngLat = that.marker.getLngLat();
|
||||||
var northSouth = (that.place.north - that.place.south) / 2,
|
var northSouth = (that.place.north - that.place.south) / 2,
|
||||||
lat = Ox.limit(
|
lat = Ox.limit(
|
||||||
e.latLng.lat(),
|
lngLat.lat,
|
||||||
Ox.MIN_LATITUDE + northSouth,
|
Ox.MIN_LATITUDE + northSouth,
|
||||||
Ox.MAX_LATITUDE - northSouth
|
Ox.MAX_LATITUDE - northSouth
|
||||||
),
|
),
|
||||||
lng = e.latLng.lng(),
|
lng = lngLat.lng,
|
||||||
span = Math.min(
|
span = Math.min(
|
||||||
that.place.sizeEastWest * Ox.getDegreesPerMeter(lat) / 2, 179.99999999
|
that.place.sizeEastWest * Ox.getDegreesPerMeter(lat) / 2, 179.99999999
|
||||||
),
|
),
|
||||||
|
|
@ -127,9 +145,7 @@ Ox.MapMarker = function(options) {
|
||||||
}
|
}
|
||||||
Ox.Log('Map', 'west', that.place.west, 'east', that.place.east, 'span', span);
|
Ox.Log('Map', 'west', that.place.west, 'east', that.place.east, 'span', span);
|
||||||
that.place.update();
|
that.place.update();
|
||||||
that.marker.setOptions({
|
that.marker.setLngLat(that.place.center)
|
||||||
position: that.place.center
|
|
||||||
});
|
|
||||||
that.place.rectangle.update();
|
that.place.rectangle.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -181,35 +197,30 @@ Ox.MapMarker = function(options) {
|
||||||
c.context.strokeStyle = 'rgb(' + border.join(', ') + ')';
|
c.context.strokeStyle = 'rgb(' + border.join(', ') + ')';
|
||||||
c.context.arc(r, r, r - 1, 0, 360);
|
c.context.arc(r, r, r - 1, 0, 360);
|
||||||
c.context.stroke();
|
c.context.stroke();
|
||||||
callback(new google.maps.MarkerImage(
|
callback(new maplibregl.MarkerImage(
|
||||||
c.canvas.toDataURL(),
|
c.canvas.toDataURL(),
|
||||||
new google.maps.Size(options.size, options.size),
|
new maplibregl.Size(options.size, options.size),
|
||||||
new google.maps.Point(0, 0),
|
new maplibregl.Point(0, 0),
|
||||||
new google.maps.Point(r, r)
|
new maplibregl.Point(r, r)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseover(e) {
|
function mouseover(e) {
|
||||||
var offset = that.map.offset(),
|
that.tooltip.setLngLat(that.place.center).setHTML(
|
||||||
xy = that.map.overlayView.getProjection()
|
'<div style="display: flex; gap: 8px; margin: 2px 2px"><img src="' + Ox.getFlagByGeoname(that.place.geoname, 16)
|
||||||
.fromLatLngToContainerPixel(e.latLng);
|
+ '" style="border-radius: 4px;margin: auto"/>'
|
||||||
that.tooltip.show(
|
+ '<div style="font-size: 9px;">'
|
||||||
offset.left + Math.round(xy.x) - 4,
|
+ that.map.options('markerTooltip')(that.place) + '</div></div>'
|
||||||
offset.top + Math.round(xy.y) + 20
|
).addTo(that.map.map);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function mouseout() {
|
function mouseout() {
|
||||||
that.tooltip.hide();
|
that.tooltip.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
function setOptions() {
|
function setColor() {
|
||||||
// workaround to prevent marker from appearing twice
|
var color = that.map.options('markerColor');
|
||||||
// after setting draggable from true to false (google maps bug)
|
|
||||||
var fix = that.marker.getDraggable() && !that.place.editing,
|
|
||||||
color = that.map.options('markerColor'),
|
|
||||||
size = that.map.options('markerSize');
|
|
||||||
//Ox.Log('Map', 'setOptions, that.map: ', that.map)
|
//Ox.Log('Map', 'setOptions, that.map: ', that.map)
|
||||||
if (color == 'auto') {
|
if (color == 'auto') {
|
||||||
that.color = typeColor[that.place.type] || typeColor['mapPlaceFeatureColor'];
|
that.color = typeColor[that.place.type] || typeColor['mapPlaceFeatureColor'];
|
||||||
|
|
@ -218,6 +229,10 @@ Ox.MapMarker = function(options) {
|
||||||
} else {
|
} else {
|
||||||
that.color = color(that.place);
|
that.color = color(that.place);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setSize() {
|
||||||
|
var size = that.map.options('markerSize');
|
||||||
if (size == 'auto') {
|
if (size == 'auto') {
|
||||||
that.size = 8;
|
that.size = 8;
|
||||||
Ox.forEach(areaSize, function(size, area) {
|
Ox.forEach(areaSize, function(size, area) {
|
||||||
|
|
@ -232,50 +247,57 @@ Ox.MapMarker = function(options) {
|
||||||
} else {
|
} else {
|
||||||
that.size = size(that.place);
|
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({
|
that.marker.setOptions({
|
||||||
// fixme: cursor remains pointer
|
// fixme: cursor remains pointer
|
||||||
cursor: that.place.editing ? 'move' : 'pointer',
|
cursor: that.place.editing ? 'move' : 'pointer',
|
||||||
draggable: that.place.editing,
|
draggable: that.place.editing,
|
||||||
icon: Ox.MapMarkerImage({
|
element: Ox.MapMarkerImage({
|
||||||
color: that.color,
|
color: that.color,
|
||||||
mode: that.place.editing ? 'editing' :
|
mode: that.place.editing ? 'editing' :
|
||||||
that.place.selected ? 'selected' : 'normal',
|
that.place.selected ? 'selected' : 'normal',
|
||||||
size: that.size,
|
size: that.size,
|
||||||
type: that.place.id[0] == '_' ? 'result' : 'place'
|
type: that.place.id[0] == '_' ? 'result' : 'place'
|
||||||
}),
|
}),
|
||||||
position: that.place.center
|
})
|
||||||
});
|
*/
|
||||||
|
//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) {
|
if (fix) {
|
||||||
that.marker.setVisible(false);
|
that.marker.setVisible(false);
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
that.marker.setVisible(true);
|
that.marker.setVisible(true);
|
||||||
}, 0);
|
}, 0);
|
||||||
}
|
}
|
||||||
setTooltip();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTooltip() {
|
|
||||||
that.tooltip && that.tooltip.remove();
|
|
||||||
that.tooltip = Ox.Tooltip({
|
|
||||||
title: '<img src="'
|
|
||||||
+ Ox.getFlagByGeoname(that.place.geoname, 16)
|
|
||||||
+ '" style="float: left; width: 16px; height: 16px; margin: 1px 0 1px -1px; border-radius: 4px"/>'
|
|
||||||
+ '<div style="float: left; margin: 4px -1px 0 4px; font-size: 9px;">'
|
|
||||||
+ that.map.options('markerTooltip')(that.place) + '</div>'
|
|
||||||
})
|
|
||||||
.addClass('OxMapMarkerTooltip');
|
|
||||||
}
|
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
add <f> add to map
|
add <f> add to map
|
||||||
() -> <f> add to map, returns MapMarker
|
() -> <f> add to map, returns MapMarker
|
||||||
@*/
|
@*/
|
||||||
that.add = function() {
|
that.add = function() {
|
||||||
that.marker.setMap(that.map.map);
|
that.marker.addTo(that.map.map);
|
||||||
google.maps.event.addListener(that.marker, 'click', click);
|
const element = that.marker.getElement()
|
||||||
google.maps.event.addListener(that.marker, 'dblclick', dblclick);
|
if(element) {
|
||||||
google.maps.event.addListener(that.marker, 'mouseover', mouseover);
|
element.addEventListener('click', click)
|
||||||
google.maps.event.addListener(that.marker, 'mouseout', mouseout);
|
element.addEventListener('dblclick', dblclick)
|
||||||
|
element.addEventListener('mouseover', mouseover)
|
||||||
|
element.addEventListener('mouseout', mouseout)
|
||||||
|
}
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -285,9 +307,9 @@ Ox.MapMarker = function(options) {
|
||||||
@*/
|
@*/
|
||||||
that.edit = function() {
|
that.edit = function() {
|
||||||
setOptions();
|
setOptions();
|
||||||
google.maps.event.addListener(that.marker, 'dragstart', dragstart);
|
that.marker.on('dragstart', dragstart);
|
||||||
google.maps.event.addListener(that.marker, 'drag', drag);
|
that.marker.on('drag', drag);
|
||||||
google.maps.event.addListener(that.marker, 'dragend', dragend);
|
that.marker.on('dragend', dragend);
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -296,8 +318,12 @@ Ox.MapMarker = function(options) {
|
||||||
() -> <f> remove marker from map, returns MapMarker
|
() -> <f> remove marker from map, returns MapMarker
|
||||||
@*/
|
@*/
|
||||||
that.remove = function() {
|
that.remove = function() {
|
||||||
that.marker.setMap(null);
|
that.marker.remove();
|
||||||
google.maps.event.clearListeners(that.marker);
|
//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;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -306,9 +332,9 @@ Ox.MapMarker = function(options) {
|
||||||
() -> <f> clear edit listeners, returns MapMarker
|
() -> <f> clear edit listeners, returns MapMarker
|
||||||
@*/
|
@*/
|
||||||
that.submit = function() {
|
that.submit = function() {
|
||||||
google.maps.event.clearListeners(that.marker, 'dragstart');
|
that.marker.off('dragstart');
|
||||||
google.maps.event.clearListeners(that.marker, 'drag');
|
that.marker.off('drag');
|
||||||
google.maps.event.clearListeners(that.marker, 'dragend');
|
that.marker.off('dragend');
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ Ox.MapMarkerImage = (function() {
|
||||||
themeData = Ox.Theme.getThemeData();
|
themeData = Ox.Theme.getThemeData();
|
||||||
|
|
||||||
if (!cache[index]) {
|
if (!cache[index]) {
|
||||||
var color = options.rectangle ? [0, 0, 0, 0]
|
var color = options.rectangle ? [255, 255, 255, 1]
|
||||||
: options.color.concat(
|
: options.color.concat(
|
||||||
[options.type == 'place' ? 0.75 : 0.25]
|
[options.type == 'place' ? 0.75 : 0.25]
|
||||||
),
|
),
|
||||||
|
|
@ -50,12 +50,10 @@ Ox.MapMarkerImage = (function() {
|
||||||
c.context.strokeStyle = 'rgba(' + border.join(', ') + ')';
|
c.context.strokeStyle = 'rgba(' + border.join(', ') + ')';
|
||||||
c.context.arc(r, r, r - 1, 0, 360);
|
c.context.arc(r, r, r - 1, 0, 360);
|
||||||
c.context.stroke();
|
c.context.stroke();
|
||||||
cache[index] = new google.maps.MarkerImage(
|
cache[index] = document.createElement('img')
|
||||||
c.canvas.toDataURL(),
|
cache[index].src = c.canvas.toDataURL()
|
||||||
new google.maps.Size(options.size, options.size),
|
cache[index].width = options.size
|
||||||
new google.maps.Point(0, 0),
|
cache[index].height = options.size
|
||||||
new google.maps.Point(r, r)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return cache[index];
|
return cache[index];
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ Ox.MapPlace = function(options) {
|
||||||
|
|
||||||
options = Ox.extend({
|
options = Ox.extend({
|
||||||
east: 0,
|
east: 0,
|
||||||
|
editable: true,
|
||||||
editing: false,
|
editing: false,
|
||||||
geoname: '',
|
geoname: '',
|
||||||
map: null,
|
map: null,
|
||||||
|
|
@ -44,21 +45,24 @@ Ox.MapPlace = function(options) {
|
||||||
update();
|
update();
|
||||||
|
|
||||||
function update(updateMarker) {
|
function update(updateMarker) {
|
||||||
|
if (that.west > that.east) {
|
||||||
|
that.east += 360;
|
||||||
|
}
|
||||||
that.points = {
|
that.points = {
|
||||||
ne: new google.maps.LatLng(that.north, that.east),
|
ne: new maplibregl.LngLat(that.east, that.north),
|
||||||
sw: new google.maps.LatLng(that.south, that.west)
|
sw: new maplibregl.LngLat(that.west, that.south)
|
||||||
};
|
};
|
||||||
that.bounds = new google.maps.LatLngBounds(that.points.sw, that.points.ne);
|
that.bounds = new maplibregl.LngLatBounds(that.points.sw, that.points.ne);
|
||||||
that.center = that.bounds.getCenter();
|
that.center = that.bounds.getCenter();
|
||||||
that.lat = that.center.lat();
|
that.lat = that.center.lat;
|
||||||
that.lng = that.center.lng();
|
that.lng = that.center.lng;
|
||||||
Ox.extend(that.points, {
|
Ox.extend(that.points, {
|
||||||
e: new google.maps.LatLng(that.lat, that.east),
|
e: new maplibregl.LngLat(that.east, that.lat),
|
||||||
s: new google.maps.LatLng(that.south, that.lng),
|
s: new maplibregl.LngLat(that.lng, that.south),
|
||||||
se: new google.maps.LatLng(that.south, that.east),
|
se: new maplibregl.LngLat(that.east, that.south),
|
||||||
n: new google.maps.LatLng(that.north, that.lng),
|
n: new maplibregl.LngLat(that.lng, that.north),
|
||||||
nw: new google.maps.LatLng(that.north, that.west),
|
nw: new maplibregl.LngLat(that.west, that.north),
|
||||||
w: new google.maps.LatLng(that.lat, that.west)
|
w: new maplibregl.LngLat(that.west, that.lat)
|
||||||
});
|
});
|
||||||
// fixme: use bounds.toSpan()
|
// fixme: use bounds.toSpan()
|
||||||
that.sizeNorthSouth = (that.north - that.south)
|
that.sizeNorthSouth = (that.north - that.south)
|
||||||
|
|
@ -79,8 +83,9 @@ Ox.MapPlace = function(options) {
|
||||||
place: that
|
place: that
|
||||||
});
|
});
|
||||||
} else if (updateMarker) {
|
} else if (updateMarker) {
|
||||||
that.marker.update();
|
console.log("fixme update marker")
|
||||||
that.rectangle.update();
|
//that.marker.update();
|
||||||
|
//that.rectangle.update();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,178 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
class MapLibreRectangle {
|
||||||
|
constructor(options = {}) {
|
||||||
|
this.id = options.id || 'rectangle-' + Ox.uid();
|
||||||
|
this.bounds = options.bounds;
|
||||||
|
this.draggable = options.draggable || false;
|
||||||
|
this.onclick = options.onclick || null
|
||||||
|
}
|
||||||
|
|
||||||
|
_createRectangle() {
|
||||||
|
const coords = this._getPolygonCoordinates();
|
||||||
|
|
||||||
|
const rectangleGeoJSON = {
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [coords]
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
id: this.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var sourceId = `${this.id}-rectangles`
|
||||||
|
this.source = this.map.getSource(sourceId)
|
||||||
|
if (!this.source) {
|
||||||
|
this.map.addSource(sourceId, {
|
||||||
|
type: 'geojson',
|
||||||
|
data: {
|
||||||
|
type: 'FeatureCollection',
|
||||||
|
features: []
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.source = this.map.getSource(sourceId)
|
||||||
|
|
||||||
|
// Add fill layer
|
||||||
|
var layerId = `${this.id}-fill`
|
||||||
|
if (!this.map.getLayer(layerId)) {
|
||||||
|
this.map.addLayer({
|
||||||
|
id: layerId,
|
||||||
|
type: 'fill',
|
||||||
|
source: sourceId,
|
||||||
|
paint: {
|
||||||
|
'fill-color': '#088',
|
||||||
|
'fill-opacity': 0.3
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add outline layer
|
||||||
|
var layerId = `${this.id}-outline`
|
||||||
|
if (!this.map.getLayer(layerId)) {
|
||||||
|
this.map.addLayer({
|
||||||
|
id: layerId,
|
||||||
|
type: 'line',
|
||||||
|
source: sourceId,
|
||||||
|
paint: {
|
||||||
|
'line-color': '#000',
|
||||||
|
'line-width': 2
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
this.source._data.features.push(rectangleGeoJSON)
|
||||||
|
this.source.setData(this.source._data)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPolygonCoordinates() {
|
||||||
|
const sw = this.bounds._sw;
|
||||||
|
const ne = this.bounds._ne;
|
||||||
|
|
||||||
|
return [
|
||||||
|
[sw.lng, ne.lat], // NW
|
||||||
|
[ne.lng, ne.lat], // NE
|
||||||
|
[ne.lng, sw.lat], // SE
|
||||||
|
[sw.lng, sw.lat], // SW
|
||||||
|
[sw.lng, ne.lat] // Close polygon
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
setBounds(bounds) {
|
||||||
|
this.bounds = bounds;
|
||||||
|
const coords = this._getPolygonCoordinates();
|
||||||
|
const updatedData = {
|
||||||
|
type: 'Feature',
|
||||||
|
geometry: {
|
||||||
|
type: 'Polygon',
|
||||||
|
coordinates: [coords]
|
||||||
|
},
|
||||||
|
properties: {
|
||||||
|
id: this.id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var updated = false;
|
||||||
|
this.source._data.features.forEach(feature => {
|
||||||
|
if (feature.properties.id == this.id) {
|
||||||
|
feature.geometry = updatedData.geometry
|
||||||
|
updated = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if (!updated) {
|
||||||
|
this.source._data.features.push(updatedData)
|
||||||
|
}
|
||||||
|
this.source.setData(this.source._data)
|
||||||
|
}
|
||||||
|
|
||||||
|
getBounds() {
|
||||||
|
return this.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
_enableDragging() {
|
||||||
|
let isDragging = false;
|
||||||
|
let startPos;
|
||||||
|
|
||||||
|
this.map.on('mousedown', `${this.id}-fill`, (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
isDragging = true;
|
||||||
|
startPos = e.lngLat;
|
||||||
|
this.map.getCanvas().style.cursor = 'grabbing';
|
||||||
|
});
|
||||||
|
|
||||||
|
this.map.on('mousemove', (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const dx = e.lngLat.lng - startPos.lng;
|
||||||
|
const dy = e.lngLat.lat - startPos.lat;
|
||||||
|
|
||||||
|
const sw = [this.bounds[0][0] + dx, this.bounds[0][1] + dy];
|
||||||
|
const ne = [this.bounds[1][0] + dx, this.bounds[1][1] + dy];
|
||||||
|
|
||||||
|
this.setBounds([sw, ne]);
|
||||||
|
startPos = e.lngLat;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.map.on('mouseup', () => {
|
||||||
|
if (isDragging) {
|
||||||
|
isDragging = false;
|
||||||
|
this.map.getCanvas().style.cursor = '';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_enableClicking() {
|
||||||
|
this.map.on('click', `${this.id}-fill`, e => {
|
||||||
|
console.log('click', e)
|
||||||
|
if (this.onclick) {
|
||||||
|
e.preventDefault()
|
||||||
|
//e.stopPropagation()
|
||||||
|
this.onclick(e)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
remove() {
|
||||||
|
this.source._data.features = this.source._data.features.filter(feature => {
|
||||||
|
return feature.properties.id != this.id
|
||||||
|
})
|
||||||
|
this.source.setData(this.source._data)
|
||||||
|
}
|
||||||
|
|
||||||
|
add() {
|
||||||
|
this.setBounds(this.bounds)
|
||||||
|
}
|
||||||
|
|
||||||
|
addTo(map) {
|
||||||
|
this.map = map;
|
||||||
|
this._createRectangle();
|
||||||
|
if (this.draggable) this._enableDragging();
|
||||||
|
this._enableClicking();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
Ox.MapRectangle <f> MapRectangle Object
|
Ox.MapRectangle <f> MapRectangle Object
|
||||||
(options) -> <o> MapRectangle Object
|
(options) -> <o> MapRectangle Object
|
||||||
|
|
@ -25,14 +198,23 @@ Ox.MapRectangle = function(options) {
|
||||||
/*@
|
/*@
|
||||||
rectangle <f> google.maps.Rectangle
|
rectangle <f> google.maps.Rectangle
|
||||||
@*/
|
@*/
|
||||||
|
/*
|
||||||
that.rectangle = new google.maps.Rectangle({
|
that.rectangle = new google.maps.Rectangle({
|
||||||
clickable: true,
|
clickable: true,
|
||||||
bounds: that.place.bounds
|
bounds: that.place.bounds
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
that.rectangle = new MapLibreRectangle({
|
||||||
|
bounds: that.place.bounds,
|
||||||
|
});
|
||||||
|
that.rectangle.addTo(that.map.map);
|
||||||
|
that.rectangle.onclick = click
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
markers <a> array of markers
|
markers <a> array of markers (only corners for rectangle resizing)
|
||||||
@*/
|
@*/
|
||||||
that.markers = Ox.map(that.place.points, function(point, position) {
|
var cornerPositions = ['ne', 'nw', 'se', 'sw'];
|
||||||
|
that.markers = cornerPositions.map(function(position) {
|
||||||
return new Ox.MapRectangleMarker({
|
return new Ox.MapRectangleMarker({
|
||||||
map: that.map,
|
map: that.map,
|
||||||
place: that.place,
|
place: that.place,
|
||||||
|
|
@ -42,7 +224,7 @@ Ox.MapRectangle = function(options) {
|
||||||
|
|
||||||
setOptions();
|
setOptions();
|
||||||
|
|
||||||
function click() {
|
function click(e) {
|
||||||
if (
|
if (
|
||||||
that.map.options('editable')
|
that.map.options('editable')
|
||||||
&& that.place.editable
|
&& that.place.editable
|
||||||
|
|
@ -64,6 +246,7 @@ Ox.MapRectangle = function(options) {
|
||||||
? 'mapPlaceEditingBorder'
|
? 'mapPlaceEditingBorder'
|
||||||
: 'mapPlaceSelectedBorder'
|
: 'mapPlaceSelectedBorder'
|
||||||
]);
|
]);
|
||||||
|
/*
|
||||||
that.rectangle.setOptions({
|
that.rectangle.setOptions({
|
||||||
bounds: that.place.bounds,
|
bounds: that.place.bounds,
|
||||||
fillColor: color,
|
fillColor: color,
|
||||||
|
|
@ -72,14 +255,24 @@ Ox.MapRectangle = function(options) {
|
||||||
strokeOpacity: that.place.id[0] == '_' ? 0.5 : 1,
|
strokeOpacity: that.place.id[0] == '_' ? 0.5 : 1,
|
||||||
strokeWeight: 2
|
strokeWeight: 2
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
console.log("fixme", {
|
||||||
|
bounds: that.place.bounds,
|
||||||
|
fillColor: color,
|
||||||
|
fillOpacity: that.place.editing ? 0.1 : 0,
|
||||||
|
strokeColor: color,
|
||||||
|
strokeOpacity: that.place.id[0] == '_' ? 0.5 : 1,
|
||||||
|
strokeWeight: 2
|
||||||
|
});
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
add <f> add
|
add <f> add
|
||||||
@*/
|
@*/
|
||||||
that.add = function() {
|
that.add = function() {
|
||||||
that.rectangle.setMap(that.map.map);
|
that.rectangle.add()
|
||||||
google.maps.event.addListener(that.rectangle, 'click', click);
|
|
||||||
return that;
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -99,8 +292,7 @@ Ox.MapRectangle = function(options) {
|
||||||
remove <f> remove
|
remove <f> remove
|
||||||
@*/
|
@*/
|
||||||
that.remove = function() {
|
that.remove = function() {
|
||||||
that.rectangle.setMap(null);
|
that.rectangle.remove();
|
||||||
google.maps.event.clearListeners(that.rectangle);
|
|
||||||
return that;
|
return that;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -121,6 +313,8 @@ Ox.MapRectangle = function(options) {
|
||||||
that.update = function() {
|
that.update = function() {
|
||||||
Ox.Log('Map', 'UPDATE...')
|
Ox.Log('Map', 'UPDATE...')
|
||||||
setOptions();
|
setOptions();
|
||||||
|
// Update the visual rectangle bounds
|
||||||
|
that.rectangle.setBounds(that.place.bounds);
|
||||||
Ox.forEach(that.markers, function(marker) {
|
Ox.forEach(that.markers, function(marker) {
|
||||||
marker.update();
|
marker.update();
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ Ox.MapRectangleMarker = function(options) {
|
||||||
that[key] = val;
|
that[key] = val;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/*
|
||||||
that.markerImage = new google.maps.MarkerImage
|
that.markerImage = new google.maps.MarkerImage
|
||||||
that.marker = new google.maps.Marker({
|
that.marker = new google.maps.Marker({
|
||||||
cursor: that.position + '-resize',
|
cursor: that.position + '-resize',
|
||||||
|
|
@ -35,20 +36,39 @@ Ox.MapRectangleMarker = function(options) {
|
||||||
position: that.place.points[that.position],
|
position: that.place.points[that.position],
|
||||||
raiseOnDrag: false
|
raiseOnDrag: false
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
// Create a simple DOM element for the corner handle
|
||||||
|
var element = document.createElement('div');
|
||||||
|
element.style.width = '8px';
|
||||||
|
element.style.height = '8px';
|
||||||
|
element.style.backgroundColor = 'white';
|
||||||
|
element.style.border = '2px solid black';
|
||||||
|
element.style.borderRadius = '2px';
|
||||||
|
element.style.cursor = that.position + '-resize';
|
||||||
|
element.style.boxSizing = 'border-box';
|
||||||
|
|
||||||
|
that.marker = new maplibregl.Marker({
|
||||||
|
draggable: true,
|
||||||
|
element: element,
|
||||||
|
});
|
||||||
|
that.marker.setLngLat(that.place.points[that.position])
|
||||||
|
|
||||||
function dragstart(e) {
|
function dragstart(e) {
|
||||||
Ox.$body.addClass('OxDragging');
|
Ox.$body.addClass('OxDragging');
|
||||||
|
// In MapLibre GL, get position from marker directly
|
||||||
|
var lngLat = that.marker.getLngLat();
|
||||||
that.drag = {
|
that.drag = {
|
||||||
lat: e.latLng.lat(),
|
lat: lngLat.lat,
|
||||||
lng: e.latLng.lng()
|
lng: lngLat.lng
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function drag(e) {
|
function drag(e) {
|
||||||
// fixme: implement shift+drag (center stays the same)
|
// fixme: implement shift+drag (center stays the same)
|
||||||
Ox.Log('Map', e.pixel.x, e.pixel.y)
|
// In MapLibre GL, get current position from marker directly
|
||||||
var lat = Ox.limit(e.latLng.lat(), Ox.MIN_LATITUDE, Ox.MAX_LATITUDE),
|
var lngLat = that.marker.getLngLat();
|
||||||
lng = e.latLng.lng();
|
var lat = Ox.limit(lngLat.lat, Ox.MIN_LATITUDE, Ox.MAX_LATITUDE),
|
||||||
|
lng = lngLat.lng;
|
||||||
that.drag = {
|
that.drag = {
|
||||||
lat: lat,
|
lat: lat,
|
||||||
lng: lng
|
lng: lng
|
||||||
|
|
@ -90,32 +110,33 @@ Ox.MapRectangleMarker = function(options) {
|
||||||
add <f> add
|
add <f> add
|
||||||
@*/
|
@*/
|
||||||
that.add = function() {
|
that.add = function() {
|
||||||
that.marker.setMap(that.map.map);
|
that.marker.addTo(that.map.map);
|
||||||
google.maps.event.addListener(that.marker, 'dragstart', dragstart);
|
that.marker.on('dragstart', dragstart);
|
||||||
google.maps.event.addListener(that.marker, 'drag', drag);
|
that.marker.on('drag', drag);
|
||||||
google.maps.event.addListener(that.marker, 'dragend', dragend);
|
that.marker.on('dragend', dragend);
|
||||||
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
remove <f> remove
|
remove <f> remove
|
||||||
@*/
|
@*/
|
||||||
that.remove = function() {
|
that.remove = function() {
|
||||||
that.marker.setMap(null);
|
// Clean up MapLibre events
|
||||||
google.maps.event.clearListeners(that.marker);
|
that.marker.off('dragstart');
|
||||||
|
that.marker.off('drag');
|
||||||
|
that.marker.off('dragend');
|
||||||
|
// Remove marker from map
|
||||||
|
that.marker.remove();
|
||||||
|
return that;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*@
|
/*@
|
||||||
update <f> update
|
update <f> update
|
||||||
@*/
|
@*/
|
||||||
that.update = function() {
|
that.update = function() {
|
||||||
that.marker.setOptions({
|
// Just update position - visual stays the same during editing
|
||||||
icon: Ox.MapMarkerImage({
|
that.marker.setLngLat(that.place.points[that.position]);
|
||||||
mode: 'editing',
|
return that;
|
||||||
rectangle: true,
|
|
||||||
type: that.place.id[0] == '_' ? 'result' : 'place'
|
|
||||||
}),
|
|
||||||
position: that.place.points[that.position]
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return that;
|
return that;
|
||||||
|
|
|
||||||
284
source/UI/maplibre-gl/maplibre-gl-geocoder.css
Normal file
284
source/UI/maplibre-gl/maplibre-gl-geocoder.css
Normal file
|
|
@ -0,0 +1,284 @@
|
||||||
|
/* Basics */
|
||||||
|
.maplibregl-ctrl-geocoder,
|
||||||
|
.maplibregl-ctrl-geocoder *,
|
||||||
|
.maplibregl-ctrl-geocoder *:after,
|
||||||
|
.maplibregl-ctrl-geocoder *:before {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder {
|
||||||
|
font-size: 18px;
|
||||||
|
line-height: 24px;
|
||||||
|
font-family: "Open Sans", "Helvetica Neue", Arial, Helvetica, sans-serif;
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 240px;
|
||||||
|
z-index: 1;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: width 0.25s, min-width 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--input {
|
||||||
|
font: inherit;
|
||||||
|
width: 100%;
|
||||||
|
border: 0;
|
||||||
|
background-color: transparent;
|
||||||
|
margin: 0;
|
||||||
|
height: 50px;
|
||||||
|
color: #404040; /* fallback */
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
padding: 6px 45px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--input::-ms-clear {
|
||||||
|
display: none; /* hide input clear button in IE */
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--input:focus {
|
||||||
|
color: #404040; /* fallback */
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
outline: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
outline: thin dotted;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder .maplibregl-ctrl-geocoder--pin-right > * {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
right: 8px;
|
||||||
|
top: 7px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder,
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions {
|
||||||
|
box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Collapsed */
|
||||||
|
.maplibregl-ctrl-geocoder.maplibregl-ctrl-geocoder--collapsed {
|
||||||
|
width: 50px;
|
||||||
|
min-width: 50px;
|
||||||
|
transition: width 0.25s, min-width 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Suggestions */
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions {
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 4px;
|
||||||
|
left: 0;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
top: 110%; /* fallback */
|
||||||
|
top: calc(100% + 6px);
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-bottom-left .suggestions,
|
||||||
|
.maplibregl-ctrl-bottom-right .suggestions {
|
||||||
|
top: auto;
|
||||||
|
bottom: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions > li > a {
|
||||||
|
cursor: default;
|
||||||
|
display: block;
|
||||||
|
padding: 6px 12px;
|
||||||
|
color: #404040;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions > .active > a,
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions > li > a:hover {
|
||||||
|
color: #404040;
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
text-decoration: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--suggestion {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibre-ctrl-geocoder--suggestion-icon {
|
||||||
|
min-width: 30px;
|
||||||
|
min-height: 24px;
|
||||||
|
max-width: 30px;
|
||||||
|
max-height: 24px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--suggestion-info {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--suggestion-match {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--suggestion-title,
|
||||||
|
.maplibregl-ctrl-geocoder--suggestion-address {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--result {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibre-ctrl-geocoder--result-icon {
|
||||||
|
min-width: 30px;
|
||||||
|
min-height: 24px;
|
||||||
|
max-width: 30px;
|
||||||
|
max-height: 24px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--result-title {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--result-title,
|
||||||
|
.maplibregl-ctrl-geocoder--result-address {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Icons */
|
||||||
|
.maplibregl-ctrl-geocoder--icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
speak: none;
|
||||||
|
fill: #757575;
|
||||||
|
top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-search {
|
||||||
|
position: absolute;
|
||||||
|
top: 13px;
|
||||||
|
left: 12px;
|
||||||
|
width: 23px;
|
||||||
|
height: 23px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--button {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: #fff;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-close {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
margin-top: 8px;
|
||||||
|
margin-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--button:hover .maplibregl-ctrl-geocoder--icon-close {
|
||||||
|
fill: #909090;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-loading {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
margin-top: 5px;
|
||||||
|
margin-right: 0px;
|
||||||
|
-moz-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
|
||||||
|
-webkit-animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
|
||||||
|
animation: rotate 0.8s infinite cubic-bezier(0.45, 0.05, 0.55, 0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Animation */
|
||||||
|
@-webkit-keyframes rotate {
|
||||||
|
from {
|
||||||
|
-webkit-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotate {
|
||||||
|
from {
|
||||||
|
-webkit-transform: rotate(0);
|
||||||
|
transform: rotate(0);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Media queries*/
|
||||||
|
@media screen and (min-width: 640px) {
|
||||||
|
.maplibregl-ctrl-geocoder.maplibregl-ctrl-geocoder--collapsed {
|
||||||
|
width: 36px;
|
||||||
|
min-width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder {
|
||||||
|
width: 33.3333%;
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 20px;
|
||||||
|
max-width: 360px;
|
||||||
|
}
|
||||||
|
.maplibregl-ctrl-geocoder .suggestions {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon {
|
||||||
|
top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-close {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
margin-top: 3px;
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-search {
|
||||||
|
left: 7px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--input {
|
||||||
|
height: 36px;
|
||||||
|
padding: 6px 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibregl-ctrl-geocoder--icon-loading {
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
margin-top: -2px;
|
||||||
|
margin-right: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.maplibre-gl-geocoder--error {
|
||||||
|
color: #909090;
|
||||||
|
padding: 6px 12px;
|
||||||
|
font-size: 16px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
2
source/UI/maplibre-gl/maplibre-gl-geocoder.min.js
vendored
Normal file
2
source/UI/maplibre-gl/maplibre-gl-geocoder.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
source/UI/maplibre-gl/maplibre-gl.css
Normal file
1
source/UI/maplibre-gl/maplibre-gl.css
Normal file
File diff suppressed because one or more lines are too long
59
source/UI/maplibre-gl/maplibre-gl.js
Normal file
59
source/UI/maplibre-gl/maplibre-gl.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -92,13 +92,15 @@ def build_oxjs(downloads=False, geo=False):
|
||||||
|
|
||||||
# copy & link
|
# copy & link
|
||||||
ui_files = {'dev': [], 'min': []}
|
ui_files = {'dev': [], 'min': []}
|
||||||
|
css_files = ['maplibre-gl.css', 'maplibre-gl-geocoder.css']
|
||||||
for path, dirnames, filenames in os.walk(source_path):
|
for path, dirnames, filenames in os.walk(source_path):
|
||||||
for filename in filenames:
|
for filename in filenames:
|
||||||
if '_' not in path and filename[0] not in '._' \
|
if '_' not in path and filename[0] not in '._' \
|
||||||
and not filename.endswith('~') \
|
and not filename.endswith('~') \
|
||||||
and not filename.endswith('.css') \
|
|
||||||
and '/UI/svg' not in path \
|
and '/UI/svg' not in path \
|
||||||
and (geo or '/Geo/' not in path):
|
and (geo or '/Geo/' not in path):
|
||||||
|
if filename.endswith('.css') and filename not in css_files:
|
||||||
|
continue
|
||||||
# write copies in min path
|
# write copies in min path
|
||||||
source = os.path.join(path, filename)
|
source = os.path.join(path, filename)
|
||||||
is_jquery = re.search(r'^jquery-[\d\.]+\.js$', filename)
|
is_jquery = re.search(r'^jquery-[\d\.]+\.js$', filename)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue