# Wednesday, October 22, 2008

Yay.  Autodesk today released an update for both the Mapguide Enterprise Web Tier, Server and Studio.  This release should catch Enterprise up a little more closely to the Open Source edition.  A PDF with containing some of the changes can be found   Some of these updates are pretty critical (ie. the FireFox 3 takes out your Mapguide server).  Looks like many performances updates have made it in also.

Thanks Autodesk! =)

Technorati Tags:
Wednesday, October 22, 2008 9:59:12 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |   |  Trackback
# Saturday, August 30, 2008

Javascript compression is something I had on my list for the next major release of RADE.  RADE is not quite ready for this step yet, so I thought I would give it a shot on Mapguide Enterprise and see what kind of results it would yield.  For a small site or an internal site this will probably not yield significant benefits.  However if you run a large public site javascript compression could squeeze out some more performance as well as save you on bandwidth costs.  Yahoo has a good article on .

I did some brief looking around and word on the internets is that the is one of the better compressors out there.  The YUI compressor is an open source java applet.   This *should* work with Mapguide Open, heck this might even be done already in MGOS - but I am only using Enterprise so I cannot confirm, deny, or test this.  

Some of the key things the compressor will do:

  1. remove all comments
  2. remove all white space and line breaks
  3. rename all local variables and parameters to single characters

The YUI compressor should not alter variable values or your logic in any way.

To use the YUI compressor you will need to .  Note that if you have the Oracle client installed, you probably have Java already  Once you have Java, you can download a copy of the (I'm going to start referring to it as the YC now...).  If you don't feel like downloading Java and the YC - I've attached the processed files at the bottom of this post.

Lets first look at the javascript shipped with Mapguide Enterprise.  If you browse to your web server extension viewer files (the default location is C:\Program Files\Autodesk\MapGuideEnterprise2009\WebServerExtensions\www\viewerfiles I believe) you will see ten javascript files.  If you choose to process these files on your own please back them up first.  Don't blame me if you don't backup your files and something goes wrong =).

Before YC:

Mapgude Enterprise Javascript before YUI Compression

Using the YC is simple:

   1: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\browserdetect.js browserdetect.js

 

Update the paths to Java and the YC as per your environment.  Using the handy dir /B *.js > go.bat I created a batch file of all the javascript files in the viewerfiles folder.  Be sure to run this from a dos window in the viewerfiles folder, or update the paths.  You will also need to create the comp folder in viewerfiles.   Quick cut and pasting created the following batch file:

   1: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\browserdetect.js browserdetect.js
   2: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\contextmenu.js contextmenu.js
   3: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\digitize.js digitize.js
   4: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\hashtable.js hashtable.js
   5: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\initdwfctrl.js initdwfctrl.js
   6: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\legend.js legend.js
   7: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\numfmt.js numfmt.js
   8: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\pngfix_map.js pngfix_map.js
   9: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\sarissa.js sarissa.js
  10: c:\oracle\product\10.2.0\client_1\jdk\bin\java.exe -jar f:\utils\YUICompress\yuicompressor-2.3.6\build\yuicompressor-2.3.6.jar -o comp\wz_jsgraphics.js wz_jsgraphics.js

 

(*Sorry about the overlap.  I'm working on a new layout and am addressing that in there)

Running that output all of the compressed of the files down somewhat, to pretty significantly.

Mapgude Enterprise Javascript After YUI Compression

A quick comparison of one of the function calls:

   1: function MenuData(menuName, arrowHeight, scrollInc, scrollDelay, minSize, iconScrollUp, iconScrollUpDisabled, iconScrollDown, iconScrollDownDisabled, owner, withIFrame, bkColor)
   2: function MenuData(F,H,I,K,E,G,D,J,A,C,B,L)

 

As you can see - the savings can be significant.

I'm not using the yet so I did not process those files, but I did check some of the JS in there and each script file had a huge header comment.  It looks like Fusion could also benefit from some compression action.  Please note, I've been running my web tier using this script for a day or two.  I've not run any serious testing on this code - so try this at your own risk.  You did make that backup I suggested, right?  That said, I have not personally experienced any problems yet.  Also, should you compress your viewerfiles and have problems be sure to try your backups.  Especially before calling your dealer or Autodesk for help. (sorry product support and ADN guys.  Please don't hate me =] ).

if you would rather not run the YC yourself.

Enjoy!

Friday, August 29, 2008 11:18:36 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |   |  Trackback
# Wednesday, July 30, 2008

