Google Maps Drag & Drop

Drag & Drop demo to add or remove icons from a Google Map bij dragging them onto it, and use the coordinates if needed. The code also requires jQuery UI.

Download the StyleSheet

Download the JavaScript   (Minified)

Download the Dutch Province outline coordinates.

Download VS Project

Icons on map: 0

Code Snippets

The HTML part.

<div class="container">
    <div class="row">
        <div class="col-12 col-md-10">

            <div id="map_container" class="map_container">
                <div id="map_dragdrop" class="map_dragdrop"></div>
                <div id="map_recyclebin" class="map_recyclebin">
                    <img src="/images/trash.png" />
                </div>
            </div>

        </div>
        <div class="col-12 col-md-2 text-center pt-4 pt-md-0">

            <div id="map_icon_container" class="map_icon_container"></div>

        </div>
    </div>
    <div class="row">
        <div class="col-12 col-md-10 pt-3">

            <textarea id="map_results" class="form-control"></textarea>

        </div>
    </div>
</div>

<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?key=YourGoogleApiKey"></script>

The CSS.

.map_container {
    width: 100%;
    height: 500px;
    overflow: hidden;
}

.map_dragdrop {
    width: 100%;
    height: 100%;
}

.map_recyclebin {
    position: relative;
    height: 60px;
    width: 60px;
    top: -85px;
    right: -10px;
    background: white;
    box-shadow: rgba(0, 0, 0, 0.4) 0px 1px 4px -1px;
    border-radius: 2px;
}

.map_icon_container {
    font-size: 12px;
    font-weight: bold;
    width: 100%;
}

.map_icon {
    cursor: pointer;
    margin: 3px 0px 15px 0px;
}

.map_button {
    width: 150px;
}

/* make responsive */

@media (max-width: 767.98px) {
    .map_icon_container div {
        float: left;
        width: 20%;
    }
}

And finally the Javascript. THere is some code in there to generate the Dutch Provinces, but you can remove that easily.

var map;
var overlay;
var poiIconWidth = 32;
var poiIconHeight = 42;
var zIndex = 100;
var poiMarkerArry = [];
var binDimensions;
var binXoffset = 10;
var binYoffset = 25;
var zoomLevel = 7;

var $map_results;
var $map_dragdrop;
var $map_iconcounter;

//the icons
var poiData = [{
    id: 10,
    name: 'Icon #1',
    icon: '/files/icon_1.png'
}, {
    id: 20,
    name: 'Icon #2',
    icon: '/files/icon_2.png'
}, {
    id: 300,
    name: 'Icon #3',
    icon: '/files/icon_3.png'
}, {
    id: 400,
    name: 'Icon #4',
    icon: '/files/icon_4.png'
}, {
    id: 5000,
    name: 'Icon #5',
    icon: '/files/icon_5.png'
}];


//timeout because jquery script is loaded later that this js file on this page
setTimeout(function () {
    $map_results = $('#map_results');
    $map_dragdrop = $('#map_dragdrop');
    binDimensions = $('#map_recyclebin').height();
    $map_iconcounter = $('#map_iconcounter');

    $('#map_reset_button').bind('click', function () {
        resetPoiMap();
    });

    $('#map_load_button').bind('click', function () {
        loadJsonData();
    });

    initializePoiMap(52.52000, 5.28662);
}, 50);


//create the map
function initializePoiMap(lat, lng) {
    //coord for the center of the map
    var startpos = new google.maps.LatLng(lat, lng);

    //map options
    var options = {
        zoom: zoomLevel,
        center: startpos,
        zoomControl: true,
        mapTypeControl: false,
        scaleControl: false,
        streetViewControl: false,
        rotateControl: false,
        fullscreenControl: false,
        mapTypeId: google.maps.MapTypeId.TERRAIN
    };

    //start the map
    map = new google.maps.Map(document.getElementById('map_dragdrop'), options);

    //add an overlay
    overlay = new google.maps.OverlayView();
    overlay.draw = function () { };
    overlay.setMap(map);

    generatePoiMarkerlist();
    loadJsonData();

    //for netherlands only
    buildProvinces();
    buildIslands();
}


