Google地图API

时间:2017-02-05 16:00:07

标签: javascript google-maps google-maps-api-3

现在,在google.com/maps页面上点击某个国家/地区或城市的名称/标签后,此区域会突出显示并打开左侧弹出窗口并显示相关信息。

是否可以通过Google Maps API实现相同的功能? 要在鼠标悬停时突出显示标签,请在点击时突出显示国家/地区,州或城市的边界,并通过用户标签/名称在js中进行选择。

我查看了Google Maps API JavaScript文档,但未找到类似的功能。

谢谢!

<小时/> 对不完整的问题感到抱歉。

有没有办法在地图上使用API​​设置可点击的国家/城市名称,例如google.com/maps页面?

1 个答案:

答案 0 :(得分:0)

推荐&#39;准​​系统&#39; GMap课程。碰巧我只是自己重新访问这个,所以尝试这个代码(它可能需要一些更改)

    /**
 *  'GMap'
 *  extends googlemaps to allow simpler coding in other apps, should be loaded after the main google maps
 *  a new instance of a google map is contructed calling this with identical arguments to the standard class and then returned
 *  the map name must be unique and reserved as a global by the client code, eg var pagemap outside of function, so the external callbacks
 *  can use this name to run a function against, eg pagemap.queryMove_do
 *
 */

