diff --git a/.gitignore b/.gitignore index d65a16f..fafea6d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ .idea/vcs.xml .idea/workspace.xml node_modules/ +.idea/ diff --git a/example/main.js b/example/main.js index 9deaa29..d1e990f 100644 --- a/example/main.js +++ b/example/main.js @@ -3,13 +3,13 @@ var markerCluster; var cluster; var layerManager; requirejs(['../libraries/WorldWind/WorldWind', - 'js/LayerManager', '../MarkerCluster'], + 'js/LayerManager', '../src/MarkerCluster'], function (ww, LayerManager, MarkerCluster) { "use strict"; WorldWind.Logger.setLoggingLevel(WorldWind.Logger.LEVEL_WARNING); - WorldWind.configuration.baseUrl = "../libraries/WorldWind/" + WorldWind.configuration.baseUrl = "libraries/WorldWind/" wwd = new WorldWind.WorldWindow("canvasOne"); var layers = [ @@ -21,7 +21,7 @@ requirejs(['../libraries/WorldWind/WorldWind', for (var l = 0; l < layers.length; l++) { layers[l].layer.enabled = layers[l].enabled; - layers[l].layer.detailControl = 5; + layers[l].layer.detailControl = 1; wwd.addLayer(layers[l].layer); } @@ -48,30 +48,33 @@ requirejs(['../libraries/WorldWind/WorldWind', wwd.navigator.lookAtLocation.latitude = 37; wwd.navigator.lookAtLocation.longitude = 15; + for (var x = -90; x < 90; x = x + 1) { + for (var y = -90; y < 90; y = y + 1) { + var p = markerCluster.newPlacemark([y, x], null, {enabled: false}, {label: x + "_" + y}); + markerCluster.add(p); + } + } + markerCluster.generateCluster(); + /* + getJSON('example/places.json', function (geojson) { + + + geojson.features.forEach(function (f) { + var coords = f.geometry.coordinates; + var p = markerCluster.newPlacemark([coords[0], coords[1]], null, {enabled: false}); + markerCluster.add(p); + }); - getJSON('places.json', function (geojson) { - // markerCluster.generateJSONCluster(geojson); - /* - geojson.features.forEach(function (f) { - var coords = f.geometry.coordinates; - var p = markerCluster.newPlacemark([coords[0], coords[1]], null, {enabled: false}); - markerCluster.add(p); - }); + } - markerCluster.generateCluster(); - }); -*/ - for (var x = -100; x < 100; x++) { - for (var y = -90; y < 90; y++) { - var p = markerCluster.newPlacemark([x, y], null, {enabled: false},{label:x+"_"+y}); - markerCluster.add(p); - } - } - markerCluster.generateCluster(); - }); + + }); + */ + + function getJSON(url, callback) { var xhr = new XMLHttpRequest(); xhr.open('GET', url, true); diff --git a/example/index.html b/index.html similarity index 90% rename from example/index.html rename to index.html index 09e2eab..fdf0092 100644 --- a/example/index.html +++ b/index.html @@ -10,12 +10,12 @@ - +
diff --git a/supercluster.min.js b/libraries/supercluster.min.js similarity index 100% rename from supercluster.min.js rename to libraries/supercluster.min.js diff --git a/MarkerCluster.js b/src/MarkerCluster.js similarity index 52% rename from MarkerCluster.js rename to src/MarkerCluster.js index 1674d00..2f87812 100644 --- a/MarkerCluster.js +++ b/src/MarkerCluster.js @@ -1,23 +1,21 @@ -requirejs.config({ - baseUrl: '.', - paths: { - supercluster: '../supercluster.min', - } -}); +define(['../libraries/supercluster.min', '../libraries/WorldWind/WorldWind'], function (supercluster, wwe) { -define(['supercluster'], function (supercluster) { - - var MarkerCluster = function (wwd, markers, options) { + var MarkerCluster = function (globe, markers, options) { if (!options) { options = { - levels: 23 + levels: 9, + smooth: true } } + + this.options = options; this.placemarks = []; this.layer = new WorldWind.RenderableLayer("MarkerCluster"); - if (wwd) { - wwd.addLayer(this.layer); + if (globe) { + globe.addLayer(this.layer); } + this.globe = globe; + this.zoomLevel = 0; this.zoomLevels = options.levels; this.levels = []; this.maxReached = this.zoomLevels; @@ -37,7 +35,7 @@ define(['supercluster'], function (supercluster) { MarkerCluster.prototype.bindNavigator = function () { - var navigator = wwd.navigator; + var navigator = this.globe.navigator; var LookAtNavigator = WorldWind.LookAtNavigator; var self = this; @@ -57,80 +55,169 @@ define(['supercluster'], function (supercluster) { navigator.handleWheelEvent = function (event) { LookAtNavigator.prototype.handleWheelEvent.apply(this, arguments); - var range = this.range; + if (self.options.smooth) { + self.globe.goToAnimator.travelTime = 600; + if (!this.busy) { + var normalizedDelta; + if (event.deltaMode == WheelEvent.DOM_DELTA_PIXEL) { + normalizedDelta = event.deltaY; + } else if (event.deltaMode == WheelEvent.DOM_DELTA_LINE) { + normalizedDelta = event.deltaY * 40; + } else if (event.deltaMode == WheelEvent.DOM_DELTA_PAGE) { + normalizedDelta = event.deltaY * 400; + } + normalizedDelta *= 5; + var scale = 1 + (normalizedDelta / 1000); + var nav = this; + + var lat = this.lookAtLocation.latitude; + var lng = this.lookAtLocation.longitude; + var alt = this.range * scale; + var newPosition = new WorldWind.Position(lat, lng, alt); + nav.busy = true; + this.worldWindow.goTo(newPosition, function () { + nav.busy = false; + }); + this.applyLimits(); + this.worldWindow.redraw(); + } + } + var range = this.range; var res; - if (range > 10000) { - res = convertToRange(range, [10000, 6165728], [0, 16]); - res = Math.round(16 - res); + + + var ranges = [100000000,5294648, 4099739, 2032591, 1650505, 800762, 500000, 100000, 7000]; + + console.log(range); + if (range >= ranges[0]) { + res = 1; + } else if (range <= ranges[ranges.length - 1]) { + res = ranges.length; } else { - res = convertToRange(range, [500, 10000], [16, 23]); - res = Math.round(23 + 16 - res); + for (var x = 0; x < ranges.length; x++) { + if (range <= ranges[x] && range >= ranges[x + 1]) { + res = x + 1; + break; + } + } } + /* + + if (range > 10000) { + res = convertToRange(range, [10000, 5000000], [0, 11]);//0-16 + res = Math.round(16 - res); + } else { + res = convertToRange(range, [500, 10000], [11, 16]);//16-23 + res = Math.round(16 - 11 + res);//23+16 + } + */ + //res = Math.round(convertToRange(res, [0, 16], [0, self.zoomLevels]));//23 - res = Math.round(convertToRange(res, [0, 23], [0, self.zoomLevels])); + self.oldZoom = self.zoomLevel || 0; + self.zoomLevel = res; console.log(res); - self.hideAll(); + if (res < self.minReached) { + self.hideAll(); self.showZoomLevel(self.minReached);//possible overhead - }else if (res > self.maxReached) { - self.showZoomLevel(self.maxReached); - }else{ - self.showZoomLevel(res); + } else if (res > self.maxReached) { + self.hideAll(); + self.showInRange(self.maxReached); + //self.showZoomLevel(self.maxReached); + } else { + if (self.levels[res].length != self.levels[self.oldZoom].length) { + // self.showZoomLevel(res); + self.hideAll(); + self.showInRange(res); + self.globe.redraw();//dynamic + } + + //self.hideOutside(res); + + } }; }; - + MarkerCluster.prototype.showInRange = function (level) { + var h = $("#canvasOne").height(); + var v = []; + if (wwd.pickTerrain(new WorldWind.Vec2(h / 2, h / 2)).objects && wwd.pickTerrain(new WorldWind.Vec3(0, 0, 0)).objects [0]) { + + var center = wwd.pickTerrain(new WorldWind.Vec2(h / 2, h / 2)); + + center = center.objects[0].position; + + var l = this.globe.navigator.range / Math.cos(Math.PI / 8); + var base = Math.sqrt(Math.pow(l, 2) - Math.pow(this.globe.navigator.range, 2)); + + base = base / 100000; + var minLat = center.latitude - base; + var maxLat = center.latitude + base; + var minLng = center.longitude - base; + var maxLng = center.longitude + base; + + var bb = { + ix: minLat, + iy: minLng, + ax: maxLat, + ay: maxLng + }; + for (var x = 0; x < this.levels[level].length; x++) { + var point = this.placemarks[this.levels[level][x]]; + var p = point.position; + + if (bb.ix <= p.latitude && p.latitude <= bb.ax && bb.iy <= p.longitude && p.longitude <= bb.ay) { + this.show(point); + } + } + } else { + this.showZoomLevel(level); + } + }; MarkerCluster.prototype.generateCluster = function () { - var myJSON = { - "type": "FeatureCollection", - "features": [] - }; + var myJSON = '{"type": "FeatureCollection","features":['; newFeature = function (position) { - return { - "type": "Feature", - "properties": {}, - "geometry": { - "type": "Point", - "coordinates": [ - position.longitude, - position.latitude - ] - } - } + return '{"type": "Feature","properties": {},"geometry": {"type": "Point","coordinates": [' + + +position.longitude + ',' + + +position.latitude + ']}}'; }; - this.placemarks.forEach(function (p) { - myJSON.features.push(newFeature(p.position)) + this.placemarks.forEach(function (p, i) { + myJSON += newFeature(p.position) + ","; }); + myJSON = myJSON.slice(0, -1); + myJSON += ']}'; - this.generateJSONCluster(myJSON); + this.generateJSONCluster(JSON.parse(myJSON)); + this.showZoomLevel(this.zoomLevel); }; MarkerCluster.prototype.generateJSONCluster = function (geojson) { - + var self = this; cluster = supercluster({ log: true, - radius: 10060,//should be dynamic - extent: 256, - maxZoom: 23 + radius: 60,//should be dynamic + extent: 128, + maxZoom: self.zoomLevels }).load(geojson.features); var end = this.zoomLevels; for (var y = 0; y <= end; y++) { - var res = cluster.getClusters([-180, -90, 180, 90], y); + var res = cluster.getClusters([-180, -90, 180, 90], y + 1); var self = this; if (this.minReached == 0 && res.length > 0) { this.minReached = y; } - if (y > 0 && res.length > 0 && res.length == this.levels[y - 1].length) { + //if (y > 0 && res.length > 0 && res.length == this.levels[y - 1].length) { + if (res.length == geojson.features.length && y > 0 && res.length > 0 && res.length == this.levels[y - 1].length) { this.maxReached = y - 1; break; } else { @@ -148,6 +235,9 @@ define(['supercluster'], function (supercluster) { }); } } + if (!this.maxReached) { + this.maxReached = end; + } }; MarkerCluster.prototype.createZoomClusters = function (n) { @@ -182,14 +272,17 @@ define(['supercluster'], function (supercluster) { MarkerCluster.prototype.generatePlacemarks = function (coordinates, placemarkAttributes, options) { var lat, lng, alt; - if (coordinates[0] && coordinates[1]) { + if (typeof(coordinates[0]) !== "undefined" && typeof(coordinates[1]) !== "undefined") { lat = coordinates[0]; lng = coordinates[1]; - } else if (coordinates.lat && coordinates.lng) { + } else if (typeof(coordinates.lat) !== "undefined" && typeof(coordinates.lng) !== "undefined") { lat = coordinates.lat; lng = coordinates.lng; + } else { + throw ("Error in coordinates"); } + if (!options) { options = {}; } @@ -202,7 +295,7 @@ define(['supercluster'], function (supercluster) { if (!placemarkAttributes) { placemarkAttributes = new WorldWind.PlacemarkAttributes(null); - placemarkAttributes.imageScale = 5; + placemarkAttributes.imageScale = 2; placemarkAttributes.imageOffset = new WorldWind.Offset( WorldWind.OFFSET_FRACTION, 0.3, WorldWind.OFFSET_FRACTION, 0.0); @@ -211,7 +304,7 @@ define(['supercluster'], function (supercluster) { WorldWind.OFFSET_FRACTION, 0.5, WorldWind.OFFSET_FRACTION, 1.0); placemarkAttributes.labelAttributes.color = WorldWind.Color.YELLOW; - placemarkAttributes.labelAttributes.font.size = 100; + placemarkAttributes.labelAttributes.font.size = 40; } @@ -219,8 +312,9 @@ define(['supercluster'], function (supercluster) { placemark.label = options.label ? options.label : "MyMarker " + lat + " - " + lng; placemark.altitudeMode = options.altitudeMode ? options.altitudeMode : WorldWind.RELATIVE_TO_GROUND; placemarkAttributes = new WorldWind.PlacemarkAttributes(placemarkAttributes); - placemarkAttributes.imageSource = options.imageSource ? options.imageSource : WorldWind.configuration.baseUrl + "images/pushpins/plain-blue.png"; + placemarkAttributes.imageSource = options.imageSource ? options.imageSource : WorldWind.configuration.baseUrl + "images/pushpins/push-pin-red.png"; placemark.attributes = placemarkAttributes; + placemark.eyeDistanceScaling = true; placemark.eyeDistanceScalingLabelThreshold = 1e20; if (options.enabled == false) { @@ -260,7 +354,7 @@ define(['supercluster'], function (supercluster) { MarkerCluster.prototype.hideAll = function () { - for (var x = 0; x <= this.zoomLevels && x < this.maxReached; x++) { + for (var x = 0; x <= this.zoomLevels && x <= this.maxReached; x++) { this.hideZoomLevel(x); } }; @@ -281,6 +375,21 @@ define(['supercluster'], function (supercluster) { } }; + MarkerCluster.prototype.hideOutside = function (zoomLevel) { + var count = 0; + if (this.levels[zoomLevel]) { + for (var x = 0; x < this.levels[zoomLevel].length; x++) { + if (this.placemarks[this.levels[zoomLevel][x]].imageBounds) { + if (!this.placemarks[this.levels[zoomLevel][x]].isVisible(wwd.drawContext)) { + this.placemarks[this.levels[zoomLevel][x]].enabled = false; + count++; + } + } + } + } + console.log(count); + }; + MarkerCluster.prototype.removePlacemark = function (placemark) { this.layer.removeRenderable(placemark); this.placemarks.splice(this.placemarks.indexOf(placemark)); diff --git a/test/MarkerCluster.test.js b/test/MarkerCluster.test.js index 40f299a..b964cce 100644 --- a/test/MarkerCluster.test.js +++ b/test/MarkerCluster.test.js @@ -1,11 +1,15 @@ -define(['MarkerCluster','libraries/WorldWind/WorldWind'], function (MarkerCluster,WorldWind) { - +define(['src/MarkerCluster', 'libraries/WorldWind/WorldWind'], function (MarkerCluster, WorldWind) { describe('MarkerCluster test', function () { it('Example test', function () { - - var m = new MarkerCluster(); - var l=1; + var wwd = { + addLayer: function () { + }, + navigator: function () { + } + }; + var m = new MarkerCluster(wwd); + var l = 1; m.setLayer(l); expect(m.layer).toBe(1); }); diff --git a/karma.conf.js b/test/karma.conf.js similarity index 79% rename from karma.conf.js rename to test/karma.conf.js index fdf43b4..8eaa499 100644 --- a/karma.conf.js +++ b/test/karma.conf.js @@ -1,14 +1,15 @@ module.exports = function (config) { config.set({ - basePath: '', + basePath: '../', frameworks: ['jasmine', 'requirejs'], files: [ 'test/test-main.js', {pattern: 'test/*.js', included: false}, - {pattern: 'MarkerCluster.js', included: false}, + {pattern: 'src/MarkerCluster.js', included: false}, + {pattern: 'libraries/supercluster.min.js', included: false}, {pattern: 'libraries/WorldWind/*.js', included: false}, {pattern: 'libraries/WorldWind/**/*.js', included: false}, ],