//add the icons that can be dragged to the html page and attach the drag function
function generatePoiMarkerlist() {
    //add the icons
    for (var i = 0; i < poiData.length; i++) {
        var content = '<div>' + poiData[i].name + '<br><img data-id="' + poiData[i].id + 
            '" data-index="' + i + '" class="map_icon" src="' + poiData[i].icon + '" /></div>';

        $('#map_icon_container').append(content);
    }

    //attach the drag event
    $('.map_icon').draggable({
        stop: function (e) {
            dragIn(e, this, $(this).data('index'));
        }
    });
}


//generate a marker on the map
function generatePoiMarker(poi) {
    var marker = new google.maps.Marker({
        position: new google.maps.LatLng(poi.mapPosition[0], poi.mapPosition[1]),
        map: map,
        draggable: true,
        icon: {
            url: poi.icon,
            size: google.maps.Size(poiIconWidth, poiIconHeight),
            target: google.maps.Point(poiIconWidth / 2, poiIconHeight / 2),
            origin: google.maps.Point(poiIconWidth / 2, poiIconHeight / 2)
        },
        title: poi.name,
        type: poi.id,
        zIndex: zIndex
    });

    marker.idnr = poiMarkerArry.length;
    poiMarkerArry.push(marker);
    zIndex++;
    updatePoiCoords();

    //add the mouse over event to put an icon always on top on hover
    google.maps.event.addListener(marker, 'mouseover', function () {
        this.setZIndex(zIndex);
        zIndex++;
    });

    //drag end event to update the marker data
    google.maps.event.addListener(marker, 'dragstart', function () {

        //set the map drag to false, otherwise the maps starts scrolling when you get to the edges
        map.setOptions({ draggable: false });
    });

    //drag end event to update the marker data
    google.maps.event.addListener(marker, 'dragend', function (e) {
        //enable map scrolling again
        map.setOptions({ draggable: true });

        var pixelPosition = getPixelPosition(this);

        //check if the icon is inside the recycle bin
        if (pixelPosition.x < binDimensions + binXoffset && 
            pixelPosition.x > binXoffset && 
            pixelPosition.y > ($map_dragdrop.height() - binDimensions) - binYoffset && 
            pixelPosition.y < $map_dragdrop.height() - binYoffset) {
            dragOut(e, this);
        } else {
            poiMarkerArry[this.idnr].position = e.latLng;
            updatePoiCoords();
        }
    });
}


//update the coord after dragging an icon
function updatePoiCoords() {
    var poiArr = [];
    for (var i = 0; i < poiMarkerArry.length; i++) {
        if (poiMarkerArry[i] !== null) {
            var poiMarker = {
                lat: poiMarkerArry[i].position.lat(),
                lng: poiMarkerArry[i].position.lng(),
                type: poiMarkerArry[i].type
            };

            poiArr.push(poiMarker);
        }
    }

    //make a json
    var json = JSON.stringify(poiArr);

    //not really needed, just used to neatly display the output inside the textarea
    json = json.replace('[', '[\n    ').replace(/},{/g, '},\n    {').replace(']', '\n]');

    //put it in an input
    $map_results.val(json);

    //show counter
    $map_iconcounter.html(poiArr.length);
}


//translate the map coordinates into pixels
function getPixelPosition(marker) {
    var scale = Math.pow(2, map.getZoom());
    var nw = new google.maps.LatLng(
        map.getBounds().getNorthEast().lat(),
        map.getBounds().getSouthWest().lng()
    );

    var worldCoordinateNW = map.getProjection().fromLatLngToPoint(nw);
    var worldCoordinate = map.getProjection().fromLatLngToPoint(marker.getPosition());
    var pixelOffset = new google.maps.Point(
        Math.floor((worldCoordinate.x - worldCoordinateNW.x) * scale),
        Math.floor((worldCoordinate.y - worldCoordinateNW.y) * scale)
    );

    return {
        x: pixelOffset.x,
        y: pixelOffset.y,
        right: $map_dragdrop.width() - pixelOffset.x,
        bottom: $map_dragdrop.height() - pixelOffset.y
    };
}


