/** * PolylineControl Class v0.1 * Copyright (c) 2008 * Author: Chris Marx * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * This class lets you add a polyline control for digitizing polylines to the GeometryControls framework. */ /** * Constructor for PolylineControl, which is used for creating/digitizing polylines. * @param {Object} opt_opts * @param {Object} button_opts * @param {String} opt_opts.img_up_url The image to display on the button in the up state * @param {String} opt_opts.img_down_url The image to display on the button in the down state * @param {String} opt_opts.name The name of the button * @param {String} opt_opts.tooltip The button's tooltip text * @param {Object} opt_opts.position Position options * @param {Array} position.controlPosition The offsets for the control's position * @param {Object} opt_opts.tooltip Tooltip Options * @param {Array} tooltip.anchor The offset for postion of tooltip * @param {String} tooltip.cursor_on The url for a custom css cursor * @param {String} tooltip.cursor_off The url for a custom css cursor * @param {Object} titles Titles used for tooltip * @param {String} titles.img_up_url Url of up image * @param {String} titles.img_down_url Url of down image * @param {String} titles.tooltip Text of tooltip * @param {Function} tooltip.callback Optional callback executed for tooltips * @param {Object} newGeometryOptions The options used when creating new geometries * @param {String} newGeometryOptions.strokeColor The stroke (line) color * @param {Integer} newGeometryOptions.strokeWieght The stroke (line) weight (width) * @param {Float} newGeometryOptions.strokeOpacity The stroke (line) opacity * @param {Object} opts * @param {Boolean} opts.clickable Is geometry clickable * @param {Boolean} opts.geodesic Is the line geodesic * @param {Boolean} multiEdit Determines whether one can add multiple geometries at once (rather than having the control turn off after each addition) * @param {Object} htmlTemplateParams Optional data object for html template * @param {String} cssId The prefix used for id's in the css * @param {Function} optionalGeometryListeners Optional function to add additional listeners to geometries * @param {Boolean} autoSave Determines whether saving geometry data also triggers a post to a DB * @param {Boolean} executeClassExtensions Should the class extenstions function be executed */ function PolylineControl(opt_opts) { var me = this; me.type = "polyline"; me.name = me.type + "Control"; me.zuper = null; me.digitizerShape = null; me.editLineHandler = null; me.endLineHandler = null; me.infoWindowHtml = ""; me.styles = { standard:{}//TODO }; /** * Array used for storage. Remember to check for nulls when exporting data * Geometries are tied to their index, so entries are cleared, not deleted * titles/descriptions are stored as [][0,1] with 0,1 entries for current/previous values */ me.storage = [/*array of GeometryControls#beans#Geometry*/]; //self documenting object with default settings me.Options = { button_opts:{ img_up_url:'http://www.google.com/intl/en_us/mapfiles/ms/t/Blu.png', img_down_url:'http://www.google.com/intl/en_us/mapfiles/ms/t/Bld.png', name:'polyline', tooltip:'Draw a line' }, position:{ controlPosition:[210,3] }, tooltip:{ anchor:[-30,-8], cursor_on:"", //only for overriding default digitizing cursor cursor_off:"", /** * Polyline titles are different from marker or polygon titles, because distance is reported in the tooltip. * Note: the tooltip function targets the first childNode (0ft ||  ) and is necessary for proper functioning */ titles:{ start: "0ft
Click to start drawing a line", middle: " 
Click to continue drawing a line", end: " 
Click a vertex once, or double click on the map to end this line" }, /** * @see GeometryControls#tooltipFactory * @param {GLatLng} latlng Latlng from mousemove listener * @param {DOM Element} tooltipContainer The div container used by tooltip */ callback:function(latlng,tooltipContainer){ if (me.digitizerShape.getVertexCount() > 0) { tooltipContainer.childNodes[0].data = me.zuper.convertFromMetric( me.digitizerShape.getVertex(me.digitizerShape.getVertexCount() - 1).distanceFrom(latlng), me.digitizerShape.getLength() ); } } }, newGeometryOptions: { strokeColor:"#0000FF", strokeWeight:3, strokeOpacity:0.25, opts:{ clickable:true, geodesic:false } }, multiEdit:false, //need this for polys? htmlTemplateParams:{}, cssId:"emmc-polygon", optionalGeometryListeners:null, autoSave:false, executeClassExtensions:true }; //overide the default marker options if(typeof(opt_opts)!="undefined"){ for (var o in opt_opts) { if(typeof(opt_opts[o]) === "object"){ for (var p in opt_opts[o]){ me.polygonOptions[o][p] = opt_opts[o][p]; } } else { me.polygonOptions[o] = opt_opts[o]; } } } else { //me._super.debug("??"); } }; PolylineControl.prototype = new GControl(); /** * Expected by GControl, sets control position */ PolylineControl.prototype.getDefaultPosition = function(){ var me = this; return me.zuper.getDefaultPosition(me.Options.position); }; /** * Extend for polygon specific implementation * @param {GMap2} map The map that has had this ExtMapTypeControl added to it. * @return {DOM Object} Div that holds the control */ PolylineControl.prototype.initialize = function(map){ var me = this; me.container = document.createElement("div"); me.container.id = "mymaps-control-"+me.Options.button_opts.name; var button = me.zuper.createButton({ controlName:me.name, button_opts:me.Options.button_opts, startDigitizing:function(){ me.startDigitizing(); }, stopDigitizing:function(t){ me.stopDigitizing(t); } }); me.container.appendChild(button.img); map.getContainer().appendChild(me.container); me.runInitFunctions(); return me.container; }; /** * Remember, all init functions get executed from #initialize because zuper isn't defined * until the control is added to zuper, with GeometryControls#addControl, and that's * when initialize is called automatically by GControl * @see #initialize */ PolylineControl.prototype.runInitFunctions = function(){ var me = this; me.tooltip(); me.assembleInfoWindowHtml(me.Options.htmlTemplateParams); me.extendGPolyline(); //check for any extended functions if(me.Options.executeClassExtensions){ me.extendBaseClass(); } }; /** * Starts digitizing process, turns on tooltip, calls function for geometry creation * @see #newGPolyline * @see #extendBaseClass Important! must annotate AOP extensions, otherwise you will be in pain later on... */ PolylineControl.prototype.startDigitizing = function() { var me = this, zuper = me.zuper, map = zuper.map, Options = me.Options; me.tooltip.on(Options.tooltip.titles["start"], Options.tooltip.callback); me.digitizerShape = me.newGPolyline([], Options.newGeometryOptions); map.addOverlay(me.digitizerShape); me.digitizerShape.enableDrawing({}); //change the tooltip text while digitizing //TODO this handler is never cleared, does the GEvent release the memory? me.editLineHandler = GEvent.addListener(me.digitizerShape,"lineupdated",function(){ switch(me.digitizerShape.getVertexCount()){ case 2: me.tooltip.tooltipContainer.innerHTML = Options.tooltip.titles["middle"]; break; case 3: me.tooltip.tooltipContainer.innerHTML = Options.tooltip.titles["end"]; break; } }); //listen for cancel events GEvent.addListener(me.digitizerShape,"cancelline",function(){ me.stopDigitizing(); }); //create permanent polyline me.endLineHandler = GEvent.addListener(me.digitizerShape,"endline",function(latlng){ var coords = []; for(var i=0;i -1){ var shortName = style.replace("stroke","").toLowerCase(); stroke[shortName] = strokeFlag = styles[style]; } } //TODO dont use flag, check if object is empty if(strokeFlag){ geometry.setStrokeStyle(stroke); } //update unsavedStyles for(var o in styles){ geometry.unsavedStyle[o] = styles[o]; } } //style.replace(/\b[a-z]/g,style[0].toUpperCase()); //capitalizes first letter }; /** * Loads polylines from json * @param {Object} record The json representation of polyline */ PolylineControl.prototype.loadPolylines = function(record){ var me = this; var polyline = me.createPolyline(record.coordinates,me.infoWindowHtml,false,record.style); me.storage[polyline.index].title = [record.title,record.title]; me.storage[polyline.index].description = [record.description,record.description]; me.zuper.map.addOverlay(polyline); return polyline; }; /** * Convenience method to be able to pass in options as an object * @param {Array} coords An array of GLatLngs * @param {Object} opts An object with the standard opts for a GPolyline */ PolylineControl.prototype.newGPolyline = function(coords, opts){ return new GPolyline(coords, opts.strokeColor, opts.strokeWeight, opts.strokeOpacity, opts.opts); }; /** * Convenience add getter/setters for objects that need translation between stored and displayed value * And do basic input validation (and revert to stored values if values are invalid) * Note: if these methods are eventually added to the api, then these methods will need to be updated * to use call() to access super method. */ PolylineControl.prototype.extendGPolyline = function(){ GPolyline.unsavedStyle = {}; GPolyline.savedStyle = {}; GPolyline.prototype.getStrokeWeight = function(){ return (this.unsavedStyle.strokeWeight || this.savedStyle.strokeWeight); }; GPolyline.prototype.setStrokeWeight = function(weight){ if(!isNaN(weight)){ this.unsavedStyle.strokeWeight = (weight > 20) ? 20 : (weight < 1) ? 1 : weight; } else { this.unsavedStyle.strokeWeight = this.savedStyle.strokeWeight; } return this.unsavedStyle.strokeWeight || this.savedStyle.strokeWeight; }; GPolyline.prototype.getStrokeOpacity = function(){ return (this.unsavedStyle.strokeOpacity || this.savedStyle.strokeOpacity) * 100; }; GPolyline.prototype.setStrokeOpacity = function(opacity){ if(!isNaN(opacity)){ var storedOpacity = (opacity > 100) ? 100 : (opacity < 0) ? 0 : opacity; this.unsavedStyle.strokeOpacity = storedOpacity / 100; } else { this.unsavedStyle.strokeOpacity = this.savedStyle.strokeOpacity; } return this.unsavedStyle.strokeOpacity || this.savedStyle.strokeOpacity; }; }; /** * AOP Extensions to the PolylineControl Class * Put All Custom Behaviors here, so that you can swap in new versions of the file * And still have your customizations intact (perhaps they will need tweaking) */ PolylineControl.prototype.extendBaseClass = function(){ var me = this, zuper = me.zuper, map = zuper.map; /** * Adds a distance readout to the tooltip during digitizing * @extends startDigitizing */ var addEditingDistanceToTooltip = function(){ zuper.aop.addAfter(me, 'addGeometryListeners', function(result){ var polyline = result; //result is the return value from original function var isTooltipOn = false; //currently there is no dragstart event. This should be used when it's added (hopefully...) /*GEvent.addListener(polyline,"dragstart",function(){ me.tooltip.on(convertFromMetric(polyline.getLength())); });*/ GEvent.addListener(polyline,"drag",function(){ if(!isTooltipOn){ me.tooltip.on(); isTooltipOn = !isTooltipOn; } me.tooltip.tooltipContainer.innerHTML = zuper.convertFromMetric(polyline.getLength()); }); GEvent.addListener(polyline,"dragend",function(){ me.tooltip.off(); isTooltipOn = !isTooltipOn; }); }); }(); };