// NEED TO HANDLE WRAPPING ON LONGITUDE ... GDB range is 0 to 360 // // Need to sort out loading so that configuration can happen sooner ////////////////////////////////////// // Configuration items LinzGdbMap.prototype.url="/apps/gdbkml/linzgdbmap.aspx"; LinzGdbMap.prototype.versionUrl="%url%?id=version"; LinzGdbMap.prototype.indexUrl="%url%?id=index&version=%version%"; LinzGdbMap.prototype.tileUrl="%url%?id=%value%&version=%version%"; LinzGdbMap.prototype.searchUrl="%url%?search=%value%&version=%version%"; LinzGdbMap.prototype.searchNamesUrl="%url%?searchnames=%value%&version=%version%"; LinzGdbMap.prototype.enableLog = true; LinzGdbMap.prototype.maxNodes = 50; // ======================================================================== function LinzGetUrl( url, texthandler ) { var xmlreq = null; if (window.XMLHttpRequest) { xmlreq=new XMLHttpRequest(); } // code for IE else if (window.ActiveXObject) { xmlreq=new ActiveXObject("Microsoft.XMLHTTP"); } if( texthandler != null ) { xmlreq.onreadystatechange = function() { if( xmlreq.readyState == 4 ){ texthandler(xmlreq.responseText ); } } } xmlreq.open("GET",url,true); xmlreq.send(null); } // // class LinzBBox function LinzBBox() { if( arguments.length == 4 ) { this.xmin = parseFloat(arguments[0]); this.xmax = parseFloat(arguments[1]); this.ymin = parseFloat(arguments[2]); this.ymax = parseFloat(arguments[3]); } else { this.xmin = null; this.xmax = null; this.ymin = null; this.ymax = null; } } LinzBBox.prototype.copy = function() { return new LinzBBox( this.xmin, this.xmax, this.ymin, this.ymax ); } LinzBBox.prototype.extend = function( box2 ) { if( box2.xmin == null ) return; if( this.xmin == null ) { this.xmin = box2.xmin; this.xmax = box2.xmax; this.ymin = box2.ymin; this.ymax = box2.ymax; return; } if( box2.xmin < this.xmin ) this.xmin = box2.xmin; if( box2.xmax > this.xmax ) this.xmax = box2.xmax; if( box2.ymin < this.ymin ) this.ymin = box2.ymin; if( box2.ymax > this.ymax ) this.ymax = box2.ymax; } LinzBBox.prototype.parse = function( text ) { var values = text.split(','); this.xmin = parseFloat(values[0]); this.xmax = parseFloat(values[1]); this.ymin = parseFloat(values[2]); this.ymax = parseFloat(values[3]); } LinzBBox.prototype.calcOffset = function( x, wrap ) { var offset = 0; if( wrap > 0 ) { var diff = (this.xmax+this.xmin)/2 - x; while( offset - diff > wrap/2 ) offset -= wrap; while( offset - diff < -wrap/2 ) offset += wrap; } return offset; } LinzBBox.prototype.intersect = function( box2, wrap ) { if( this.xmin == null || box2.xmin == null ) return null; var offset = this.calcOffset( (box2.xmin+box2.xmax)/2, wrap ); if( box2.xmin+offset > this.xmax || box2.xmax+offset < this.xmin || box2.ymin > this.ymax || box2.ymax < this.ymin ) return null; var xmin = this.xmin < box2.xmin+offset ? box2.xmin+offset : this.xmin; var xmax = this.xmax < box2.xmax+offset ? this.xmax : box2.xmax+offset; var ymin = this.ymin < box2.ymin ? box2.ymin : this.ymin; var ymax = this.ymax < box2.ymax ? this.ymax : box2.ymax; return new LinzBBox( xmin, xmax, ymin, ymax ); } LinzBBox.prototype.contains = function( box2, wrap ) { if( this.xmin == null || box2.xmin == null ) return false; var offset = this.calcOffset( (box2.xmin+box2.xmax)/2, wrap ); return this.xmin <= box2.xmin+offset && this.xmax >= box2.xmax+offset && this.ymin <= box2.ymin && this.ymax >= box2.ymax; } LinzBBox.prototype.containsPoint = function( x, y, wrap ) { if( this.xmin == null ) return false; var offset = this.calcOffset( x, wrap ); return this.xmin <= x+offset && this.xmax >= x+offset && this.ymin <= y && this.ymax >= y; } LinzBBox.prototype.area = function() { return (this.xmax-this.xmin)*(this.ymax-this.ymin); } function SplitLinzBbox( bbox, dir, splitval ) { this.lbox = null; this.rbox = null; if( dir == 'X' ) { if( splitval < bbox.xmin ) { this.rbox = bbox.copy(); } else if( splitval > bbox.xmax ) { this.lbox = bbox.copy(); } else { this.lbox = new LinzBBox( bbox.xmin, splitval, bbox.ymin, bbox.ymax ); this.rbox = new LinzBBox( splitval, bbox.xmax, bbox.ymin, bbox.ymax ); } } else if( dir == 'Y' ) { if( splitval < bbox.ymin ) { this.rbox = bbox.copy(); } else if( splitval > bbox.ymax ) { this.lbox = bbox.copy(); } else { this.lbox = new LinzBBox( bbox.xmin, bbox.xmax, bbox.ymin, splitval ); this.rbox = new LinzBBox( bbox.xmin, bbox.xmax, splitval, bbox.ymax ); } } } // class LinzGdbMapLayer LinzGdbMapLayer.prototype.txtvalRegex = /^([^\,]*)\,(.*)$/; function LinzGdbMapLayer( text ) { var parts = this.txtvalRegex.exec( text ); this.code = parts[1]; this.name = parts[2]; this.active = true; } function LinzGdbMapLayerList() { this.layers = new Array(); this.lastcode = ''; this.lastid = 0; } LinzGdbMapLayerList.prototype.AddLayer = function(layer) { layer.id = this.layers.length; this.layers.push(layer); } LinzGdbMapLayerList.prototype.Apply = function(layerfunc) { for( var i = 0; i < this.layers.length; i++ ){ layerfunc(this.layers[i]); } } LinzGdbMapLayerList.prototype.Layer = function(id) { return this.layers[id]; } LinzGdbMapLayerList.prototype.Length = function() { return this.layers.length; } LinzGdbMapLayerList.prototype.GetId = function(code) { if( code == this.lastcode ) return this.lastid; for( var i = 0; i < this.layers.length; i++ ) { if( code == this.layers[i].code ) { this.lastcode = code; this.lastid = i; return i; } } return 0; } LinzGdbMapLayerList.prototype.LayerFromCode = function(code) { return this.Layer( this.GetId(code) ); } // class LinzGdbMapMark function LinzGdbMark( text ) { var parts = text.split("|"); this.id = parts[0]; this.x = parseFloat(parts[1]); this.y = parseFloat(parts[2]); this.layer = parts[3]; this.name = parts[4]; } // class LinzGdbMapTile // // status = 0 Not loaded // status = 1 Load in progress // status = 2 Loaded function LinzGdbMapTile( id, layers ) { this.id = id; this.status = 0; this.marks = null; this.layers = layers; } LinzGdbMapTile.prototype.load = function( text ) { var lines = text.split("\n"); this.marks = new Array(); for( var i = 0; i < lines.length; i++ ) { if( lines[i] != "" ) { var mark = new LinzGdbMark(lines[i]); mark.tileid = this.id; mark.layerid = this.layers.GetId(mark.layer); this.marks.push( mark ); } } this.status = 2; } LinzGdbMapTile.prototype.draw = function( gdbmap ) { this.viewVersion = gdbmap.ViewVersion(); if( this.status == 0 ) { var url = gdbmap.TileUrl( this.id ); this.status = 1; var tile = this; LinzGetUrl( url, function( text ) { tile.load(text); if( gdbmap.ViewVersion() == tile.viewVersion ) tile.draw(gdbmap); } ); } else if( this.status == 1 ) { // Don't need to do anything - but view version is updated.. } else if( this.status == 2 ) { var bbox = gdbmap.ViewBBox(); for( var i = 0; i < this.marks.length; i++ ) { var mark = this.marks[i]; if( bbox.containsPoint( mark.x, mark.y, 360 ) ) { gdbmap.DrawMark( mark ); } } gdbmap.TileFinished(); } } // class LinzGdbMapIndexNode LinzGdbMapIndexNode.prototype.txtvalRegex = /^(\d+)([XYL])(.*)$/; function LinzGdbMapIndexNode( lines, i, layers ) { var ndef = this.txtvalRegex.exec(lines[i]); this.type = ndef[2]; var n1 = parseInt(ndef[1]); switch( this.type ) { case 'X': case 'Y': this.splitVal = parseFloat(ndef[3]); this.lnode = new LinzGdbMapIndexNode(lines,i+1,layers); this.rnode = new LinzGdbMapIndexNode(lines,i+n1,layers); this.count = this.lnode.count + this.rnode.count; break; case 'L': this.tile = new LinzGdbMapTile(n1,layers); this.count = parseInt(ndef[3]); break; } } LinzGdbMapIndexNode.prototype.CheckMarkCount = function( bboxMap, bboxIndex, maxNodes ) { // If the map cotains this entire tree, then just subtract the tree count ... if( bboxMap.contains( bboxIndex, 360 )) return maxNodes - this.count; var intersect = bboxIndex.intersect(bboxMap,360); if( intersect == null ) return maxNodes; // If this is a leaf node, then estimate the number of nodes in the intersecting part // of the block if( this.type == 'L' ) { var areaRatio = intersect.area()/bboxIndex.area(); var nnodes = this.count * areaRatio; return maxNodes - nnodes; } // Otherwise split the box based on the split direction.. var splitBox = new SplitLinzBbox( bboxIndex, this.type, this.splitVal ); if( splitBox.lbox != null ) { maxNodes = this.lnode.CheckMarkCount( bboxMap, splitBox.lbox, maxNodes ); } if( maxNodes > 0 && splitBox.rbox != null ) { maxNodes = this.rnode.CheckMarkCount( bboxMap, splitBox.rbox, maxNodes ); } return maxNodes; } LinzGdbMapIndexNode.prototype.GetAllTiles = function( tiles ) { if( this.type == 'L' ) { tiles.push( this.tile ); } else { this.lnode.GetAllTiles(tiles); this.rnode.GetAllTiles(tiles); } } LinzGdbMapIndexNode.prototype.GetTiles = function( bboxMap, bboxIndex, tiles ) { // If the map cotains this entire tree, then just grab all nodes without looking further.. if( bboxMap.contains( bboxIndex, 360 )) { this.GetAllTiles(tiles); return; } var intersect = bboxIndex.intersect(bboxMap,360); if( intersect == null ) return; // If this is a leaf node, then estimate the number of nodes in the intersecting part // of the block if( this.type == 'L' ) { tiles.push( this.tile ); return; } // Otherwise split the box based on the split direction.. var splitBox = new SplitLinzBbox( bboxIndex, this.type, this.splitVal ); if( splitBox.lbox != null ) { this.lnode.GetTiles( bboxMap, splitBox.lbox, tiles ); } if( splitBox.rbox != null ) { this.rnode.GetTiles( bboxMap, splitBox.rbox, tiles ); } return; } // class LinzGdbMapIndexTree LinzGdbMapIndexTree.prototype.txtvalRegex = /^I(\d+)\,(.*)$/; function LinzGdbMapIndexTree( lines, i, layers ) { this.nextLine = i+3; var tdef = this.txtvalRegex.exec(lines[i]); this.layers = lines[i+1].split(","); this.bbox = new LinzBBox() this.bbox.parse( lines[i+2] ); this.name = tdef[2]; this.nextLine += parseInt(tdef[1]); this.headnode = new LinzGdbMapIndexNode( lines, i+3, layers ); } // Check whether a region should be drawn based on estimated number of nodes // in a bounding box.. LinzGdbMapIndexTree.prototype.CheckMarkCount = function( bboxMap, maxNodes ) { var result = this.headnode.CheckMarkCount( bboxMap, this.bbox, maxNodes ); return result > 0; } LinzGdbMapIndexTree.prototype.GetTiles = function( bboxMap, tiles ) { var result = this.headnode.GetTiles( bboxMap, this.bbox, tiles ); return result > 0; } // =========================================================================== // class LinzGdbMapIndexRegion LinzGdbMapIndexRegion.prototype.txtvalRegex = /^R(X?)(\d+)\,(.*)$/; function LinzGdbMapIndexRegion( lines, i, layers ) { this.nextLine = i+2; var rdef = this.txtvalRegex.exec(lines[i]); this.exclusive = rdef[1] == 'X'; this.name = rdef[3]; this.trees = new Array(parseInt(rdef[2])); this.bbox = new LinzBBox; this.bbox.parse(lines[i+1]); for( var nb = 0; nb < this.trees.length; nb++ ) { var tree = new LinzGdbMapIndexTree( lines, this.nextLine, layers ); this.nextLine = tree.nextLine; this.trees[nb] = tree; } } LinzGdbMapIndexRegion.prototype.GetTiles = function( bboxMap, maxNodes, tiles, layers ) { if( this.bbox.intersect( bboxMap, 360) == null ) return; var toomany = false; for( var i = 0; i < this.trees.length; i++ ) { var tree = this.trees[i]; if( tree == null ) break; if( maxNodes > 0 && ! tree.CheckMarkCount(bboxMap, maxNodes) ) { toomany = true; break; } tree.GetTiles(bboxMap, tiles); for( var j=0; j < tree.layers.length; j++ ) { layers.push( tree.layers[j] );} } return toomany; } // class LinzGdbMapIndex function LinzGdbMapIndex( ) { this.valid = false; } LinzGdbMapIndex.prototype.Load = function( text ) { this.status = "Loading"; var lines = text.split("\n"); if( lines[0] != "LinzGdbTileIndex_V1" ) { this.status = "Invalid index format version"; return; } this.version = lines[1]; this.regions = new Array; this.layers = new LinzGdbMapLayerList(); var nlayers = parseInt(lines[2].substr(1)); var i = 3; while( nlayers-- ) { var layer = new LinzGdbMapLayer( lines[i] ); this.layers.AddLayer( layer ); i++; } while( i+3 < lines.length ) { var region = new LinzGdbMapIndexRegion( lines, i, this.layers ); this.regions.push(region); i = region.nextLine; } this.status = "Loaded successfully"; this.valid = true; } LinzGdbMapIndex.prototype.GetTiles = function( bboxMap, maxNodes ) { var tiles = new Array(); var activelayers = new Array(); var toomany = false; for( var i = 0; i < this.regions.length; i++ ) { var region = this.regions[i]; var regiontoomany = region.GetTiles( bboxMap, maxNodes, tiles, activelayers ); toomany = toomany || regiontoomany; if( region.exclusive && region.bbox.contains( bboxMap, 360 )) break; } for( var i = 0; i < this.layers.Length(); i++ ) { this.layers.Layer(i).active = false; } for( var i = 0; i < activelayers.length; i++ ) { var layerid = this.layers.GetId( activelayers[i] ); this.layers.Layer(layerid).active = true; } return { tiles: tiles, toomany: toomany }; } // class LinzGdbMap function LinzGdbMap() { this.viewVersion = 0; this.index = new LinzGdbMapIndex(); this.onindexloaded = null; this.onviewset = null; this.onviewcomplete = null; } LinzGdbMap.prototype.LoadIndex = function() { if( this.index_loaded ) return; this.index_loaded = 1; var versionUrl = this.VersionUrl(); var gdbmap = this; LinzGetUrl( versionUrl, function(text){ gdbmap.LoadIndexVersion( text ); } ); } LinzGdbMap.prototype.LoadIndexVersion = function( version ) { version = version.replace(/\s/g,""); var indexUrl = this.IndexUrl( version ); var gdbmap = this; LinzGetUrl( indexUrl, function(text) { gdbmap.index.Load( text ); gdbmap.OnIndexLoaded(); gdbmap.index_loaded = 2; gdbmap.RunWithIndexFuncs(); }); } LinzGdbMap.prototype.OnIndexLoaded = function( bbox ) { if( this.onindexloaded ) this.onindexloaded(); if( this.draw_pending > 0 ) { this.DrawMarks(); this.draw_pending = false; } } LinzGdbMap.prototype.WithIndex = function( f ) { if( this.index_loaded == 2 ) f(); if( ! this.index_waiting_list ) this.index_waiting_list = new Array(); this.index_waiting_list.push(f); } LinzGdbMap.prototype.RunWithIndexFuncs = function() { if( this.index_waiting_list ) { for( var i = 0; i < this.index_waiting_list.length; i++) { this.index_waiting_list[i](); } this.index_waiting_list = null; } } LinzGdbMap.prototype.SetView = function( xmin, xmax, ymin, ymax, allmarks ) { this.LoadIndex(); while( xmin > xmax ) xmax += 360; while( (xmin+xmax)/2 < 0 ) { xmin+=360; xmax+=360; } while( (xmin+xmax)/2 > 360 ) { xmin-=360; xmax-=360; } this.bbox = new LinzBBox( xmin, xmax, ymin, ymax ); this.viewVersion++; if( this.index_loaded == 2 ) { this.DrawMarks( allmarks ); } else { this.draw_pending = true; } } LinzGdbMap.prototype.ViewVersion = function( ) { return this.viewVersion; } LinzGdbMap.prototype.ViewBBox = function( ) { return this.bbox; } LinzGdbMap.prototype.DrawMarks = function( allmarks ) { if( this.index.valid ) { var maxNodes = allmarks ? 0 : this.maxNodes; var result = this.index.GetTiles( this.bbox, maxNodes ); var tiles = result.tiles; this.toomany = result.toomany; if( this.onviewset ) this.onviewset(); this.tilecount = tiles.length; for( var i = 0; i < tiles.length; i++ ) { tiles[i].draw( this ); } } } LinzGdbMap.prototype.TileFinished = function() { if( this.tilecount > 0 ) { this.tilecount--; if( this.tilecount == 0 && this.onviewcomplete ) { this.onviewcomplete(); } } } LinzGdbMap.prototype.Layers = function() { if( this.index.valid ) return this.index.layers; return null; } LinzGdbMap.prototype.SearchId = function( id, onfind ) { var lmap = this; LinzGetUrl( this.SearchUrl(id), function( text ) { lmap.SendFoundResults( text, onfind ); } ); } LinzGdbMap.prototype.SearchNames = function( names, onfind ) { var lmap = this; LinzGetUrl( this.SearchNamesUrl(names), function( text ) { lmap.WithIndex( function(){ lmap.SendFoundResults( text, onfind );}); } ); } LinzGdbMap.prototype.SendFoundResults = function( text, onfind ) { var foundmarks = new Array(); var resultstatus; if( text.match(/too many/i) ) { resultstatus = text; } else { var markdefs = text.split("\n"); var nfound = markdefs.length; for( var i = 0; i < nfound; i++ ) { if( markdefs[i] == "" ) continue; var mark = new LinzGdbMark(markdefs[i]); mark.layerid = this.Layers().GetId(mark.layer); foundmarks.push( mark ); } foundmarks.sort( function(a,b){ return b.y - a.y; } ); nfound = foundmarks.length; var resultstatus; if( nfound > 0 ) { resultstatus = nfound + " mark" + (nfound = 1 ? "s" : "") + " found"; } else { resultstatus = "No marks found"; } } onfind( foundmarks, resultstatus ); } LinzGdbMap.prototype.DrawMark = function( mark ) { if( this.ondrawmark ) this.ondrawmark( mark ); } LinzGdbMap.prototype.BuildUrl = function( url, value, version ) { if( ! version ) { version = ""; if( this.index && this.index.version ) version=this.index.version; } url = url.replace(/\%url\%/,this.url); url = url.replace(/\%value\%/,encodeURI(value)); url = url.replace(/\%version\%/,version); return url; } LinzGdbMap.prototype.VersionUrl = function() { return this.BuildUrl( this.versionUrl ); } LinzGdbMap.prototype.IndexUrl = function( version ) { return this.BuildUrl( this.indexUrl, "", version ); } LinzGdbMap.prototype.TileUrl = function( id ) { return this.BuildUrl( this.tileUrl, id ); } LinzGdbMap.prototype.SearchUrl = function( code ) { return this.BuildUrl( this.searchUrl, code ); } LinzGdbMap.prototype.SearchNamesUrl = function( names ) { return this.BuildUrl( this.searchNamesUrl, names ); }