问题
Trying to build a location search app, that displays a google map - on the user drawing on the map it reveals any users that are located within that region.
I've also found this example - but it seems clunky/unsmooth - http://jsfiddle.net/pPMqQ/34/
http://jsfiddle.net/4xXQT/156/
var width = 960,
height = 500;
var points = d3.range(1, 5).map(function(i) {
return [i * width / 5, 50 + Math.random() * (height - 100)];
});
var dragged = null,
selected = points[0];
var line = d3.svg.line();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.attr("tabindex", 1);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.on("mousedown", mousedown);
svg.append("path")
.datum(points)
.attr("class", "line")
.call(redraw);
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup)
.on("keydown", keydown);
d3.select("#interpolate")
.on("change", change)
.selectAll("option")
.data([
"linear",
"step-before",
"step-after",
"basis",
"basis-open",
"basis-closed",
"cardinal",
"cardinal-open",
"cardinal-closed",
"monotone"
])
.enter().append("option")
.attr("value", function(d) { return d; })
.text(function(d) { return d; });
svg.node().focus();
function redraw() {
svg.select("path").attr("d", line);
var circle = svg.selectAll("circle")
.data(points, function(d) { return d; });
circle.enter().append("circle")
.attr("r", 1e-6)
.on("mousedown", function(d) { selected = dragged = d; redraw(); })
.transition()
.duration(750)
.ease("elastic")
.attr("r", 6.5);
circle
.classed("selected", function(d) { return d === selected; })
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });
circle.exit().remove();
if (d3.event) {
d3.event.preventDefault();
d3.event.stopPropagation();
}
}
function change() {
line.interpolate(this.value);
redraw();
}
function mousedown() {
points.push(selected = dragged = d3.mouse(svg.node()));
redraw();
}
function mousemove() {
if (!dragged) return;
var m = d3.mouse(svg.node());
dragged[0] = Math.max(0, Math.min(width, m[0]));
dragged[1] = Math.max(0, Math.min(height, m[1]));
redraw();
}
function mouseup() {
if (!dragged) return;
mousemove();
dragged = null;
}
function keydown() {
if (!selected) return;
switch (d3.event.keyCode) {
case 8: // backspace
case 46: { // delete
var i = points.indexOf(selected);
points.splice(i, 1);
selected = points.length ? points[i > 0 ? i - 1 : 0] : null;
redraw();
break;
}
}
}
回答1:
The latest code "http://jsfiddle.net/Cbk9J/188/"
This demonstrates how to draw polygons and freehand shapes.
$(".drawfreehand").click(function(e) {
e.preventDefault();
console.log("drawfreehand");
function drawFreeHand(){
//the polygon
that.newShape= new google.maps.Polyline({map:that.map,clickable:false});
//move-listener
var move = google.maps.event.addListener(that.map,'mousemove',function(e){
that.newShape.getPath().push(e.latLng);
});
//mouseup-listener
google.maps.event.addListenerOnce(that.map,'mouseup',function(e){
google.maps.event.removeListener(move);
var path= that.newShape.getPath();
that.newShape.setMap(null);
that.newShape=new google.maps.Polygon({map:that.map,path:path});
google.maps.event.clearListeners(that.map.getDiv(), 'mousedown');
enable()
that.newShape.setEditable(false);
that.revealInsidePolygon(that.newShape);
});
}
function disable(){
that.map.setOptions({
draggable: false,
zoomControl: false,
scrollwheel: false,
disableDoubleClickZoom: false
});
}
function enable(){
that.map.setOptions({
draggable: true,
zoomControl: true,
scrollwheel: true,
disableDoubleClickZoom: true
});
}
disable()
google.maps.event.addDomListener(that.map.getDiv(),'mousedown',function(e){
drawFreeHand()
});
});
回答2:
I've cleaned up the drawing code - and I've placed a function to try and simplify the shape. Having trouble though trying to achieve this though. The path becomes corrupt and becomes empty leaving just a single line.
Here is the code - http://jsfiddle.net/pPMqQ/53/
simplifyShape: function(svg){
console.log("simplifier");
var area = 1,
simplify = d3.geo.transform({
point: function(x, y, z) {
if (z >= area){
this.stream.point(x, y);
}
}
});
var path = d3.geo.path()
.projection(simplify);
var shape = d3.select('.selection');
var coordinates = shape.attr("d");
area = 1.3;
shape.attr("d", path)
}
回答3:
Bringing it all together. A challenge.
- The desired result waiting to happen
Ok so I have the drawing and simplifying shape code merged.
I've also found a google map, d3 example - which places down markers in an overlay state on a google map. The markers appear to pan/zoom with the map accordingly.
I would like to bring the drawing shape code into place with the markers - so everything is zoomable/pannable as expected.
Once this is complete then I would like to work on - editing the shape - highlighting markers only inside the shape area.
http://jsfiddle.net/Vjxpr/49/
shapeSelector = {
invoke: function(){
var that = this;
var width = 500;
var height = 400;
// An array to hold the coordinates
// of the line drawn on each svg.
var coords = []
this.line = d3.svg.line();
this.drag = this.getDragBehaviours()
this.svg = d3.select("#canvas1").append("svg")
.attr({
width: width,
height: height
}).call(that.drag);
this.bindEvents();
}
handles the shape drawing features
googleMapHandler.invoke is handling the generate of the google map - and the marker positions/overlay settings.
回答4:
In this example - I've ADDED a dummy shape to the map - similar way to the markers - but I am unsure at this stage - if its possible for the user to draw or edit/engage with the polygon.
I am trying to just move the polygon and rescale it in reference to the map.
http://jsfiddle.net/Cbk9J/91/
var shapelayer = d3.select(this.getPanes().overlayLayer).append("div")
.attr("class", "shape");
^ add shape layer div
//** shape area **//
var paths = "M184,174L209,266L345,247L364,143L256,144 L184,174L256,144";
var shapeArea = shapelayer.append("svg:svg")
.attr("width", "100%")
.attr("height", "100%")
.append("path")
.attr("d", paths)
.each(transformshape)
.attr("class", "selectioncomplete")
function transformshape(d) {
//d = new google.maps.LatLng(d.value.coordinates[1], d.value.coordinates[0]);
//d = projection.fromLatLngToDivPixel(d);
return d3.select(this)
//.style("left", (d.x-25) + "px")
//.style("top", (d.y-25) + "px");
}
//** shape area **//
add the shape area with dummy path data.
回答5:
I am now using the general Google map drawing tools for this. As the panning/zooming is handled with the UI.
My outstanding issue now - is to show ONLY the markers in a given shape area. I've started to incorporate code that should hide/show markers - but I need some help to get this working.
http://jsfiddle.net/Cbk9J/139/
setAllMap: function(map) {
for (var i = 0; i < this.markers.length; i++) {
this.markers[i].setMap(map);
}
},
clearMarkers: function() {
this.setAllMap(null);
},
showMarkers: function() {
this.setAllMap(map);
},
回答6:
A working example of drawing freehand and converting to a polygon: http://jsfiddle.net/PDf9G/1/
Basically, you do the drawing the way you were already doing it. Keep the canvas div for drawing in back, when the button is clicked, move it to the front. When dragend is called convert the array of pixel coordinates you were keeping to lat and lng like this:
for (var i = 0; i < divCoords.length; i++)
geoCoords.push(overlay.getProjection().fromContainerPixelToLatLng(new google.maps.Point(Number(divCoords[i][0]), Number(divCoords[i][1]))));
Still needs a lot of cleanup. I was going to continue getting the d3 marker hiding working, but looks like you figured that out.
来源:https://stackoverflow.com/questions/22423786/d3-js-lasso-drawing-polygon-shape-search-tool-on-a-google-map-getting-the-coo