function GMap(mapId, options) {
  this.name = null;
  this.map = null;
  this.bounds = null; //view point boundary
  this.gui_reg;
  this.mapoptions;
  this.calledbackonload;
  this.polygons = {};
  this.lngoffset = {'direction': '', referenceelem: ''};
  this.infowindows = {};
  this.infowindowindex = 0;
  GMap.prototype.__construct = function (mapId, options) {
    var mapbuffer = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects

    if (typeof window.MAP_INTERFACE_CTRL === 'undefined') {//if not created, global constant to co-ordinate ajax requests with map objects and 3rd party API, and allow funneling back to local objects
      window.MAP_INTERFACE_CTRL = new Data_buffer(); //this has to be vague as 3rd party api data is not always the same format, simply return data to the logged map interface objects
    }
    options.panControlOptions = model.array_defaults(
            options.ctrlcfg,
            {
              position: google.maps.ControlPosition.RIGHT_BOTTOM
            }
    );
    options.zoomControlOptions = model.array_defaults(
            options.ctrlcfg,
            {
              position: google.maps.ControlPosition.RIGHT_CENTER
            }
    );
    this.mapoptions = model.array_defaults(
            options,
            {
              center: new google.maps.LatLng(54.5, -7),
              zoom: 6,
              minzoom: 15,
              maxzoom: 21
            }
    );
    this.gui_reg = {location: {address: new Array()}};
    this.name = mapId;
    this.map = new google.maps.Map(document.getElementById(mapId), this.mapoptions);
    var selfobj = this;
    google.maps.event.addListener(//processes client registration for registered events
            this.map,
            'dragend', //center changed causes too many requests
            function () {
              selfobj.update_client_go("location");
            }
    );
//create invisible scratchpad
    if ($('#map_pool').length == 0) {
      $('body').append("<div id='map_pool'></div>");
      $('#map_pool').hide();
    }
    /*
     callback_obj = this;
     google.maps.event.addListener(this.map, 'tilesloaded', function(evt) {
     callback_obj.map.setZoom(9);
     });*/
  }

  /*****************************************************************/
  /* METHODS FOR BOTH GMAP AND CLIENT */
  /*****************************************************************/

  /*
   * defines offset of mapcentre away from an element such as custom controls
   * which overlay a large amount of map
   * positive or negative according to offset direction where the element might be on the
   * left or the right
   */
  GMap.prototype.set_lng_offset = function (elemid, dir) {
    var direction = 'right';
    if (dir == 'left') {
      direction = dir;
    }
    this.lngoffset = {direction: direction, referenceelem: elemid};
  }

  /*
   * returns lng offset according to definitions and current map situation
   */
  GMap.prototype.calc_lng_offset = function () {
//get lng width of map
    var eastlng = this.map.getBounds().getNorthEast().lng();
    var westlng = this.map.getBounds().getSouthWest().lng();
    var lngspan;
    if (westlng <= 0 && eastlng >= 0) {
      lngspan = Math.abs(westlng) + eastlng;
    } else if (westlng >= 0 && eastlng <= 0) {
      lngspan = (180 - westlng) + (180 - Math.abs(eastlng));
    } else if (westlng < 0 && eastlng < 0) {
      lngspan = Math.abs(eastlng) + westlng;
    } else {
      lngspan = eastlng - westlng;
    }

//get px width of map and referenced element
    var mapwidth = $("#" + this.name).width();
    var offsetpx = $("#" + this.lngoffset.referenceelem).width();
    //convert this px to lng offset for map by multiplying by lngpx
    var lngperpx = lngspan / mapwidth; //calc lng/px as lngpx
    //calc the offset lng
    var offsetlng = offsetpx * lngperpx;
    if (this.lngoffset.direction == 'left') {
      var offsetlng = 0 - offsetlng;
    }
    return offsetlng;
  }

  /*
   * returns an object with north south east west and centre coordinates of the current view
   * args.precision states number of figures after decimal returned - if not give a full result is returned.
   * @returns {float north,float east,float south,float west,float lat,float lng}
   */
  GMap.prototype.get_viewport_coords = function (args) {
    if (args == undefined) {
      args = {};
    }
    var coords = {};
    coords.north = this.map.getBounds().getNorthEast().lat();
    coords.east = this.map.getBounds().getNorthEast().lng();
    coords.south = this.map.getBounds().getSouthWest().lat();
    coords.west = this.map.getBounds().getSouthWest().lng();
    coords.lat = this.map.getCenter().lat();
    coords.lng = this.map.getCenter().lng();
    if (args.precision != undefined) {
      coords.north = coords.north.toFixed(args.precision);
      coords.east = coords.east.toFixed(args.precision);
      coords.south = coords.south.toFixed(args.precision);
      coords.west = coords.west.toFixed(args.precision);
    }
    return coords;
  }

  /*
   *    'map_query'
   *    takes a text query and queries to google for info
   *
   *    required due to APIs not being the same, a uuid is registered globally and a global process used to redirect
   *    because of this the global process must identify a map object by UUID and only expect a single argument as an object literal (JSON)
   *    this is then passed back to the object that initiated the ajax or UUID request
   *
   *    @param OBJECT query_cfg to send to gmap geocode
   *      @param string query_cfg.type states what type of query and what action to take, allowing multiple use for this method
   *      @param string query_cfg.query_data a string containing address data such as '<postcode>, <street>, <property>'
   *      @param string query_cfg.callback to return the data to
   */
  GMap.prototype.geo_query = function (query_cfg) {

//package pre query data and set defaults if not set
    query_cfg = model.array_defaults(
            query_cfg,
            {
              clientid: this.name,
              callback: 'update_client_do'//client code can set its own callback,else set callback to this
            }

    );
    var geo_queryid = MAP_INTERFACE_CTRL.callback_push(query_cfg);
    switch (query_cfg.type) {
      case "location"://returns location data at given lat lng coords
        var gcoder = new google.maps.Geocoder();
        gcoder.geocode({'location': {lat: query_cfg.lat, lng: query_cfg.lng}}, function (results, status) {
          var server_response = {results: results, status: status};
          eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
        });
        break;
      case "address_latlng":
        var gcoder = new google.maps.Geocoder();
        gcoder.geocode({'address': query_cfg.query_data}, function (results, status) {
          var server_response = {results: results, status: status};
          eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
        });
        break;
      case "sv_pano_latlng"://gets streetview pano for given lat lng
        var sv_service = new google.maps.StreetViewService();
        sv_service.getPanoramaByLocation(query_cfg.latlng, query_cfg.radius, function (results, status) {
          var server_response = {results: results, status: status};
          eval("MAP_INTERFACE_CTRL.callback_pop('" + geo_queryid + "',server_response)");
        });
        break;
      case "viewport_range"://returns lat lng for viewport and centre to callback
        var gcoder = new google.maps.Geocoder();
        var bounds = this.bounds;
        var center = this.map.getCenter();
        gcoder.geocode({address: change_request}, function (results, status) {
          callback_obj.viewport_range_do(results, status)
        });
        break;
    }
  }

  /*
   *    'formatted_geo_result'
   *    interprets geocoded results to a standard uniform independent of map api
   *    @param OBJECT geo_result data returned (in this map interface from google)
   */
  GMap.prototype.formatted_geo_result = function (geodata) {
    var formatted = {};
    var itemdefaults = {
      address: '',
      lat: null,
      lng: null
    }
    for (var i in geodata.results) {
      var item = {
        address: geodata.results[i].formatted_address,
        lat: geodata.results[i].geometry.location.lat(),
        lng: geodata.results[i].geometry.location.lng(),
      }
      formatted[i] = model.array_defaults(item, itemdefaults);
    }
    return formatted;
  }
  /*
   *    'map_change'
   *    takes a request and processes and changes map accordingly
   *
   *    callerobj is optional, if not given then callback will be back to this map object
   *    if a callerobj is given this must be to an object which can accept it
   *    (if callerobj is a top level function it needs to pass the object as keyword window - not 'window')
   *    The callback method is named as query type with _do appended eg address_move_do
   *    Some query types require the caller object to handle the query and DO NOT have a method defined in the map object
   *    If this map object DOES have the callback but a callback_obj is given then it will override this map object
   *
   *    @param string change_request free text to send to gmap object
   *    @param string change_type states what type of query and what action to take, allowing multiple use for this method
   *    @param string callback function to run on change completion.
   */
  GMap.prototype.map_change = function (change_type, change_data, callback) {
    switch (change_type) {
      case "lat_lng":
        this.map.panTo({lat: change_data.latlng.lat, lng: change_data.latlng.lng + this.calc_lng_offset()});
        break;
      case "address"://to address_latlng as translation for this map object
        this.geo_query({type: "address_latlng", clientid: this.name, callback: "map_change_do", change_type: change_type, query_data: change_data});
        break;
    }
    if (callback != undefined) {
      this.loadedCallback(callback);
    }
  }

  /*
   *    'map_change_do'
   *    processes any ajax return required by map_change
   *
   *    @param string change_request free text to send to gmap object
   *    @param string change_type states what type of query and what action to take, allowing multiple use for this method
   */
  GMap.prototype.map_change_do = function (pre_data, callback_data) {
    switch (pre_data.change_type) {
      case "address":
        this.map_change("lat_lng", {latlng: {lat: callback_data.results[0].geometry.location.lat(), lng: callback_data.results[0].geometry.location.lng()}}); //callback_data.results[0].geometry.location.lat()
        break;
    }
  }

  /*
   * permanently deletes all objects such as map markers (pins) etc
   */
  GMap.prototype.clearmapobjects = function () {
    this.map.clearMarkers();
  }

  GMap.prototype.panotestA = function () {
    var locquery = "framlingham tech centre";
    this.map_change("address", locquery);
    this.map.setZoom(20);
    this.geo_query("location", locquery)
    this.geo_query({type: "address_latlng", clientid: this.name, callback: "panotestB", query_data: locquery});
  }
  GMap.prototype.panotestB = function (pre_data, callback_data) {
    var pin = this.add_pin(callback_data.results[0].geometry.location.lat(), callback_data.results[0].geometry.location.lng(), {infopop: {content: 'hello', streetview: true}});
  }
  /*
   *    'add_pin'
   *    adds a pin to a map
   *
   *    @param float lat is latitude position
   *    @param float lng is longitude position
   *    @param object info_args other pin constructs such as the info displayed while clicking it
   *    @return object marker
   */
  GMap.prototype.add_pin = function (lat, lng, info_args) {
    info_args = model.array_defaults(
            info_args,
            {
              width: 16,
              height: 16,
              animation: google.maps.Animation.DROP,
              icon: {
                url: "http://" + ROOTUC + "//css/images/icons/ROYOdot_a.gif"
              },
              infopop: null
            }
    );
    /*
     var icon = {
     url: info_args.icon.url,
     size: new google.maps.Size(20, 20),
     origin: new google.maps.Point(0, 0),
     anchor: new google.maps.Point(0, 0)
     };
     */
    var iconImage = new google.maps.MarkerImage(
            info_args.icon.url, // url to image inc http://
            null, // desired size
            null, // offset within the scaled sprite
            null, // anchor point is half of the desired size
            new google.maps.Size(info_args.width, info_args.height) // required size
            );
    var pin = new google.maps.Marker({
      position: new google.maps.LatLng(lat, lng),
      map: this.map,
      title: info_args.label,
      icon: iconImage,
      optimized: false, //to allow for animations
      animation: info_args.animation,
    });

    if (info_args.infopop != null) {
      /*content is forced into div with black font as google default
       * is white text on white background ?!?!?!
       */

      info_args.infopop = model.array_defaults(
              info_args.infopop,
              {
                fontcolor: '#000000',
                infowidth: '40em',
                streetview: false
              });
      //standard google defs
      var infowindow = new google.maps.InfoWindow();
      //extra seperate data ref for client apps
      infowindow.metadata = {
        parent: this,
        parentid: this.infowindowindex,
      };
      this.infowindows[this.infowindowindex] = {
        infowidth: info_args.infopop.infowidth,
        content: info_args.infopop.content,
        fontcolor: info_args.infopop.fontcolor,
        latlng: {lat: lat, lng: lng},
        streetview: info_args.infopop.streetview
      };
      this.infowindowindex++;

      //build for pin click
      google.maps.event.addListener(pin, 'click', function () {
        var infodata = infowindow.metadata.parent.infowindows[infowindow.metadata.parentid];

        //build content
        var infocontent = "<div id='" + infowindow.metadata.parent.name + "infowindow" + (infowindow.metadata.parentid) + "' style='color:" + infodata.fontcolor + ";width:" + infodata.infowidth + "'>";
        infocontent += infodata.content;
        if (infodata.streetview) {
          infocontent += "<div id='" + infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid) + "' class='infostreetviewframe' >";
          infocontent += ". . . loading view</div>";
        }
        infocontent += "</div>";
        infowindow.setContent(infocontent);
        infowindow.open(this.map, pin);
        if (infodata.streetview) {
          var service = new google.maps.StreetViewService();
          infowindow.metadata.parent.geo_query({type: "sv_pano_latlng", latlng: infodata.latlng, radius: 50, clientid: infowindow.metadata.parent.name, streetviewframe: infowindow.metadata.parent.name + "infowindowsvframe" + (infowindow.metadata.parentid), callback: "add_streetview_do"});
        }
      });
    }
  }

  /*
   *    'add_streetview'
   *    for flexibility addition of a streetview into a named dom element id
   *
   *  @param object args
   *      @param.args string elemid the element to put the streetview into
   *
   */
  GMap.prototype.add_streetview_do = function (predata, callbackdata) {
    var svelement = document.getElementById(predata.streetviewframe);
    if (callbackdata.status == google.maps.StreetViewStatus.OK) {
      var targetPOVlocation = predata.latlng
      var svLatLng = {lat: callbackdata.results.location.latLng.lat(), lng: callbackdata.results.location.latLng.lng()};

      // var svLatLng = new google.maps.LatLng(callbackdata.results.location.latLng.lat(), callbackdata.results.location.latLng.lng());

      //var svLatLng = callbackdata.results.location.latLng;

      var povyaw = this.getBearing(svLatLng, targetPOVlocation);
      var sv = new google.maps.StreetViewPanorama(svelement);
      var svoptions = {
        position: svLatLng,
        addressControl: false,
        linksControl: false,
        panControl: false,
        zoomControlOptions: {
          style: google.maps.ZoomControlStyle.SMALL
        },
        pov: {
          heading: povyaw,
          pitch: 10,
          zoom: 1
        },
        enableCloseButton: false,
        visible: true
      };
      sv.setOptions(svoptions);
    } else {
      svelement.innerHTML = "no streetview available";
    }
  }

  /*
   *    'getBearing'
   *    calcs bearing from two latlngs
   *
   */
  GMap.prototype.getBearing = function (fromLatLng, targetLatLng) {
    var DEGREE_PER_RADIAN = 57.2957795;
    var RADIAN_PER_DEGREE = 0.017453;

    var dlat = targetLatLng.lat - fromLatLng.lat;
    var dlng = targetLatLng.lng - fromLatLng.lng;
    // We multiply dlng with cos(endLat), since the two points are very closeby,
    // so we assume their cos values are approximately equal.
    var bearing = Math.atan2(dlng * Math.cos(fromLatLng.lat * RADIAN_PER_DEGREE), dlat)
            * DEGREE_PER_RADIAN;

    if (bearing >= 360) {
      bearing -= 360;
    } else if (bearing < 0) {
      bearing += 360;
    }
    return bearing;
  }

  /*
   *    'add_polygon'
   *    adds a polygon to a map using an array of lat lng coords
   *    these are pushed in the order they appear in the locationarray
   *
   *  @param array locationarray - which event/change type triggers update of this element
   *
   */
  GMap.prototype.add_polygon = function (args) {

//construct and store polygon
    var polyobj = model.array_defaults(args, {name: "polygon", polygon: null, infohtml: null, box: null, focuson: false});
    //remove any same named polygon
    if (this.polygons[args.name] != undefined) {
      this.polygons[args.name].polygon.setMap(null);
      this.polygons[args.name] = null;
    }


    var newpolygon = new Array();
    polyobj.box = new google.maps.LatLngBounds();
    for (var l in args.polygon) {
      var point = new google.maps.LatLng(args.polygon[l].lat, args.polygon[l].lng);
      newpolygon.push(point);
      polyobj.box.extend(point);
    }
    polyobj.polygon = new google.maps.Polygon(
            {
              map: this.map,
              paths: newpolygon,
              strokeColor: '#00ff00',
              strokeOpacity: 0.75,
              strokeWeight: 2,
              fillColor: '#00ff00',
              fillOpacity: 0.15
            }
    );
    this.polygons[args.name] = polyobj;
    //set required changes to map
    if (args.infohtml != null) {
// content is forced into div with black font as google default is white text on white background ?!?!?!
      polyobj.info = new google.maps.InfoWindow(
              {
                content: "<div style='color:#000000'>" + args.infohtml + "</div>"
              });
      var selfobj = this;
      google.maps.event.addListener(this.polygons[args.name].polygon, 'click', function (event) {
        var point = event.latLng;
        selfobj.polygons[args.name].info.setPosition(point);
        selfobj.polygons[args.name].info.open(selfobj.map);
      });
    }

    if (polyobj.focuson) {
      this.map.fitBounds(polyobj.box);
    }
  }


  /*****************************************************************/
  /* methods for CLIENT --> GMAP change */
  /*********************************************************S ********/

  /*
   * allows gui to register elements so when changes / events occur in the map gui, its
   * parent gui can be updated.
   *    @param string change_type - which event/change type triggers update of this element
   *    @param string change_return - data required to be returned eg latlng or address etc
   *    @param string callback - the page element identifier to update
   */
  GMap.prototype.register_elem = function (change_type, event_return_cfg) {
    switch (change_type) {
      case "location":
        switch (event_return_cfg.return_type) {
          case "address":
            this.gui_reg.location.address.push(event_return_cfg);
            break;
        }
        break;
    }
  }

  /*****************************************************************/
  /* methods for GMAP --> CLIENT change */
  /*****************************************************************/

  /*
   * checks registered change type to send back to client
   *    @param string change_type - which event/change type triggers update of this element
   *    @param string data_request - data required to be returned
   *    @param string regelem - the parent gui identifier to update5
   *    @param object attr - which attribute to update eg for input, its value, for div its html
   */
  GMap.prototype.update_client_go = function (change_type, data_request) {
    switch (change_type) {
      case "location":
        var loc = this.get_viewport_coords();
        this.geo_query({type: "location", lat: loc.lat, lng: loc.lng});
    }
  }

  /*
   * actions client_update
   *    @param string change_type - which event/change type triggers update of this element
   *    @param string data_request - data required to be returned
   *    @param string regelem - the parent gui identifier to update5
   *    @param object attr - which attribute to update eg for input, its value, for div its html
   */
  GMap.prototype.update_client_do = function (pre_data, callback_data) {
    if (callback_data.status != "ZERO_RESULTS") {
      switch (pre_data.type) {
        case "location":
          var location_data_registrars = this.gui_reg.location;
          for (var loc_type_list in location_data_registrars) {
            switch (loc_type_list) {
              case "address":
                var address = callback_data.results[0].formatted_address;
                for (var r in location_data_registrars[loc_type_list]) {
                  var event_return = location_data_registrars[loc_type_list][r];
                  if (event_return != undefined) {
                    event_return.address = this.address_parse("postcode", address);
                    if (event_return.callback != undefined) {
                      eval(event_return.callback + "(event_return)");
                    }
                  }
                }
                break;
            }
          }
          break;
      }
    }
  }

  /*****************************************************************/
  /* PRIVATE METHODS */
  /*****************************************************************/
  GMap.prototype.address_parse = function (required_part, address) {
    var result = "";
    address = address.split(", ");
    var country = address[address.length - 1];
    switch (country) {
      case "UK":
        switch (required_part) {
          case "postcode":
            result = address[address.length - 2];
            result = result.substr(result.indexOf(" ") + 1);
            break;
        }
        break;
    }
    return result;
  }

  GMap.prototype.loadedCallback = function (callback) {
    this.calledbackonload = false;
    google.maps.event.addListenerOnce(this.map, 'tilesloaded', function () {
      google.maps.event.clearListeners(this.map, 'idle');
      eval(callback + '()');
    });
    google.maps.event.addListenerOnce(this.map, 'idle', function () {
      eval(callback + '()'); //add idle as a catch all
    });
    /*
     google.maps.event.addListenerOnce(this.map, 'idle', function() {
     eval(callback + '()')
     });
     */
  }

  /*
   * zooms map to next level from current depending on dir being '+' or '-'
   */
  GMap.prototype.incrementZoom = function (dir, callback) {
    var newZoom = this.map.getZoom();
    if (dir == '+') {
      newZoom++;
    }
    if (dir == '-') {
      newZoom--;
    }
    this.map.setZoom(newZoom);
    if (callback != undefined) {
      this.loadedCallback(callback);
    }
  }

  /*
   * zooms map to fully zoomed in
   */
  GMap.prototype.zoomMax = function (callback) {
    this.map.setZoom(this.mapoptions.maxzoom);
    if (callback != undefined) {
      this.loadedCallback(callback);
    }
  }

  /*
   * zooms map to fully zoomed out
   */
  GMap.prototype.zoomMin = function (callback) {
    this.map.setZoom(this.mapoptions.minzoom);
    if (callback != undefined) {
      this.loadedCallback(callback);
    }
  }

  /*
   * zooms map to contain the given box as per gmaps southwest / northeast corner specs
   */
  GMap.prototype.boxZoom = function (latfrom, latto, lngfrom, lngto) {
    var bottomleft = {lat: latfrom, lng: lngfrom};
    var topright = {lat: latto, lng: lngto};
    var box = new google.maps.LatLngBounds(bottomleft, topright);
    this.map.fitBounds(box);
  }

  /*
   * forces zoom in to min max params and returns true if it has been constrained
   * additional callback if required for when zoom to constraints has finished
   */
  GMap.prototype.constrainzoom = function (callback) {
    if (this.map.getZoom() > this.mapoptions.maxzoom) {
      this.map.setZoom(this.mapoptions.maxzoom);
    } else if (this.map.getZoom() < this.mapoptions.minzoom) {
      this.map.setZoom(this.mapoptions.minzoom);
    } else {
      return false;
    }
    if (callback != undefined) {
      this.loadedCallback(callback);
    }
    return true;
  }

  /*add extras to generic map object*/
  google.maps.Map.prototype.clearMarkers = function () {
    for (var i = 0; i < this.markers.length; i++) {
      this.markers[i].setMap(null);
    }
    this.markers = new Array();
  };
  /*
   * places an existing div by id as a map control
   */
  GMap.prototype.addcontrol = function (divid, posn) {
    if (posn == undefined) {
      posn = google.maps.ControlPosition.TOP_LEFT;
    }
    this.map.controls[posn].push(document.getElementById(divid));
  }
  /* translate normal functions to internal map object */
  /* trigger setup */
  this.__construct(mapId, options);
}