1 /**
  2  * @class Pdok.Api
  3  *
  4  * 
  5  * Class implements an API for an easy to use embedding of
  6  * our pre-defined mapservices in any website with an OpenLayers slippy map.
  7  * This class is based upon the Terrestris WMS-API
  8  * The markerstyles are based upon the Mapicons from Nicolas Mollet (http://mapicons.nicolasmollet.com)
  9  * 
 10  * @examples
 11  * 
 12  *  <iframe width="400" height="300" frameborder="0" 
 13  *    scrolling="no" marginheight="0" marginwidth="0" 
 14  *    src="api/api.html?mloc=136260,456394&loc=136260,456394&zoom=8"
 15  *  >
 16  * OR
 17  *  <iframe width="400" height="300" frameborder="0" 
 18  *    scrolling="no" marginheight="0" marginwidth="0" 
 19  *    src="api/api.html?mloc=136260,456394&bbox=130000,450000,150000,470000"
 20  *  >
 21  */
 22 
 23 /** 
 24  * Pdok namespace, will hold Api namespace
 25  * @namespace 
 26  */
 27 Pdok = {}; 
 28 
 29 // current PdokKaartApi version
 30 Pdok.API_VERSION_NUMBER = '1.0.0';
 31 
 32 // CONFIG
 33 
 34 // The api-url is the base-url for api.js, markersdefs, layerdefs etc
 35 // The proxyhost is needed for the geocoder
 36 
 37 // PDOK LOKET PRODUKTIE
 38 Pdok.ApiUrl = 'http://kaart.pdok.nl/api';
 39 OpenLayers.ProxyHost = "http://"+window.location.host+"/proxy.php?url=";  // current pdokloket proxy
 40 
 41 // TEST
 42 //Pdok.ApiUrl = 'http://www.duif.net/pdok/api';
 43 //OpenLayers.ProxyHost = "http://"+window.location.host+"/cgi-bin/proxy.cgi?url=";
 44 
 45 // ONTWIKKEL
 46 //Pdok.ApiUrl = 'http://localhost/pdokkaart/api';
 47 //OpenLayers.ProxyHost = "http://"+window.location.host+"/cgi-bin/proxy.cgi?url=";
 48 
 49 
 50 OpenLayers.ImgPath = './img/';
 51 
 52 
 53 OpenLayers.Feature.Vector.style['default'].strokeColor = 'red';
 54 OpenLayers.Feature.Vector.style['default'].fillColor = 'red';
 55 OpenLayers.Feature.Vector.style['default'].pointRadius = 5;
 56 OpenLayers.Feature.Vector.style['default'].fillOpacity = 0.8;
 57 // small point otherwise we see circle
 58 OpenLayers.Feature.Vector.style['temporary'].pointRadius = 0;
 59 OpenLayers.Feature.Vector.style['temporary'].strokeColor = 'red';
 60 OpenLayers.Feature.Vector.style['temporary'].fillColor = 'red';
 61 // some translations
 62 OpenLayers.Lang["nl"] = OpenLayers.Util.applyDefaults({
 63     'unhandledRequest': "Het verzoek is niet afgehandeld met de volgende melding: ${statusText}",
 64     'Permalink': "Permanente verwijzing",
 65     'Overlays': "Kaartlagen",
 66     'Base Layer': "Ondergrond",
 67     'noFID': "Een optie die geen FID heeft kan niet bijgewerkt worden.",
 68     'browserNotSupported': "Uw browser ondersteunt het weergeven van vectoren niet.\nMomenteel ondersteunde weergavemogelijkheden:\n${renderers}",
 69     'minZoomLevelError': "De eigenschap minZoomLevel is alleen bedoeld voor gebruik lagen met die afstammen van FixedZoomLevels-lagen.\nDat deze WFS-laag minZoomLevel controleert, is een overblijfsel uit het verleden.\nWe kunnen deze controle echter niet verwijderen zonder op OL gebaseerde applicaties die hervan afhankelijk zijn stuk te maken.\nDaarom heeft deze functionaliteit de eigenschap \'deprecated\' gekregen - de minZoomLevel wordt verwijderd in versie 3.0.\nGebruik in plaats van deze functie de mogelijkheid om min/max voor resolutie in te stellen zoals op de volgende pagina wordt beschreven:\nhttp://trac.openlayers.org/wiki/SettingZoomLevels",
 70     'commitSuccess': "WFS-transactie: succesvol ${response}",
 71     'commitFailed': "WFS-transactie: mislukt ${response}",
 72     'googleWarning': "De Google-Layer kon niet correct geladen worden.\x3cbr /\x3e\x3cbr /\x3e\nOm deze melding niet meer te krijgen, moet u een andere achtergrondkaart kiezen in de laagwisselaar in de rechterbovenhoek.\x3cbr /\x3e\x3cbr /\x3e\nDit komt waarschijnlijk doordat de bibliotheek ${layerLib} niet correct ingevoegd is.\x3cbr /\x3e\x3cbr /\x3e\nOntwikkelaars: \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik hier\x3c/a\x3e om dit werkend te krijgen.",
 73     'getLayerWarning': "De laag ${layerType} kon niet goed geladen worden.\x3cbr /\x3e\x3cbr /\x3e\nOm deze melding niet meer te krijgen, moet u een andere achtergrondkaart kiezen in de laagwisselaar in de rechterbovenhoek.\x3cbr /\x3e\x3cbr /\x3e\nDit komt waarschijnlijk doordat de bibliotheek ${layerLib} niet correct is ingevoegd.\x3cbr /\x3e\x3cbr /\x3e\nOntwikkelaars: \x3ca href=\'http://trac.openlayers.org/wiki/${layerLib}\' target=\'_blank\'\x3eklik hier\x3c/a\x3e om dit werkend te krijgen.",
 74     'Scale = 1 : ${scaleDenom}': "Schaal = 1 : ${scaleDenom}",
 75     'W': "W",
 76     'E': "O",
 77     'N': "N",
 78     'S': "Z",
 79     'reprojectDeprecated': "U gebruikt de optie \'reproject\' op de laag ${layerName}.\nDeze optie is vervallen: deze optie was ontwikkeld om gegevens over commerciële basiskaarten weer te geven, maar deze functionaliteit wordt nu bereikt door ondersteuning van Spherical Mercator.\nMeer informatie is beschikbaar op http://trac.openlayers.org/wiki/SphericalMercator.",
 80     'methodDeprecated': "Deze methode is verouderd en wordt verwijderd in versie 3.0.\nGebruik ${newMethod}."
 81 });
 82 OpenLayers.Lang.setCode('nl'); 
 83 
 84 
 85 Proj4js.defs["EPSG:28992"] = "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.040,49.910,465.840,-0.40939,0.35971,-1.86849,4.0772";
 86 
 87 Pdok.createBaseUri = function(){
 88     var pathname = window.location.pathname;
 89     var path = pathname;
 90     if (pathname.search(/\.html|\.php|.jsp/)>0){
 91         pathparts = pathname.substr(0,pathname.search(/\.html|\.php|.jsp/)).split('/');
 92         path = pathparts.slice(0, pathparts.length-1);
 93         path = path.join('/');
 94         path +='/';
 95     }
 96     base = window.location.protocol+'//'+window.location.host + path;
 97     return base;
 98 }
 99 