Everyone knows there is a , right?  Well ok, maybe you didn't - but there is.  What that limit is depends on the browser.  There is a lot of conflicting information out there on the magical .  An RFC defines it, but no one really seems to pay attention to those anyhow.  (Are you listening Microsoft, of course you are! =]).  Anyhow, for IE, the query string length is usually around 2000 characters.

However, in most cases you're better off using a form and posting your data up to the server side that way, as the limitations on data are so large you will likely not need to worry about them.  Sometimes, query strings can also show your users just a little too much information - though I'm by no means a proponent of "security through obscurity" hiding a little bit more from your users will keep the curious ones a little more in check =).

When working with / we need to grab some potentially massive XML strings from the MgMap object and pass these along to the server side for processing.  Take the selection XML from the MgMap object, even a single entity selection can use a significant portion of the characters available in the query string.  On top of that, passing this data via query string requires that the data be URL encoded, using even more of our precious query string characters.

Depending on the design of your application it may not always be feasible to define a hard coded form, or even a form defined server side using ASP.NET.  In some cases (you guessed it - my case) you may want to use javascript and do all the work on the client side to define a form and pass the data long that way.

Well you're in luck, I found a pretty nice solution (WELL, at least I'm liking it  =]).   I'm kicking myself for not realizing this long ago, but oh well.  So the following javascript function demonstrates how to build, add, populate, and submit a form on the fly using some information from the MgMap object.

   1: function postData()   
   2: {   3: //get the map   
   3: mapObj = GetMap();   
   4:  
   5: //define the new form   
   6: var newForm = document.createElement("form");   
   7: //set the method to POST - the opposite of query strings..   
   8: newForm.method="POST";  
   9:  //add the new form to the current document  
  10:  document.body.appendChild(newForm);  
  11:  
  12:  //lets get some data and add it to the form  
  13:  AddFormElement(newForm, "MapName", oMap.GetMapName());  
  14:  AddFormElement(newForm, "SID", oMap.GetSessionId());  
  15:  //be sure you escape the selection XML - or you will get an error on post about a   
  16:  //"potentially dangerous form value".  Remember on the server side to Server.UrlDecode() it  
  17:  AddFormElement(newForm, "sel", escape(oMap.GetSelectionXML()));  
  18:  
  19:  //lets create our new window  
  20:  var szTarget = "targetWin"  
  21:  newForm.target = szTarget;  
  22:  //set the name/path of the ASPX file you want to process your form with  
  23:  newForm.action = "/url_to_open/file.aspx"  
  24:  
  25:  //open a new window to submit the form to.  Its a good idea to have a blank.htm so you don't get a file not found error  
  26:  var oWin = window.open("blank.htm",szTarget,'menubar=yes, resizable=yes,scrollbars=yes, status=no,toolbar=no,width=300, height=300');  
  27:  
  28:  //give the window focus.  Users like this  
  29:  oWin.focus();  
  30:  
  31:  //submit the form - it will now open in the new window  
  32:  newForm.submit();  
  33:  //remove the form from the document, we're done with it  
  34:  document.body.removeChild(newForm);  
  35:  }  
  36:  
  37:  function AddFormElement(form, elementName, elementVal)  
  38:  {  
  39:      var newElement = document.createElement("<input name='" + elementName + "' type='hidden'/>");  
  40:      newElement.value = elementVal;  
  41:      form.appendChild(newElement);  
  42:      return form;  
  43:  }

 

On the server side, you can now access this data from ASP.NET using Request.Form, for example Request.Form("MapName") would give you the map name.  Don't forget when retrieving the selection XML to run that through Server.UrlDecode, or HttpUtility.UrlDecode.

As usual, any comments, bugs, or rotten fruit - send em my way.  Enjoy!

  Technorati Profile
Wednesday, July 30, 2008 3:22:27 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |   |  Trackback
# Tuesday, July 29, 2008

Tonight I came across the .  I had read about it, and made a mental note that one day it would be an issue.  That note wasn't very good, and I completely forgot about it.   I was wrapping up the updates around the report this eve and fortunately I bothered to select the entire map and run through a report.

RADE Report - but what, only 20 records returned?

Report looked good.  The results were sorting, checkboxes working.   Wait a minute, I selected the entire map.  There should be more than twenty items returned.  So a quick search of my twelve thousand saved e-mails from the turned up what needed to be done.  Fortunately Autodesk added a GenerateFilters call to MgSelection.  This call returns an MgStringCollection of filters.

