server/camlistored/ui: map aspect, add switch for clustering

controlled by the CAMLI_DEV_MAP_CLUSTERING env var

Change-Id: I7d790ca354e4c06b45833affb94337cb120ec7d3
This commit is contained in:
mpl 2017-08-30 19:32:14 +02:00
parent 109b101b33
commit 1a830e5444
3 changed files with 55 additions and 15 deletions

View File

@ -497,12 +497,14 @@ func (ui *UIHandler) discovery() *camtypes.UIDiscovery {
pubRoots[v.Name] = rd
}
mapClustering, _ := strconv.ParseBool(os.Getenv("CAMLI_DEV_MAP_CLUSTERING"))
uiDisco := &camtypes.UIDiscovery{
UIRoot: ui.prefix,
UploadHelper: ui.prefix + "?camli.mode=uploadhelper",
DownloadHelper: path.Join(ui.prefix, "download") + "/",
DirectoryHelper: path.Join(ui.prefix, "tree") + "/",
PublishRoots: pubRoots,
MapClustering: mapClustering,
}
return uiDisco
}

View File

@ -96,6 +96,8 @@ type UIDiscovery struct {
// PublishRoots lists discovery information for all publishing roots,
// mapped by the respective root name.
PublishRoots map[string]*PublishRootDiscovery `json:"publishRoots"`
// MapClustering defines whether to cluster position markers on the map aspect.
MapClustering bool `json:"mapClustering"`
}
// PublishRootDiscovery contains discovery information for the publish roots.

View File

@ -88,7 +88,8 @@ cam.MapAspect = React.createClass({
availHeight: React.PropTypes.number.isRequired,
searchSession: React.PropTypes.instanceOf(cam.SearchSession).isRequired,
config: React.PropTypes.object.isRequired,
// TODO(mpl): add missing ones
updateSearchBar: React.PropTypes.func.isRequired,
setPendingQuery: React.PropTypes.func.isRequired,
},
componentWillMount: function() {
@ -98,6 +99,11 @@ cam.MapAspect = React.createClass({
East: 0.0,
West: 0.0,
};
this.clusteringOn = this.props.config.mapClustering;
if (this.clusteringOn == false) {
// Even 100 is actually too much, and https://github.com/camlistore/camlistore/issues/937 ensues
this.QUERY_LIMIT_ = 100;
}
// isMoving, in conjunction with ZOOM_COOLDOWN_, allows to actually ask for
// new results only once we've stopped zooming/panning.
this.isMoving = false;
@ -105,8 +111,11 @@ cam.MapAspect = React.createClass({
this.markers = {};
if (this.cluster) {
this.cluster.clearLayers();
} else if (this.markersGroup) {
this.markersGroup.clearLayers();
}
this.cluster = null;
this.markersGroup = null;
this.mapQuery = null;
this.locationFound = false;
this.locationFromMarkers = null;
@ -338,17 +347,27 @@ cam.MapAspect = React.createClass({
if (blobs == null) {
blobs = [];
}
if (this.cluster == null) {
this.cluster = L.markerClusterGroup({
// because we handle ourselves below what the visible markers are.
removeOutsideVisibleBounds: false,
animate: false,
});
// TODO(mpl): instead of all the ifs everywhere, we could just keep on using the
// cluster as a layer group, but completely disable clustering and spiderifying.
if (this.clusteringOn) {
if (this.cluster == null) {
this.cluster = L.markerClusterGroup({
// because we handle ourselves below what the visible markers are.
removeOutsideVisibleBounds: false,
animate: false,
});
}
this.cluster.addTo(this.map);
var toAdd = [];
this.cluster.unfreeze();
} else {
if (this.markersGroup == null) {
this.markersGroup = L.layerGroup();
this.markersGroup.addTo(this.map);
}
var toAdd = L.layerGroup();
}
this.cluster.unfreeze();
var toKeep = {};
var toAdd = [];
blobs.forEach(function(b) {
var br = b.blob;
var alreadyMarked = this.markers[br]
@ -409,7 +428,11 @@ cam.MapAspect = React.createClass({
}
toKeep[br] = true;
this.markers[br] = marker;
toAdd.push(marker);
if (this.clusteringOn) {
toAdd.push(marker);
} else {
toAdd.addLayer(marker);
}
if (!this.locationFromMarkers) {
// initialize it as a square of 0.1 degree around the first marker placed
@ -421,19 +444,32 @@ cam.MapAspect = React.createClass({
this.locationFromMarkers.extend(L.latLng(location.latitude, location.longitude));
}
}.bind(this));
var toRemove = [];
if (this.clusteringOn) {
var toRemove = [];
} else {
var toRemove = L.layerGroup();
}
goog.object.forEach(this.markers, function(mark, br) {
if (mark == null) {
return;
}
if (!toKeep[br]) {
this.markers[br] = null;
toRemove.push(mark);
if (this.clusteringOn) {
toRemove.push(mark);
} else {
toRemove.addLayer(mark);
}
}
}.bind(this));
this.cluster.removeLayers(toRemove);
this.cluster.addLayers(toAdd);
this.cluster.freeze();
if (this.clusteringOn) {
this.cluster.removeLayers(toRemove);
this.cluster.addLayers(toAdd);
this.cluster.freeze();
} else {
this.markersGroup.removeLayer(toRemove);
this.markersGroup.addLayer(toAdd);
}
// TODO(mpl): reintroduce the Around/Continue logic later if needed. For now not
// needed/useless as MapSorted queries do not support continuation of any kind.