100 // it is possible to override the markerdefinitions with a request parameter markersdef
101 if( OpenLayers.Util.getParameters()['markersdef'] != null){
102     Pdok.markersdef = OpenLayers.Util.getParameters()['markersdef'];
103 }
104 else {
105     // we use the markersdef from the api
106     Pdok.markersdef = Pdok.ApiUrl+'/js/pdok-markers.js';
107 }
108 // inject a script include for the markersdef, being either an external or the api included one
109 document.write('<script type="text/javascript" src="'+Pdok.markersdef+'"></script>');
110 
111 // it is possible to override the layerdefinitions with a request parameter layersdef
112 if( OpenLayers.Util.getParameters()['layersdef'] != null){
113     Pdok.layersdef = OpenLayers.Util.getParameters()['layersdef'];
114 }
115 else {
116     // we use the layersdef from the api
117     Pdok.layersdef = Pdok.ApiUrl+'/js/pdok-layers.js';
118 }
119 // inject a script include for the layersdef, being either an external or the api included one
120 document.write('<script type="text/javascript" src="'+Pdok.layersdef+'"></script>');
121 
122 /**
123  * Creates an instance of the Pdok Api based on an optional config object <a href="#constructor">more ...</a>
124  * 
125  * <p>If there is not a config object, all properties of the api have a default value.</p>
126  * 
127  * The config object is a hash object like: <pre>{ loc:'150000,380000', div:'map' }</pre>
128  * 
129  * <p>The OpenLayers.Util.getParameters() function returns such an object.
130  * Giving the possiblitiy to create an Api object which uses url params in just
131  * two lines of code:</p>
132  * <pre>
133  *    var config = OpenLayers.Util.getParameters();
134  *    api = Pdok.Api(config);
135  * </pre>
136  * All properties of the config object are overriding default values of Api
137  *
138  * @constructor
139  * @this {Pdok.Api}
140  * @param {Object} config A hash object with all possible config key/value pairs
141  */
142 Pdok.Api = function(config) {
143 
144     /**
145      * The zoom level property = the zoom level to start in (between 0 and 14)
146      * 
147      * @type int
148      */
149     this.zoom = null;
150 
151 	/**
152      * The location (x,y) to zoom to. A x,y commaseparated String (epsg:28992)
153      * @type String
154      */
155     this.loc = null;
156 
157     /**
158      * A boundingbox (w,s,e,n) to zoom to. A commaseparated String of west,south,east,north
159      * @type String
160      */
161     this.bbox = null;
162 
163     /**
164      * A commaseparated list of pdok id's (defined in pdok-layers.js). Eg: 'brt,top10'
165      * @type String
166      */
167     this.pdoklayers = null;
168 
169     /**
170      * Reference to OpenLayers Map object
171      * @type OpenLayers.Map
172      */
173     this.map = null;
174 
175     /**
176      * The location to put a marker. mloc = markerlocation
177      *
178      * By giving a mloc param (eg 125000,450000) in the config or querystring, a marker will be placed on that point (using mt0 as style)
179      * @type String
180      */
181     this.mloc = null;
182 
183     /**
184      * Reference to an image URL for the marker. To be used in combination with mloc
185      *
186      * It is overriding the mt0 externalGraphic image
187      * @type URL
188      */
189     this.mimg = null;
190 
191 
192     /**
193      * Markertype property. You can set a mt (styletype) for your mloc. Eg 'mt3'
194      * @type String
195      */
196     this.mt = null;
197 
198     /**
199      * If a popup should be used or not. Defaults to true
200      * @type boolean
201      */
202     this.showPopup = true;
203 
204     /**
205      * If a popup hover should be used or not Defaults to false
206      * @type boolean
207      */
208     this.hoverPopup = false;
209 
210     /**
211      * Reference to popup titel, only used in case of the use of mloc
212      * @type String
213      */
214     this.titel = null;
215 
216     /**
217      * Reference to popup text, only used in case of the use of mloc
218      * @type String
219      */
220     this.tekst = null;
221 
222     /**
223      * an URL to a text file, to be used loaded as features
224      *
225      * The text file format is determined by the OpenLayers.Format.Text Class
226      * 
227      * <p>Examples of such txt files:
228      * <pre>
229      * point  title   description
230      * # defaulting to epsg:28992 / RD
231      * # !!!!!! FIRST Y THEN X
232      * 517000,117960  foo omschrijving foo
233      * 511800,222000  faa omschrijving faa
234      * 541611,155111  fii omschrijving fii
235      * # alternative epsg:4326 / LatLon
236      * #52.64,4.84    foo omschrijving foo
237      * #52.59,6.38    faa omschrijving faa
238      * #51.73,5.39    fii omschrijving fii
239      * <pre>
240      * or 
241      * <pre>
242      * * lat  lon title   description
243      * # defaulting to epsg:28992 / RD
244      * # !!!!!! FIRST Y THEN X
245      * 517000 117960  foo omschrijving foo
246      * 511800 222000  faa omschrijving faa
247      * 541611 155111  fii omschrijving fii
248      * # alternative epsg:4326 / LatLon
249      * #52.64 4.84    foo omschrijving foo
250      * #52.59 6.38    faa omschrijving faa
251      * #51.73 5.39    fii omschrijving fii
252      * </pre>
253      * or an example with images and their dimensions
254      * <pre>
255      * lat   lon title   description iconSize    iconOffset  icon
256      * 450000 140000  Ministerie ELenI    Economische Zaken Landbouw en Innovatie 32,37   16,-37  http://kaart.pdok.nl/api/markertypes/flag-nl.png
257      * 460000 160000  Kadaster    Kadaster    32,37   -16,-18 http://kaart.pdok.nl/api/markertypes/flag-blue.png
258      * 470000 170000  Rijkswaterstaat Infrastructuur en Milieu    64,72   -32,-36 http://kaart.pdok.nl/api/markertypes/vlc.png
259      * 480000 180000  Ministerie IenM Infrastructuur en Milieu    40,46   -20,-23 http://kaart.pdok.nl/api/markertypes/flag-blue.png
260      * </pre>
261      * </p>
262      * @see <a href="../../../documentatie/examples/data/test1.txt">test1.txt</a>
263      * @see <a href="../../../documentatie/examples/data/test2.txt">test2.txt</a>
264      * @type URL
265      */
266     this.txturl = null;
267 
268     /**
269      * Wmts URL to be used as wmts layer. Always together with a wmtslayer and wmtsmatrixset parameter
270      * @type URL
271      */
272     this.wmtsurl = null;
273 
274     /**
275      * The layername of the wmts service. ALways together with a wmtsurl and wmtsmatrixset parameter
276      * @type String
277      */
278     this.wmtslayer = null;
279 
280     /**
281      * The matrixset of the wmts service. ALways together with a wmtsurl and wmtslayer parameter
282      * @type String
283      */
284     this.wmtsmatrixset = null;
285 
286     /**
287      * The WMS url to be used as a wms layer. Always together with a wmslayers parameter
288      * @type URL
289      */
290     this.wmsurl = null;
291 
292     /**
293      * The wms layers parameter, a commaseparated string of layername(s). Always together with a wmsurl parameter
294      * @type String
295      */
296     this.wmslayers = null;
297 
298     /**
299      * The TMS url to be loaded as a layer. Always together with tmslayer
300      * @type URL
301      */
302     this.tmsurl = null;
303     /**
304      * The tms layer parameter, a layer name of the tms service. Always together with a tmsurl parameter
305      * @type String
306      */
307     this.tmslayer = null;
308     /**
309      * The tmstype parameter, the image format to use (defaults to .png). Always together with a tmsurl and tmslayer parameter
310      * @type String
311      */
312     this.tmstype = 'png';
313 
314     /**
315      * Url to a KML file, to be used as feature layer (note that in KML coordinates always in wgs84/latlon)
316      * <p>
317      * An example of a minimalistic KML:
318      * <pre>
319      *  <kml xmlns="http://earth.google.com/kml/2.0">
320      *  <Placemark>
321      *  <name>Naam</name>
322      *  <description>Omschrijving</description>
323      *  <Point><coordinates>4.835397256016842,53.03622449301179</coordinates></Point>
324      *  </Placemark>
325      *  </kml>·
326      * <pre>
327      * Or one in which we define one of our styletypes;
328      * <pre>
329      *  <kml xmlns="http://earth.google.com/kml/2.0">
330      *  <Placemark>
331      *  <name>Naam</name>
332      *  <description>Omschrijving</description>
333      *  <Point><coordinates>4.835397256016842,53.03622449301179</coordinates></Point>
334      *  <ExtendedData><Data name="styletype"><value>mt3</value></Data></ExtendedData>
335      *  </Placemark>
336      *  </kml>·
337      * <pre>
338      * </p>
339      * The Styles in the KML will be used by default. If you do not want that, set the kmlstyles parameter to false
340      * @type URL
341      */
342     this.kmlurl = null;
343 
344     /**
345      * Property to determine if the internal styles of a KML file should be used for visualisation. 
346      *
347      * Defaults to false. In that case our styletype mt0 will be used. Of which the externalGraphic can be overridden via the mimg (markerimg) parameter
348      * @type Boolean
349      */
350     this.kmlstyles = true; // defaults to taking internal KML styling into account
351 
352     // Reference to layerswitch object (not to be used at the moment)
353     this.ls = false;
354 
355     /**
356      * To determine if the layer switcher should be shown or not. Defaults to true
357      * @type Boolean
358      */
359     this.showlayerswitcher = true;
360 
361     /**
362      * Reference to the DIV-id the map should be rendered in.
363      * Note that you have to set this one to have two maps in one page!
364      * @type String
365      */
366     this.div = 'map';
367 
368 	/**
369      * Reference to internal styles Object with all marker, lines and polygon rules.
370      *
371      * This Object is created from the pdok-markers.js file. Which is a json file with marker definitions
372      * @type Object
373      */
374     this.styles = null;
375 
376     // internal name of the features layer
377     this.FEATURESLAYER_NAME = "Markers";
378 
379     // this.features can come as KML string from config/params
380     // after handling this, it contains an array of features
381     this.features = [];
382 
383     /**
384      * Reference to featuresLayer (= layer where you draw feature on)
385      * @type OpenLayers.Layer.Vector
386      */
387     this.featuresLayer = null;
388 
389     // References to different drawing controls we use for the edit tools
390     this.drawFeaturePointControl = null;
391     this.drawFeatureLineControl = null;
392     this.drawFeaturePolygonControl = null;
393     this.editFeatureControl = null;
394     // References to select control for features in the featurelayer
395     this.selectControl = null;
396     // Reference to locationLayer (= layer to be used by the locationtool)
397     this.locationLayer = null;
398     // References to different drawing controls to be used by the locationtool
399     this.drawLocationPointControl = null;
400     this.drawLocationLineControl = null;
401     this.drawLocationPolygonControl = null;
402     this.editLocationControl = null;
403 
404     /**
405      * Boolean which determines if the Api started in Locationtool/Lokatieprikker modus or not. Defaults to false
406      * @type Boolean
407      */
408     this.locationtool = false;
409     /**
410      * Style to be used for the locationtool. Defaults to 'mt0'. NOTE: this also determines the TYPE of the locationtool (point, line or polygon) !!
411      * @type String
412      */ 
413     this.locationtoolstyle = 'mt0';
414     /**
415      * Name for X field to be used for the locationtool. Defaults to 'x'.
416      * @type String
417      */ 
418     this.locationtoolxfield = 'x';
419     /**
420      * Name for Y field to be used for the locationtool. Defaults to 'y'.
421      * @type String
422      */ 
423     this.locationtoolyfield = 'y';
424     /**
425      * Name for wkt field to be used for the locationtool. Defaults to 'wkt'. If this field is set, x and y will be ignored.
426      * @type String
427      */ 
428     this.locationtoolwktfield = 'wkt';
429     /**
430      * Minimal zoom to be able to set a point in the locationtool modus
431      * @type int
432      */ 
433     this.locationtoolzmin = '0';
434     /**
435      * Maximal zoom to be able to set a point in the locationtool modus
436      * @type int
437      */ 
438     this.locationtoolzmax = '30';
439 
440     /**
441      * Url to a markersdefinition file.
442      * <p>
443      * This is a simple json file with actual style properies like from 
444      * OpenLayers.Feature.Vector.style.default
445      * An example:
446      * <pre>
447      * Pdok.Api.prototype.defaultStyles=[ 
448      *         // all point marker styles will use mt0 as default 
449      *         // so you only have to define the props that are different from mt0 
450      *         // mt0, pt0, lt0 are defined in pdok-api.js, so defining it here will override that one 
451      *     { 
452      *         id: 'mt1', 
453      *         name: 'Informatiebord blauw', 
454      *         externalGraphic: Pdok.ApiUrl + '/markertypes/emblem-notice.png', 
455      *         graphicHeight: 32, 
456      *         graphicWidth: 32, 
457      *         graphicYOffset: -16 
458      *     }, 
459      *     {
460      *         id: 'pt18', 
461      *         fillOpacity: 0.0, 
462      *         strokeColor: 'orange', 
463      *         strokeWidth: 2, 
464      *         name: 'Oranje'
465      *     },
466      *     {
467      *         id:'lt8',  
468      *         strokeColor:'green',  
469      *         strokeWidth:3,  
470      *         strokeOpacity:0.5,  
471      *         name:'groen 2px transparant'  
472      *     }
473      * ]
474      * </pre>
475      * </p>
476      * @type url
477      */
478     this.markersdef = null;
479 
480     // an external markersdef is temporarily parked in Pdok.markersdef
481     if (Pdok.markersdef) {
482         this.markersdef = ''+Pdok.markersdef;
483     }
484 
485 
486     this.layersdef = null;
487 
488     // an external layersdef is temporarily parked in Pdok.layersdef
489     if (Pdok.layersdef) {
490         this.layersdef = ''+Pdok.layersdef;
491     }
492 
493     this.defaultLayers = OpenLayers.Util.applyDefaults(
494         this.defaultPdokLayers, this.defaultLayers);
495 
496     // create this.styles, based on either this.defaultStyles object, OR via a this.customStyles object
497     this.createStyles();
498 
499     if (config) {
500         // hack to make x and y fields null
501         // better fix would be to remove one of the three locationtool fields
502         // so you have either ONE field (wkt) or TWO (x and y)
503         if (config.locationtoolwktfield){
504             config.locationtoolxfield = null;
505             config.locationtoolyfield = null;
506         }
507         OpenLayers.Util.extend( this, config );
508     }
509 
510     this.createOlMap();
511 }
512 
513 // Array will contain OpenLayers.Style Objects
514 Pdok.Api.prototype.defaultStyles=[];
515 
516 /**
517  * Object which holds a Map of an shortname/id to'layer'-configuration objects. All layer objects holds hold an layertype, and other(OpenLayers-option) properties specific for that type of (OpenLayers)-layer.
518  * Be carefull to use the right properties! Eg a OpenLayers-WMS-Layer has a property 'layers' (note the plural form), while the OpenLayers-WMTS-Layer has a property 'layer' (without s!)
519  * @type Object
520  */
521 Pdok.Api.prototype.defaultPdokLayers = {
522         BRT: {
523             layertype: 'WMTS',
524             name: 'BRT Achtergrondkaart (WMTS)',
525             url: 'http://geodata.nationaalgeoregister.nl/wmts/',
526             layer: 'brtachtergrondkaart',
527             style: null,
528             matrixSet: 'EPSG:28992',
529             visibility: true, 
530             isBaseLayer: true,
531             attribution: '(c) OSM & Kadaster'
532         },
533         CBS_GEMEENTEN: {
534             layertype: 'WMS',
535             name: 'CBS Gemeentegrenzen 2008 (WMS)',
536             url: 'http://geodata.nationaalgeoregister.nl/bevolkingskernen2008/wms',
537             layers: 'gemeentegrens_generalisatie_2008',
538             transparent: 'true',
539             format: 'image/png',
540             visibility: true,
541             isBaseLayer: false,
542             singleTile: true
543         },
544         CBS_PROVINCIES: {
545             layertype: 'WMS',
546             name: 'CBS Provinciegrenzen 2008 (WMS)',
547             url: 'http://geodata.nationaalgeoregister.nl/bevolkingskernen2008/wms',
548             layers: 'provgrens_generalisatie_2008',
549             transparent: 'true',
550             format: 'image/png',
551             visibility: true,
552             isBaseLayer: false,
553             singleTile: true
554             }
555     }
556 
557 /**
558  *
559  * Given all properties of this Api-instance, create an OpenLayers Map object and return it
560  * For internal use only
561  *
562  * @return OpenLayers.Map object
563  */
564 Pdok.Api.prototype.createOlMap = function() {
565     var controls = []
566     if (!this.showlayerswitcher){
567     	controls = [
568             new OpenLayers.Control.Attribution(),
569             new OpenLayers.Control.Navigation(),
570             new OpenLayers.Control.Zoom(),
571 			new OpenLayers.Control.ScaleLine({bottomOutUnits:'',bottomInUnits:''})
572         ]
573     }
574     else if (this.showlayerswitcher == true || this.showlayerswitcher.toLowerCase() == "true"){
575     	controls = [
576             new OpenLayers.Control.Attribution(),
577             new OpenLayers.Control.Navigation(),
578             new OpenLayers.Control.Zoom(),
579 			new OpenLayers.Control.ScaleLine({bottomOutUnits:'',bottomInUnits:''}),
580             new OpenLayers.Control.LayerSwitcher()
581         ]
582     }
583     else
584     {
585     	controls = [
586             new OpenLayers.Control.Attribution(),
587             new OpenLayers.Control.Navigation(),
588             new OpenLayers.Control.Zoom(),
589 			new OpenLayers.Control.ScaleLine({bottomOutUnits:'',bottomInUnits:''})
590         ]
591     }
592     var olMap = new OpenLayers.Map ({
593         controls: controls,
594         maxExtent: new OpenLayers.Bounds(-285401.92,22598.08,595401.9199999999,903401.9199999999),
595         theme: null,
596 		resolutions: [3440.64, 1720.32, 860.16, 430.08, 215.04, 107.52, 53.76,
597 					26.88, 13.44, 6.72, 3.36, 1.68, 0.84, 0.42, 0.21],
598         units: 'm',
599         projection: new OpenLayers.Projection("EPSG:28992"),
600         div: this.div
601     });
602     this.map = olMap;
603 	
604 	function showBRT(){
605 		var layers = olMap.getLayersByName("BRT Achtergrondkaart");
606 		for(var layerIndex = 0; layerIndex < layers.length; layerIndex++)
607 			{
608 				olMap.setBaseLayer(layers[layerIndex]);
609 			}
610 	}
611 	function showTOP10(){
612 		var layers = olMap.getLayersByName("TOP10NL");
613 		for(var layerIndex = 0; layerIndex < layers.length; layerIndex++)
614 			{
615 				olMap.setBaseLayer(layers[layerIndex]);
616 			}
617 	}
618 	if (this.ls == true){
619 		function showLufo(){
620 			alert("Helaas, er zijn nog geen\nluchtfoto's beschikbaar binnen PDOK'");
621 		}
622 	    var panel = new OpenLayers.Control.Panel();
623 		var openBRT = new OpenLayers.Control({
624 			title:"Toon de BRT Achtergrondkaart als ondergrond",
625 			type: OpenLayers.Control.TYPE_BUTTON,
626 			trigger: showBRT,
627 			displayClass: "openBRT"
628 		});
629 		var openTOP10 = new OpenLayers.Control({
630 			title:"Toon de TOP10NL als ondergrond",
631 			type: OpenLayers.Control.TYPE_BUTTON,
632 			trigger: showTOP10,
633 			displayClass: "openTOP10"
634 		});
635 		var openLufo = new OpenLayers.Control({
636 			title:"Toon de luchtfoto's als ondergrond",
637 			type: OpenLayers.Control.TYPE_BUTTON,
638 			trigger: showLufo,
639 			displayClass: "openLufo"
640 		});
641 	
642 		panel.addControls([openLufo,openTOP10,openBRT]);
643 		olMap.addControl(panel);
644 	}
645 
646     // apply layer if a layer was given
647     if (this.pdoklayers != null) {
648         // if there is just one layer (without comma's), OL returns a String:
649         if (typeof this.pdoklayers == 'string') {
650             this.pdoklayers=this.pdoklayers.split(',');
651         }
652         this.addLayers(this.pdoklayers, olMap);
653         // if the map does NOT have a baseLayer, always add BRT layer
654         if (!olMap.baseLayer){
655             //olMap.addLayer(this.createWMTSLayer( this.defaultLayers.BRT ));
656             this.addLayers(['BRT']);
657         }
658     }
659     else {
660         // not layer param, at least load one default layer
661         this.addLayers(['BRT'], olMap);
662     }
663 
664     // possiblitiy to override externalGraphic of mt0
665     if (this.mimg != null){
666         this.styles['mt0'].externalGraphic = this.mimg;
667     }
668 
669     // apply WMSURL and WMSLAYERS if applicable
670     if ((this.wmsurl != null) && (this.wmslayers != null)) {
671         this.addWMS(this.wmsurl, this.wmslayers);
672     }
673 
674     // apply WMTSURL and WMTSLAYER and WMTSMATRIXSET if applicable
675     if ((this.wmtsurl != null) && (this.wmtslayer != null) && (this.wmtsmatrixset != null)) {
676         this.addWMTS(this.wmtsurl, this.wmtslayer, this.wmtsmatrixset);
677     }
678 
679     // apply TMSURL and TMSLAYERS if applicable
680     if ((this.tmsurl != null) && (this.tmslayer != null)) {
681         this.addTMS(this.tmsurl,this.tmslayer, this.tmstype);
682     }
683 
684     // apply KMLURL if applicable
685     if ((this.kmlurl != null)) {
686         this.addKML(this.kmlurl, this.kmlstyles);
687     }
688 
689     // apply TXTURL if applicable
690     if (this.txturl != null) {
691         this.addTxt(this.txturl);
692     }
693 
694     // apply BBOX or zoomlevel and location
695     if (this.bbox != null) {
696         olMap.zoomToExtent(OpenLayers.Bounds.fromArray(this.bbox).transform(olMap.displayProjection, olMap.getProjectionObject()));
697     }
698     else if (this.zoom != null && this.loc != null) {
699         if (typeof this.loc == 'string') {
700             this.loc = this.loc.split(',');
701         }
702         olMap.setCenter (new OpenLayers.LonLat(parseInt(this.loc[0]), parseInt(this.loc[1])), parseInt(this.zoom));
703     } else {
704         //olMap.zoomToMaxExtent();
705         olMap.zoomToExtent([-15000,300000,300000,640000],true);
706     }
707 
708     // featuresLayer is used for all features/markers
709     this.featuresLayer = new OpenLayers.Layer.Vector(this.FEATURESLAYER_NAME);
710     olMap.addLayer(this.featuresLayer);
711 
712     // locationLayer holds features for 'kaarprikker/locationtool'
713     this.locationLayer = new OpenLayers.Layer.Vector('locationtool', {
714         name:'locations', displayInLayerSwitcher:false
715     });
716     olMap.addLayer(this.locationLayer);
717 
718     if (typeof this.features == 'object' || typeof this.features == 'string') {
719         // meaning we received a features string (kml) from the outside
720         // features string handled, this.features now used as feature array
721         if (this.features.toString().length>0) {
722             var kmlString = this.features.toString();
723             this.features = [];
724             this.addFeaturesFromString(kmlString, 'KML');
725         }
726     }
727 
728     // add marker and use markertype if given, otherwise the default marker
729     // backward compatibility: mloc is alway point
730     if (this.mloc != null) {
731         var wkt = 'POINT('+this.mloc[0]+' '+this.mloc[1]+')';
732         if (this.mt==null){
733             this.mt='mt0'; // mt0 is default point symbol
734         }
735         this.features.push(this.createFeature(wkt, this.mt, this.titel, this.tekst));
736     }
737 
738     // selectControl for popups
739     this.selectControl = new OpenLayers.Control.SelectFeature(
740             this.featuresLayer, 
741             {
742                 hover:this.hoverPopup
743                 // implement some on magic to have a visible selection,
744                 // which we lost when we gave every feature a style
745                 /*
746                 onBeforeSelect:function(feature){
747                     if(feature.style){
748                         feature.style.strokeWidth+=2;
749                         feature.style.graphicWidth+=5;
750                         feature.style.graphicHeight+=5;
751                     }
752                 },
753                 onUnselect:function(feature){
754                     if(feature.style){
755                         feature.style.strokeWidth-=2;
756                         feature.style.graphicWidth-=5;
757                         feature.style.graphicHeight-=5;
758                     }
759                 }
760                 */
761             });
762     olMap.addControl(this.selectControl);
763     if (this.showPopup){
764         this.enablePopups();
765         this.selectControl.activate();
766     }
767 
768     this.featuresLayer.addFeatures(this.features);
769 
770     // enable Locationtool IF this.locationtool is set via config
771     if (this.locationtool){
772         var xorwkt = this.locationtoolwktfield;
773         if(this.locationtoolyfield){
774             yorwkt = this.locationtoolyfield;
775         }
776         this.enableLocationTool( this.locationtoolstyle,
777             this.locationtoolzmin,
778             this.locationtoolzmax,
779             xorwkt,
780             this.locationtoolyfield
781             );
782     }
783     return olMap;
784 }
785 
786 /**
787  * @private
788  * Click handler for a feature to show a popup
789  * Some magic is needed to handle empty popup content
790  */
791 Pdok.Api.prototype.onPopupFeatureSelect = function(evt) {
792     feature = evt.feature;
793     var content = "";
794     if (feature.attributes['name']!=null){
795         content=feature.attributes['name'];
796     }
797     if (feature.attributes['description']!=null){
798         content=content+"<br/>"+feature.attributes['description'];
799     }
800     if (!content || content.length==0)
801     {
802         content = ' ';
803     }
804     popup = new OpenLayers.Popup.FramedCloud("featurePopup",
805                 feature.geometry.getBounds().getCenterLonLat(),
806                 new OpenLayers.Size(100,100),
807                 content,
808                 null, true, function(evt) {
809                     this.hide();
810                     // deselect ALL features to be able to select this one again
811                     popup.feature.layer.selectedFeatures=[];
812                 }
813             );
814     feature.popup = popup;
815     popup.feature = feature;
816     this.map.addPopup(popup, true);
817 }
818 
819 /**
820  * @private
821  * Click handler for a feature to unselect a feature and close the popup
822  * Some magic is needed to handle empty popup content
823  */
824 Pdok.Api.prototype.onPopupFeatureUnselect = function(evt) {
825     feature = evt.feature;
826     if (feature.popup) {
827         popup.feature = null;
828         this.map.removePopup(feature.popup);
829         feature.popup.destroy();
830         feature.popup = null;
831     }
832 }
833 
834 /**
835  * Api method to disable popups in this Api instance
836  * @public
837  */
838 Pdok.Api.prototype.disablePopups = function(){
839         this.featuresLayer.events.un({
840             'featureselected': this.onPopupFeatureSelect,
841             'featureunselected': this.onPopupFeatureUnselect
842         });
843         return true;
844 }
845 /**
846  * Api method to enable popups in this Api instance
847  * @public
848  */
849 Pdok.Api.prototype.enablePopups = function(){
850         this.featuresLayer.events.on({
851             'featureselected': this.onPopupFeatureSelect,
852             'featureunselected': this.onPopupFeatureUnselect
853         });
854         return true;
855 }
856 
857 /**
858  * Returns the current map object of this instance.
859  * @public
860  * @return OpenLayer.Map object
861  */
862 Pdok.Api.prototype.getMapObject = function() {
863 	return this.map;
864 }
865 
866 /**
867  * Method to create a feature based on a wkt string and a typestyle (eg mt3), and giving it a title/name and description.
868  * @depricated only internally used at the moment?
869  * @param {String} wkt a WKT string for the geometry
870  * @param {String} typestyle a style id for a defined style (like mt0, mt3, pt0 or lt0)
871  * @param {String} name a title or name, to be shown as title in the popup
872  * @param {String} wkt a WKT string for the geometry
873  * @returns OpenLayers.Feature
874  */ 
875 Pdok.Api.prototype.createFeature = function(wkt, typestyle, name, description){
876     var wktFormat = new OpenLayers.Format.WKT();
877     // OpenLayers.Util.getParameters() splits parameters with comma's into an array
878     // because a LINESTRING wkt contains comma we have to concat them back
879     if (wkt instanceof Array) {
880         wkt = wkt.join();
881     }
882     var feature = wktFormat.read(wkt);
883     feature.attributes['name']=name;
884     feature.attributes['description']=description;
885     // only if we have this typestyle available
886     if (this.styles[typestyle]){
887         // TODO check if this style corresponds with the geometry type (eg for points only mt* etc)
888         feature.style = this.styles[typestyle];
889         feature.attributes['styletype']=typestyle;
890     }
891     else{
892         if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.Point'){
893             feature.style = this.styles['mt0'];
894         }
895         else if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.LineString'){
896             feature.style = this.styles['lt0'];
897         }
898         else if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.Polygon'){
899             feature.style = this.styles['pt0'];
900         }
901     }
902     return feature;
903 }
904 
905 /**
906  * Internal function to create all styles definition into 'this.styles'.
907  *
908  * Every geometry type has a default style: mt0, pt0, lt0. They are based on the OpenLayers.Feature.Vector.style['default']-style.
909  *
910  * By defining an markerdefs url with style definitions more styles will be created.
911  */
912 Pdok.Api.prototype.createStyles = function(){
913 
914     var olDefault = OpenLayers.Feature.Vector.style['default'];
915 
916     this.styles = {};
917 
918     // create a default styles for point: mt0, line: lt0 and polygon pt0
919     //.these HAVE to be here because there is code depending on the availability of those
920     // you can off course override this
921     var pdokDefaultPoint = OpenLayers.Util.applyDefaults(
922         {
923             id: 'mt0',
924             name: 'Standaard marker',
925             externalGraphic: Pdok.ApiUrl+"/markertypes/document-properties.png",
926             graphicHeight: 32,
927             graphicWidth: 32,
928             graphicYOffset: -32
929         }, {});
930     this.styles.mt0 = pdokDefaultPoint;
931     var pdokDefaultLine = OpenLayers.Util.applyDefaults(
932         {
933             id: 'lt0', 
934             strokeColor: '#273397', 
935             strokeWidth: 5,
936             strokeOpacity: 0.5, 
937             name: 'Standaard lijn'
938         }, {});
939     this.styles.lt0 = pdokDefaultLine;
940     var pdokDefaultPolygon = OpenLayers.Util.applyDefaults(
941         {
942             id: 'pt0', 
943             fillColor: '#273397',
944             fillOpacity: 0.3, 
945             strokeColor: '#273397', 
946             strokeWidth: 2, 
947             name: 'Standaard vlak'
948         }, {});
949     this.styles.pt0 = pdokDefaultPolygon;
950 
951     var pdokDefaultStyle = OpenLayers.Util.applyDefaults(
952     {
953         externalGraphic: Pdok.ApiUrl+"/markertypes/default.png",
954         graphicHeight: 37,
955         graphicWidth: 32,
956         graphicYOffset: -37,
957         pointRadius: 1
958     }, {});
959 
960     // if the the user added their own styles, they should create a variable 'defaultStyles'
961     // hereby overriding the inbuild defaultStyles
962     for (var i = 0; i<this.defaultStyles.length; i++){
963         var style = this.defaultStyles[i];
964         this.styles[style.id] = OpenLayers.Util.applyDefaults( style, pdokDefaultStyle);
965     }
966 
967     return true;
968 }
969 
970 /**
971  * Activating/enabling the Api DrawingTool. Important: based on the first char of the styletype, the type of feature geometry is set: m = marker/point, l = linestring, p = polygon
972  * 
973  * It will add a OpenLayers.Control.DrawFeature control to the api, so clicking in the map will generate a feature, to be added in the this.featuresLayer
974  * 
975  * @param {String} styletype (eg 'mt1' or 'lt1' or 'pt1') also determining the geometry type
976  * @param {Function} featureAddedCallback  Function handler to be called after feature is added
977  */
978 Pdok.Api.prototype.enableDrawingTool = function(styletype, featureAddedCallback){
979     //console.log('enableDrawingTool start');
980     this.disableDrawingTool();  // to be sure we do not have two drawfeature controls active at once
981     var apiStyles = this.styles;
982     var apiFeaturesLayer = this.featuresLayer;
983     var currentDrawControl;
984     // default to mt0 if called without a styletype
985     if (styletype == undefined) {
986         styletype = 'mt0';
987     }
988     if (styletype[0]=='m'){
989         if (this.drawFeaturePointControl==null){
990             this.drawFeaturePointControl = new OpenLayers.Control.DrawFeature(this.featuresLayer, OpenLayers.Handler.Point);
991             this.map.addControl(this.drawFeaturePointControl);
992         }
993         currentDrawControl = this.drawFeaturePointControl;
994         //currentDrawControl.handler.style = apiStyles[styletype];
995     }
996     else if (styletype[0]=='l'){
997         if (this.drawFeatureLineControl==null){
998             this.drawFeatureLineControl = new OpenLayers.Control.DrawFeature(this.featuresLayer, OpenLayers.Handler.Path);
999             this.map.addControl(this.drawFeatureLineControl);
1000         }
1001         currentDrawControl = this.drawFeatureLineControl;
1002         currentDrawControl.handler.style = apiStyles[styletype];
1003         currentDrawControl.handler.style.externalGraphic = null;
1004     }
1005     else if (styletype[0]=='p'){
1006         if (this.drawFeaturePolygonControl==null){
1007             this.drawFeaturePolygonControl = new OpenLayers.Control.DrawFeature(this.featuresLayer, OpenLayers.Handler.Polygon);
1008             this.map.addControl(this.drawFeaturePolygonControl);
1009         }
1010         currentDrawControl = this.drawFeaturePolygonControl;
1011         currentDrawControl.handler.style = apiStyles[styletype];
1012         currentDrawControl.handler.style.externalGraphic = null;
1013     }
1014     currentDrawControl.featureAdded = function(feature){
1015             feature.style = apiStyles[styletype];
1016             // also set an attribute 'styletype' to be able to export features with styletype
1017             feature.attributes['styletype'] = styletype;
1018             // we add a name attribute to be able to write to KML (the KML format uses that attribute as name)
1019             feature.attributes['name'] = ' ';  // we add   here because null or '' will cause the KML writer to not see it as value
1020             apiFeaturesLayer.redraw();
1021             if (featureAddedCallback){
1022                 featureAddedCallback(feature);
1023             }
1024     }
1025     currentDrawControl.activate();
1026     return true;
1027 }
1028 
1029 /**
1030  * Deactivating/disabling the Api DrawingTool.
1031  *
1032  */
1033 Pdok.Api.prototype.disableDrawingTool = function(){
1034     //console.log('disableDrawingTool');
1035     if (this.drawFeaturePointControl!=null){
1036         this.drawFeaturePointControl.deactivate();
1037     }
1038     if (this.drawFeatureLineControl!=null){
1039         this.drawFeatureLineControl.deactivate();
1040     }
1041     if (this.drawFeaturePolygonControl!=null){
1042         this.drawFeaturePolygonControl.deactivate();
1043     }
1044     return true;
1045 }
1046 
1047 /**
1048  * Deactivating/disabling the Api EditingTool.
1049  *
1050  */
1051 Pdok.Api.prototype.disableEditingTool = function(){
1052     if (this.editFeatureControl) {
1053         this.editFeatureControl.deactivate();
1054     }
1055     return true;
1056 }
1057 
1058 /**
1059  * Api method for ctivating/enabling the Api EditingTool.
1060  * 
1061  * It will add a OpenLayers.Control.ModifyFeature control to the api, so clicking on a feature in the map will select it and make it editable, and when finished call the featureModifiedFunction
1062  * 
1063  * @param {Function} featureModifiedFunction  Function handler to be called when you are ready with changing the geometry
1064  */
1065 Pdok.Api.prototype.enableEditingTool = function(featureModifiedFunction){
1066     if (this.editFeatureControl == null) {
1067         this.editFeatureControl = new OpenLayers.Control.ModifyFeature(this.featuresLayer);
1068         this.map.addControl(this.editFeatureControl);
1069         /*featureModifiedFunction = function(ft){
1070             console.log(ft)
1071         }
1072         featureBeforeModifiedFunction = function(ft){
1073             console.log(ft)
1074         }*/
1075         this.featuresLayer.events.on({
1076               //"beforefeaturemodified": featureBeforeModifiedFunction,
1077               "beforefeaturemodified": featureModifiedFunction,
1078               "featuremodified": featureModifiedFunction
1079         });
1080     }
1081     this.editFeatureControl.activate();
1082     return true;
1083 }
1084 
1085 /**
1086  * Api method to set the current center of the map.
1087  * 
1088  * @param {Array or Atring} loc An array of two coordinates: like [x,y] OR a commaseparated String with the two coordinates
1089  */
1090 Pdok.Api.prototype.setLocation = function(loc) {
1091     // if loc is a string like '150000,450000', split
1092     if( typeof(loc) == 'string'){
1093         loc = loc.split(',');
1094     }
1095     this.map.setCenter (new OpenLayers.LonLat(parseInt(loc[0]), parseInt(loc[1])));
1096     return true;
1097 }
1098 
1099 /**
1100  * Api method to set the current zoom level of this Api map
1101  * 
1102  * @param {integer} zoomlevel the zoomlevel (0 is full map, 14 is fully zoomed in)
1103  */
1104 Pdok.Api.prototype.setZoomLevel = function(zoomlevel) {
1105     this.map.zoomTo (zoomlevel);
1106     return true;
1107 }
1108 
1109 /**
1110  * Api helper method to reproject a lat,lon coordinate (epsg:4326/latlon) into an Rijksdriehoekstelsel coordinate (epsg:28992)
1111  * NOTE: x = lon and y = lat !!
1112  * returning what is called an OpenLayersLonLat-object, but what is actually a coordinate in RD
1113  * 
1114  * @param {double} lat The latitude value (== the y value in epsg:4326)
1115  * @param {double} lon The longitude value (== the x value in epsg:4326)
1116  * @returns {OpenLayers.LonLat} the reprojected latlon coordinate actually a RD coordinate
1117  */
1118 Pdok.Api.prototype.reprojectWGS84toRD = function(lat,lon){
1119 	pointRD = new OpenLayers.LonLat(lon,lat)
1120         .transform(
1121             new OpenLayers.Projection("EPSG:4326"), // transform from wgs84 
1122             new OpenLayers.Projection("EPSG:28992") // new RD
1123         );
1124 	return(pointRD);
1125 }
1126 
1127 /**
1128  * Generic api method to add an already constructed OpenLayers layer 
1129  * to our map. 
1130  * The use of this method guarantees you that locationLayer and featureLayer 
1131  * are always on top
1132  * Can be used to add an external layer to the map.
1133  * @param {OpenLayer.Layer} a valid OpenLayers.Layer layer
1134  */
1135 Pdok.Api.prototype.addOLLayer = function(openLayersLayer) {
1136     this.map.addLayer(openLayersLayer);
1137     this.moveVectorLayersToTop();
1138 }
1139 
1140 /**
1141  * Api method to add a TMS layer to the map, based on three strings
1142  * 
1143  * @param {String} tmsurl a valid URL string
1144  * @param {String} tmslayer a valid layername of the above tmsurl service
1145  * @param {String} tmstype Optional tmstype, defaulting to 'png'
1146  */
1147 Pdok.Api.prototype.addTMS = function(tmsurl,tmslayer,tmstype) {
1148     if (tmstype == null){
1149         tmstype="png";
1150     }
1151     var lyrTMS = this.createTMSLayer({
1152             url: tmsurl,
1153             layername: tmslayer,
1154             type: tmstype
1155         });
1156     this.addOLLayer(lyrTMS);
1157     return true;
1158 }
1159 
1160 /**
1161  * Method to create a TMS layer and add it to the map based on a layer configuration object. Normally you'll use the addTMS method, but you can also use this way.
1162  * @param {Object} layerConfigObj a layer configuration object as described in markersdef
1163  */
1164 Pdok.Api.prototype.createTMSLayer = function(layerConfigObj) {
1165 
1166     // default TMS layer object to set defaults:
1167     // missing values in config object will be replaced by sensible defaults:
1168     var defaults = {
1169             name: 'tms layer',
1170             url: '',
1171             layertype: '',
1172             layername: '',
1173             type: 'png',
1174             visibility: true,
1175             isBaseLayer: false,
1176 			attribution:''
1177     };
1178 
1179     layerConfigObj = OpenLayers.Util.applyDefaults(layerConfigObj, defaults);
1180 
1181     var layer = new OpenLayers.Layer.TMS(
1182         layerConfigObj.name,
1183         layerConfigObj.url,
1184         {   layername: layerConfigObj.layername, 
1185             type:layerConfigObj.type, 
1186             visibility: layerConfigObj.visibility, 
1187             isBaseLayer: layerConfigObj.isBaseLayer,
1188 			attribution:layerConfigObj.attribution
1189         }
1190     );
1191 
1192     return layer;
1193 }
1194 
1195 /**
1196  * Api method to add a WMTS layer to the map, based on five strings
1197  * 
1198  * @param {String} wmtsurl a valid URL string
1199  * @param {String} wmtslayer a valid layername of the above tmsurl service
1200  * @param {String} wmtsmatrixset a valid matrixset id
1201  * @param {String} wmtsstyle a valid style (defaults to 'default')
1202  * @param {String} wmtsmatrixids (optional) will be created
1203  */
1204 Pdok.Api.prototype.addWMTS = function(wmtsurl, wmtslayer, wmtsmatrixset, wmtsstyle, wmtsmatrixids) {
1205     this.wmtsurl = wmtsurl;
1206     this.wmtslayer = wmtslayer;
1207     this.wmtsmatrixset = wmtsmatrixset;
1208     var lyrWMTS = this.createWMTSLayer({
1209             name: wmtslayer,
1210             url: wmtsurl,
1211             layer: wmtslayer,
1212             matrixSet: wmtsmatrixset,
1213             matrixIds: wmtsmatrixids,
1214             style: wmtsstyle
1215         });
1216     this.addOLLayer(lyrWMTS);
1217     return true;
1218 }
1219 
1220 /**
1221  * Method to create a WMTS layer and add it to the map based on a layer configuration object. Normally you'll use the addWMTS method, but you can also use this way.
1222  * @param {Object} layerConfigObj a layer configuration object as described in markersdef
1223  */
1224 Pdok.Api.prototype.createWMTSLayer = function(layerConfigObj) {
1225     // From WMTS openlayers example:
1226     // If tile matrix identifiers differ from zoom levels (0, 1, 2, ...)
1227     // then they must be explicitly provided.
1228     var matrixIds = new Array(26);
1229     for (var i=0; i<26; ++i) {
1230         matrixIds[i] = layerConfigObj.matrixSet+':' + i;
1231     }
1232 
1233     // default WMTS layer object to set defaults:
1234     // missing values in config object will be replaced by sensible defaults:
1235     var defaults = {
1236             name: 'wmts layer',
1237             url: '',
1238             layer: '',
1239             style: 'default',
1240             matrixSet: '',
1241             matrixIds: matrixIds,
1242             visibility: true,
1243             isBaseLayer: false,
1244             format: 'image/png8',
1245             attribution:''
1246     };
1247 
1248     layerConfigObj = OpenLayers.Util.applyDefaults(layerConfigObj, defaults);
1249 
1250      /* var wmts = new OpenLayers.Layer.WMTS({
1251      *     name: "My WMTS Layer",
1252      *     url: "http://example.com/wmts",·
1253      *     layer: "layer_id",
1254      *     style: "default",
1255      *     matrixSet: "matrix_id"
1256      * });
1257      */
1258     var layer = new OpenLayers.Layer.WMTS(
1259         {
1260             name: layerConfigObj.name,
1261             url:layerConfigObj.url,
1262             layer: layerConfigObj.layer,
1263             style: layerConfigObj.style,
1264             matrixSet: layerConfigObj.matrixSet,
1265             matrixIds: layerConfigObj.matrixIds,
1266             format: layerConfigObj.format,
1267             visibility: layerConfigObj.visibility,
1268             isBaseLayer: layerConfigObj.isBaseLayer,
1269             attribution: layerConfigObj.attribution
1270         }
1271     );
1272     return layer;
1273 }
1274 
1275 /**
1276  * Api method to add a WMS layer to the map, based on two strings
1277  * 
1278  * @param {String} wmsurl a valid URL string
1279  * @param {String} wmslayers a valid layername of the above url service
1280  */
1281 Pdok.Api.prototype.addWMS = function(wmsurl,wmslayers) {
1282     this.wmsurl = wmsurl;
1283     this.wmslayers = wmslayers;
1284     var lyrWMS = this.createWMSLayer({
1285             url: wmsurl,
1286             layers: wmslayers,
1287             transparent: true
1288         });
1289     this.addOLLayer(lyrWMS);
1290     return true;
1291 }
1292 
1293 
1294 /**
1295  * Method to create a WMS layer and add it to the map based on a layer configuration object. Normally you'll use the addWMS method, but you can also use this way.
1296  * @param {Object} layerConfigObj a layer configuration object as described in markersdef
1297  */
1298 Pdok.Api.prototype.createWMSLayer = function(layerConfigObj) {
1299 
1300     // default WMS layer object to set defaults:
1301     // missing values in config object will be replaced by sensible defaults:
1302     var defaults = {
1303             name: 'WMS layer',
1304             url: '',
1305             layers: '',
1306             styles: '',
1307             visibility: true,
1308             isBaseLayer: false,
1309             format: 'image/png',
1310             singleTile: true,
1311 			attribution:''
1312     };
1313 
1314     layerConfigObj = OpenLayers.Util.applyDefaults(layerConfigObj, defaults);
1315 
1316     var layer = new OpenLayers.Layer.WMS(
1317             layerConfigObj.name,
1318             layerConfigObj.url,
1319             {
1320                 layers: layerConfigObj.layers, 
1321                 transparent: layerConfigObj.transparent, 
1322                 format: layerConfigObj.format
1323             },
1324             {
1325                 visibility: layerConfigObj.visibility, 
1326                 isBaseLayer: layerConfigObj.isBaseLayer, 
1327                 singleTile: layerConfigObj.singleTile,
1328 				attribution:layerConfigObj.attribution 
1329             }
1330     );
1331 
1332     return layer;
1333 }
1334 
1335 /**
1336  * Api Interface addLayers to add layers the map, based on their layerkey-names Eg: 'BRT,TOP10NL2,CBS_PROVINCIES'
1337  * @param {array} An javascript array of layer names
1338  * @param {OpenLayers.Map} the Pdok.Api-map to add the layers to
1339  */
1340 Pdok.Api.prototype.addLayers = function(arrLayerNames, map){
1341 
1342     if (arrLayerNames==null){
1343         alert('null object as layernames');
1344         return;
1345     }
1346     else if (arrLayerNames == '-') {
1347         // this is the 'header' of the selectbox: "choose ..."
1348         return;
1349     }
1350 
1351     if (map == undefined){
1352         map = this.map;
1353     }
1354     for (l in arrLayerNames)
1355     {
1356         var layerId = arrLayerNames[l];
1357         if (this.defaultLayers[layerId]){
1358             var lyr;
1359             if (this.defaultLayers[layerId].layertype.toUpperCase()=='WMS'){
1360                 lyr = this.createWMSLayer( this.defaultLayers[layerId]);
1361             }
1362             else if (this.defaultLayers[layerId].layertype.toUpperCase()=='WMTS'){
1363                 lyr = this.createWMTSLayer( this.defaultLayers[layerId]);
1364             }
1365             else if (this.defaultLayers[layerId].layertype.toUpperCase()=='TMS'){
1366                 lyr = this.createTMSLayer( this.defaultLayers[layerId]);
1367             }
1368             else {
1369                 alert('layertype not available (wrong config?): ' + this.defaultLayers.l.layertype);
1370             }
1371             if (lyr){
1372                 lyr.pdokId = layerId;
1373                 map.addLayer(lyr);
1374             }
1375         }
1376         else{
1377             alert('layerid not available: ' + layerId);
1378         }
1379     }
1380     // to be sure featuresLayer and locationLayer are always on top
1381     this.moveVectorLayersToTop();
1382     return true;
1383 }
1384 
1385 /**
1386  * Move the featuresLayer and the locationLayer (layer used by the locationTool) to the top after adding other layers.
1387  */
1388 Pdok.Api.prototype.moveVectorLayersToTop = function(){
1389     // TODO ??take all vector layers into account??
1390     if(this.featuresLayer) {
1391         this.map.setLayerIndex(this.featuresLayer, this.map.layers.length-1);
1392     }
1393     if(this.locationLayer) {
1394         this.map.setLayerIndex(this.locationLayer, this.map.layers.length);
1395     }
1396 }
1397 
1398 
1399 /**
1400  * Api method to disable the locationTool (actually deactivate all needed OpenLayers controls)
1401  */
1402 Pdok.Api.prototype.disableLocationTool = function(){
1403 
1404     if (this.drawLocationPointControl!=null){
1405         this.drawLocationPointControl.deactivate();
1406     }
1407     if (this.drawLocationLineControl!=null){
1408         this.drawLocationLineControl.deactivate();
1409     }
1410     if (this.drawLocationPolygonControl!=null){
1411         this.drawLocationPolygonControl.deactivate();
1412     }
1413     return true;
1414 }
1415 
1416 /**
1417  * Api method to set the api location properties, detached from starting of the
1418  * locationtool to be able to only configure the tool within the wizard
1419  * @param {String} styletype a styletype string like mt0 
1420  * @param {int} zmin the minimal zoom level the user can click
1421  * @param {int} zmaxt the maximal zoom level the user can click
1422  * @param {String} xorwkt the name of the field to be used as X-field OR WKT-field (in this case you should not define y)
1423  * @param {String} y the name of the field to be used as Y-field (without setting this one the tool will only set WKT field)
1424  */
1425 
1426 Pdok.Api.prototype.setLocationToolProps = function(styletype, zmin, zmax, xorwkt, y){
1427 
1428     //console.log('setting locationtoolprops: '+styletype+' '+zmin+' '+ zmax+' '+ xorwkt+' '+y);
1429     this.locationtool = true;
1430     if (styletype){
1431         this.locationtoolstyle = styletype;
1432     }
1433     // if y is defined, this function is called with an x and y field
1434     if(y) {
1435         this.locationtoolxfield = xorwkt;
1436         this.locationtoolyfield = y;
1437         this.locationtoolwktfield = null; // NO wkt
1438     }
1439     else if(xorwkt) {
1440         // apparently only a wkt field
1441         this.locationtoolxfield = null;
1442         this.locationtoolyfield = null;
1443         this.locationtoolwktfield = xorwkt;
1444     }
1445     else {
1446         // default to x y field from api defaults
1447         this.locationtoolwktfield = null; // NO wkt
1448     }
1449     // be carefull not to use if(zmin) or if(zmax), they can be 0 (which solves to false)
1450     if(zmin != undefined){
1451         this.locationtoolzmin = zmin;
1452     }
1453     if(zmax != undefined){
1454         this.locationtoolzmax = zmax;
1455     }
1456     return true;
1457 }
1458 
1459 
1460 /**
1461  * Api method to UNset the locationtool, and set the api location properties to default values
1462  * this is necceary to be able to create code and a map link with or without the locationtool-props
1463  *
1464  */
1465 Pdok.Api.prototype.removeLocationToolProps = function(){
1466     //console.log('remove locationtoolprops');
1467     // back to defaults
1468     this.locationtool = false;
1469     this.locationtoolstyle = 'mt0';
1470     this.locationtoolxfield = 'x';
1471     this.locationtoolyfield = 'y';
1472     this.locationtoolwktfield = 'wkt';
1473     this.locationtoolzmin = '0';
1474     this.locationtoolzmax = '30';
1475     return true;
1476 }
1477 
1478 /**
1479  * Enable the location tool. Either use all locationtool defaults, OR set them via setLocationToolProps method.
1480  */
1481 Pdok.Api.prototype.enableLocationTool = function(){
1482     var apiObject = this;
1483 
1484     var alerted = false;
1485     var locationToolCheck = function() {
1486         if (apiObject.locationLayer.features.length > 0) {
1487             // stop all locationtool controls
1488             if (confirm('Er is al een geldige lokatie. \nKlik OK om verder te gaan,\nof Annuleren/Cancel om opnieuw te klikken.')) {
1489                 apiObject.map.events.unregister("moveend", apiObject.map, locationToolCheck);
1490                 apiObject.map.events.unregister("click", apiObject.map, locationToolCheck);
1491                 apiObject.disableLocationTool();
1492             }
1493             else {
1494                 apiObject.locationLayer.removeAllFeatures();
1495                 apiObject.removeFormCoordinates();
1496                 apiObject.startLocationTool();
1497             }
1498             return false;
1499         }
1500         else if (apiObject.map.getZoom() >= apiObject.locationtoolzmin && apiObject.map.getZoom() <= apiObject.locationtoolzmax) {
1501             if(!apiObject.locationtoolstyle){
1502                 apiObject.locationtoolstyle = 'mt0';
1503             }
1504             apiObject.startLocationTool();
1505         } else {
1506             var msg = "U kunt alleen tekenen tussen de zoomnivo's: "+apiObject.locationtoolzmin+" en "+apiObject.locationtoolzmax+". \nU zit nu op zoomnivo: "+apiObject.map.getZoom();
1507             var zoom;
1508             if (apiObject.map.getZoom() < apiObject.locationtoolzmin){
1509                 msg += "\nKlik op OK om "+(apiObject.locationtoolzmin-apiObject.map.getZoom())+" zoomnivo's in te zoomen \n(of Annuleren/Cancel om het zelf te doen)";
1510                 zoom = apiObject.locationtoolzmin;
1511             }
1512             else{
1513                 msg += "\nKlik op OK om "+(apiObject.map.getZoom()-apiObject.locationtoolzmax)+" zoomnivo's uit te zoomen \n(of Annuleren/Cancel om het zelf te doen)";
1514                 zoom = apiObject.locationtoolzmax;
1515 
1516             }
1517             if (!alerted){
1518                 //alerted = true;
1519                 if(confirm(msg)){
1520                     apiObject.map.zoomTo(zoom);
1521                 }
1522                 else{
1523                     apiObject.disableLocationTool();
1524                 }
1525             }
1526         }
1527     }
1528     // register above check function to listen to moveend en click events of the map
1529     this.map.events.register("moveend", this.map, locationToolCheck);
1530     this.map.events.register("click", this.map, locationToolCheck);
1531     // first check
1532     locationToolCheck();
1533     return true;
1534 }
1535 
1536 /**
1537  * Clean up the locationtool form inputs
1538  */
1539 Pdok.Api.prototype.removeFormCoordinates = function(){
1540     var apiObject = this;
1541 	if (apiObject.locationtoolxfield && apiObject.locationtoolyfield) {
1542 		apiObject[apiObject.locationtoolxfield] = "";
1543 		apiObject[apiObject.locationtoolyfield] = "";
1544 		if (document.getElementById(apiObject.locationtoolxfield) && document.getElementById(apiObject.locationtoolyfield)) {
1545 			document.getElementById(apiObject.locationtoolxfield).value = ""
1546 			document.getElementById(apiObject.locationtoolyfield).value = ""
1547 		}
1548 	}
1549 	if (apiObject.locationtoolwktfield) {
1550 		apiObject[apiObject.locationtoolwktfield] = "";
1551 		if (document.getElementById(apiObject.locationtoolwktfield)){
1552 			document.getElementById(apiObject.locationtoolwktfield).value = "";
1553 		}
1554 	}
1555 }
1556 
1557 /**
1558  * Start the locationtool
1559  */
1560 Pdok.Api.prototype.startLocationTool = function(){
1561     this.disableLocationTool();  // to be sure we do not have two drawfeature controls active at once
1562 
1563     // selectControl and popups interfere with editing tools: disable all
1564     this.selectControl.deactivate();
1565     this.disablePopups();
1566 
1567     // create controls
1568     var currentDrawControl;
1569     if (this.locationtoolstyle == undefined) {
1570         this.locationtoolstyle = 'mt0';
1571     }
1572     if (this.locationtoolstyle.charAt(0)=='m'){
1573         if (this.drawLocationPointControl==null){
1574             this.drawLocationPointControl = new OpenLayers.Control.DrawFeature(this.locationLayer, OpenLayers.Handler.Point);
1575             this.map.addControl(this.drawLocationPointControl);
1576         }
1577         currentDrawControl = this.drawLocationPointControl;
1578         //currentDrawControl.handler.style = this.styles[this.locationtoolstyle];
1579     }
1580     else if (this.locationtoolstyle.charAt(0)=='l'){
1581         if (this.drawLocationLineControl==null){
1582             this.drawLocationLineControl = new OpenLayers.Control.DrawFeature(this.locationLayer, OpenLayers.Handler.Path);
1583             this.map.addControl(this.drawLocationLineControl);
1584         }
1585         currentDrawControl = this.drawLocationLineControl;
1586         currentDrawControl.handler.style = this.styles[this.locationtoolstyle];
1587         //currentDrawControl.handler.style.externalGraphic = null;
1588     }
1589     else if (this.locationtoolstyle.charAt(0)=='p'){
1590         if (this.drawLocationPolygonControl==null){
1591             this.drawLocationPolygonControl = new OpenLayers.Control.DrawFeature(this.locationLayer, OpenLayers.Handler.Polygon);
1592             this.map.addControl(this.drawLocationPolygonControl);
1593         }
1594         currentDrawControl = this.drawLocationPolygonControl;
1595         currentDrawControl.handler.style = this.styles[this.locationtoolstyle];
1596         currentDrawControl.handler.style.externalGraphic = null;
1597     }
1598 
1599     var apiObject = this;
1600     var locationtoolfeatureadded = function(feature) {
1601         // sometimes we receive an event object (with a feature)
1602         if(feature.feature){
1603             feature = feature.feature;
1604         }
1605         feature.style = apiObject.styles[apiObject.locationtoolstyle];
1606         feature.layer.redraw();
1607         // also set an attribute 'styletype' to be able to export features with styletype
1608         feature.attributes['styletype'] = apiObject.locationtoolstyle;
1609 
1610         var wktFormat = new OpenLayers.Format.WKT();
1611 
1612         if (apiObject.locationtoolxfield && apiObject.locationtoolyfield) {
1613             apiObject[apiObject.locationtoolxfield] = feature.geometry.x;
1614             apiObject[apiObject.locationtoolyfield] = feature.geometry.y;
1615             // only for points
1616             if (feature.geometry.x && feature.geometry.y){
1617                 if (document.getElementById(apiObject.locationtoolxfield) && document.getElementById(apiObject.locationtoolyfield)) {
1618                     document.getElementById(apiObject.locationtoolxfield).value = feature.geometry.x
1619                     document.getElementById(apiObject.locationtoolyfield).value = feature.geometry.y
1620                 }
1621             }
1622         }
1623         if (apiObject.locationtoolwktfield) {
1624             apiObject[apiObject.locationtoolwktfield] = wktFormat.write(feature);
1625             if (document.getElementById(apiObject.locationtoolwktfield)){
1626                 document.getElementById(apiObject.locationtoolwktfield).value = wktFormat.write(feature);
1627             }
1628         }
1629         currentDrawControl.deactivate();
1630     }
1631 
1632     currentDrawControl.featureAdded = locationtoolfeatureadded;
1633     currentDrawControl.activate();
1634 
1635     return true;
1636 }
1637 
1638 /**
1639  * @private
1640  * handle the response to retrieve external features via an ajax request (kml etc)
1641  */
1642 Pdok.Api.prototype.handleGetFeaturesResponse = function(response){
1643     //  trying to catch proxy errors
1644     if (response.status == 502 || response.status == 403){
1645         alert('Fout bij het ophalen van de url.\nDit lijkt een proxy probleem.\nKomt de data van een ander domein dan de web applicatie?\nDan moet het data domein opgenomen worden in de proxy-instellingen.');
1646         return
1647     }
1648     else if (response.status != 200){
1649         alert('Fout bij het ophalen van de url\n(Let op: een externe url moet met \'http://\' beginnen) ');
1650         return
1651     }
1652     var data = response.responseText;
1653     // we have data now: add to map
1654     api.addFeaturesFromString(data, this.dataType, this.zoomToFeatures);
1655 }
1656 
1657 /**
1658  * Add features via a String. Either KML or TXT format
1659  * @param {String} data actual string of data
1660  * @param {String} type String one of 'KML' or 'TXT'
1661  * @param {Boolean} zoomToFeatures boolean to determine if we should zoom to the extent of the just added features
1662  */
1663 Pdok.Api.prototype.addFeaturesFromString = function(data, type, zoomToFeatures){
1664     var format;
1665     var features;
1666     var options = {
1667         externalProjection: new OpenLayers.Projection("EPSG:4326"),
1668         internalProjection: this.map.baseLayer.projection,
1669         extractStyles: this.kmlstyles
1670     };
1671     if (type.toUpperCase() == 'KML') {
1672         format = new OpenLayers.Format.KML(options);
1673         if (data.search(/\n/) > -1 && data.search(/\n/) < data.length){
1674         	//alert("Er zijn returns gevonden in de KML, deze zijn vervangen door een spatie.")
1675         	//features = format.read(data.replace(/\n/g," ").slice(0,data.replace(/\n/g," ").lastIndexOf(" ")) +"\n");
1676         	features = format.read(data.replace(/\n/g," ") +"\n");
1677         }
1678         else{
1679 	        features = format.read(data);
1680 	    }
1681     }
1682     else if(type.toUpperCase() == "TXT"){
1683         // TXT files will default to epsg:28992 / RD coordinates
1684         options = {
1685             externalProjection: new OpenLayers.Projection("EPSG:28992"),
1686             internalProjection: this.map.baseLayer.projection
1687         };
1688         format = new OpenLayers.Format.Text(options);
1689         format.defaultStyle.externalGraphic = null;
1690         features = format.read(data);
1691         // default OpenLayers.Text format uses 'title' as 'name' attribute
1692         // we add a 'name' attribute here
1693         for (f in features){
1694             var feature = features[f];
1695             feature.attributes['name'] = feature.attributes['title'];
1696         }
1697     }
1698     else{
1699         alert('addFeaturesFromUrl aanroep met een niet ondersteund type: '+type);
1700         return;
1701     }
1702     // add styling to features
1703     for (f in features){
1704         var feature = features[f];
1705         //console.log(feature);
1706         if (feature.attributes['styletype']) {
1707             var styletype = feature.attributes['styletype'];
1708             // some formats (KML) return attr as objects instead of strings
1709             if (typeof styletype == 'object') {
1710                 styletype = styletype.value;
1711             }
1712             feature.style = this.styles[styletype];
1713         }
1714         else if (type=='KML' && this.kmlstyles){
1715             // ok a KML layer containing styles
1716         }
1717         else if (type=='TXT' && feature.style.externalGraphic != undefined) {
1718             //console.log('TXT feature WITH style:', feature.style);
1719             // this is a TXT feature with some style information
1720             // this is possible via the txturl parameter
1721             // in combination with OpenLayers.Format.Txt
1722         }
1723         else {
1724             //console.log("feature WITHOUT style, adding some");
1725             if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.Point'){
1726                 feature.style = this.styles['mt0'];
1727             }
1728             else if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.LineString'){
1729                 feature.style = this.styles['lt0'];
1730             }
1731             else if (feature.geometry.CLASS_NAME == 'OpenLayers.Geometry.Polygon'){
1732                 feature.style = this.styles['pt0'];
1733             }
1734         }
1735     }
1736     if (features.length==0) {
1737         // mmm, no featues
1738         alert('Geen features aangemaakt. Is het formaat wel ok?\nU had gekozen voor het formaat: "'+type+'".\nRaadpleeg eventueel de help pagina\'s voor de juiste formaten.');
1739         return true;
1740     }
1741     this.featuresLayer.addFeatures(features);
1742     if (zoomToFeatures && this.featuresLayer.features.length>0) {
1743         // zoom to dataextent
1744         var totalFeaturesExtent = this.featuresLayer.getDataExtent();
1745         this.featuresLayer.map.zoomToExtent(totalFeaturesExtent);
1746     }
1747     return true;
1748 }
1749 
1750 /**
1751  * Deleting all layers, visible baseLayer and both internal vector layers
1752  * featureLayer and LocationsLayer will never be deleted
1753  * @param {Boolean} nonVectorLayers delete all non-vector layers
1754  * @param {Boolean} VectorLayers delete all vector layers
1755  */
1756 Pdok.Api.prototype.deleteLayers = function(nonVectorLayers, vectorLayers) {
1757     // defaulting to always the nonVectorLayers and NOT the vectorLayer
1758     nonVectorLayers?nonVectorLayers:nonVectorLayers=true;
1759     vectorLayers?vectorLayers:vectorLayers=false;
1760     for (var i = this.map.layers.length-1; i>=0; i--) {
1761         var layer = this.map.layers[i];
1762         if (layer.isBaseLayer && layer.visibility == true) {
1763             // current visible baselayer: do NOT remove
1764         }
1765         else if (layer != this.featuresLayer && layer != this.locationLayer){
1766             if (layer.CLASS_NAME == 'OpenLayers.Layer.Vector' 
1767                     && vectorLayers) {
1768                 this.map.removeLayer(layer);
1769             }
1770             else if(nonVectorLayers) {
1771                 this.map.removeLayer(layer);
1772             }
1773         }
1774     }
1775 }
1776 
1777 /**
1778  * Add features to the this.featuresLayer of the map via an URL and type
1779  * @param url {String} URL of either a text or a kml file to load
1780  * @param type {String} type, one of 'KML' or 'TXT'
1781  * @param {Boolean} zoomToFeatures boolean to determine if we should zoom to the extent of the just added features
1782  */
1783 Pdok.Api.prototype.addFeaturesFromUrl = function(url, type, zoomToFeatures){
1784 
1785     var apiObject = this;
1786     var context = {};
1787     // little dirty way to pass type and zoomToFeatures:
1788     context.dataType = type;
1789     context.zoomToFeatures = zoomToFeatures;
1790 
1791     if (type.toUpperCase() == "KML"){
1792         // kml
1793         this.kmlurl = url;
1794     }
1795     else if(type.toUpperCase() == "TXT"){
1796         // tab separated txt file (in EPSG:28992)
1797         // format (including header!)
1798         //
1799         // point    title   description
1800         // 150000,350000  foo omschrijving foo
1801         //
1802         // OR
1803         //
1804         // lat  lon title   description
1805         // 150000   350000    foo omschrijving foo
1806         this.txturl = url;
1807     }
1808     else{
1809         alert('addFeaturesFromUrl aanroep met een niet ondersteund type: '+type);
1810         return;
1811     }
1812     OpenLayers.Request.GET({
1813             url: url,
1814             callback: apiObject.handleGetFeaturesResponse,
1815             scope: context
1816     });
1817 
1818     return true;
1819 }
1820 
1821 /**
1822  * Shorthand to add KML features via a KML url (always zooming to the extent of the KML)
1823  */
1824 Pdok.Api.prototype.addKML = function(url){
1825     this.addFeaturesFromUrl(url, 'KML', true);
1826 }
1827 
1828 //Wellicht moet dit een andere call worden omdat nu een combinatie van txturl en mloc niet goed gaat
1829 /**
1830  * Shorthand to add TXT features via a TXT url (always zooming to the extent of the TXT)
1831  */
1832 Pdok.Api.prototype.addTxt = function(url){
1833     this.addFeaturesFromUrl(url, 'TXT', true);
1834 }
1835 
1836 /**
1837  * Create the iframe tags string for this instance
1838  */
1839 Pdok.Api.prototype.createIframeTags = function(){
1840     // map div size
1841     var mapSize = this.map.getSize();
1842     var iframeTags = '<iframe width="'+mapSize.w+'" height="'+mapSize.h+'" frameborder="0" scrolling=no marginheight="0" marginwidth="0" src="'+this.createMapLink()+'" title="PDOK Kaart"></iframe>';
1843     return iframeTags;
1844 }
1845 
1846 /**
1847  * Create the object tags string for this instance
1848  */
1849 Pdok.Api.prototype.createObjectTags = function(){
1850     // map div size
1851     var mapSize = this.map.getSize();
1852     var objectTags = '<object width="'+mapSize.w+'" height="'+mapSize.h+'" codetype="text/html" data="'+this.createMapLink()+'" title="PDOK Kaart"></object>';
1853     return objectTags;
1854 }
1855 
1856 /**
1857  * Create the link string for this instance
1858  */
1859 Pdok.Api.prototype.createMapLink = function(){
1860 	pathname = window.location.pathname;
1861     if (pathname.toLowerCase().search("index.html") > -1){
1862     	pathname = window.location.pathname.substr(0,window.location.pathname.toLowerCase().search("index.html"));
1863     }
1864     base = window.location.host + pathname;
1865     return 'http://'+base+'api/api.html?'+OpenLayers.Util.getParameterString(this.getConfig());
1866 }
1867 
1868 /**
1869  * Create the email link string for this instance
1870  */
1871 Pdok.Api.prototype.createMailLink = function(){
1872 	pathname = window.location.pathname;
1873     if (pathname.toLowerCase().search("index.html") > -1){
1874     	pathname = window.location.pathname.substr(0,window.location.pathname.toLowerCase().search("index.html"));
1875     }
1876     base = window.location.host + pathname;
1877     return 'mailto:UwMailAdres@provider.nl?Subject=PDOKKaart%20URL&BODY=URL%3A%20' + encodeURIComponent('http://'+base+'api/api.html?'+OpenLayers.Util.getParameterString(this.getConfig()));
1878 }
1879 
1880 /**
1881  * Create the html body string for this instance
1882  */
1883 Pdok.Api.prototype.createHtmlBody = function(){
1884     var html = '<div id="map"></div>\n'+
1885                '<script>var  api = createPDOKKaart();\n'+
1886                '</script>';
1887     return html;
1888 }
1889 
1890 /**
1891  * Create the html head string for this instance
1892  */
1893 Pdok.Api.prototype.createHtmlHead = function(){
1894     var base = Pdok.createBaseUri();
1895     // styles and layers definitions
1896     var stylesAndLayers = '';
1897     if (this.markersdef) {
1898         stylesAndLayers += '\n<script src="'+this.markersdef+'"></script>';
1899     }
1900     if (this.layersdef) {
1901         stylesAndLayers += '\n<script src="'+this.layersdef+'"></script>';
1902     }
1903     var head = '<script src="'+base+'api/js/OpenLayers.js"></script>'+
1904     '\n<script src="'+base+'api/js/proj4js-compressed.js"></script>'+
1905     '\n<script src="'+base+'api/js/pdok-api.js"></script>'+
1906     stylesAndLayers +
1907     '\n<link rel="stylesheet" href="'+base+'api/styles/default/style.css" type="text/css">'+
1908     '\n<link rel="stylesheet" href="'+base+'api/styles/api.css" type="text/css">'+
1909     '\n<script>'+
1910     '\nOpenLayers.ImgPath="'+Pdok.ApiUrl+'/img/";'+
1911     '\nvar config = '+this.serialize(this.getConfig(), true)+';\n'+
1912     '\nfunction createPDOKKaart() {var api = new Pdok.Api(config);return api;}\n</script>';
1913     return head;
1914 }
1915 
1916 /**
1917  * Api call to get a config object which can be used to start an Api instance in current state
1918  */
1919 Pdok.Api.prototype.getConfig = function() {
1920     var config = {};
1921 
1922     // zoom
1923     config.zoom = this.map.getZoom();
1924     // only add the LayerSwitcher parameter if false (default value is true)
1925     if (!this.showlayerswitcher){
1926 	    config.showlayerswitcher = this.showlayerswitcher;
1927 	}
1928     // bbox
1929     // config.bbox = this.map.getExtent().toArray();
1930     // or better ? loc
1931     config.loc = this.map.getCenter().toShortString();
1932     // layers
1933     var layers = [];
1934     for (layer in this.map.layers){
1935         var pdokId = this.map.layers[layer].pdokId;
1936         // only layers with a pdokId, and NOT our this.featuresLayer
1937         if (pdokId && this.map.layers[layer].name != this.FEATURESLAYER_NAME){
1938             layers.push(pdokId);
1939         }
1940         else{
1941             // we have a layer from 'outside'
1942         }
1943     }
1944     if (layers.length>0) {
1945         config.pdoklayers = [layers.join()];
1946     }
1947     // wmsurl AND wmslayers
1948     if(this.wmsurl && this.wmsurl.length>0 && this.wmslayers && this.wmslayers.length>0) {
1949         config.wmsurl = this.wmsurl;
1950         config.wmslayers = this.wmslayers;
1951     }
1952     // wmts
1953     if (this.wmtsurl != null && this.wmtslayer != null && this.wmtsmatrixset != null && 
1954         this.wmtsurl.length>0 && this.wmtslayer.length>0 && this.wmtsmatrixset.length>0) {
1955         config.wmtsurl = this.wmtsurl;
1956         config.wmtslayer = this.wmtslayer;
1957         config.wmtsmatrixset = this.wmtsmatrixset;
1958     }
1959     // locationtool
1960     if(this.locationtool) {
1961         config.locationtool = true;
1962         config.locationtoolstyle = this.locationtoolstyle;
1963         if (this.locationtoolwktfield) {
1964             config.locationtoolwktfield = this.locationtoolwktfield;
1965         }
1966         else {
1967             config.locationtoolxfield = this.locationtoolxfield;
1968             config.locationtoolyfield = this.locationtoolyfield;
1969         }
1970         config.locationtoolzmin = this.locationtoolzmin;
1971         config.locationtoolzmax = this.locationtoolzmax;
1972     }
1973     // markersdef
1974     if(this.markersdef) {
1975         config.markersdef = this.markersdef;
1976     }
1977     // layersdef
1978     if(this.layersdef) {
1979         config.layersdef = this.layersdef;
1980     }
1981     // kmlurl OR txturl OR features
1982     // at this moment NOT a combination of these two
1983     // all features from KML or TXT are added to 'featureslayer'
1984     // so if the user added even more markers/features
1985     // we should try to make a diff, to know which features to add in the features-kmlstring-parameter
1986     // but if the user has made changes by hand in wizard, it is getting even more comples
1987     // so for now: there is either a kmlurl and/or a txturl OR only features as parameter
1988     if (this.featuresLayer.features.length>0) {
1989         var doFeatures = true;
1990         if (this.kmlurl) {
1991             config.kmlurl = this.kmlurl;
1992             doFeatures = false;
1993             // if kmlstyles
1994             if (this.kmlstyles) {
1995                 config.kmlstyles = true;
1996             }
1997         }
1998         if (this.txturl) {
1999             config.txturl = this.txturl;
2000             doFeatures = false;
2001         }
2002         if (this.mimg != null){
2003             config.mimg = this.mimg;
2004         }
2005         if (doFeatures) {
2006             // If only one feature is added and this is a point then use the parameter mloc
2007             if (this.featuresLayer.features.length == 1 && this.featuresLayer.features[0].geometry.CLASS_NAME == "OpenLayers.Geometry.Point"){
2008             	config.mloc = this.featuresLayer.features[0].geometry.x + "," + this.featuresLayer.features[0].geometry.y;
2009             	config.titel = this.featuresLayer.features[0].attributes.name;
2010             	config.tekst =  this.featuresLayer.features[0].attributes.description;
2011             	config.mt = this.featuresLayer.features[0].attributes.styletype;
2012             }
2013 			else{
2014 				var kmlformat = new OpenLayers.Format.KML({
2015 					foldersDesc: null,
2016 					foldersName: null,
2017 					placemarksDesc: ' ',   // we add   here because null or '' will cause the KML writer to not see it as value
2018 					internalProjection: this.map.baseLayer.projection,
2019 					externalProjection: new OpenLayers.Projection("EPSG:4326")
2020 				});
2021 				config.features=kmlformat.write(this.featuresLayer.features);
2022 			}
2023         }
2024     }
2025     return config;
2026 }
2027 
2028 /**
2029  * Method do serialize the config object to a json string
2030  * @private
2031  */
2032 Pdok.Api.prototype.serialize = function(obj, stringQuotes){
2033   var returnVal;
2034   if(stringQuotes){}else{stringQuotes = false;}
2035   if(obj != undefined){
2036   switch(obj.constructor)
2037   {
2038    case Array:
2039     //var vArr="[";
2040     var vArr="'";
2041     for(var i=0;i<obj.length;i++)
2042     {
2043      if(i>0) vArr += ",";
2044      vArr += this.serialize(obj[i]);
2045     }
2046     //vArr += "]"
2047     vArr += "'"
2048     return vArr;
2049    case String:
2050     returnVal = obj;
2051     if (stringQuotes){
2052         //returnVal = escape("'" + obj + "'");
2053         returnVal = "'" + obj + "'";
2054     }
2055     return returnVal;
2056    case Number:
2057     returnVal = isFinite(obj) ? obj.toString() : null;
2058     return returnVal;    
2059    case Date:
2060     returnVal = "#" + obj + "#";
2061     return returnVal;  
2062    default:
2063     if(typeof obj == "object"){
2064      var vobj=[];
2065      for(attr in obj)
2066      {
2067       if(typeof obj[attr] != "function")
2068       {
2069        vobj.push('\n"' + attr + '":' + this.serialize(obj[attr], stringQuotes));
2070       }
2071      }
2072       if(vobj.length >0)
2073        return "{" + vobj.join(",") + "\n}";
2074       else
2075        return "{}";
2076     }  
2077     else
2078     {
2079      return obj.toString();
2080     }
2081   }
2082   }
2083   return null;
2084 }
2085 
2086 /**
2087  * Function to toggle visibility of the OpenLayers.LayerSwitcher
2088  * @param {Boolean} isVisible to show the layer or not
2089  */
2090 Pdok.Api.prototype.setLayerSwitcherVisible = function(isVisible){
2091     if (isVisible){
2092         this.showlayerswitcher = true;
2093     }
2094     else{
2095         this.showlayerswitcher = false;
2096     }
2097 }
2098