Basically the solution to this problem is to use GenerateFilters instead of GenerateFilter, and loop through the results.  Each time, appending the returned keys to the complete list.  For example:

   1: Public Shared Function GetSelectedKeysString(ByRef siteConn As MgSiteConnection, _
   2:     ByRef resSvc As MgResourceService, ByVal oMap As MgMap, ByVal SessionId As String, _
   3:     ByVal SelectionXML As String, ByVal layerResID As MgResourceIdentifier, _
   4:     ByVal keyFieldName As String) As String
   5:  
   6:     Dim szKeys As String = ""
   7:     Dim oSel As New MgSelection(oMap)
   8:     oSel.FromXml(SelectionXML)
   9:     Dim curLay As MgLayerBase
  10:     'isolate the layer
  11:     For Each layerItem As MgLayerBase In oSel.GetLayers
  12:         If layerItem.Name = layerResID.Name Then
  13:             curLay = layerItem
  14:         End If
  15:     Next
  16:  
  17:     Dim featSvc As MgFeatureService = siteConn.CreateService(MgServiceType.FeatureService)
  18:     Dim queryOptions As New MgFeatureQueryOptions
  19:     Dim featureClassName As String = curLay.GetFeatureClassName
  20:     'workaround using GenerateFilters and looping through the results as needed
  21:     Dim featureReader As MgFeatureReader
  22:     Dim filters As MgStringCollection = oSel.GenerateFilters(curLay, featureClassName, 20)
  23:     Dim filterCnt As Integer = 0
  24:     While filterCnt < filters.GetCount
  25:         queryOptions.SetFilter(filters.GetItem(filterCnt))
  26:         featureReader = featSvc.SelectFeatures(New MgResourceIdentifier(curLay.GetFeatureSourceId), featureClassName, queryOptions)
  27:         While featureReader.ReadNext
  28:             If szKeys = "" Then
  29:                 szKeys = ConvertPropertyToString(featureReader, keyFieldName)
  30:             Else
  31:                 szKeys &= "," & ConvertPropertyToString(featureReader, keyFieldName)
  32:             End If
  33:         End While
  34:         featureReader.Close()
  35:         featureReader.Dispose()
  36:         filterCnt += 1
  37:     End While
  38:     Return szKeys
  39: End Function

 

In this code, we get the selection from the MgMap object and then isolate the layer in question.  Then we generate the collection of filters and loop through them.  Each time, adding the appropriate value to the list of keys.  Note the use of ConverPropertyToString, this needs to be used to ensure that the various data types are converted over to string.

I've included this function as well as a couple other handy related ones in the attached zip file

.  If you're looking for a C# example of how to use GenerateFilters one is provided in the .

After making these changes my reports now look all proper! hurray.  Maybe I can hit the sack now?  nahh.

RADE report - but this time everything looks ok 

Tuesday, July 29, 2008 10:37:46 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |   |  Trackback
# Monday, July 07, 2008

I had another problem with Mapguide Enterprise 2009 recently pertaining to objects not being selectable.  Unlike my previous post on errors caused by layers, no errors were logged to the Mapguide server logs.  This problem is very likely specific to the enterprise version, as I was using the Autodesk FDO Provider for Oracle.

So a data connection was created pointing to the Oracle 10g schema.  This database contained a number of tables containing Lat/Long point geometry entities.  I created a layer pointing to the table in question.  I created a layer pointing to that data connection.  At this point the entities showed up as expected in the both the DWF and the AJAX viewers.  Once again, I could not select any of the entities.  I checked the server logs, no errors were reported.

In troubleshoot mode, I tried just about everything I could think of but nothing would work.  I simplified the theme, created new layers, removed all other layers from them map.  Eventually I created a new layer from another table.  Luckily, the entities on this layer could be selected.  It must be a problem with the underlying data.  On a hunch, I checked the table definitions - specifically looking at primary keys.  The selectable layer had a primary key defined, the problem layer did not.  (Good thing I cannot take credit for creating this source data =))

So, I created a primary key on the problem table, did a touch on the layer definition (opening the definition in Studio and saving it without any changes) and then left for twenty minutes.  I'm thinking there is some sort of caching going within the server, and I'm not sure how it works.  Immediately reloading the map after adding the primary key did not work - the entities were still not selectable.  When I came back - the items in the viewers were now selectable.

So long story short, if again you cannot select map entities using the Autodesk FDO Provider for Oracle ensure that the source table has a primary key defined.  Hopefully this saves someone some grief =)

Monday, July 07, 2008 2:41:35 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |   |  Trackback