//an icon is dragged
function dragIn(e, icon, index) {
    var x = e.pageX - $map_dragdrop.offset().left;
    var y = e.pageY - $map_dragdrop.offset().top + 25;

    //check if the drag is on the map
    if (x > 0 && x < $map_dragdrop.width() && y > 0) {
        var point = new google.maps.Point(x, y);
        var position = overlay.getProjection().fromContainerPixelToLatLng(point);

        var poi = {
            mapPosition: [position.lat(), position.lng()],
            icon: poiData[index].icon,
            name: poiData[index].name,
            id: poiData[index].id
        };

        generatePoiMarker(poi);
    }

    $(icon).attr('style', 'position: relative; left: 0px; top: 0px');
}


//an icon is stopped dragging
function dragOut(e, marker) {
    poiMarkerArry[marker.idnr] = null;
    marker.setMap(null);
    updatePoiCoords();
}


//load the json data from the textbox
function loadJsonData() {
    //if there are existings icons, add them to the map
    if ($map_results.val() === '')
        return;

    //here the data comes from a textarea. but could be from any other source
    var data = $.parseJSON($map_results.val());

    //reset map also
    resetPoiMap();

    //loop all poi's
    for (var i = 0; i < data.length; i++) {

        //find the right marker based on id
        for (var j = 0; j < poiData.length; j++) {

            //if the item matches the id of the poi icon
            if (poiData[j].id === data[i].type) {

                var poi = {
                    mapPosition: [data[i].lat, data[i].lng],
                    icon: poiData[j].icon,
                    name: poiData[j].name,
                    id: poiData[j].id
                };

                generatePoiMarker(poi);
            }
        }
    }
}


//reset the map data
function resetPoiMap() {
    for (var i = 0; i < poiMarkerArry.length; i++) {
        poiMarkerArry[i].setMap(null);
    }

    $map_iconcounter.html('0');
    $map_results.val('');
    poiMarkerArry = [];

    //if you use the dutch province polygons
    for (var i = 0; i < polygonArr.length; i++) {
        polygonArr[i].setMap(null);
    }

    polygonArr = [];
}


//below code specifically for The Netherlands

var polygonArr = [];

//build the dutch province polygons
function buildProvinces() {
    for (var i = 0; i < provincesNL.length; i++) {
        var polygon = new google.maps.Polygon({
            paths: provincesNL[i],
            strokeColor: "#000000",
            strokeOpacity: 1.0,
            strokeWeight: 2,
            fillColor: provinceColors[i],
            fillOpacity: 0.2,
            clickable: true
        });

        polygonArr.push(polygon);

        polygon.setMap(map);
        attachToPolygon(polygon, provinceCenters[i]);
    }
}


//build the dutch island polygons
function buildIslands() {
    for (var i = 0; i < islandsNL.length; i++) {
        var polygon = new google.maps.Polygon({
            paths: islandsNL[i],
            strokeColor: "#000000",
            strokeOpacity: 1.0,
            strokeWeight: 2,
            fillColor: islandColors[i],
            fillOpacity: 0.2,
            clickable: true
        });

        polygonArr.push(polygon);

        polygon.setMap(map);
        attachToPolygon(polygon, islandCenters[i]);
    }
}


//add a function to the polygon that centers on the province centre on clicking
function attachToPolygon(poly, centerPoint) {

    //zoom and center to province
    google.maps.event.addListener(poly, 'click', function () {
        map.panTo(centerPoint);
        map.setZoom(9);
    });

    //zoom out the province to original zoom level
    google.maps.event.addListener(poly, 'rightclick', function () {
        map.setZoom(zoomLevel);
        map.panTo(dutchCenterPoint);
    });
}