6 <title>Marker drawing techniques</title>
8 <meta name="viewport" content="width=device-width, minimum-scale=1, maximum-scale=1">
9 <meta name="description" content="marker example">
10 <meta name="author" content="Ryan Westphal">
11 <link rel="stylesheet" type="text/css" href="css/style.css" />
13 <style type="text/css">
23 vertical-align: text-bottom;
26 /* boat launch css markers (as geomap shape labels)
27 * this style will affect all labels on the boat-launch-css service
28 * since these are our only real shapes, we could omit the #boat-launch-css
29 * part of the rule but it's here for example
30 * this does not affect the boat-launch-canvas service (those are drawn without labels)
32 * the top-left pixel of each label is placed at the boat launch point on the map
33 * by using height, width, and negative margins, we can make each background image appear an the correct location
35 * when we append the shapes in javascript, we have to pass an empty string (instead of null or undefined)
36 * so that a label gets generated, even though the label has no text
38 #boat-launch-css .geo-label {
39 background-image: url(img/boat.png);
53 <a href="../" class="docLink">< docs</a>
54 <a href="http://jsfiddle.net/ryanttb/uxEVX/embedded/" class="fiddleLink"><img src="img/jsfiddle.png" alt="" /> jsFiddle ></a>
56 <h1>Marker drawing techniques</h1>
57 <p>There are two reasonable ways to draw point markers. This example demos them. Check out the jsFiddle for docs.</p>
59 <legend>technique</legend>
61 <input type="radio" name="technique" value="css" checked />
62 <span>CSS labels</span>
65 <input type="radio" name="technique" value="canvas" />
66 <span>canvas service</span>
71 <script src="http://code.jquery.com/jquery-1.8.1.min.js"></script>
72 <!--<script src="../../js/excanvas.js"></script>
73 <script src="../../js/jsrender.js"></script>
74 <script src="../../js/jquery.mousewheel.js"></script>
75 <script src="../../js/jquery.ui.widget.js"></script>
76 <script src="../../js/jquery.geo.core.js"></script>
77 <script src="../../js/jquery.geo.geographics.js"></script>
78 <script src="../../js/jquery.geo.geomap.js"></script>
79 <script src="../../js/jquery.geo.tiled.js"></script>
80 <script src="../../js/jquery.geo.shingled.js"></script>-->
81 <script src="../jquery.geo-test.min.js"></script>
85 var canvas = document.createElement("canvas"), //< a canvas where we draw markers for the second service in the canvas service's src function
86 boatImage = $( '<img src="img/boat.png" />' ).load( function( ) {
87 // the image doesn't always load fast enough
88 // we could cache it but this is just as easy
89 // refresh the canvas-based service once when the image it needs is ready
91 // we could also have a flag to denote that the image hasn't loaded yet
92 // and not bother drawing the service the first time
93 boatLaunchCanvas.geomap( "refresh" );
94 } )[ 0 ], // HTML image element to draw onto the canvas
96 // check for cached data, why wait if it's not going to change that often
97 boatLaunchData = window.localStorage.getItem( "boatLaunchData" );
99 if ( boatLaunchData ) {
100 // cached data is a string, we need to parse it
101 boatLaunchData = JSON.parse( boatLaunchData );
105 var map = $("#map").geomap({
106 center: [ -120.5, 47.5 ], //< center on WA
107 zoom: 7, //< zoom in a bit
109 // an array of service objects
111 // MapQuest OSM basemap
112 // tiled, web mercator
115 src: function( view ) {
116 return "http://otile" + ((view.index % 4) + 1) + ".mqcdn.com/tiles/1.0.0/osm/" + view.zoom + "/" + view.tile.column + "/" + view.tile.row + ".png";
118 attr: "<p>Tiles Courtesy of <a href='http://www.mapquest.com/' target='_blank'>MapQuest</a> <img src='http://developer.mapquest.com/content/osm/mq_logo.png'></p>"
121 // a service where we draw boat launch sites as CSS markers
122 // it doesn't need a src, all the data is appended as shapes
123 // this is the quick and easy way, the second way (using canvas) is
124 // an advanced technique but can be a lot faster
126 id: "boat-launch-css",
131 // a service where we draw boat launch sites as dynamically generated canvas
132 // the src is a function that renders points for the current map view onto
133 // a canvas and returns the data URI
134 // note: this technique will not work in IE8
136 id: "boat-launch-canvas",
138 src: function( view ) {
139 // set the canvas' size to the currently requested view's size
140 canvas.width = view.width;
141 canvas.height = view.height;
143 var context = canvas.getContext( "2d" ), //< used to draw on the canvas
144 pixelSize = map.geomap( "option", "pixelSize" ), //< map size of a pixel in the current zoom level
145 halfMarkerWidth = pixelSize * 16, //< 1/2 width of the marker image in this zoom level
146 halfMarkerHeight = pixelSize * 18, //< 1/2 height of the marker image in this zoom level
147 coords, //< coordinates of the current boat dock in the loop below
148 pixelPoint; //< pixel location of a single boat dock when we draw them on the canvas
150 $.each( boatLaunchData, function( ) {
151 // first, determine if the point should be drawn in this view
152 // make sure to account for the size of the marker image
153 coords = this.geometry.coordinates;
155 // for a speed improvement, compare the map position of the marker image
156 // with the bbox of the current view
157 // if the marker should be in this view, draw it
158 // this "if" statement isn't required but with a large number of points
159 // can produce significant performance increases (don't draw if you don't have to)
160 if ( coords[ 0 ] >= view.bbox[ 0 ] - halfMarkerWidth &&
161 coords[ 1 ] >= view.bbox[ 1 ] - halfMarkerHeight &&
162 coords[ 0 ] <= view.bbox[ 2 ] + halfMarkerWidth &&
163 coords[ 1 ] <= view.bbox[ 3 ] + halfMarkerHeight ) {
165 // if we should draw this one, convert the map point to a pixel location for the current view
166 pixelPoint = map.geomap( "toPixel", this.geometry.coordinates );
168 // draw the image using canvas
169 // the x location is in position 0 of pixelPoint, the y location is in position 1
170 // however, we need to offset because of the shape of the marker image
171 context.drawImage( boatImage, pixelPoint[ 0 ] - 16, pixelPoint[ 1 ] - 32 );
175 // once done drawing, return the image as a data URI
176 return canvas.toDataURL( );
179 // this service is hidden by default;
180 // no sense drawing both, they have the same data
187 $( "input" ).click( function( ) {
188 // handle technique change clicks on the input buttons
189 // hide the service that is not the checked technique
190 // show the service that is
191 var technique = $("input:checked").val();
193 switch ( technique ) {
195 boatLaunchCanvas.geomap( "toggle", false );
196 boatLaunchCss.geomap( "toggle", true );
200 boatLaunchCss.geomap( "toggle", false );
201 boatLaunchCanvas.geomap( "toggle", true );
206 // keep references to services we interact with
207 var boatLaunchCss = $("#boat-launch-css").geomap( "option", "shapeStyle", { width: 0, height: 0 } ),
208 boatLaunchCanvas = $("#boat-launch-canvas");
210 // download boat launch sites (unless we already have them)
211 if ( boatLaunchData ) {
212 // immediately append them to the CSS-based services
213 // we only need to do this once
214 boatLaunchCss.geomap( "append", boatLaunchData, "" );
216 // show the canvas service now that we have them
217 //$( "#boat-launch-canvas" ).geomap( "toggle", true );
219 // wa.gov doesn't support CORS but AGS supports JSONP
221 url: "http://gismanager.rco.wa.gov/ArcGIS/rest/services/RCOBoatFacs/MapServer/1/query?where=1%3D1&returnGeometry=true&outFields=OBJECTID,FACILITY_NM,WTR_BDY&f=json",
223 success: function(result) {
224 if ( result && result.features ) {
225 // convert to GeoJSON Point Features
226 var geoJsonResults = $.map( result.features, function( value ) {
231 coordinates: [ value.geometry.x, value.geometry.y ]
234 name: value.attributes.FACILITY_NM,
235 waterBody: value.attributes.WTR_BDY
241 // may fail if quota exceeded
242 window.localStorage.setItem( "boatLaunchData", JSON.stringify( geoJsonResults ) );
245 // we finally have the boat launch data
246 // append them to the CSS-based services
247 // again, we only need to do this once
248 boatLaunchCss.geomap( "append", geoJsonResults, "" );
250 // show the canvas service now that we have them
251 //$( "#boat-launch-canvas" ).geomap( "toggle", true );
254 alert( "There are no Boat Launches :O" );
257 error: function(xhr) {
259 alert(":( " + xhr.statusText);