0
1
1.1
1.2
1.3
1.4
2
2.1
2.1.1
2.1.1.1
2.1.1.2
2.1.1.2.1
2.1.1.2.2
2.1.1.2.3
2.1.1.2.4
2.1.1.3
2.1.1.4
2.1.1.5
2.1.2
2.1.2.1
2.1.2.2
2.1.2.3
2.1.2.3.1
2.1.2.3.2
2.1.2.3.3
2.1.2.3.4
2.1.2.3.5
2.1.2.4
2.1.2.4.1
2.1.2.4.2
2.1.2.4.3
2.1.2.4.4
2.1.2.5
2.1.2.5.1
2.1.2.5.2
2.2
2.2.1
2.2.1.1
2.2.1.2
TableofContentsIntroduction
Overview
Prerequisites
References
Setup
Data
GeoServer
Basics
Administration
Loginandfrontendoverview
About&Status
ServerStatus
GeoServerLogs
ContactInformation
AboutGeoServer
Services
Settings
Security
Publishing
Prerequisites
Workspaces
Vectorlayers
Import
Store
Layer
Preview
Style
Rasterlayers
Preparation
Store
Layer
Preview
Grouplayers
Layer
Preview
Advanced
REST
Read
Create
MoMoworkshop
2
2.2.1.3
2.2.1.4
2.2.2
2.2.2.1
2.2.2.2
2.2.2.3
2.2.2.4
2.2.2.5
2.2.2.6
3
3.1
3.1.1
3.1.2
3.1.3
3.1.4
3.2
3.2.1
3.2.2
3.2.3
3.3
3.3.1
3.3.2
3.3.3
3.4
3.4.1
3.4.2
3.4.3
3.4.4
3.5
3.5.1
3.5.2
3.6
3.6.1
3.6.2
3.6.3
3.7
3.7.1
3.7.2
3.8
3.8.1
3.8.2
3.8.3
Update
Delete
GeoWebCache
Prerequisites
Configureanewgridset
Configureacachedlayer
Generatemaptiles
Checkcachedirectory
Checkcache-headers
JavaScript
Basics
Comments
Variables
Types
Equality
Numbers
Creation
BasicOperators
AdvancedOperators
Strings
Creation
Concatenation
Length
ConditionalLogic
If
Else
Comparators
Concatenate
Arrays
Indices
Length
Loops
For
While
Do...While
Functions
Declare
Higherorder
Objects
Creation
Properties
Mutable
MoMoworkshop
3
3.8.4
3.8.5
3.8.6
3.8.7
3.8.8
4
4.1
4.1.1
4.1.2
4.1.3
4.2
4.2.1
4.2.2
4.2.3
4.2.4
4.2.5
4.3
4.3.1
4.3.2
4.3.3
4.3.4
4.4
4.4.1
4.4.2
4.4.3
4.5
4.5.1
4.5.2
5
5.1
5.1.1
5.2
5.2.1
5.2.2
5.2.3
5.3
5.3.1
5.3.2
5.3.3
5.3.4
5.3.5
5.3.6
Reference
Prototype
Delete
Enumeration
Globalfootprint
OpenLayers
Basics
Creatingamap
Dissectingyourmap
Resources
LayersandSources
WMSsources
Tiledsources
Proprietarytileproviders
Vectordata
Imagevectorsource
Controls
Scalelinecontrol
Selectinteraction
Drawinteraction
Modifyinteraction
VectorTopics
Formats
Stylingconcepts
Customstyles
CustomBuilds
Concepts
Createcustombuilds
ExtJS
Introduction
WorkshopSetup
Basics
IncludeExtJS
HelloExtJS
Viewport
Layouts
Column
HBox
VBox
Accordion
Table
Border
MoMoworkshop
4
5.4
5.4.1
5.4.2
5.4.3
5.4.4
5.4.5
5.5
5.5.1
5.5.2
5.5.3
5.6
5.6.1
5.6.2
5.6.3
6
6.1
6.1.1
6.1.2
6.1.3
6.1.4
6.1.5
6.2
6.2.1
6.2.2
6.2.3
6.2.4
6.2.5
6.2.6
6.3
6.3.1
6.3.2
6.3.3
6.3.4
6.4
6.4.1
6.4.2
6.4.3
6.4.4
6.5
6.5.1
6.5.2
6.5.3
Components
Panel
Image
Form
Tree
Grid
Data
Preparation
Model
Proxyandstore
Events
Eventclick
Eventafterrender
Eventchange
GeoExt
Metainformation
About
Targetaudience
Goals
Developmentenvironment
Notes
Firststeps
Helloexercise
HelloOpenLayers
HelloExtJS
HelloGeoExt
Usefulresources
Summary
Map
Basicexample
Dissectingtheexample
Configurationvariants
Summary
Layertree
Preparelayout
CreateaTreePanel
AssignLayersTreestore
Summary
Featuregrid
Preparelayout
Createafeaturegrid
Summary
MoMoworkshop
5
6.6
6.6.1
6.6.2
6.6.3
7
Popups,Overviewmap&othercomponents
Popup
Overviewmap
Other
Synopsis
MoMoworkshop
6
Welcometotheworkshop
IntroductiontocoretechnologiesbehindtheMoMogeoportal
UlanBator,Mongolia
A5-dayworkshopfrom2016-02-22to2016-02-26
SourcesWorkshopURLDownloadworkshop(ZIP)Downloadworkshop(PDF)Downloadworkshop(EPUB)Gitrepositoryongithub
AuthorsMarcJansen([email protected])
DanielKoch([email protected])
MoMoworkshop
7Introduction
OverviewThisworkshopisintendedtointroducecertainkeysoftwarepackagesbehindthecurrentandupcomingMoMoGeoPortal.
Thecurrentversionofthesoon-to-be-updatedMoMoGeoportal
Inthisoverviewsectionwe'lllearnaboutthefollowingtopics
PrerequisitesReferencesSetupData
MoMoworkshop
8Overview
Prerequisites
Starttheworkshop
ThisworkshopisintendedtobeexecutedincombinationwithanLinuximagedeliveredonanUSBflashdriveespeciallypreparedforthisworkshop.Torunthisimage(andtheworkshop)pleasefollowthesesteps:
Incaseyoudidn'treceiveabootableUSBwiththeabovesystem,youcangrabanimageunderthefollowingURL:http://files.terrestris.de/momo-master.tar.gz.Thisfileisroughly2.2GBbig,andonceitisdecompresseditwillhaveasizeof16GB.
1. ConnecttheprovidedUSBflashdrivetoyourcomputerandturnonthecomputer.2. NormallyyoushouldseetheLinuxMintbootscreensimilartotheimagebelowafteronlyafewseconds.
LinuxMintbootscreen.
Note:Ifyourcomputerisn'tbootingLinuxMint,pleaseensureyourPCisableandcorrectlyconfiguredtobootfromanUSBdevice.Todoso,accessyourBIOSbypressingDELorF2duringtheearlybootprocess(usuallyyouwillseethecorrectkeydisplayedon-screenduringthebootprocess).Presstherequiredkeyatthecorrecttimeandyourcomputer'sBIOSwillappear.Onceyou'reintheBIOS,trytofindamenucalledsomethinglikeBootorsimilar.NavigatetothismenuandlookforsomesortofanentrycalledBootOptionPrioritiesorsimilar.UsuallyyouwillfindthebootorderasaprioritylistinwhichyoucanmovetheUSBdevicebootoptionuptothetopofthislist.NowsaveyourchangesandexittheBIOStorestartyourcomputer.
Congratulations!Nowyouarereadytostarttheworkshop!
MoMoworkshop
9Prerequisites
WorkshopreferencebookHereyouwillfindsomeusefulinformationsabouttheworkshopimage.
Credentials
Linux:User:momoPassword:momo
GeoServer:User:adminPassword:geoserver
PostgreSQL:User:momoPassword:momo
Usefulpaths
Yourhomedirectory:/home/momoWorkshopdirectory:/home/momo/materialsTomcatwebappdirectory:/opt/tomcat/webapps
Usefulterminalcommands
AsyoumaynotfamiliarwithLinuxyouwillfindasmalllistcontainingthemosthelpfulterminalcommandsusedinthisworkshop.
Navigation
Navigatetoadirectory:$cd{PATH_TO_DIRECTORY}Navigatetotheupperdirectory:$cd..Navigatetoyourhomedirectory:$cd~Navigatetotherootdirectory:$cd/Listallfilesanddirectoriesofafolder(inlonglistformat):$ls-lor$ll
Fileanddirectorymanipulation
CreationOfafile:$touch{FILE_PATH_AND_NAME}Ofadirectory:$mkdir{DIRECTORY_PATH_AND_NAME}
RemovalOfafile:$rm{FILE_PATH_AND_NAME}Ofadirectory:$rm-rf{DIRECTORY_PATH_AND_NAME}
Changeownership:Forasinglefile$chown{GROUP_NAME}:{USER_NAME}{FILE_PATH_AND_NAME}Foracompleterfolder(recursively)$chown-R{GROUP_NAME}:{USER_NAME}{FILE_PATH_AND_NAME}
Execution
Makeafileexecutable:$chmod+x{FILE_PATH_AND_NAME}
MoMoworkshop
10References
Runanexecutablefile:$./myExecutable.sh
Compressandextract
Createanarchive:$tar-cvzf{ARCHIVE_FILE_NAME}.tar.gz{DIRECTORY_TO_ARCHIVE}Extractanarchive:$tar-xvzf{ARCHIVE_FILE_NAME}.tar.gz
Services
Start/Stop/RestartPostgreSQL:$sudoservicepostgresqlstart|stop|restartStart/Stop/RestartApache:$sudoserviceapache2start|stop|restartStart/Stop/RestartTomcat:$sudostart|stop|restarttomcat
Otherusefulcommands
Executecommandwithsuperuser(root)permissions:$sudo{COMMAND_TO_EXECUTE}Showmanualofatool:$man{COMMAND_TOOL_NAME}Showterminalhistory:$historyLive-monitoringofachangingfile(e.g.alogfile):$tail-f{FILE_PATH_AND_NAME}Executethelastcommandused:$!!
Exercises
1. OpentheterminalbyclickingtheTerminalicon( )inthebottomtoolbar.2. Navigatetoyourhomedirectoryandcreateafoldernamednotesbytyping:
$cd~
$mkdirnotes
3. Gotothenewlycreatedfolderandcreateafilenamedworkshop-notes.mdbytyping:
$cdnotes/
$touchworkshop-notes.md
4. OpenthisfilewithgeditandenterLinuxisgreat!bytyping:
$geditworkshop-notes.md
MoMoworkshop
11References
WorkshopLinuxMintSetupTheprovidedworkshopLinuximageisjustaLinuxMintwithpre-installedandconfiguredprogramsandtools.Thispageliststhechangestothedefaultsystemconfigurationonlyandisnointegralpartoftheworkshop.
Operatingsystem:LinuxMint17.3CinnamonEdition(32bit)
Additionallyinstalledsoftware:Apache2
InstalledfrompackagemanagerLinkedhomedirectory/home/momotohttp://localhost:80/momoAddedtoautostart
ApacheTomcat8InstalledfromhereAddedtoautostart
GeoServer2.8.2InstalledfromhereInstalledplugins:
pyramid-pluginwps-pluginoracle-pluginimporter-plugin
PublishedviaApache2onport80http://localhost:80/geoserver
AtomEditorInstalledfromhere
ChromeInstalledfromhere
PostgreSQL/PostGISInstalledfrompackagemanagerAddedtoautostart
gitInstalledfrompackagemanager
nvmInstalledfromhere
bash-git-promptInstalledfromhere
QGISInstalledfromhere
GDALInstalledfrompackagemanager
Removedpackages:
$sudoapt-getremovethunderbirdvlcvlc-plugin-notifyvlc-plugin-pulse\
vlc-datavlc-noxtotem-commonbraserobansheegimphexchatpidgintotem\
seahorsecowsaymint-backgrounds-qianamint-backgrounds-rafaela\
mint-backgrounds-rebeccamint-backgrounds-rosasoxttf-indic-fonts-core\
ttf-punjabi-fontsttf-wqy-microheifonts-kacstfonts-kacst-one\
fonts-khmeros-corefonts-laofonts-lklug-sinhalafonts-thai-tlwg\
fonts-tibetan-machinefonts-tlwg-garudafonts-tlwg-kinnarifonts-tlwg-loma\
fonts-tlwg-norasifonts-tlwg-purisafonts-tlwg-sawasdeefonts-wqy-microhe\
fonts-notofonts-sil-abyssinicafonts-sil-padaukfonts-takao-pgothic\
fonts-tlwg-umpushfonts-tlwg-wareegimp-help-enfirefoxfirefox-locale-en\
simple-scantransmission-commontransmission-gtkmintwelcome
MoMoworkshop
12Setup
Removedremainingdependenciesnolongerneeded
$sudoapt-getautoremove
Updatedandupgradedpackgestolatestversion
$sudoapt-get-yupdate&&sudoapt-get-yupgrade
Incaseyoudidn'treceiveabootableUSBwiththeabovesystem,youcangrabanimageunderthefollowingURL:http://files.terrestris.de/momo-master.tar.gzThisfileisroughly2.2GBbig,andonceitisdecompresseditwillhaveasizeof16GB.Datausedintheworkshop:
NaturalEarthLargescaledata,1:10mCultural
DownloadherePhysical
DownloadhereRaster(Oceanbottom)
DownloadhereExtractedto~/materials/natural_earth
OSMsampleexportMongoliaDownloadhereExtractedto~/materials/osm_mongolia
MoMoworkshop
13Setup
WorkshopdataThissectionshowsthedataandthedatasourceswe'llusintheworkshop.
NaturalEarth(~/materials/natural_earth)
TheNaturalEarthdatasetisafreecollectionofvectorandrasterdatapublishedbytheNorthAmericanCartographicInformationSocietytoencouragemapping.InthisworkshopwewillusethedatasetsCultural,PhysicalandRasterinlargescale(1:10m).
OpenStreetMap(~/materials/osm_mongolia)
OpenStreetMapisafreeeditablemapdatabaseoftheworld.InthisworkshopweuseasmallexcerptofthehugeOSMdatabasefocusedonmongoliawhichisprovidedbythecompanygeofabrikasacollectionofsingleshapefiles.
MoMoworkshop
14Data
GeoServer
GeoServerisanOpenSourcesoftwareserverwritteninJavathatallowsuserstoshareandeditgeospatialdata.Designedforinteroperability,itpublishesdatafromanymajorspatialdatasourceusingopenstandards.GeoServeristhereferenceimplementationoftheOpenGeospatialConsortium(OGC)WebFeatureService(WFS)andWebCoverageService(WCS)standards,aswellasahighperformancecertifiedcompliantWebMapService(WMS).GeoServerformsacorecomponentoftheGeospatialWeb.
Inthismodulewewillfocusonthegeodata-managementusingtheGeoServeradministrationfrontend.Thuswewilllearnbothhowtosetthemostcommonconfigureoptionsandhowtoload,publish,style,andsharegeospatialdatawithGeoServer.
Theworkshopissubdividedintotwomaincategories:
GeoServerBasics(Administration&Publishing)GeoServerAdvanced(REST&GeoWebCache)
PartsofthisworkshopareheavilyinspiredbyworkshopspreparedbytheGeoServercommunityandboundless.Feelfreetolookatthesesourcesforfurtherinformations.
MoMoworkshop
15GeoServer
GeoServerBasicsInthissectionwe'lllearnhowtodealwiththemostcommonusecaseswhenworkingwiththeGeoServer:ManagetherunningGeoServerinstancebyadjustingthemostimportantconfigurationsettingsandpublishinggeospatialdata.
AdministrationsettingsPublishinggeospatialdata
MoMoworkshop
16Basics
AdministrationThismodulewillgiveyouabriefintroductionintotheGeoServeradministrationfrontendwherewe'llfocusonthemainlyusedconfigurationsettings.ThebasisstructureofthefollowingchaptersisdirectlyinspiredbyGeoServersfrontendcomposition(Note:TheDataandTileCachingsectionswillbetreatedintheparticularchaptersPublishingandGeoWebCache).
LoginandfrontendoverviewAbout&StatusServicesSettingsSecurity
MoMoworkshop
17Administration
LoginandfrontendoverviewGeoServerincludesaweb-basedadministrationinterface.MostGeoServerconfigurationcanbedonethroughthisinterface,withouttheneedtoeditconfigurationfilesbyhandoruseanAPI.Thissectionwillgiveabriefoverviewtothewebinterface.Subsequentsectionswillusethewebinterfaceingreaterdetail.
Welcomepage
ToopentheGeoServerUIopenthefollowingaddressinyourbrowser:
http://localhost/geoserver
TheinitialpageiscalledtheWelcomepage.ToreturntotheWelcomepagefromanywhere,justclicktheGeoServerlogointhetopleftcornerofthepage.
GeoServerwelcomepage
Whiletheunauthenticated/anonymousWelcomepageisnotvoidoffeatures,itreallyjustletsyouseethings(configuredongeoserver)butnottouchthem(andmakeconfigurationchanges).
Forsecurityreasons,mostGeoServerconfigurationtasksrequireyoutobeloggedinfirst.Bydefault,theGeoServeradministrationcredentialsareadminandgeoserver,althoughthiscanandshouldbechanged(seechapterSecurity).
Login
LoginintoGeoServerbyusingthedefaultadministrationcredentialsfromabove.
MoMoworkshop
18Administration
GeoServerwelcomepage
Afterthelogin,manymoreoptionswillbedisplayed.
Basiclayout
UsethelinksontheleftsidecolumntomanageGeoServer,itsservices,data,securitysettingsandmore.Alsoonthemainpagearedirectlinkstothecapabilitiesdocumentsforeachservice(WFS,WMS,WCS).We'llbeusingthelinksontheleftunderData-amongthemLayerPreview,Workspaces,Stores,Layers,LayerGroups,andStyles-veryofteninthisworkshop,soitisgoodtofamiliarizeyourselfwiththeirlocation.Thuswe'llstartthismoduleinthefollowingchaptersbyintroducingthefrontendstructure.
MoMoworkshop
19Administration
AboutandStatusThissectionofthewebadministrationinterfaceprovidesahighleveloverviewoftherunningapplication.ContactinformationforOGCservicesisalsomanagedhere.
ServerStatusGeoServerLogsContactInformationAboutGeoServer
MoMoworkshop
20Administration
ServerStatusTheServerStatuspageshowsyoumanymetainformationaboutthecurrentGeoServerconfigurationandoverallstatus.
Serverstatuspage.
ItprovidesausefuldiagnostictoolinatestingandproductionenvironmentandshouldbeyourfirstplacetogoifarefacinganyproblemwithyourrunningGeoServerinstance.
Certainlyit'salwaysusefultohavealookattheLogsadditionally.
Statusindicators
Thefollowingtabledescribesthecurrentstatusindicators(asdescribedhere).
MoMoworkshop
21Administration
Option Description
Datadirectory Theabsolutepathtoyourdatadirectory.
Locks
AWFShastheabilitytolockfeaturestopreventmorethanonepersonfromupdatingthefeatureatonetime.Ifdataislocked,editscanbeperformedbyasingleWFSeditor.Whentheeditsareposted,thelocksarereleasedandfeaturescanbeeditedbyotherWFSeditors.Azerointhelocksfieldmeansalllocksarereleased.Iflocksisnon-zero,thenpressingFreelocksreleasesallfeaturelockscurrentlyheldbytheserver,andupdatesthefieldvaluetozero.
Connections Referstothenumbersofvectorstores,intheabovecase6,thatwereabletoconnect.
MemoryUsage TheamountofmemorycurrentlyusedbyGeoServer.ClickingontheFreeMemorybutton,cleansupmemorymarkedfordeletionbyrunningthegarbagecollector.
JVMVersion DenoteswhichversionoftheJVM(JavaVirtualMachine)isbeenusedtopowertheserver.
AvailableFonts AlistofallfontsGeoServerhasaccessto.Thesecanbereferencedinthelayerstyle.
NativeJAIGeoServerusesJavaAdvancedImaging(JAI)frameworkforimagerenderingandcoveragemanipulation.Whenproperlyinstalled(true),JAImakesWCSandWMSperformancefasterandmoreefficient.
NativeJAIImageIO GeoServerusesJAIImageIO(JAI)frameworkforrasterdataloadingandimageencoding.Whenproperlyinstalled(true),JAIImageI/OmakesWCSandWMSperformancefasterandmoreefficient.
JAIMaximumMemory Expressesinbytestheamountofmemoryavailablefortilecache.
JAIMemoryUsage Run-timeamountofmemoryisusedforthetilecache.ClickingontheFreeMemorybutton,clearsavailableJAImemorybyrunningthetilecacheflushing.
JAIMemoryThreshold
Referstothepercentage,e.g.75,ofcachememorytoretainduringtileremoval.JAIMemoryThresholdvaluemustbebetween0.0and100.
NumberofJAITileThreads Thenumberofparallelthreadsusedbytoschedulertohandletiles.
JAITileThreadPriority
Schedulestheglobaltileschedulerpriority.Thepriorityvalueisdefaultsto5,andmustfallbetween1and10.
ThreadPoolExecutorCorePoolSize
TheimageMosaicreadermayload,inparallel,differentfilesthatmakeupthemosaicbymeansofaThreadPoolExecutor.AglobalThreadPoolExecutorinstanceissharedbyallthereaderssupportingandusingconcurrentreads.HerethecurrentcorepoolsizeoftheThreadPoolExecutorislisted.
ThreadPoolExecutorMaxPoolSize HerethecurrentmaximumcorepoolsizeoftheThreadPoolExecutorislisted.
ThreadPoolExecutorKeepAliveTime(ms)
ThetimetobewaitedbytheThreadPoolExecutorbeforeterminatinganidlethreadincasetherearemorethreadsthanavailableinthecorepoolsize.
UpdateSequence Referstothenumberoftimestheserverconfigurationhasbeenmodified.
ResourceCache
GeoServerdoesnotcachedata,butitdoescacheconnectiontostores,featuretypedefinitions,externalgraphics,fontdefinitionsandCRSdefinitionsaswell.TheClearbuttonforcesthosecachestoemptyandmakesGeoServerreopenthestoresandre-readimageandfontinformation,aswellasthecustomCRSdefinitionsstoredin${GEOSERVER_DATA_DIR}/user_projections/epsg.properties.
Configurationandcatalog
GeoServerkeepsinmemoryallofitsconfigurationdata.Ifforanyreasonthatconfigurationinformationhasbecomestale(e.g.anexternalutilityhasmodifiedtheconfigurationondisk)theReloadbuttonwillforceGeoServertoreloadallofitsconfigurationfromdisk.
Exercise
OpenuptheServerStatuspageonyourGeoServerandpressthebuttonFreememorybesidesMemoryUsage.Whatdoyouobserve?
MoMoworkshop
22Administration
MoMoworkshop
23Administration
GeoServerLogsGeoServerdisplaysthecontentsoftheapplicationlogsdirectlythroughthewebinterface.Readingthelogscanbeveryhelpfulwhentroubleshooting.Toviewthelogs,clickonGeoServerLogsontheleftunderAbout&Status.
Loggingpage.
Exercise
OpenuptheGeoServerLogssectionandinvestigatethelastentriesinyourlogfile.Canyoufindanyconspicuousorinterestingentry?
MoMoworkshop
24Administration
ContactInformationEachOGCwebserviceservedbytheGeoServercontainsthecontactdetailsassociatedwiththeserveraspartoftheircapabilitiesdocument.YoucanreadandmanipulatetheseinformationinthesectionContactInformationunderAbout&Status.
Contactpage.
Exercise
FillinyourcontactinformationsinContactInformationpage.ClickSubmit.OpenthefollowinglinktorequesttheGetCapabilitesdocumentprovidedbyyourGeoServerinstanceandverifyyourchanges:
http://localhost/geoserver/ows?SERVICE=WMS&REQUEST=GetCapabilities&VERSION=1.1.0
YoucanalsorequesttheaboveGetCapabilitiesdocumentbyvisitingtheWelcomePageandselectthelinksontherighthandedside.
MoMoworkshop
25Administration
MoMoworkshop
26Administration
AboutGeoServerTheaboutpagelistsgeneralinformationsabouttheGeoServeryou'rerunning.Theselistingsareespaciallyhelpful,ifyouwanttocontactanysupportorifyouwanttoknowtheinstalledGeoServerversionforgettingcompatiblecommunitymodulesandextensions.
Note:You'llseealinkDocumentationattheendofthepage.ThislinkwillguideyoutotheofficialdocumentationoftheprojectandisaveryhelpfuladdressifyouneedanyinformationaboutGeoServer.
Aboutpage.
Exercise
FollowthementionedDocumentationlinkandhaveaquicklookaroundtoseethevaluableresourcesavailablehere!
MoMoworkshop
27Administration
ServicesTheServicessectionisforconfiguringtheservicespublishedbyGeoServer,wherewecanmanage:
Themetadata,resourcelimits,andSRSavailabilityforWCS.Themetadata,featurepublishing,serviceleveloptions,anddata-specificoutputforWFS.Themetadata,resourcelimits,SRSavailability,andotherdata-specificoutputforWMS.
Thefollowingexerciseswillgiveyouabriefintroductioninthemostimportantadministrationoptionsavailablehere.
ForfurtherinstructionspleasehavealookattheofficialGeoServerdocumentation.
LimitSRSoutputlist
ThedefaultGetCapabilitiesdocumentcontainsacomprehensivelistofallavailablespatialreferencesystems(SRS)ofyourGeoServerWMSserverinstance.Generallyitisnotneeded,thatyoulistallsupportedsystemsasyoutypicallywanttopublishdatainanlimitedlistofprojectionsonly.FurthermorelimitingthislistwillreducethefilesizeoftherespondingGetCapabilitesdocument!
Exercise
OpenthefollowingURLinyourbrowsertoopentheGetCapabilitesdocumentofyourGeoServerinstance:GetCapabilitiesIntheresultingXMLdocumentfindtheelementLayerwhichcontainsthelargelist(~6000lines)ofallsupportedEPSGprojections.
Inthenextstepwe'lllimitthislisttocontainthesystemsEPSG:4326,EPSG:3857andEPSG:900913only.
NavigatetoServices❭WMS.FindthedescriptionfieldentitledwithLimitedSRSlistandfillin4326,3857,900913.
ClickSubmit.ReopentheGetCapabilitesdocumentandyouwillnote,thatthedocumentisreducedbyahugeamountoflines.
DisableWFS-TGeoServerisconfiguredtoactasafullytransactionalWebFeatureServiceserverperdefault.AtransactionalWFSallowscreation,deletion,andupdatingoffeatures.Thisimplies,thateachpublishedfeaturetypecanbeeditedbyanyclient.Generallyyoudon'twanttoallowclientstoedityourdatapublishedwithGeoServer(unlessyoureallywanttoallowit),especiallyifyourGeoServerisaccessiblegloballythroughtheInternet.Inthenextiterationwe'regoingtodisabletheWFS-Tfunctionality.
Exercise
NavigatetoServices❭WFS.FindthecheckboxgroupentitledwithServiceLevelandselectthelevelBasictodisableWFS-Tcompatibility.
MoMoworkshop
28Administration
ClickSubmit.Optional:ImportagivenWMSlayerintoQGISandtrytoeditit.
MoMoworkshop
29Administration
SettingsTheSettingssectioninvolvesconfigurationsettingsthatapplytotheentireserver.Again,insteadofexplainingeachcheckbox,we'llfocusonthemostimportantadministrationtoolsavailableinthissectionanditssubsections.
ForfurtherinstructionspleasehavealookattheofficialGeoServerdocumentation.
Sethandledataandconfigurationproblems
ThissettingdetermineshowGeoServerwillrespondwhenalayerbecomesinaccessibleforsomereason.Bydefault,whenalayerhasanerror(forexample,whenthedefaultstyleforthelayerisdeleted),aserviceexceptionisprintedaspartofthecapabilitiesdocument,makingthedocumentinvalid.Forclientsthatrelyonavalidcapabilitiesdocument,thiscaneffectivelymakeaGeoServerappeartobe"offline".AsadministratoryoumayprefertoconfigureGeoServertosimplyomittheproblemlayerfromthecapabilitiesdocument,thusretainingthedocumentintegrityandallowingclientstoconnecttootherpublishedlayers.
Exercise
GotoSettings❭Global.SelectSkippingmisconfiguredlayersinthecomboentitledwithHandledataandconfigurationproblemsincapabilitiesdocumentsby....
ClickSubmit.
Reducethenumberofdecimals
ToreducetheoutputsizereturnedinaGetFeatureresponse(andthereforeoptimizingthebandwith)wecanrestrictthenumberofdecimalplacesinaGetFeatureresponse.Herewewillsetthevalueto2.
Exercise
GotoSettings❭Global.SetNumberofDecimalsto2.
ClickSubmit.
Changelogginglevel
Atsomepointinyourproduction(ordevelopment)usageofGeoServeryou'llencounteraproblemwhereyou'llneedtogetfurtheranddetailedinformationstofindthecauseoftheproblem.Inthiscaseyoucanincreasetheleveloflogging.Thefollowingstepswillguideyouhowtosettheloggingleveltoaverbose-likelevelcontainingvaluableinformationsabouttheimageprocessingprocess.
Exercise
GotoSettings❭Global.SelectthecheckboxbesideVerboseExceptionReportingtoreturnserviceexceptionswithfullJavastacktraces.FindthedescriptionfieldentitledwithLoggingProfileandselecttheprofileVERBOSE_LOGGING.propertiestoenabletheDEBUGlevelloggingonGeoToolsandGeoServer.
MoMoworkshop
30Administration
ClickSubmit.TovalidatethechangespleaseopentheOpenLayerslayerpreviewforalayerofyourchoice(Data❭LayerPreview)andzoomslightlyintothemap.GotoAbout&Status❭GeoServerLogstoopenupthelogfileandscrolldowntotheendofthefile.Havealookatthetimestampandyou'llnoticeplentyoflogsforjustasimpleGetMaprequest(youdidinthelayerpreview).
MoMoworkshop
31Administration
Asthegivensettingsarenotreallyneededforthemoment(wearen'tfacinganyproblems)wecanresettheloggingleveltoDEFAULT_LOGGING.properties.
MoMoworkshop
32Administration
SecurityGeoServerhasarobustsecuritysubsystem,modeledonSpringSecurity.MostofthesecurityfeaturesareavailablethroughtheWebadministrationinterface.AdetailedexplanationofallsecurityoptionsisfarbeyondthegoalsofthisworkshopandthedefaultsettingsarealmostgoodenoughforthebasicusageofGeoServeraswell.Herewe'llfocusonthemostimportantfactafterinstallingaGeoServer:Changingthedefaultadminuserandmasterpassword.(YoumayhaverecognizedtheappropriatewarningsontheGeoServerwelcomepage.)
ForfurtherinstructionspleasehavealookattheofficialGeoServerdocumentation.
Changemasterpassword
Themasterpasswordservestwopurposes:
Protectaccesstothekeystore.ProtectaccesstotheGeoServerRootaccount(Therootaccountisalwaysactive,regardlessofthestateofthesecurityconfiguration.MuchlikeitsUNIX-stylecounterpart,thisaccountprovides"superuser"status,andismeanttoprovideanalternativeaccessmethodforfixingconfigurationissues.Theusernamefortherootaccountisroot.Itsnamecannotbechanged.).
Exercise
Tochangethemasterpasswordfollowthesesteps:
GotoSecurity❭Passwords.SelectChangepassword.Intheupcomingformusethefollowingvaluestochangethepasswordtomomo-ws:
Currentpassword:geoserverNewpassword/Confirmation:momo-ws(PleaseuseamoresecurepasswordinarealworldGeoServerusage!)
ClickChangePassword.
Changeadminuserpassword
Exercise
Tochangethepasswordfortheuseradminpleasefollowthesesteps:
GotoSecurity❭Users,Groups,Roles.OpenpanelUsers/Groupswhereyou'llseealistofallcurrentusersofyourGeoServerinstance.Atthemoment(andinmostfutureapplications)you'llfindtheuseradminonly.Selecttheuseradminbyclickingonitsusername.Intheupcomingformusethefollowingvaluestochangethepasswordtomomo-ws:
Password/ConfirmPassword:momo-ws(PleaseuseamoresecurepasswordinarealworldGeoServerusage!)
ClickSave.
MoMoworkshop
33Administration
MoMoworkshop
34Administration
PublishingInthissectionwe'lllearnthecoreprincipalsabouttheGeoServerdatamanagementandhowtoprepareandimport(vector-/raster)dataintoGeoServerandhowtopublishitasvector,rasterandgrouplayer.
PublishvectorlayerPublishrasterlayerPublishgrouplayer
MoMoworkshop
35Publishing
PrerequisitesBeforewecanstartimportingandreadingdatainthismodules,wehavetocreateanewdatabaseonthe(alreadyinstalled)PostgreSQLdatabaseserverasdatasourcefornewlayers.ThuswewillusetheadministrationtoolpgAdminIII.Let'sstartbyopeningit:
ClickMenuinthelowerleftcornerandsearchforpgadmin
IntheresultinglistselectpgAdminIII( )toopenthetool.
AddanewserverconnectioninpgAdmin
OncepgAdminhasstartedwecancreateanewconnectiontoourPostgreSQLdatabaseserver:
CreateanewserverconnectionbyselectingFile❭AddServerinthetopmenubarandenterthefollowing:Name:momo-workshopHost:localhostPort:5432Username:momoPassword:momoStorepassword:checked
ClickOK
CreatingadatabaseNowwecanconnecttothisserverbyadoubleclickonthenewlycreatedentryinthelefthandsidedObjectbrowser(oropenthecontextmenuforthisentryandselectConnectasshownbelow).
MoMoworkshop
36Publishing
Withinthenextstepswewillcreateanewdatabaseonthisdatabaseserver:
OpentheSQL-Querywindowbyclickingtheicon intheuppertoolbar.Note:Iftheiconisgreyedout,selecttheexistingdatabasepostgresfirst.CopythefollowingSQLblockintotheSQL-Querywindow:
CREATEDATABASEdb_momo_ws
WITHOWNER=momo
ENCODING='UTF8'
TABLESPACE=pg_default
CONNECTIONLIMIT=-1;
ClickExecutequery( )intheuppertoolbartorunthequery.AftersuccessfulexecutiongobacktotheObjectbrowser,selecttheserverandrefreshtheactualview(bypressingRefresh
theselectedobject( )inthetoptoolbar)andensureyouhaveanewdatabaseentrynameddb_momo_wspresent.ClosetheSQL-Querywindow.
Creatingaschema
Oncethedatabaseiscreated,we'llcreateanewschemainthisdatabase.Thisschemawillbeusedtostoreanygeodatatablewearegoingtoimportinthisworkshop.
Selectthenewlycreateddatabasedb_momo_wsintheObjectbrowserandopentheSQLwindow( ).Ifyouhaven'tclosedtheSQL-Querywindowbefore,pleaseverifythatyouareconnectedtothecorrectdatabaseintheuppertoolbar.OtherwiseallsubsequentSQLquerieswillbeexecutedonthewrongdatabase!CopythefollowingSQLblockintotheSQL-Querywindowtocreateanewschemanamedgeodata:
CREATESCHEMAgeodata
AUTHORIZATIONmomo;
ClickExecutequery( )torunthequery.RefreshtheObjectbrowserandensurethenewschemaisbeingcreatedinthedatabasedb_momo_ws.
EnablespatialfunctionalityInthefinalstepwewilladdsupportforgeographicobjectsbyenablingthespatialdatabaseextensionPostGISforourdatabasedb_momo_ws.
OpentheSQLwindow(ifnotalreadyopened)andpasteinthefollowingSQLblocktospatiallyenablethedatabasedb_momo_ws:
CREATEEXTENSIONpostgis;
ClickExecutequerytorunthequery.Ensuretheextensionisbeingsuccessfullyinstalledbyexecutingthefollowingquery:
SELECTPostGIS_full_version();
MoMoworkshop
37Publishing
Thecorrespondingoutputshouldlooklike:
"POSTGIS="2.1.2r12389"GEOS="3.4.2-CAPI-1.8.2r3921"PROJ="Rel.4.8.0,6March2012"GDAL="GDAL1.10.1,released2013/08/26"LIBXML="2.9.1"LIBJSON="UNKNOWN"RASTER"
MoMoworkshop
38Publishing
WorkspaceAworkspace(sometimesreferredtoasanamespace)isthenameforanotionalcontainerforgroupingsimilardatatogether.Itisdesignedtobeaseparate,isolatedspacerelatingtoacertainproject.Usingworkspaces,itispossibletouselayerswithidenticalnameswithoutconflicts.
Workspacesareusuallydenotedbyaprefixtoalayernameorstorename.Forexample,alayercalledstreetswithaworkspaceprefixcallednycwouldbereferredtobynyc:streets.Thiswouldnotconflictwithanotherlayercalledstreetsinanotherworkspacecalleddc(dc:streets).
Storesandlayersmustallhaveanassociatedworkspace.Stylesmayoptionallybeassociatedwithaworkspace,butcanalsobeglobal.
Technically,thenameofaworkspaceisaURI,nottheshortprefix.AURIisaUniformResourceIdentifier,whichissimilartoaURL,butdoesnotneedtoresolvetoawebsite.Intheaboveexample,thefullworkspacecouldhavebeenhttp://nycinwhichcasethefulllayernamewouldbehttp://nyc:streets.GeoServerintelligentlyreplacestheworkspaceprefixwiththefullworkspaceURI,butitcanbeusefultoknowthedifference.
Creatinganewworkspace
Inthissectionwearegoingtocreateanewworkspacecalledmomo.
NavigatetoData❭Workspaces.ClickAddnewworkspaceandenterthefollowing:
Name:momoNamespaceURI:http://localhost:80/momoDefaultWorkspace:checked
ClickSubmit
Addnewworkspace
Theworkspacehasbeencreatedandisnowactive.Thegreencheckmarkindicatesthattheworkspaceisthedefaultone.
MoMoworkshop
39Publishing
VectorlayersInthissectionwewilllearnhowtoimportashapefiletoaspatiallyenabledPostgreSQLdatabase,setupanewvectorstoreandpublishanewvectorlayerwithGeoServer.
ImportshapefiletodatabaseCreateanewPostgreSQL/PostGISdatastorePublishanewvectorlayerPreviewthelayerusingGeoServer'slayerpreviewChangingthelayerstyle
MoMoworkshop
40Publishing
ImportOurworkshopdata(seehere)isactuallygivenasacollectionofsingleshapefiles.Inordertoenhancetheperformancewhilereadandwriteprocessesandenablewritingatall(shapefilescan'tbemanipulatedviaWFS-Taftertheyhavebeenpublished),wewillimportthegivendataintoourownPostgreSQLdatabaseentitywejustcreated.
ImportcountrypolygonsOpenanewterminalwindowandnavigatetothematerialsdirectory:
$cd~/materials
Listalldirectoriesinthecurrentfolderwith:
$ls-l
Youshouldbeabletoseethefoldersnatural_earthandosm_mongolia.Ifyouinspectthemfurtheron,youwillseethateachfoldercontainsawidelistofsingleshapefiles,butforthemomentwewanttouseasmallportionofthemtoimportintothedatabaseonly.Thereforewearegoingtostartwithimportingashapefileinvolvingworldwidecountrypolygons.Navigatetodirectorymaterials/natural_earth/10m_culturalandcheckifyoucanfindafilenamedne_10m_admin_0_countries.shp.Toimporttheshapefilewewillusethecommandlinetoolshp2pgsql.Thefollowingcommandwilltransformtheinputshapefilene_10m_admin_0_countries.shpwiththeinputencodingLATIN1andinputprojection4326toaSQLstatementwhichwillfillthedatainanewtabletbl_countriesinschemageodata.Afterexecutiontheoutputofshp2pgsqlisdirectlypipedtopsqlwhichwillruntheSQLoutputagainsttheworkshopdatabase.Youcaneasilycopythecommandbelowintotheterminalwindowandexecuteitsubsequently.
$shp2pgsql\
-s4326\
-WLATIN1\
-Ine_10m_admin_0_countries.shp\
geodata.tbl_countries|\
psql\
-hlocalhost\
-p5432\
-Umomo\
-W\
-ddb_momo_ws
Afterthesuccessfulexecutionoftheabovecommand(re-)openpgAdminIII,markthedatabaseservermomo-workshopintreeviewandclickRefreshtheselectedobjecttorefreshthedatabase/tablelistandexpandthetreetoDatabases❭db_momo_ws❭Schemas❭geodata❭Tables(ifnotalreadyhappened)asshownbelow:
MoMoworkshop
41Publishing
Selecttabletbl_countriesandclickViewthedataintheselectedobjecttoopenthedataviewfortheimportedtableasshownbelow:
MoMoworkshop
42Publishing
Congratulations!You'vesuccessfullyimportedashapefileintoPostgreSQLthatcannoweasilybepublishedthroughtheGeoServerinstance!
MoMoworkshop
43Publishing
StoreAstoreisthenameforacontainerofgeographicdata.Astorereferstoaspecificdatasource,beitashapefile,database,oranyotherdatasourcethatGeoServersupports.
Astorecancontainmanylayers,suchasthecaseofadatabasethatcontainsmanytables.Astorecanalsohaveasinglelayer,suchasinthecaseofashapefileorGeoTIFF.Astoremustcontainatleastonelayer.
GeoServersavestheconnectionparameterstoeachstore(thepathtotheshapefile,credentialstoconnecttothedatabase).Eachstoremustalsobeassociatedwithone(andonlyone)workspace.
Astoreissometimesreferredtoasa"datastore"inthecontextofvectordata,or"coveragestore"inthecontextofraster(coverage)data.
Creatinganewstore
Nowwecanaddanewstoretoournewworkspacemomo.ThisstoretellsGeoServerhowtoconnecttothedatasource,inourcasethePostgreSQLdatabase.
NavigatetoData❭Stores.ClickAddnewStore.ClickPostGIS-PostGISDatabaseSettheWorkspacetomomoifitisn'tsetalready.Configurethenewstoreasfollows:
DataSourceName:db_momo_wsEnabled:checkeddbtype:postgishost:localhostport:5432database:db_momo_wsschema:geodatauser:momopasswd:momo
ClickSave
MoMoworkshop
44Publishing
Addnewstore
MoMoworkshop
45Publishing
LayerAlayer(sometimesknownasafeaturetype)isacollectionofgeospatialfeaturesoracoverage.Typicallyalayercontainsonetypeofdata(points,lines,polygons,raster)andhasasingleidentifiablesubject(streets,houses,countryboundaries,etc.).Alayercorrespondstoatableorviewfromadatabase,oranindividualfile.
GeoServerstoresinformationassociatedwithalayer,suchasprojectioninformation,boundingbox,andassociatedstyles.Eachlayermustbeassociatedwithone(andonlyone)workspace.
Publishingalayer
Oncethenewstoreiscreated,GeoServerautomaticallygivesustheoptionofpublishinglayersfromthisstore.Herewechosethetabletbl_countriesbyclickingPublish.
Publishalayer
Minimallayerconfiguration
Afterpublishing,GeoServerautomaticallygivesustheoptionofconfiguringthenewlycreatedlayer.Forthemoment,wewanttosetupthelayerwithsomebasicconfigurationonly.Thusweignorecustomstylingorcachingthatwillbehandledlateron.
Configurethenewlayerasfollows:Name:countriesEnabled:checkedAdvertised:checkedTitle:CountriesAbstract:Countriesoftheworld.NativeSRS:EPSG:4326DeclaredSRS:EPSG:4326SRShandling:Keepnative
LetGeoServercalculatetheboundsofthedatabyclickingComputefromdata.ConvertthenativeboundstotheLat/LonBoundingBoxbyclickingComputefromnativebounds.ClickSave.
MoMoworkshop
46Publishing
Publishanewlayer
MoMoworkshop
47Publishing
PreviewingalayerYoujustpublishedthelayerwithGeoServer!Nowlet'sseehowitlooksbyusingtheLayerPreview.
NavigatetoData❭LayerPreviewSearchforcountries.ClickOpenLayers.
LayerPreview
Helloworld!
AsyoupublishedthelayercountriesGeoServernotonlyservesthislayerasWMS,inadditionitautomaticallypublishesthefeaturetypeviaitsWFSserver.
ReturntotheLayerPreviewsiteandsearchforcountries(seesteps1.and2.ahead).SelectaWFSformat(e.g.thecommonformatGeoJSON)inthedropdownmenuAllformats.
MoMoworkshop
48Publishing
AfterselectingtheentryyoushouldseeanewbrowsertaborwindowcontainingtheGeoJSONrepresentationofthelayercountriessimilartofollowingexcerpt:
{
"type":"FeatureCollection",
"totalFeatures":254,
"features":[{
"type":"Feature",
"id":"countries.1",
"geometry":{
"type":"MultiPolygon",
"coordinates":[
[
[
[-69.99693762899994,12.577582098000022],
[-69.93639075399997,12.531724351000037],
[-69.92467200399997,12.519232489000018],
(...)
]
]
]
},
"geometry_name":"geom",
"properties":{
"scalerank":3,
"featurecla":"Admin-0country",
"labelrank":5,
"sovereignt":"Netherlands",
(...)
}
},
(...)
]
};
MoMoworkshop
49Publishing
StyleAstyleisavisualizationdirectiveforrenderinggeographicdata.Astylecancontainrulesforcolor,shape,andsize,alongwithlogicforstylingcertainfeaturesorpointsincertainwaysbasedonattributesorscalelevel.
Everylayermustbeassociatedwithatleastonestyle.GeoServerrecognizesstylesinStyledLayerDescriptor(SLD)format.TheStylingsectionwillgointothistopicingreaterdetail.
Createandassignastyletoalayer
GotoData❭Styles❭AddanewstyleCreateanewstyleasfollows:
Name:countriesWorkspace:momoFormat:SLDCopyandpastethefollowingSLDcontentintothestylefield:
MoMoworkshop
50Publishing
<?xmlversion="1.0"encoding="UTF-8"?>
<sld:StyledLayerDescriptor
xmlns="http://www.opengis.net/sld"
xmlns:sld="http://www.opengis.net/sld"
xmlns:ogc="http://www.opengis.net/ogc"
xmlns:gml="http://www.opengis.net/gml"
version="1.0.0">
<sld:NamedLayer>
<sld:Name>countries</sld:Name>
<sld:UserStyle>
<sld:Name>Countries</sld:Name>
<sld:Title>Countries</sld:Title>
<sld:FeatureTypeStyle>
<sld:Name>countries</sld:Name>
<sld:Rule>
<sld:PolygonSymbolizer>
<sld:Fill>
<sld:CssParametername="fill">#EDEDED</sld:CssParameter>
</sld:Fill>
<sld:Stroke>
<sld:CssParametername="stroke">#969696</sld:CssParameter>
<sld:CssParametername="stroke-width">0.5</sld:CssParameter>
</sld:Stroke>
</sld:PolygonSymbolizer>
<sld:TextSymbolizer>
<sld:Label>
<ogc:PropertyName>name</ogc:PropertyName>
</sld:Label>
<sld:Font>
<CssParametername="font-family">DejaVuSans</CssParameter>
<CssParametername="font-size">10</CssParameter>
</sld:Font>
<sld:LabelPlacement>
<sld:PointPlacement>
<sld:AnchorPoint>
<sld:AnchorPointX>0.5</sld:AnchorPointX>
<sld:AnchorPointY>0.5</sld:AnchorPointY>
</sld:AnchorPoint>
</sld:PointPlacement>
</sld:LabelPlacement>
<sld:Halo>
<sld:Radius>1</sld:Radius>
<sld:Fill>
<CssParametername="fill">#FFFFFF</CssParameter>
</sld:Fill>
</sld:Halo>
<sld:Fill>
<CssParametername="fill">#707070</CssParameter>
</sld:Fill>
</sld:TextSymbolizer>
</sld:Rule>
</sld:FeatureTypeStyle>
</sld:UserStyle>
</sld:NamedLayer>
</sld:StyledLayerDescriptor>
GotoData❭Layers,searchforcountriesandselectitinthelist.
GototabPublishing.
MoMoworkshop
51Publishing
Selectmomo:countriesindropdownlistDefaultStyle.
ClickSave.Openthelayerpreviewforthelayercountriesandyouwillseethatthelayerwillhaveanewappearance(lightgreypolygonfill)includinglabelsforeachcountry.
Layerpreviewcenteredtomongolia.
MoMoworkshop
52Publishing
RasterlayersInthissectionwewilllearnhowtopreparealargeGeoTIFFfilewithGDAL,setupanewrasterstoreandpublishanewrasterlayerwithGeoServer.
PreparelargeGeoTIFFwithGDALCreateanewImagePyramiddatastorePublishanewrasterlayerPreviewthelayerusingGeoServer'slayerpreview
MoMoworkshop
53Publishing
PreparingthedataAnimagepyramidbuildsmultiplemosaicsofimages,eachoneatadifferentzoomlevel,makingitsothateachtileisstoredinaseparatefile.Thiscomeswithacompositionoverheadtobringbackthetilesintoasingleimage,butcanspeedupimagehandlingaseachoverviewistiled,andthusasub-setofitcanbeaccessedefficiently(asopposedtoasingleGeoTIFF,wherethebaselevelcanbetiled,buttheoverviewsneverare).
OutinputrasterfromnaturalearthisasimplehugeGeoTIFFfile(~400MB)withoutoverviews.Notexactlywhatwe'dwanttouseforhighperformancedataserving,butgoodforredistributionandasastartingpointtobuildapyramid.
Inordertobuildthepyramidwe'llusethegdal_retile.pyutility,partoftheGDALcommandlineutilitiesandavailableforvariousoperatingsystems.
Openterminalandnavigatetodirectory~/materials/natural_earth/OB_LR.Createanewfoldernamedpyramidwith:
$mkdirOB_LR_pyramid/
Runthefollowingcommandthatwillbuildapyramid(Note:Thismaytakeawhile!):
$gdal_retile.py-v\
-s_srsEPSG:4326\
-rbilinear\
-levels4\
-ps512512\
-co"TILED=YES"\
-co"COMPRESS=JPEG"\
-targetDirOB_LR_pyramid/\
OB_LR.tif
Shortexplanation:
-v:Verboseoutput,allowstheusertoseeeachfilecreationscrollby,thusknowingprogressisbeingmade.-rbilinear:Usebilinearinterpolationwhenbuildingthelowerresolutionlevels.ThisiskeytogetgoodimagequalitywithoutaskingGeoServertoperformexpensiveinterpolationsinmemory.-levels4:Thenumberoflevelsinthepyramid.-ps512512:Eachtileinthepyramidwillbea512x512GeoTIFF.-co"TILED=YES":EachGeoTIFFtileinthepyramidwillbeinnertiled.-co"COMPRESS=JPEG":EachGeoTIFFtileinthepyramidwillbeJPEGcompressed(tradessmallsizeforhigherperformance,tryoutitwithoutthisparametertoo).-targetDirpyramid:Buildthepyramidinthepyramiddirectory.ThetargetdirectorymustexistandbeemptyOB_LR.tif:ThesourcefileAsGeoServerneedstohavereadandwriteaccesstothepyramidwejustcreated,we'llmovetheOB_LR_pyramidfoldertotheGeoServerdatadirectory:
$sudomvOB_LR_pyramid//opt/tomcat/webapps/geoserver/data/data/
Navigatetothedatadirectory:
$cd/opt/tomcat/webapps/geoserver/data/data/
Assignreadandwriteaccesstothetomcatuser:
$sudochown-Rtomcat:tomcatOB_LR_pyramid;sudochmod-R755OB_LR_pyramid/
MoMoworkshop
54Publishing
MoMoworkshop
55Publishing
CreatinganewstoreGotoData❭Stores❭AddanewStoreSelectImagePyramid
Createthenewstoreasfollows:Workspace:momoDataSourcename:ocean-bottom-reliefEnabled:checkedURL:file:data/OB_LR_pyramid
ClickSave.
MoMoworkshop
56Publishing
PublishingalayerOncethenewstoreiscreated,GeoServerautomaticallygivesustheoptionofpublishinglayersfromthisstore.HerewechosetheentryOB_LR_pyramidbyclickingPublish.
ConfigureImagePyramidstore
MinimallayerconfigurationAfterpublishing,GeoServerautomaticallygivesustheoptionofconfiguringthenewlycreatedlayer.Forthemoment,wewanttosetupthelayerwithsomebasicconfigurationonly.Thusweignorecustomstylingorcachingthatwillbehandledlateron.
Configurethenewlayerasfollows:Name:ocean-bottom-reliefEnabled:checkedAdvertised:checkedTitle:OceanbottomreliefAbstract:BlendeddepthcolorsandreliefshadingoftheoceanbottomderivedfromCleanTOPO2data.NativeSRS:EPSG:4326DeclaredSRS:EPSG:4326SRShandling:Keepnative
LetGeoServercalculatetheboundsofthedatabyclickingComputefromdata.ConvertthenativeboundstotheLat/LonBoundingBoxbyclickingComputefromnativebounds.
MoMoworkshop
57Publishing
ClickSave.
MoMoworkshop
58Publishing
PreviewingalayerYoujustpublishedtherasterlayerwithGeoServer!Nowlet'sseehowitlooksbyusingtheLayerPreview.
1. NavigatetoData❭LayerPreview2. Searchforocean.3. ClickOpenLayers.
Thebathymetrylayer.
MoMoworkshop
59Publishing
GrouplayersInthissectionwewilllearnhowtosetupanewgroupedlayerwithGeoServer.
PublishanewgroupedlayerPreviewthelayerusingGeoServer'slayerpreview
MoMoworkshop
60Publishing
LayergroupAlayergroup,asitsnamesuggests,isacollectionoflayers.AlayergroupmakesitpossibletorequestmultiplelayerswithasingleWMSrequest.Alayergroupcontainsinformationaboutthelayersthatcomprisethelayergroup,theorderinwhichtheyarerendered,theprojection,associatedstyles,andmore.Thisinformationcanbedifferentfromthedefaultsforeachindividuallayer.
Layergroupsdonotrespecttheconceptofworkspace,andarerelevantonlytoWMSrequests.
CreategroupedlayerUsecountriesandoceanbottomrelief
GotoData❭LayerGroups❭Addnewlayergroup.Createanewlayergroupasfollows:
Name:world-layerTitle:WorldlayerWorkspace:momo
FindandselectEPSG:4326underCoordinateReferenceSystem.
ClickLayers❭AddLayer,searchforocean-bottomandclicktheocean-bottom-reliefinthelisttoaddthelayertothegroup.
MoMoworkshop
61Publishing
Repeatthestepaboveforlayercountries.LetGeoServergeneratetheboundsforthislayerbypressingGeneratebounds.
MoMoworkshop
62Publishing
ClickSave.
MoMoworkshop
63Publishing
PreviewingalayerYoujustpublishedthegrouplayerwithGeoServer!Nowlet'sseehowitlooksbyusingtheLayerPreview.
NavigatetoData❭LayerPreviewSearchforworld-layer.ClickOpenLayers.
ThegrouplayercenteredtothePhilippines
MoMoworkshop
64Publishing
GeoServerAdvancedInthissectionwe'lllearntwoaspectsofadvancedusageofGeoServer,wherewe'llfocusontheconfiugurationviatheRESTinterfaceandthecachingoflayerswiththebuilt-incachingengineGeoWebCache.
RESTAPICachingwithGeoWebCache
MoMoworkshop
65Advanced
Excursion:RESTinterfaceThischapterwillgiveyouashortintroductiontoGeoServersREST(RepresentationalStateTransfer)interface.TheRESTAPIallowsyoutoread,write,updateandremove(almost)allGeoServercatalogelementsdirectlyviatheHTTPprotocol.Thesecaninclude,forexample,themanipulationofworkspaces,datastorages,layerstylesandlayersitself.AbenefitofusingtheRESTinterfaceisthatyoucanscriptrecurringstepsofwork,forinstancepublishingalargenumberoflayersatoncefromaremotemachine.
Principleofoperation,source:https://github.com/boundlessgeo/workshops/blob/master/workshops/geoserver/adv/doc/source/catalog/img/rest_theory.png
WhatisREST?
REST(sometimesasReST)isanacronymforRepresentationalStateTransferandisanarchitecturalstylefortherealizationofwebservicesandisthereforefrequentlymentionedinconnectionwithRESTfulWebservices.TheideabehindisthatoneshouldbeabletousesimpleandlightweightHTTPcallstoconnectbetween(web-)clientsandremoteservers.SothecapabilitiesoftheRESTAPIconsistsoftheactions(verbs)wecanusetomakeHTTPrequestscombinedwiththeconfigurableresourcesinGeoServer.ForeachoftheresourcesinGeoServer(workspaces,stores,layers,styles,layergroups,etc.)wecanperformthefollowingoperations(source):
MoMoworkshop
66REST
Operation Description
GET TheGETmethodrequestsarepresentationofthespecifiedresource.RequestsusingGETshouldonlyretrievedataandshouldhavenoothereffect.
POST ThePOSTmethodrequeststhattheserveraccepttheentityenclosedintherequestasanewsubordinateofthewebresourceidentifiedbytheURI.
PUTThePUTmethodrequeststhattheenclosedentitybestoredunderthesuppliedURI.IftheURIreferstoanalreadyexistingresource,itismodified;iftheURIdoesnotpointtoanexistingresource,thentheservercancreatetheresourcewiththatURI.
PATCH ThePATCHmethodappliespartialmodificationstoaresource.
DELETE TheDELETEmethoddeletesthespecifiedresource.
HEAD TheHEADmethodasksforaresponseidenticaltothatofaGETrequest,butwithouttheresponsebody.Thisisusefulforretrievingmeta-informationwritteninresponseheaders,withouthavingtotransporttheentirecontent.
OPTIONS TheOPTIONSmethodreturnstheHTTPmethodsthattheserversupportsforthespecifiedURL.
CONNECT TheCONNECTmethodconvertstherequestconnectiontoatransparentTCP/IPtunnel,usuallytofacilitateSSL-encryptedcommunication(HTTPS)throughanunencryptedHTTPproxy.
TRACE TheTRACEmethodechoesthereceivedrequestsothataclientcanseewhat(ifany)changesoradditionshavebeenmadebyintermediateservers.
Tosumitup,intheGeoServerRESTAPIweareabletousethemethodsasfollows:
GETtoreadanexistingresourcePOSTtoaddanewresourcePUTtoupdateanexistingresourceDELETEtoremovearesource
Inrelationtothemethodsmentionedaboveeachrequestwillrespondwithacertainresponsecode:
Statuscode Statustext Description
200 OK Requestwassuccessful
201 Created Aresource(e.g.alayer)wassuccessfullycreated
403 Forbidden Notauthorized
404 NotFound Resourceorendpointnotfound
405 MethodNotAllowed
Wrongoperationforresourceorendpoint(e.g.GET-request,butonlyPUT/POSTallowed)
500 InternalServerError Errorwhileexecution(e.g.syntaxerrorinrequest)
MoMoworkshop
67REST
ReadingthecatalogInthismodulewewilllearnhowtoreadouttheGeoServerconfigurationviatheRESTAPI.
Asalreadymentionedinthepreviouschapter,akeyconditionofRESTistheaddressability.Therebyeachcatalogconfiguration(=resourceorendpoint)inGeoServerhasanuniqueURL.
AtfirstwewillinvestigatetheRESTAPIviathebrowser.AtthesametimeweareusingtheHTTPoperationGETtoretrieveinformationfromtheserver.
OpenupabrowserwindowandnavigatetothefollowingURL(Note:YouwillbepromptedforyourGeoServeruserandpassword):
http://localhost/geoserver/rest
YouwillseeasimpleHTMLlistwhichcontainsthetopendpointsprovidedbytheRESTAPI.Thelistviewisfullycontrollableandclearlyassigned.Aselectioninthebrowser(forexampletheentryworkspaces)navigatesthebrowsertouniqueURLhttp://localhost/geoserver/rest/workspaces.Thestructureofthelist(whenselectingaworkspace)followsthelogicalstructureoftheGeoServercatalogwealreadymetintheprevioussections:
workspace
|
+--datastore
|
+--featuretype
TheaboveactionsinthebrowserwillcallanendpointinHTMLformatbydefault.TheGeoServeralsosupportstheformatsJSON(JavaScriptObjectNotation)andXML(ExtensibleMarkupLanguage),whichareparticularlyrelevantinthemanipulationofaresourcewewilluselateron.
Switchtoanewtabinyourbrowser.Thenopenandcomparethefollowingoutputs:
http://localhost/geoserver/rest/workspaces
http://localhost/geoserver/rest/workspaces.json
http://localhost/geoserver/rest/workspaces.xml
MoMoworkshop
68REST
InthenextstepwewanttogetafulldescriptionofthefeaturetypecountrieswecreatedinthepreviousmoduleinformatJSON.Copythefollowingrequestinyourbrowserandexploretheoutput:
http://localhost/geoserver/rest/workspaces/momo/datastores/db_momo_ws/featuretypes/countries.json
MoMoworkshop
69REST
CreatinganewresourceInthisexercisewearegoingtousetheRESTinterfaceincombinationwiththeHTTPoperationsPOSTandPUTtocreatearesourceontheserver.IncontrasttothepreviousmoduleherewearegoingtousethecommandlinetoolcURLtoaccessthecatalog.cURLisacommandlinetooltotransferdatafromortoaserverusingoneofthesupportedprotocols(e.g.HTTPorFTP).Formoreaboutthetoolhavealookathere.
Creatinganewworkspace
InthismodulewearegoingtorepeatthestepswehavedoneinchapterPublishingavectorlayer.Butaswedon'twanttooverrideourprogress(oranyindividualchanges)madetotheworkspacemomo,wewillcreatenewworkspacemomo-restfortheensuingexercises.
Openuptheterminal(ifnotalreadyopenend)andtypeinthefollowingcommandtocreateanewworkspacenamedmomo-rest:
$curl\
-v\
-uadmin:momo-ws\
-XPOST\
-H"Content-type:text/xml"\
-d"<workspace>
<name>momo-rest</name>
</workspace>"\
http://localhost/geoserver/rest/workspaces
Thecallabovediffersintwoessentialpointsfromthepreviousreadoperations:UnliketheHTTPoperationGETweusetheoperationPOSTandinadditionwetransferaXMLcontentcontainingasimpleworkspacedefinitiontotheuniqueendpointworkspaces.HitEntertoexecutetheabovecommandandyouwillseeanoutputlikethis:
*HostnamewasNOTfoundinDNScache
*Trying127.0.0.1...
*Connectedtolocalhost(127.0.0.1)port80(#0)
*ServerauthusingBasicwithuser'admin'
>POST/geoserver/rest/workspacesHTTP/1.1
>Authorization:BasicYWRtaW46Z2Vvc2VydmVy
>User-Agent:curl/7.35.0
>Host:localhost:80
>Accept:*/*
>Content-type:text/xml
>Content-Length:57
>
*uploadcompletelysentoff:57outof57bytes
<HTTP/1.1201Created
<Date:Wed,03Feb201610:30:39GMT
<Location:http://localhost:80/geoserver/rest/workspaces/momo-rest
*ServerNoelios-Restlet-Engine/1.0..8isnotblacklisted
<Server:Noelios-Restlet-Engine/1.0..8
<Transfer-Encoding:chunked
<
*Connection#0tohostlocalhostleftintact
Here,twoinformationsarecrucialtous:HTTP/1.1201Created:Therequesthasbeensuccessfullyprocessedandtheresourcehasbeencreated.http://localhost/geoserver/rest/workspaces/momo-rest:TheRESTendpointURLofournewworkspace.
WecanverifythattheworkspacewasactuallycreatedeitherbyusingtheGeoServerUIortheRESTinterface:OpentheGeoServeruserinterface,navigatetothepageData❭Workspacesandensureanewworkspacenamedmomo-restisavailableinthelist.OpentheterminalandrunthefollowingcommandtogetaXMLrepresentationofallavailableworkspaces:
MoMoworkshop
70REST
$curl\
-v\
-uadmin:momo-ws\
-XGET\
-H"Accept:text/xml"\
http://localhost/geoserver/rest/workspaces
CreatinganewstoreNowthatwehavecreatedanewworkspace,we'lladdanewdatastoretoit.Herewearereusingthedatabasewealreadycreated(andaddedtothegeoserver).
OpentheterminalandinsertthefollowingcommandtocreateanewPostGISdatastorenameddb_momo_ws_rest:
$curl\
-v\
-uadmin:momo-ws\
-XPOST\
-H"Content-type:text/xml"\
-d"<dataStore>
<name>db_momo_ws_rest</name>
<connectionParameters>
<host>localhost</host>
<port>5432</port>
<database>db_momo_ws</database>
<schema>geodata</schema>
<user>momo</user>
<passwd>momo</passwd>
<dbtype>postgis</dbtype>
</connectionParameters>
</dataStore>"\
http://localhost/geoserver/rest/workspaces/momo-rest/datastores
HitEntertoexecutethecommand.Thiswillresultinthefollowingoutput,assuringthatthestorewassuccessfullycreated:
*HostnamewasNOTfoundinDNScache
*Trying127.0.0.1...
*Connectedtolocalhost(127.0.0.1)port80(#0)
*ServerauthusingBasicwithuser'admin'
>POST/geoserver/rest/workspaces/momo-rest/datastoresHTTP/1.1
>Authorization:BasicYWRtaW46Z2Vvc2VydmVy
>User-Agent:curl/7.35.0
>Host:localhost:80
>Accept:*/*
>Content-type:text/xml
>Content-Length:347
>
*uploadcompletelysentoff:347outof347bytes
<HTTP/1.1201Created
<Date:Wed,03Feb201610:59:04GMT
<Location:http://localhost:80/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest
*ServerNoelios-Restlet-Engine/1.0..8isnotblacklisted
<Server:Noelios-Restlet-Engine/1.0..8
<Transfer-Encoding:chunked
<
*Connection#0tohostlocalhostleftintact
OnceagainwecanverifythesuccessfulcreationintheGeoServerUI(Data❭Stores).
PublishingalayerInthenextstepwe'regoingtopublishthetabletbl_countriesasanewlayer.
Opentheterminalandinsertthefollowingcommandtocreateanewfeaturetype(andlayer)namedcountries_rest:
MoMoworkshop
71REST
$curl\
-v\
-uadmin:momo-ws\
-XPOST\
-H"Content-type:text/xml"\
-d"<featureType>
<name>countries_rest</name>
<nativeName>tbl_countries</nativeName>
<title>Countries</title>
<nativeCRS>EPSG:4326</nativeCRS>
<enabled>true</enabled>
</featureType>"\
http://localhost/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest/featuretypes
Andagain,verifythattheresponsecontainsthelines
HTTP/1.1201Created
and
Location:http://localhost/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest/featuretypes/countries_rest
Additionallywecanalsohavealookatthepreviewpagetoensurethelayeriscorrectlypublished.
CreateanduploadstyleWecanusetheRESTAPIbothtocreateanewstyleobjectinGeoServerandtoinsertanexistingSLD-fileintoit.AtfirstweneedtocreateanewSLDfileonourlocalmachinewe'llneedinthenextstep.Forthispurposewecanusethestylealreadyusedinthepreviousmodule.
Opentheterminalandnavigatetoyourhomedirectorywith:
$cd~
CreateandopenanewSLDfilecountries-style.sldinthisdirectorywith:
$nanocountries-style.sld
CopythelinkedSLDcontent(seehere)thenewlycreatedfileandsaveitwithCtrl+O.YoucannowclosethenanoeditorwithCtrl+X.
WewillnowcreatethestyleanduploadtheSLDfilewejustcreated.
Copythefollowingblockintoyourterminalandexecuteittocreateanewstyleobject:
$curl\
-v\
-uadmin:momo-ws\
-XPOST\
-H"Content-type:text/xml"\
-d"<style>
<name>countries_rest</name>
<filename>countries-style.sld</filename>
</style>"\
http://localhost/geoserver/rest/workspaces/momo-rest/styles
Andagain,verifythattheresponsecontainsthelines
HTTP/1.1201Created
MoMoworkshop
72REST
and
Location:http://localhost:80/geoserver/rest/workspaces/momo-rest/styles/countries_rest
Afterwardswecanuploadthestylecreatedabovewith(Note:Ensurethepathtofilecountries-style.sldiscorrect!):
$curl\
-v\
-uadmin:momo-ws\
-XPUT\
-H"Content-type:application/vnd.ogc.sld+xml"\
http://localhost/geoserverrest/workspaces/momo-rest/styles/countries_rest
Thiscommandshouldcompletewith:
HTTP/1.1200OK
Assignalayerstyle
Afterwehavecreatedthestyle,wecanassignthisstyletothelayercountries_rest.
Copyandexecutethefollowingcommandintheterminalwindow:
$curl\
-v\
-uadmin:momo-ws\
-XPUT\
-H"Content-type:text/xml"\
-d"<layer>
<defaultStyle>
<name>countries_rest</name>
<workspace>momo-rest</workspace>
</defaultStyle>
</layer>"\
http://localhost/geoserver/rest/layers/momo-rest:countries_rest
AfterfinishedwithHTTP/1.1200OKwecanopenthepreviewpagetoreviewthechangesmadetothelayerstyle.
MoMoworkshop
73REST
LayercreatedandstyledviatheRESTAPI.
MoMoworkshop
74REST
UpdatingalayerBasicallywecanchangeeveryelementofcatalogbytheuseoftheRESTAPI.Inthefollowingexamplewewillchangethecountries_restlayer'sdefaultoutputprojectiontoEPSG:54009(Mollweideprojection).
Executethefollowingterminalcommandtoupdatethelayercountries_rest.(Note:Everyupdateneedstheproperty<enabled>true</enabled>
otherwisethecatalogentry,inthiscasethelayer,willbedisabledandnotbevisibletoanyuser!)
$curl\
-v\
-uadmin:momo-ws\
-XPUT\
-H"Content-type:text/xml"\
-d"<featureType>
<enabled>true</enabled>
<srs>EPSG:54009</srs>
<projectionPolicy>REPROJECT_TO_DECLARED</projectionPolicy>
</featureType>"\
http://localhost/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest/featuretypes/countries_rest
AfterthisstephasbeenconfirmedassuccessfullyfinishedwithHTTP/1.1200OK,wecanthenautomaticallycalculatethenewnativeandlat/lonboundingboxofthelayerbyappendingtheparameterrecalculate=nativebbox,latlonbboxtotheRESTURL:
$curl\
-v\
-uadmin:momo-ws\
-XPUT\
-H"Content-type:text/xml"\
-d"<featureType>
<enabled>true</enabled>
</featureType>"\
http://localhost/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest/featuretypes/countries_rest?recalculate=nativebbox,latlonbbox
ReviewthatthelayerhasbeenupdatedcorrectlybyopeningthelayerconfigurationintheGeoServerUI(Data❭Layers)andhavealookatthesubsectionCoordinateReferenceSystemandBoundingBoxes,whichshouldcontainyourrequestedchanges.
Finallyhavealookatthelayerpreviewpageandnote,thatthedefaultSRSissettoEPSG:54009.
MoMoworkshop
75REST
LayerinEPSG:54009.
MoMoworkshop
76REST
RemovearesourceYoushouldhavenoticedthatthelayercountriesisavailabletwicenow.Thefirstonewascreated"manually"bytheuseoftheGeoServerUI,thesecondoneviatheRESTAPI.Becausewedon'twanttounnecessarilypublishalayertwice(andofcoursetolearnhowtodeletearesourcebymeansofREST),wewilldeletethecountries_restlayerbyusingtheHTTPoperationDELETE.
Executethefollowingcommandtodeletethefeaturetypecountries_restandthecorrespondinglayerbyappendingrecurse=truetotherequest:
$curl\
-v\
-uadmin:momo-ws\
-XDELETE\
http://localhost/geoserver/rest/workspaces/momo-rest/datastores/db_momo_ws_rest/featuretypes/countries_rest?recurse=
AftertheabovecommandhassuccessfullyexecutedwithHTTP/1.1200OKtrytofindthelayerintheGeoServerLayerconfigurationpageandifanythingworkedfine,youshouldn'tbeabletofindit
MoMoworkshop
77REST
GeoWebCacheThemostcommonrequesttoGeoServeristoprovideanOGC-compliantWMSinterfaceandthusgeneratingmapsinrasterformat.Forthisreason,cachingoftheseWMSrequestsmayhaveadecisiveinfluencetotheperformanceoftheserverandshouldbecarriedoutoneach(productive)systemwhereverpossible.Forcachingmaptilesthereisavarietyofgoodopensourcecachingenginesavailable,butherewe'llusetheGeoServerintegratedGeoWebCache(GWC),whichactsasaproxybetweentheclientandGeoServer.
GeoWebCacheasproxy,source:http://geowebcache.org/docs/current/_images/how_it_works.png
Inthefollowingsectionswe'llinitiateallrequiredstepstogenerateacacheforthelayermomo:countries:
PrerequisitesConfigureanewgridsetConfigureacachedlayerGeneratemaptiles.CheckcachedirectoryCheckcache-headers
MoMoworkshop
78GeoWebCache
PrerequisitesBeforewecanstartcachingasetoflayersweneedtoconfigureadirectorywhereGWCshouldsaveallcachedtiles.Toaccomplishthis,pleasefollowthesesteps:
Opentheterminalandcreatethecachedirectory(Note:You'llbepromptedfortheadminpassword):
$sudomkdir/opt/tomcat/webapps/geoserver/data/gwc
EnsureGeoServerhasreadandwriteaccesstothisdirectorybychangigtheownershiptouserandgrouptomcat:
$sudochowntomcat:tomcat/opt/tomcat/webapps/geoserver/data/gwc
Opentheterminalandcopythefollowingcommandtoopenthefileweb.xmlinthetexteditorgedit:
$sudogedit/opt/tomcat/webapps/geoserver/WEB-INF/web.xml
ThefollowingblockwilladviseGWCtostoreallcachedtilesintothedirectory/opt/tomcat/webapps/geoserver/data/gwc.Insertitatline~64inthealreadyopenedfile.
<!--TheGWCdatadirectory-->
<context-param>
<param-name>GEOWEBCACHE_CACHE_DIR</param-name>
<param-value>/opt/tomcat/webapps/geoserver/data/gwc</param-value>
</context-param>
Savethechangesandclosethetexteditor.Toapplythechanges,weneedtorestartGeoServer.Gotoyourterminalandrunthefollowingcommand:
$sudorestarttomcat
Openthenewlycreateddirectoryintheterminaltocheckthereisafilenamedgeowebache.xmlonly:
$cd/opt/tomcat/webapps/geoserver/data/gwc
MoMoworkshop
79GeoWebCache
Tilesandgridsets
Tiles
GeoWebCachecachesimagesretrievedfromaWMS.Thesmallestunitofimagecachedisknownasatile.Alltilesareassumedtobethesamedimensionsandaretypicallysquare(i.e.256pixelsby256pixels).Thetilesarestoredinarectangulargrid,indexedby(x,y)coordinates.Azcoordinate(zero-indexed)isusedtodenotethezoomlevel,resultingineachtilebeingindexedasatriplet(x,y,z).
Gridsets
GridsetsandgridsubsetsrefertothespatialreferencesystemofthelayersservedbyGeoWebCache.WhenGeoWebCachemakesarequesttoaWMS,itusesthegridsetandgridsubsetinformationtoconvertitsinternaltileindextoaspatialrequestthattheWMSwillunderstand.
Compositionofagridset,source:http://3.bp.blogspot.com/_0_xIiXP5xuY/S5pEpCjenaI/AAAAAAAAAKY/PDKTGZ6vzGI/s1600-h/Image_Pyramid.gif
Agridsetisaglobaldefinition(i.e.notlayer-specific)specifying:
Aspatialreferencesystem.Aboundingboxdescribingtheextent,typicallythemaximumextentfortheabovereferencesystem.Oneofeitheralistofscaledenominators,resolutions,orzoomlevels.Thetiledimensionsinpixels(constantforallzoomlevels).
Agridsubsetisalayer-specificdefinitionspecifying:
Thegridsetforthelayer.(Optional)Theboundingboxforthatlayer(whichmustbeasubsetoftheextentofthegridSet).(Optional)Alistofzoomlevels(whichmustbeasubsetofwhatisdefinedinthegridSet).
Forfurtherinstructionshavealookatthesourceoftheaboveexplanations,here.
Configureanewgridset
MoMoworkshop
80GeoWebCache
So,ourfirststepwillbetocreateanewgridset:
GotoTileCaching❭Gridsets
ClickCreateanewgridsettocreateanewgridsetandusethefollowingoptionsforthecreation:Name:momo-4326CoordinateReferenceSystem:Usethefind-buttontoselectEPSG:4326Gridsetbounds:ClickComputefrommaximumextentofCRSTilewidthinpixels:512Tileheightinpixels:512Definegridsbasedon:SelectScaledenominators
ClickAddzoomleveltocreateanewzoomlevel.Enterthescale300.000.000andthename0.OnceagainclickAddzoomlevel.Youwillseethatthescalevalueisautomaticallycutintohalves(150.000.000).Justenterthename1andrepeatthisstepuntilyoureachedatotalcountof8zoomlevels.Thelastscalevalueshouldbe2.343.750.
MoMoworkshop
81GeoWebCache
ClickSave.
MoMoworkshop
82GeoWebCache
CachedlayerInthenextstepwe'llconfigurethelayercountriestoapplytoallneededcache-properties.
GotoData❭Layersandselectthecountrieslayer.OpenthepanelTileCaching.
HerewecanconfigureallGWC-dependendpropertiesinaper-layer-basis.Themostimportantconfigurationparametersare:
Createacachedlayerforthislayer:Shouldthislayerbecached?Metatilingfactors:Metatilesarelargermaptilesfromwhichthecachedtileswillbecut.Thefactorinthiscaseindicatesthesizeofthemetatiles.Afactorof3x3meansthatthescreenwidthofthetargettileisincreasedbyafactorofthreethatresults(byarequestedtilesizeof256px)inanmetatiletilesizeof768px.Primarilymetatilesareneededtopreventduplicatemaplabels(forexampleforroadlayers)intwoadjacenttiles.Guttersizeinpixels:Additionalframe(inpx)toberequestedbyatile.Onlyusefulwhentherearelayoutproblemsinthepreparationoflabelsand/orfeaturesonthetileedgeinconjunctionwiththeuseofmetatiles.TileImageFormats:Thestandardimageformatforthetiles.STYLES:Isthereanyotherstyleexistingforthegivenlayerthatshouldbecached,itmustbeselectedhere.Inmostcasesitwillbesufficienttosetthedefaultlayerstyles(LAYERDEFAULT)only.Gridset:Thegridsetdefinesthegridthestoredtilesareindexedandthusdefinesthespatialindexoftheindividualtiles.Thesingletileintherectangulargridisidentifiedbymeansofax,y,zcoordinatetriple.Thexandycoordinatesdeterminethehorizontalandverticalposition,thezcoordinatethezoomlevel.Seepreviouschapteraswell.
Configureacachedlayer
Withthisinmind,wecanconfigurethelayercountriesasfollows:
Selectthefollowingvalues:Createacachedlayerforthislayer:checkedEnabletilecachingforthislayer:checkedMetatilingfactors:4x4Guttersizeinpixels:0TileImageFormats:Checkimage/pngonlyExpireservercacheafternseconds:0Expireclientcacheafternseconds:0Styles:SelectLAYERDEFAULTGridset:Selectmomo-4326intheAddgridsubsetcomboboxandclickthegreenplusicon.Removeanyotherpreconfiguredgridsetbyclickingtheredminusicon.
MoMoworkshop
83GeoWebCache
ClickSave.
MoMoworkshop
84GeoWebCache
GeneratemaptilesGenerallyspeaking,GWCappliestwomethodsforcreatingcachedmaptiles:
1. On-the-flyprocessing:IfaGWClayerisprimarilyrequestedbyaclient,theappropriatemaptilesarerenderedandsubsequentlystoredintheGWCdatadirectory.Thenextclient,requestingthesamelayeronthesamelocationreceivesa(muchfaster)responsefromthecache.
2. Preprocessingofmaptiles:Thetilesofalayerwillbepreprocessedandstoredinadefinedboundingboxandindefinedzoomlevelsalongthegivengridset.Incontrasttotheon-the-flycalculation,thismethodrequires,dependingontheavailablesystemresources,significantlymorecomputingtime,butallclientswillreceiveadirectresponsefromthecache.
Withthefollowingstepswe'llpreprocessthetilesandstartthesocalledSeedingjob.
GotoTileCaching❭TileLayers.Findthelayermomo:countriesandselectSeed/Truncate.
IntheupcomingmaskwecanconfigureaGWC-taskforseedingthelayerscountries.Herewecanusethefollowingconfiguration:
Numberoftaskstouse:04Typeofoperation:Reseed-regeneratealltiles(TheoptionSeed-generatemissingtileswouldbehavethesamehereaswehaven'tanycachepresent)GridSet:momo-4326Format:image/pngZoomstart:00Zoomstop:07
MoMoworkshop
85GeoWebCache
ClickSubmit.InthesamewindowthesectionListofcurrentlyexecutingtaskswillbefilledwiththerecenttasksandinvolvessomebasicinformationsaboutit.
Dependingonyoursystemresourcestheseedingtasksshouldnotcovermorethanafewminutes.ClickRefreshlisttoseeifthetasksarefinishedornot.
MoMoworkshop
86GeoWebCache
CheckingthecachedirectoryOncealltasksarecompleted,weshouldverifythecontentofthecachedirectorywecreatedabove.Giventhateverythingworkedfineinthepreviousstep,thecachedirectoryshouldcontainalotoftilesbuildingupthetilepyramidforthelayercountries.
OpentheterminalandnavigatetotheGWCcachedirectoryforthecountrieslayer:
$cd/opt/tomcat/webapps/geoserver/data/gwc/momo_countries
Listthedirectorycontentswith:
$ls-l
Explorethatthecachedirectoryisbuiltupbyfollowingpattern:
momo_countries/(layername)
|
+--momo-4326_07/(gridsetname+zoomlevel)
|
+--07_03/(internalnotationbasedongridset+zoomlevel)
|
+--0119_0057.png(tileindex)
MoMoworkshop
87GeoWebCache
Checkingthecache-headersFinallywearegoingtoinspecttheresponsesendfromtheGeoServer/GeoWebCachetotheclientinmoredetail.AssoonasalayerisbeingcachedbyGeoWebCache,theresponseheadersofsingletileareextendedbythefollowingHTTP-headers:
Header Description
geowebcache-cache-result Ifthetileisdeliveredbythecache,thevalueisHITotherwiseit'sMISS.
geowebcache-crs Thecoordinatesystemofthetile.
geowebcache-gridset Thenameoftheunderlyinggridset.
geowebcache-tile-bounds Theboundingboxofthetile.
geowebcache-tile-index Theindexofthetile(x,y,z)inthegridset.
Tocheckiftheseheadersareset,weneedtoopentheGeoServeruserinterfaceagain:
GotoTileCaching❭TileLayers.Findthelayermomo:countriesandselectmomo-4326/pngunderPreview.
Inthepreviewwindow/tabpressF12toopenthebrowsersDeveloperToolbar,activatetheNetworktab,selecttheImgsubsectionandreloadthepagetorecordthenetworkactivity.
MoMoworkshop
88GeoWebCache
ClearthelistcontentwiththeClearbutton( )Zoomintoalocationofyourchoice,findaWMSGetMaprequestinthedevelopertoolbarandselectit.ExploretherighthandsidedinformationpanelandfindtheResponseHeaderssection.Inthisyoushouldfindtheheaderslookingsimilarthefollowingones:
geowebcache-cache-result:HIT
geowebcache-crs:EPSG:4326
geowebcache-gridset:momo-4326
geowebcache-tile-bounds:158.05400771985825,54.880289022796376,206.3474373941237,103.17371869706184
geowebcache-tile-index:[7,3,3]
MoMoworkshop
89GeoWebCache
LearnJavascriptThisbookwillteachyouthebasicsofprogrammingandJavascript.Whetheryouareanexperiencedprogrammerornot,thisbookisintendedforeveryonewhowishestolearntheJavaScriptprogramminglanguage.
Screen
JavaScript(JSforshort)istheprogramminglanguagethatenableswebpagestorespondtouserinteractionbeyondthebasiclevel.Itwascreatedin1995,andistodayoneofthemostfamousandusedprogramminglanguages.
MoMoworkshop
90JavaScript
BasicsaboutProgrammingInthisfirstchapter,we'lllearnthebasicsofprogrammingandtheJavascriptlanguage.
Programmingmeanswritingcode.Abookismadeupofchapters,paragraphs,sentences,phrases,wordsandfinallypunctuationandletters,likewiseaprogramcanbebrokendownintosmallerandsmallercomponents.Fornow,themostimportantisastatement.Astatementisanalogoustoasentenceinabook.Onitsown,ithasstructureandpurpose,butwithoutthecontextoftheotherstatementsaroundit,itisn'tthatmeaningful.
Astatementismorecasually(andcommonly)knownasalineofcode.That'sbecausestatementstendtobewrittenonindividuallines.Assuch,programsarereadfromtoptobottom,lefttoright.Youmightbewonderingwhatcode(alsocalledsourcecode)is.Thathappenstobeabroadtermwhichcanrefertothewholeoftheprogramorthesmallestpart.Therefore,alineofcodeissimplyalineofyourprogram.
Hereisasimpleexample:
varhello="Hello";
varworld="World";
//Messageequals"HelloWorld"
varmessage=hello+""+world;
Thiscodecanbeexecutedbyanotherprogramcalledaninterpreterthatwillreadthecode,andexecuteallthestatementsintherightorder.
MoMoworkshop
91Basics
CommentsCommentsarestatementsthatwillnotbeexecutedbytheinterpreter,commentsareusedtomarkannotationsforotherprogrammersorsmalldescriptionsofwhatyourcodedoes,thusmakingiteasierforotherstounderstandwhatyourcodedoes.
InJavascript,commentscanbewrittenin2differentways:
Linestartingwith//:
//Thisisacomment,itwillbeignoredbytheinterpreter
vara="thisisavariabledefinedinastatement";
Sectionofcodestartingwith/*andendingwith*/,thismethodisusedformulti-linecomments:
/*
Thisisamulti-linecomment,
itwillbeignoredbytheinterpreter
*/
vara="thisisavariabledefinedinastatement";
Exercise
Marktheeditor'scontentsasacomment
Markmeasacomment
orI'llthrowanerror
MoMoworkshop
92Comments
VariablesThefirststeptowardsreallyunderstandingprogrammingislookingbackatalgebra.Ifyourememberitfromschool,algebrastartswithwritingtermssuchasthefollowing.
3+5=8
Youstartperformingcalculationswhenyouintroduceanunknown,forexamplexbelow:
3+x=8
Shiftingthosearoundyoucandeterminex:
x=8-3
->x=5
Whenyouintroducemorethanoneyoumakeyourtermsmoreflexible-youareusingvariables:
x+y=8
Youcanchangethevaluesofxandyandtheformulacanstillbetrue:
x=4
y=4
or
x=3
y=5
Thesameistrueforprogramminglanguages.Inprogramming,variablesarecontainersforvaluesthatchange.Variablescanholdallkindofvaluesandalsotheresultsofcomputations.Variableshaveanameandavalueseparatedbyanequalssign(=).Variablenamescanbeanyletterorword,butbearinmindthattherearerestrictionsfromlanguagetolanguageofwhatyoucanuse,assomewordsarereservedforotherfunctionality.
Let'scheckouthowitworksinJavascript,Thefollowingcodedefinestwovariables,computestheresultofaddingthetwoanddefinesthisresultasavalueofathirdvariable.
varx=5;
vary=6;
varresult=x+y;
MoMoworkshop
93Variables
VariabletypesComputersaresophisticatedandcanmakeuseofmorecomplexvariablesthanjustnumbers.Thisiswherevariabletypescomein.Variablescomeinseveraltypesanddifferentlanguagessupportdifferenttypes.
Themostcommontypesare:
NumbersFloat:anumber,like1.21323,4,-33.5,100004or0.123Integer:anumberlike1,12,-33,140butnot1.233
String:alineoftextlike"boat","elephant"or"damn,youaretall!"Boolean:eithertrueorfalse,butnothingelseArrays:acollectionofvalueslike:1,2,3,4,'Iamborednow'Objects:arepresentationofamorecomplexobjectnull:avariablethatcontainsnullcontainsnovalidNumber,String,Boolean,Array,orObjectundefined:theundefinedvalueisobtainedwhenyouuseanobjectpropertythatdoesnotexist,oravariablethathasbeendeclared,buthasnovalueassignedtoit.
JavaScriptisa“looselytyped”language,whichmeansthatyoudon'thavetoexplicitlydeclarewhattypeofdatathevariablesare.Youjustneedtousethevarkeywordtoindicatethatyouaredeclaringavariable,andtheinterpreterwillworkoutwhatdatatypeyouareusingfromthecontext,anduseofquotes.
Exercise
Createavariablenamed`a`usingthekeyword`var`.
MoMoworkshop
94Types
EqualityProgrammersfrequentlyneedtodeterminetheequalityofvariablesinrelationtoothervariables.Thisisdoneusinganequalityoperator.
Themostbasicequalityoperatoristhe==operator.Thisoperatordoeseverythingitcantodetermineiftwovariablesareequal,eveniftheyarenotofthesametype.
Forexample,assume:
varfoo=42;
varbar=42;
varbaz="42";
varqux="life";
foo==barwillevaluatetotrueandbaz==quxwillevaluatetofalse,asonewouldexpect.However,foo==bazwillalsoevaluatetotruedespitefooandbazbeingdifferenttypes.Behindthescenesthe==equalityoperatorattemptstoforceitsoperandstothesametypebeforedeterminingtheirequality.Thisisincontrasttothe===equalityoperator.
The===equalityoperatordeterminesthattwovariablesareequaliftheyareofthesametypeandhavethesamevalue.Withthesameassumptionsasbefore,thismeansthatfoo===barwillstillevaluatetotrue,butfoo===bazwillnowevaluatetofalse.baz===quxwillstillevaluatetofalse.
MoMoworkshop
95Equality
NumbersJavaScripthasonlyonetypeofnumbers–64-bitfloatpoint.It'sthesameasJava'sdouble.Unlikemostotherprogramminglanguages,thereisnoseparateintegertype,so1and1.0arethesamevalue.
Inthischapter,we'lllearnhowtocreatenumbersandperformoperationsonthem(likeadditionsandsubtractions).
MoMoworkshop
96Numbers
CreationCreatinganumberiseasy,itcanbedonejustlikeforanyothervariabletypeusingthevarkeyword.
Numberscanbecreatedfromaconstantvalue:
//Thisisafloat:
vara=1.2;
//Thisisaninteger:
varb=10;
Orfromthevalueofanothervariable:
vara=2;
varb=a;
Exercise
Createavariable`x`whichequals`10`andcreateavariable`y`whichequals`a`.
vara=11;
MoMoworkshop
97Creation
OperatorsYoucanapplymathematicoperationstonumbersusingsomebasicoperatorslike:
Addition:c=a+bSubtraction:c=a-bMultiplication:c=a*bDivision:c=a/b
Youcanuseparenthesesjustlikeinmathtoseparateandgroupexpressions:c=(a/b)+d
Exercise
Createavariable`x`equaltothesumof`a`and`b`dividedby`c`andfinallymultipliedby`d`.
vara=2034547;
varb=1.567;
varc=6758.768;
vard=45084;
varx=
MoMoworkshop
98BasicOperators
AdvancedOperatorsSomeadvancedoperatorscanbeused,suchas:
Modulus(divisionremainder):x=y%2Increment:Givena=5
c=a++,Results:c=5anda=6c=++a,Results:c=6anda=6
Decrement:Givena=5c=a--,Results:c=5anda=4c=--a,Results:c=4anda=4
Exercise
Defineavariable`c`asthemodulusofthedecrementedvalueof`x`by3.
varx=10;
varc=
MoMoworkshop
99AdvancedOperators
StringsJavaScriptstringssharemanysimilaritieswithstringimplementationsfromotherhigh-levellanguages.Theyrepresenttextbasedmessagesanddata.
Inthiscoursewewillcoverthebasics.Howtocreatenewstringsandperformcommonoperationsonthem.
Hereisanexampleofastring:
"HelloWorld"
MoMoworkshop
100Strings
CreationYoucandefinestringsinJavaScriptbyenclosingthetextinsinglequotesordoublequotes:
//Singlequotescanbeused
varstr='Ourlovelystring';
//Doublequotesaswell
varotherStr="Anothernicestring";
InJavascript,StringscancontainUTF-8characters:
"��españolEnglish������ ةيبرعلا português�����русский������������";
Note:Stringscannotbesubtracted,multipliedordivided.
Exercise
Createavariablenamed`str`settothevalue`"abc"`.
MoMoworkshop
101Creation
ConcatenationConcatenationinvolvesaddingtwoormorestringstogether,creatingalargerstringcontainingthecombineddataofthoseoriginalstrings.ThisisdoneinJavaScriptusingthe+operator.
varbigStr='Hi'+'JSstringsarenice'+'and'+'easytoadd';
Exercise
Addupthedifferentnamessothatthe`fullName`variablecontainsJohn'scompletename.
varfirstName="John";
varlastName="Smith";
varfullName=
MoMoworkshop
102Concatenation
LengthIt'seasyinJavascripttoknowhowmanycharactersareinstringusingtheproperty.length.
//Justusetheproperty.length
varsize='Ourlovelystring'.length;
Note:Stringscannotbesubstracted,multipliedordivided.
Exercise
Storeinthevariablenamed`size`thelengthof`str`.
varstr="HelloWorld";
varsize=
MoMoworkshop
103Length
ConditionalLogicAconditionisatestforsomething.Conditionsareveryimportantforprogramming,inseveralways:
Firstofallconditionscanbeusedtoensurethatyourprogramworks,regardlessofwhatdatayouthrowatitforprocessing.Ifyoublindlytrustdata,you’llgetintotroubleandyourprogramswillfail.Ifyoutestthatthethingyouwanttodoispossibleandhasalltherequiredinformationintherightformat,thatwon’thappen,andyourprogramwillbealotmorestable.Takingsuchprecautionsisalsoknownasprogrammingdefensively.
Theotherthingconditionscandoforyouisallowforbranching.Youmighthaveencounteredbranchingdiagramsbefore,forexamplewhenfillingoutaform.Basically,thisreferstoexecutingdifferent“branches”(parts)ofcode,dependingoniftheconditionismetornot.
Inthischapter,we'lllearnthebaseofconditionallogicinJavascript.
MoMoworkshop
104ConditionalLogic
ConditionIfTheeasiestconditionisanifstatementanditssyntaxisif(condition){dothis…}.Theconditionhastobetrueforthecodeinsidethecurlybracestobeexecuted.Youcanforexampletestastringandsetthevalueofanotherstringdependentonitsvalue:
varcountry='France';
varweather;
varfood;
varcurrency;
if(country==='England'){
weather='horrible';
food='filling';
currency='poundsterling';
}
if(country==='France'){
weather='nice';
food='stunning,buthardlyevervegetarian';
currency='funny,smallandcolourful';
}
if(country==='Germany'){
weather='average';
food='wurstthingever';
currency='funny,smallandcolourful';
}
varmessage='thisis'+country+',theweatheris'+
weather+',thefoodis'+food+'andthe'+
'currencyis'+currency;
Note:Conditionscanalsobenested.
Exercise
Fillupthevalueof`name`tovalidatethecondition.
varname=
if(name==="John"){
}
MoMoworkshop
105If
ElseThereisalsoanelseclausethatwillbeappliedwhenthefirstconditionisn’ttrue.Thisisverypowerfulifyouwanttoreacttoanyvalue,butsingleoutoneinparticularforspecialtreatment:
varumbrellaMandatory;
if(country==='England'){
umbrellaMandatory=true;
}else{
umbrellaMandatory=false;
}
Theelseclausecanbejoinedwithanotherif.Letsremaketheexamplefromthepreviousarticle:
if(country==='England'){
...
}elseif(country==='France'){
...
}elseif(country==='Germany'){
...
}
Exercise
Fillupthevalueof`name`tovalidatethe`else`condition.
varname=
if(name==="John"){
}elseif(name==="Aaron"){
//Validthiscondition
}
MoMoworkshop
106Else
ComparatorsLetsnowfocusontheconditionalpart:
if(country==="France"){
...
}
Theconditionalpartisthevariablecountryfollowedbythethreeequalsigns(===).Threeequalsignstestsifthevariablecountryhasboththecorrectvalue(France)andalsothecorrecttype(String).Youcantestconditionswithdoubleequalsigns,too,howeveraconditionalsuchasif(x==5)wouldthenreturntrueforbothvarx=5;andvarx="5";.Dependingonwhatyourprogramisdoing,thiscouldmakequiteadifference.Itishighlyrecommendedasabestpracticethatyoualwayscompareequalitywiththreeequalsigns(===and!==)insteadoftwo(==and!=).
Otherconditionaltest:
x>a:isxbiggerthana?x<a:isxlessthana?x<=a:isxlessthanorequaltoa?x>=a:isxgreaterthanorequaltoa?x!=a:isxnota?x:doesxexist?
Exercise
Addaconditiontochangethevalueof`a`tothenumber10if`x`isbiggerthan5.
varx=6;
vara=0;
LogicalComparisonInordertoavoidtheif-elsehassle,simplelogicalcomparisonscanbeutilised.
vartopper=(marks>85)?"YES":"NO";
Intheaboveexample,?isalogicaloperator.Thecodesaysthatifthevalueofmarksisgreaterthan85i.e.marks>85,thentopper=YES;otherwisetopper=NO.Basically,ifthecomparisonconditionprovestrue,thefirstargumentisaccessedandifthecomparisonconditionisfalse,thesecondargumentisaccessed.
MoMoworkshop
107Comparators
ConcatenateconditionsFurthermoreyoucanconcatenatedifferentconditionswith"or”or“and”statements,totestwhethereitherstatementistrue,orbotharetrue,respectively.
InJavaScript“or”iswrittenas||and“and”iswrittenas&&.
Sayyouwanttotestifthevalueofxisbetween10and20—youcoulddothatwithaconditionstating:
if(x>10&&x<20){
...
}
Ifyouwanttomakesurethatcountryiseither“England”or“Germany”youuse:
if(country==='England'||country==='Germany'){
...
}
Note:Justlikeoperationsonnumbers,Condtionscanbegroupedusingparenthesis,ex:if((name==="John"||name==="Jennifer")&&country==="France").
Exercise
Fillupthe2conditionssothat`primaryCategory`equals`"E/J"`onlyifnameequals`"John"`andcountryis`"England"`,andsothat`secondaryCategory`equals`"E|J"`onlyifnameequals`"John"`orcountryis`"England"`
varname="John";
varcountry="England";
varprimaryCategory,secondaryCategory;
if(/*Fillhere*/){
primaryCategory="E/J";
}
if(/*Fillhere*/){
secondaryCategory="E|J";
}
MoMoworkshop
108Concatenate
ArraysArraysareafundamentalpartofprogramming.Anarrayisalistofdata.Wecanstorealotofdatainonevariable,whichmakesourcodemorereadableandeasiertounderstand.Italsomakesitmucheasiertoperformfunctionsonrelateddata.
Thedatainarraysarecalledelements.
Hereisasimplearray:
//1,1,2,3,5,and8aretheelementsinthisarray
varnumbers=[1,1,2,3,5,8];
MoMoworkshop
109Arrays
IndicesSoyouhaveyourarrayofdataelements,butwhatifyouwanttoaccessaspecificelement?Thatiswhereindicescomein.Anindexreferstoaspotinthearray.indiceslogicallyprogressonebyone,butitshouldbenotedthatthefirstindexinanarrayis0,asitisinmostlanguages.Brackets[]areusedtosignifyyouarereferringtoanindexofanarray.
//Thisisanarrayofstrings
varfruits=["apple","banana","pineapple","strawberry"];
//Wesetthevariablebananatothevalueofthesecondelementof
//thefruitsarray.Rememberthatindicesstartat0,so1isthe
//secondelement.Result:banana="banana"
varbanana=fruits[1];
Exercise
Definethevariablesusingtheindicesofthearray
varcars=["Mazda","Honda","Chevy","Ford"]
varhonda=
varford=
varchevy=
varmazda=
MoMoworkshop
110Indices
LengthArrayshaveapropertycalledlength,andit'sprettymuchexactlyasitsounds,it'sthelengthofthearray.
vararray=[1,2,3];
//Result:l=3
varl=array.length;
Exercise
Definethevariableatobethenumbervalueofthelengthofthearray
vararray=[1,1,2,3,5,8];
varl=array.length;
vara=
MoMoworkshop
111Length
LoopsLoopsarerepetitiveconditionswhereonevariableintheloopchanges.Loopsarehandy,ifyouwanttorunthesamecodeoverandoveragain,eachtimewithadifferentvalue.
Insteadofwriting:
doThing(cars[0]);
doThing(cars[1]);
doThing(cars[2]);
doThing(cars[3]);
doThing(cars[4]);
Youcanwrite:
for(vari=0;i<cars.length;i++){
doThing(cars[i]);
}
MoMoworkshop
112Loops
ForLoopTheeasiestformofaloopistheforstatement.Thisonehasasyntaxthatissimilartoanifstatement,butwithmoreoptions:
for(condition;endcondition;change){
//doit,doitnow
}
Letsforexampleseehowtoexecutethesamecodeten-timesusingaforloop:
for(vari=0;i<10;i=i+1){
//dothiscodeten-times
}
:i=i+1canbewritteni++.
Exercise
Usingafor-loop,createavariablenamed`message`thatequalstheconcatenationofintegers(0,1,2,...)from0to99.
varmessage="";
MoMoworkshop
113For
WhileLoopWhileLoopsrepetitivelyexecuteablockofcodeaslongasaspecifiedconditionistrue.
while(condition){
//doitaslongasconditionistrue
}
Forexample,theloopinthisexamplewillrepetitivelyexecuteitsblockofcodeaslongasthevariableiislessthan5:
vari=0,x="";
while(i<5){
x=x+"Thenumberis"+i;
i++;
}
TheDo/WhileLoopisavariantofthewhileloop.Thisloopwillexecutethecodeblockoncebeforecheckingiftheconditionistrue.Itthenrepeatstheloopaslongastheconditionistrue:
do{
//codeblocktobeexecuted
}while(condition);
Note:Becarefultoavoidinfiniteloopingiftheconditionisalwaystrue!
Exercise
Usingawhile-loop,createavariablenamed`message`thatequalstheconcatenationofintegers(0,1,2,...)aslongasitslength(`message.length`)islessthan100.
varmessage="";
MoMoworkshop
114While
Do...WhileLoopThedo...whilestatementcreatesaloopthatexecutesaspecifiedstatementuntilthetestconditionevaluatestobefalse.Theconditionisevaluatedafterexecutingthestatement.Syntaxfordo...whileis
do{
//statement
}
while(expression);
Letsforexampleseehowtoprintnumberslessthan10usingdo...whileloop:
vari=0;
do{
document.write(i+"");
i++;//incrementingiby1
}while(i<10);
:i=i+1canbewritteni++.
Exercise
Usingado...while-loop,printnumbersbetweenlessthan5.
vari=0;
MoMoworkshop
115Do...While
FunctionsFunctions,areoneofthemostpowerfulandessentialnotionsinprogramming.
Functionslikemathematicalfunctionsperformtransformations,theytakeinputvaluescalledargumentsandreturnanoutputvalue.
MoMoworkshop
116Functions
DeclaringFunctionsFunctions,likevariables,mustbedeclared.Let'sdeclareafunctiondoublethatacceptsanargumentcalledxandreturnsthedoubleofx:
functiondouble(x){
return2*x;
}
Note:thefunctionabovemaybereferencedbeforeithasbeendefined.
FunctionsarealsovaluesinJavaScript;theycanbestoredinvariables(justlikenumbers,strings,etc...)andgiventootherfunctionsasarguments:
vardouble=function(x){
return2*x;
};
Note:thefunctionabovemaynotbereferencedbeforeitisdefined,justlikeanyothervariable.
Exercise
Declareafunctionnamed`triple`thattakesanargumentandreturnsitstriple.
MoMoworkshop
117Declare
HigherOrderFunctionsHigherorderfunctionsarefunctionsthatmanipulateotherfunctions.Forexample,afunctioncantakeotherfunctionsasargumentsand/orproduceafunctionasitsreturnvalue.SuchfancyfunctionaltechniquesarepowerfulconstructsavailabletoyouinJavaScriptandotherhigh-levellanguageslikepython,lisp,etc.
Wewillnowcreatetwosimplefunctions,add_2anddouble,andahigherorderfunctioncalledmap.mapwillaccepttwoarguments,funcandlist(itsdeclarationwillthereforebeginmap(func,list)),andreturnanarray.func(thefirstargument)willbeafunctionthatwillbeappliedtoeachoftheelementsinthearraylist(thesecondargument).
//Definetwosimplefunctions
varadd_2=function(x){
returnx+2;
};
vardouble=function(x){
return2*x;
};
//mapiscoolfunctionthataccepts2arguments:
//functhefunctiontocall
//listaarrayofvaluestocallfuncon
varmap=function(func,list){
varoutput=[];//outputlist
for(idxinlist){
output.push(func(list[idx]));
}
returnoutput;
}
//Weusemaptoapplyafunctiontoanentirelist
//ofinputsto"map"themtoalistofcorrespondingoutputs
map(add_2,[5,6,7])//=>[7,8,9]
map(double,[5,6,7])//=>[10,12,14]
Thefunctionsintheaboveexamplearesimple.However,whenpassedasargumentstootherfunctions,theycanbecomposedinunforeseenwaystobuildmorecomplexfunctions.
Forexample,ifwenoticethatweusetheinvocationsmap(add_2,...)andmap(double,...)veryofteninourcode,wecoulddecidewewanttocreatetwospecial-purposelistprocessingfunctionsthathavethedesiredoperationbakedintothem.Usingfunctioncomposition,wecoulddothisasfollows:
process_add_2=function(list){
returnmap(add_2,list);
}
process_double=function(list){
returnmap(double,list);
}
process_add_2([5,6,7])//=>[7,8,9]
process_double([5,6,7])//=>[10,12,14]
Nowlet'screateafunctioncalledbuildProcessorthattakesafunctionfuncasinputandreturnsafunc-processor,thatis,afunctionthatappliesfunctoeachinputinlist.
MoMoworkshop
118Higherorder
//afunctionthatgeneratesalistprocessorthatperforms
varbuildProcessor=function(func){
varprocess_func=function(list){
returnmap(func,list);
}
returnprocess_func;
}
//callingbuildProcessorreturnsafunctionwhichiscalledwithalistinput
//usingbuildProcessorwecouldgeneratetheadd_2anddoublelistprocessorsasfollows:
process_add_2=buildProcessor(add_2);
process_double=buildProcessor(double);
process_add_2([5,6,7])//=>[7,8,9]
process_double([5,6,7])//=>[10,12,14]
Let'slookatanotherexample.We'llcreateafunctioncalledbuildMultiplierthattakesanumberxasinputandreturnsafunctionthatmultipliesitsargumentbyx:
varbuildMultiplier=function(x){
returnfunction(y){
returnx*y;
}
}
vardouble=buildMultiplier(2);
vartriple=buildMultiplier(3);
double(3);//=>6
triple(3);//=>9
Exercise
Defineafunctionnamed`negate`thattakes`add1`asargumentandreturnsafunction,thatreturnsthenegationofthevaluereturnedby`add1`.(Thingsgetabitmorecomplicated;))
varadd1=function(x){
returnx+1;
};
varnegate=function(func){
//TODO
};
//Shouldreturn-6
//Because(5+1)*-1=-6
negate(add1)(5);
MoMoworkshop
119Higherorder
ObjectsTheprimitivetypesofJavaScriptaretrue,false,numbers,strings,nullandundefined.Everyothervalueisanobject.
InJavaScriptobjectscontainpropertyName:propertyValuepairs.
MoMoworkshop
120Objects
CreationTherearetwowaystocreateanobjectinJavaScript:
1. literal
varobject={};
//Yes,simplyapairofcurlybraces!
thisistherecomendedway.
2. andobject-oriented
varobject=newObject();
it'salmostlikeJava.
MoMoworkshop
121Creation
PropertiesObject'spropertyisapropertyName:propertyValuepair,wherepropertynamecanbeonlyastring.Ifit'snotastring,itgetscastedintoastring.Youcanspecifypropertieswhencreatinganobjectorlater.Theremaybezeroormorepropertiesseparatedbycommas.
varlanguage={
name:'JavaScript',
isSupportedByBrowsers:true,
createdIn:1995,
author:{
firstName:'Brendan',
lastName:'Eich'
},
//Yes,objectscanbenested!
getAuthorFullName:function(){
returnthis.author.firstName+""+this.author.lastName;
}
//Yes,functionscanbevaluestoo!
};
Thefollowingcodedemonstateshowtogetaproperty'svalue.
varvariable=language.name;
//variablenowcontains"JavaScript"string.
variable=language['name'];
//Thelinesabovedothesamething.Thedifferenceisthatthesecondoneletsyouuselitteralyanystringasapropertyname,butit'slessreadable.
variable=language.newProperty;
//variableisnowundefined,becausewehavenotassignedthispropertyyet.
Thefollowingexampleshowshowtoaddanewpropertyorchangeanexistingone.
language.newProperty='newvalue';
//Nowtheobjecthasanewproperty.Ifthepropertyalreadyexists,itsvaluewillbereplaced.
language['newProperty']='changedvalue';
//Onceagain,youcanaccesspropertiesbothways.Thefirstone(dotnotation)isrecomended.
MoMoworkshop
122Properties
MutableThedifferencebetweenobjectsandprimitivevaluesisthatwecanchangeobjects,whereasprimitivevaluesareimmutable.
varmyPrimitive="firstvalue";
myPrimitive="anothervalue";
//myPrimitivenowpointstoanotherstring.
varmyObject={key:"firstvalue"};
myObject.key="anothervalue";
//myObjectpointstothesameobject.
MoMoworkshop
123Mutable
ReferenceObjectsarenevercopied.Theyarepassedaroundbyreference.
//ImagineIhadapizza
varmyPizza={slices:5};
//AndIshareditwithYou
varyourPizza=myPizza;
//Ieatanotherslice
myPizza.slices=myPizza.slices-1;
varnumberOfSlicesLeft=yourPizza.slices;
//NowWehave4slicesbecausemyPizzaandyourPizza
//referencetothesamepizzaobject.
vara={},b={},c={};
//a,b,andceachrefertoa
//differentemptyobject
a=b=c={};
//a,b,andcallreferto
//thesameemptyobject
MoMoworkshop
124Reference
PrototypeEveryobjectislinkedtoaprototypeobjectfromwhichitinheritsproperties.
Allobjectscreatedfromobjectliterals({})areautomaticallylinkedtoObject.prototype,whichisanobjectthatcomesstandardwithJavaScript.
WhenaJavaScriptinterpreter(amoduleinyourbrowser)triestofindaproperty,whichYouwanttoretrieve,likeinthefollowingcode:
varadult={age:26},
retrievedProperty=adult.age;
//Thelineabove
First,theinterpreterlooksthrougheverypropertytheobjectitselfhas.Forexample,adulthasonlyoneownproperty—age.Butbesidesthatoneitactuallyhasafewmoreproperties,whichwereinheritedfromObject.prototype.
varstringRepresentation=adult.toString();
//thevariablehasvalueof'[objectObject]'
toStringisanObject.prototype'sproperty,whichwasinherited.Ithasavalueofafunction,whichreturnsastringrepresentationoftheobject.Ifyouwantittoreturnamoremeaningfulrepresentation,thenyoucanoverrideit.Simplyaddanewpropertytotheadultobject.
adult.toString=function(){
return"I'm"+this.age;
}
IfyoucallthetoStringfunctionnow,theinterpreterwillfindthenewpropertyintheobjectitselfandstop.
Thustheinterpreterretrievesthefirstpropertyitwillfindonthewayfromtheobjectitselfandfurtherthroughitsprototype.
TosetyourownobjectasaprototypeinsteadofthedefaultObject.prototype,youcaninvokeObject.createasfollows:
varchild=Object.create(adult);
/*ThiswayofcreatingobjectsletsuseasilyreplacethedefaultObject.prototypewiththeonewewant.Inthiscase,thechild'sprototypeistheadultobject.*/
child.age=8;
/*Previously,childdidn'thaveitsownageproperty,andtheinterpreterhadtolookfurthertothechild'sprototypetofindit.
Now,whenwesetthechild'sownage,theinterpreterwillnotgofurther.
Note:adult'sageisstill26.*/
varstringRepresentation=child.toString();
//Thevalueis"I'm8".
/*Note:wehavenotoverriddenthechild'stoStringproperty,thustheadult'smethodwillbeinvoked.IfadultdidnothavetoStringproperty,thenObject.prototype'stoStringmethodwouldbeinvoked,andwewouldget"[objectObject]"insteadof"I'm8"*/
child'sprototypeisadult,whoseprototypeisObject.prototype.Thissequenceofprototypesiscalledprototypechain.
MoMoworkshop
125Prototype
Deletedeletecanbeusedtoremoveapropertyfromanobject.Itwillremoveapropertyfromtheobjectifithasone.Itwillnotlookfurtherintheprototypechain.Removingapropertyfromanobjectmayallowapropertyfromtheprototypechaintoshinethrough:
varadult={age:26},
child=Object.create(adult);
child.age=8;
deletechild.age;
/*Removeagepropertyfromchild,revealingtheageoftheprototype,becausethenitisnotoverriden.*/
varprototypeAge=child.age;
//26,becausechilddoesnothaveitsownageproperty.
MoMoworkshop
126Delete
EnumerationTheforinstatementcanloopoverallofthepropertynamesinanobject.Theenumerationwillincludefunctionsandprototypeproperties.
varfruit={
apple:2,
orange:5,
pear:1
},
sentence='Ihave',
quantity;
for(kindinfruit){
quantity=fruit[kind];
sentence+=quantity+''+kind+
(quantity===1?'':'s')+
',';
}
//Thefollowinglineremovesthetrailingcoma.
sentence=sentence.substr(0,sentence.length-2)+'.';
//Ihave2apples,5oranges,1pear.
MoMoworkshop
127Enumeration
GlobalfootprintIfyouaredevelopingamodule,whichmightberunningonawebpage,whichalsorunsothermodules,thenyoumustbewarethevariablenameoverlapping.
Supposewearedevelopingacountermodule:
varmyCounter={
number:0,
plusPlus:function(){
this.number:this.number+1;
},
isGreaterThanTen:function(){
returnthis.number>10;
}
}
thistechniqueisoftenusedwithclosures,tomaketheinternalstateimmutablefromtheoutside.
Themodulenowtakesonlyonevariablename—myCounter.IfanyothermoduleonthepagemakesuseofsuchnameslikenumberorisGreaterThanTenthenit'sperfectlysafe,becausewewillnotoverrideeachothersvalues;
MoMoworkshop
128Globalfootprint
OpenLayersWorkshopWelcometotheOpenLayers3Workshop.ThisworkshopisdesignedtogiveyouacomprehensiveoverviewofOpenLayersasawebmappingsolution.
Setup
Theseinstructionsassumethatyouarestartingwithanopenlayers-workshop.ziparchivefromthelatestworkshoprelease.Inaddition,you'llneedNodeinstalledtorunadevelopmentserverfortheOpenLayerslibrary.
Afterextractingthezip,changeintotheopenlayers-workshopdirectoryandinstallsomeadditionaldependencies:
npminstall
Nowyou'rereadytostarttheworkshopserver.ThisservesuptheworkshopdocumentationinadditiontoprovidingadebugloaderfortheOpenLayerslibrary.
npmstart
Thiswillstartadevelopmentserverwhereyoucanreadtheworkshopdocumentationandworkthroughtheexercises:http://terrestris.github.io/momo3-ws/.
OverviewThisworkshopispresentedasasetofmodules.Ineachmoduleyouwillperformasetoftasksdesignedtoachieveaspecificgoalforthatmodule.Eachmodulebuildsuponlessonslearnedinpreviousmodulesandisdesignedtoiterativelybuildupyourknowledgebase.
Thefollowingmoduleswillbecoveredinthisworkshop:
Basics-LearnhowtoaddamaptoawebpagewithOpenLayers.LayersandSources-Learnaboutlayersandsources.ControlsandInteractions-Learnaboutusingmapcontrolsandinteractions.VectorTopics-Explorevectorlayersindepth.CustomBuilds-Createcustombuilds.
MoMoworkshop
129OpenLayers
BasicsCreatingamapDissectingyourmapUsefulresources
MoMoworkshop
130Basics
CreatingaMapInOpenLayers,amapisacollectionoflayersandvariousinteractionsandcontrolsfordealingwithuserinteraction.Amapisgeneratedwiththreebasicingredients:markup,styledeclarations,andinitializationcode.
WorkingExample
Let'stakealookatafullyworkingexampleofanOpenLayers3map.
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<title>OpenLayers3example</title>
<scriptsrc="/loader.js"type="text/javascript"></script>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
});
</script>
</body>
</html>
Tasks
1. Makesureyou'vecompletedthesetupinstructionstoinstalldependenciesandgetthedebugserverrunning.2. Copythetextaboveintoanewfilecalledmap.html,andsaveitintherootoftheworkshopdirectory.3. Opentheworkingmapinyourwebbrowser:http://terrestris.github.io/momo3-ws//map.html
MoMoworkshop
131Creatingamap
Aworkingmapdisplayingimageryoftheworld
Havingsuccessfullycreatedourfirstmap,we'llcontinuebylookingmorecloselyattheparts.
MoMoworkshop
132Creatingamap
DissectingYourMapAsdemonstratedintheprevioussection,amapisgeneratedbybringingtogethermarkup,styledeclarations,andinitializationcode.We'lllookateachofthesepartsinabitmoredetail.
MapMarkup
Themarkupforthemapinthepreviousexamplegeneratesasingledocumentelement:
<divid="map"></div>
This<div>elementwillserveasthecontainerforourmapviewport.Hereweusea<div>element,butthecontainerfortheviewportcanbeanyblock-levelelement.
Inthiscase,wegivethecontaineranidattributesowecanreferenceitasthetargetofourmap.
MapStyle
OpenLayerscomeswithadefaultstylesheetthatspecifieshowmap-relatedelementsshouldbestyled.We'veexplicitlyincludedthisstylesheetinthemap.htmlpage(<linkrel="stylesheet"href="/ol.css"type="text/css">).
OpenLayersdoesn'tmakeanyguessesaboutthesizeofyourmap.Becauseofthis,followingthedefaultstylesheet,weneedtoincludeatleastonecustomstyledeclarationtogivethemapsomeroomonthepage.
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
Inthiscase,we'reusingthemapcontainer'sidvalueasaselector,andwespecifythewidth(512px)andtheheight(256px)forthemapcontainer.
Thestyledeclarationsaredirectlyincludedinthe<head>ofourdocument.Inmostcases,yourmaprelatedstyledeclarationswillbeapartofalargerwebsitethemelinkedinexternalstylesheets.
MapInitializationThenextstepingeneratingyourmapistoincludesomeinitializationcode.Inourcase,wehaveincludeda<script>elementatthebottomofourdocument<body>todothework:
MoMoworkshop
133Dissectingyourmap
<script>
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
});
</script>
Theorderofthesestepsisimportant.Beforeourcustomscriptcanbeexecuted,theOpenLayerslibrarymustbeloaded.Inourexample,theOpenLayerslibraryisloadedinthe<head>ofourdocumentwith<scriptsrc="/loader.js"></script>.
Similarly,ourcustommapinitializationcode(above)cannotrununtilthedocumentelementthatservesastheviewportcontainer,inthiscase<divid="map"></div>,isready.Byincludingtheinitializationcodeattheendofthedocument<body>,weensurethatthelibraryisloadedandtheviewportcontainerisreadybeforegeneratingourmap.
Let'slookinmoredetailatwhatthemapinitializationscriptisdoing.Ourscriptcreatesanewol.Mapobjectwithafewconfigoptions:
target:'map'
Weusetheviewportcontainer'sidattributevaluetotellthemapconstructorwheretorenderthemap.Inthiscase,wepassthestringvalue"map"asthetargettothemapconstructor.Thissyntaxisashortcutforconvenience.Wecouldbemoreexplicitandprovideadirectreferencetotheelement(e.g.document.getElementById("map")).
Thelayersconfigcreatesalayertobedisplayedinourmap:
layers:[
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
],
Don'tworryaboutthesyntaxhereifthispartisnewtoyou.Layercreationwillbecoveredinanothermodule.Theimportantparttounderstandisthatourmapviewisacollectionoflayers.Inordertoseeamap,weneedtoincludeatleastonelayer.
Thefinalstepisdefiningtheview.Wespecifyaprojection,acenterandazoomlevel.WealsospecifyamaxResolutiontomakesurewedon'trequestboundingboxesthatGeoWebCachecannothandle.
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
You'vesuccessfullydissectedyourfirstmap!Nextlet'slearnmoreaboutdevelopingwithOpenLayers.
MoMoworkshop
134Dissectingyourmap
MoMoworkshop
135Dissectingyourmap
OpenLayersResourcesTheOpenLayerslibrarycontainsawealthoffunctionality.Thoughthedevelopershaveworkedhardtoprovideexamplesofthatfunctionalityandhaveorganizedthecodeinawaythatallowsotherexperienceddeveloperstofindtheirwayaround,manyusersfinditachallengetogetstartedfromscratch.
LearnbyExample
NewuserswillmostlikelyfinddivingintotheOpenLayer'sexamplecodeandexperimentingwiththelibrary'spossiblefunctionalitythemostusefulwaytobegin.
http://openlayers.org/en/master/examples/
BrowsetheDocumentation
Forfurtherinformationonspecifictopics,browsethegrowingcollectionofOpenLayersdocumentation.
http://openlayers.org/en/master/doc/quickstart.htmlhttp://openlayers.org/en/master/doc/tutorials
FindtheAPIReferenceAfterunderstandingthebasiccomponentsthatmake-upandcontrolamap,searchtheAPIreferencedocumentationfordetailsonmethodsignaturesandobjectproperties.IfyouonlywanttoseethestablepartoftheAPI,makesuretochecktheStableOnlycheckbox.
http://openlayers.org/en/master/apidoc/
JointheCommunity
OpenLayersissupportedandmaintainedbyacommunityofdevelopersanduserslikeyou.Whetheryouhavequestionstoaskorcodetocontribute,youcangetinvolvedbyusingtheopenlayers-3tagonStackOverflowforusagequestionsorsigningupforthedevelopersmailinglist.
http://stackoverflow.com/questions/tagged/openlayers-3https://groups.google.com/forum/#!forum/ol3-dev
ReportingissuesForreportingissuesitisimportanttounderstandtheseveralflavoursinwhichtheOpenLayerslibraryisdistributed:
ol.js-thescriptwhichisbuiltusingtheClosureCompilerinadvancedmode(nothumanreadable)ol-debug.js-humanreadableversiontobeusedduringdevelopment
Whenyouencounteranissue,itisimportanttoreporttheissueusingol-debug.js.AlsoincludethefullstacktracewhichyoucanfindusingWebDevelopertoolssuchasChrome'sDeveloperTools.Totestthisoutwearegoingtomakeamistakeinmap.htmlbychangingol.layer.Tileintool.layer.Image.Theerroryouwillseeis:UncaughtTypeError:undefinedisnotafunction.Ifyoureportthistothemailinglist,nobodywillknowwhatitmeans.Sofirst,wearegoingtochangethescripttagwhichpointstool.jstopointtool-debug.jsinstead.Reloadthepage.Thedebuggerwillnowstopontheerror,andwecanseethefullstacktrace.
MoMoworkshop
136Resources
Atabreakpointinthedebugger
MoMoworkshop
137Resources
LayersandSourcesWMSsourcesTiledsourcesProprietarytileprovidersVectordataImagevectorsource
MoMoworkshop
138LayersandSources
WebMapServiceLayersWhenyouaddalayertoyourmap,thelayer'ssourceistypicallyresponsibleforfetchingthedatatobedisplayed.Thedatarequestedcanbeeitherrasterorvectordata.Youcanthinkofrasterdataasinformationrenderedasanimageontheserverside.Vectordataisdeliveredasstructuredinformationfromtheserverandmayberenderedfordisplayontheclient(yourbrowser).
Therearemanydifferenttypesofservicesthatproviderastermapdata.ThissectiondealswithprovidersthatconformwiththeOGC(OpenGeospatialConsortium,Inc.)WebMapService(WMS)specification.
CreatingaLayer
We'llstartwithafullyworkingmapexampleandmodifythelayerstogetanunderstandingofhowtheywork.
Let'stakealookatthefollowingcode:
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<scriptsrc="/loader.js"type="text/javascript"></script>
<title>OpenLayers3example</title>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
});
</script>
</body>
</html>
Tasks
1. Ifyouhaven'talreadydoneso,savethetextaboveasmap.htmlintherootofyourworkshopdirectory.2. Openthepageinyourbrowsertoconfirmthingswork:http://terrestris.github.io/momo3-ws//map.html
Theol.layer.TileConstructor
MoMoworkshop
139WMSsources
Theol.layer.Tileconstructorgetsanobjectliteraloftypeolx.layer.TileOptionssee:http://openlayers.org/en/master/apidoc/ol.layer.Tile.htmlInthiscaseweareprovidingthesourcekeyoftheoptionswithanol.source.TileWMS.Ahuman-readabletitleforthelayercanbeprovidedwiththetitlekey,butbasicallyanyarbitrarynameforthekeycanbeusedhere.InOpenLayers3thereisaseparationbetweenlayersandsources,whereasinOpenLayers2thiswasallpartofthelayer.
ol.layer.Tilerepresentsaregulargridofimages,ol.layer.Imagerepresentsasingleimage.Dependingonthelayertype,youwoulduseadifferentsource(ol.source.TileWMSversusol.source.ImageWMS)aswell.
Theol.source.TileWMSConstructor
Theol.source.TileWMSconstructorhasasingleargumentwhichisdefinedby:http://openlayers.org/en/master/apidoc/ol.source.TileWMS.html.TheurlistheonlineresourceoftheWMSservice,andparamsisanobjectliteralwiththeparameternamesandtheirvalues.SincethedefaultWMSversionis1.3.0nowinOpenLayers,youmightneedtoprovidealowerversionintheparamsifyourWMSdoesnotsupportWMS1.3.0.
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
]
Tasks
1. ThissameWMSoffersaNaturalEarthlayernamed'ne:NE1_HR_LC_SR_W_DR'.ChangethevalueoftheLAYERSparameterfrom'nasa:bluemarble'to'ne:NE1_HR_LC_SR_W_DR'.Yourrevisedol.layer.TileConstructorshouldlooklike:
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'ne:NE1_HR_LC_SR_W_DR',VERSION:'1.1.1'}
})
})
2. Changeyourlayerandsourcetohaveasingleimageinsteadoftiles.LookatthefollowingAPIdocpagesforhints:http://openlayers.org/en/master/apidoc/ol.layer.Image.htmlandhttp://openlayers.org/en/master/apidoc/ol.source.ImageWMS.html.UsetheNetworktabofyourbrowser'sdevelopertoolstomakesureasingleimageisrequestedandnot256x256pixeltiles.
MoMoworkshop
140WMSsources
HavingworkedwithdynamicallyrendereddatafromaWebMapService,let'smoveontolearnaboutcachedtileservices.
MoMoworkshop
141WMSsources
CachedTilesBydefault,theTilelayermakesrequestsfor256x256(pixel)imagestofillyourmapviewportandbeyond.Asyoupanandzoomaroundyourmap,morerequestsforimagesgoouttofilltheareasyouhaven'tyetvisited.Whileyourbrowserwillcachesomerequestedimages,alotofprocessingworkistypicallyrequiredfortheservertodynamicallyrenderimages.
Sincetiledlayersmakerequestsforimagesonaregulargrid,itispossiblefortheservertocachetheseimagerequestsandreturnthecachedresultnexttimeyou(orsomeoneelse)visitsthesamearea-resultinginbetterperformanceallaround.
ol.source.XYZ
TheWebMapServicespecificationallowsalotofflexibilityintermsofwhataclientcanrequest.Withoutconstraints,thismakescachingdifficultorimpossibleinpractice.
Attheoppositeextreme,aservicemightoffertilesonlyatafixedsetofzoomlevelsandonlyforaregulargrid.ThesecanbegeneralizedastiledlayerswithanXYZsource-youcanconsiderXandYtoindicatethecolumnandrowofthegridandZtorepresentthezoomlevel.
ol.source.OSM
TheOpenStreetMap(OSM)projectisanefforttocollectandmakefreelyavailablemapdatafortheworld.OSMprovidesafewdifferentrenderingsoftheirdataascachedtilesets.TheserenderingsconformtothebasicXYZgridarrangementandcanbeusedinanOpenLayersmap.Theol.source.OSMlayersourceaccessesOpenStreetMaptiles.
Tasks
1. Openthemap.htmlfilefromtheprevioussectioninatexteditorandchangethemapinitializationcodetolooklikethefollowing:
<script>
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([126.97,37.56]),
zoom:9
}),
controls:ol.control.defaults({
attributionOptions:{
collapsible:false
}
})
});
</script>
2. Inthe<head>ofthesamedocument,addafewstyledeclarationsforthelayerattribution.
MoMoworkshop
142Tiledsources
<style>
#map{
width:512px;
height:256px;
}
.ol-attributiona{
color:black;
}
</style>
3. Saveyourchanges,andrefreshthepageinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
ACloserLook
Projections
Reviewtheviewdefinitionofthemap:
view:newol.View({
center:ol.proj.fromLonLat([126.97,37.56]),
zoom:9
})
Geospatialdatacancomeinanynumberofcoordinatereferencesystems.Onedatasetmightusegeographiccoordinates(longitudeandlatitude)indegrees,andanothermighthavecoordinatesinalocalprojectionwithunitsinmeters.Afulldiscussionofcoordinatereferencesystemsisbeyondthescopeofthismodule,butitisimportanttounderstandthebasicconcept.
OpenLayers3needstoknowthecoordinatesystemforyourdata.Internally,thisisrepresentedwithanol.proj.Projectionobjectbutstringscanalsobesupplied.
TheOpenStreetMaptilesthatwewillbeusingareinaMercatorprojection.Becauseofthis,weneedtosettheinitialcenterusingMercatorcoordinates.Sinceitisrelativelyeasytofindoutthecoordinatesforaplaceofinterestingeographiccoordinates,weusetheol.proj.fromLonLatmethodtoturngeographiccoordinates('EPSG:4326')intoMercatorcoordinates('EPSG:3857').
AlternativeProjections
OpenLayers3includestransformsbetweenGeographic('EPSG:4326')andWebMercator('EPSG:3857')coordinatereferencesystems.Becauseofthis,wecanusetheol.proj.fromLonLatfunctionabovewithoutanyextrawork.Ifyouwanttoworkwithdatainadifferentprojection,youneedtoincludesomeadditionalinformationbeforeusingtheol.proj.*functions.
MoMoworkshop
143Tiledsources
Forexample,ifyouwantedtoworkwithdatainthe'EPSG:21781'coordinatereferencesystem,youwouldincludethefollowingtwoscripttagsinyourpage:
<scriptsrc="http://cdnjs.cloudflare.com/ajax/libs/proj4js/2.3.6/proj4.js"type="text/javascript"></script>
<scriptsrc="http://epsg.io/21781-1753.js"type="text/javascript"></script>
Theninyourapplicationcode,youcouldregisterthisprojectionandsetitsvalidityextentasfollows:
//ThiscreatesaprojectionobjectfortheEPSG:21781projection
//andsetsa"validityextent"inthatprojectionobject.
varprojection=ol.proj.get('EPSG:21781');
projection.setExtent([485869.5728,76443.1884,837076.5648,299941.7864]);
Theextentinformationcanbelookedupathttp://epsg.io/,usingtheEPSGcode.
LayerCreation
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
Asbefore,wecreatealayerandaddittothelayersarrayofourmapconfigobject.Thistime,weacceptallthedefaultoptionsforthesource.
Style
.ol-attributiona{
color:black;
}
Atreatmentofmapcontrolsisalsooutsideofthescopeofthismodule,butthesestyledeclarationsgiveyouasneakpreview.Bydefault,anol.control.Attributioncontrolisaddedtoallmaps.Thisletslayersourcesdisplayattributioninformationinthemapviewport.Thedeclarationsabovealterthestyleofthisattributionforourmap(noticetheCopyrightlineatthebottomrightofthemap).
AttributionControlConfiguration
Bydefaulttheol.control.Attributionaddsani(information)buttonthatcanbepressedtoactuallydisplaystheattributioninformation.TocomplytoOpenStreetMap'sTermsOfUse,andalwaysdisplaytheOpenStreetMapattributioninformation,thefollowingisusedintheoptionsobjectpassedtotheol.Mapconstructor:
controls:ol.control.defaults({
attributionOptions:{
collapsible:false
}
})
Thisremovestheibutton,andmakestheattributioninformationalwaysvisible.
Havingmasteredlayerswithpubliclyavailablecachedtilesets,let'smoveontoworkingwithproprietaryrasterlayers.
MoMoworkshop
144Tiledsources
ProprietaryRasterLayersInprevioussections,wedisplayedlayersbasedonastandardscompliantWMS(OGCWebMapService)andacustomtilecache.Onlinemapping(oratleastthetiledmapclient)waslargelypopularizedbytheavailabilityofproprietarymaptileservices.OpenLayersprovideslayertypesthatworkwiththeseproprietaryservicesthroughtheirAPIs.
Inthissection,we'llbuildontheexampledevelopedintheprevioussectionbyaddingalayerusingtilesfromBing.
Bing!
Let'saddaBinglayer.
Tasks
1. Inyourmap.htmlfile,findwheretheOSM(OpenStreetMap)sourceisconfiguredandchangeitintoanol.source.BingMaps
source:newol.source.BingMaps({
imagerySet:'Road',
key:'<YourBingMapsKeyHere>'
})
Note-TheBingtilesAPIrequiresthatyouregisterforanAPIkeytousewithyourmappingapplication.TheexamplehereusesanAPIkeythatyoushouldnotuseinproduction.TousetheBinglayerinproduction,registerforanAPIkeyathttps://www.bingmapsportal.com/.
2. Saveyourchangesandreloadmap.htmlinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
CompleteWorkingExampleYourrevisedmap.htmlfileshouldlooksomethinglikethis:
MoMoworkshop
145Proprietarytileproviders
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
.ol-attributiona{
color:black;
}
</style>
<scriptsrc="/loader.js"type="text/javascript"></script>
<title>OpenLayers3example</title>
</head>
<body>
<h1>MyMap</h1>
<divid="map"class="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.BingMaps({
imagerySet:'Road',
key:'<YourBingMapsKeyHere>'
})
})
],
view:newol.View({
center:ol.proj.fromLonLat([126.97,37.56]),
zoom:9
})
});
</script>
</body>
</html>
MoMoworkshop
146Proprietarytileproviders
VectorLayersVectorLayersarerepresentedbyol.layer.Vectorandhandletheclient-sidedisplayofvectordata.CurrentlyOpenLayers3supportsfullvectorrenderingintheCanvasrenderer,butonlypointgeometriesintheWebGLrenderer.
RenderingFeaturesClient-Side
Let'sgobacktotheWMSexampletogetabasicworldmap.We'lladdsomefeaturedataontopofthisinavectorlayer.
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<title>OpenLayers3example</title>
<scriptsrc="/loader.js"type="text/javascript"></script>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
});
</script>
</body>
</html>
Tasks
1. Openmap.htmlinyourtexteditorandcopyinthecontentsoftheinitialWMSexample.Saveyourchangesandconfirmthatthingslookgoodinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
2. InyourmapinitializationcodeaddanotherlayeraftertheTilelayer(pastethefollowing).ThisaddsanewvectorlayertoyourmapthatrequestsasetoffeaturesstoredinGeoJSON:
MoMoworkshop
147Vectordata
newol.layer.Vector({
title:'Earthquakes',
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:3,
fill:newol.style.Fill({color:'white'})
})
})
})
ACloserLook
Let'sexaminethatvectorlayercreationtogetanideaofwhatisgoingon.
newol.layer.Vector({
title:'Earthquakes',
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:3,
fill:newol.style.Fill({color:'white'})
})
})
})
Thelayerisgiventhetitle'Earthquakes'andsomecustomoptions.Intheoptionsobject,we'veincludedasourceoftypeol.source.Vectorwhichpointstoaurl.We'vegiventhesourceaformatthatwillbeusedforparsingthedata.
Note-Inthecasewhereyouwanttostylethefeaturesbasedonanattribute,youwoulduseastylefunctioninsteadofanol.style.Styleforthestyleconfigoptionofol.layer.Vector.
BonusTasks
1. Thewhitecirclesonthemaprepresentol.Featureobjectsonyourol.layer.Vectorlayer.Eachofthesefeatureshasattributedatawithtitleandsummaryproperties.Registera'singleclick'listeneronyourmapthatcallsforEachFeatureAtPixelonthemap,anddisplaysearthquakeinformationbelowthemapviewport.
2. ThedataforthevectorlayercomesfromanearthquakefeedpublishedbytheUSGS(http://earthquake.usgs.gov/earthquakes/catalogs/).Seeifyoucanfindadditionaldatawithspatialinformationinaformat
MoMoworkshop
148Vectordata
supportedbyOpenLayers3.Ifyousaveanotherdocumentrepresentingspatialdatainyourdatadirectory,youshouldbeabletoviewitinavectorlayeronyourmap.
Solutions
Asasolutiontothefirstbonustaskyoucanaddaninfodivbelowthemap:
<divid="info"></div>
andaddthefollowingJavaScriptcodetodisplaythetitleoftheclickedfeature:
map.on('singleclick',function(e){
varfeature=map.forEachFeatureAtPixel(e.pixel,function(feature){
returnfeature;
});
varinfoElement=document.getElementById('info');
infoElement.innerHTML=feature?feature.get('title'):'';
});
MoMoworkshop
149Vectordata
ImageVectorInthepreviousexampleusinganol.layer.Vectoryoucanseethatthefeaturesarere-renderedcontinuouslyduringanimatedzooming(thesizeofthepointsymbolizersremainsfixed).Withavectorlayer,OpenLayerswillre-renderthesourcedatawitheachanimationframe.Thisprovidesconsistentrenderingoflinestrokes,pointsymbolizers,andlabelswithchangesintheviewresolution.
Analternativerenderingstrategyistoavoidre-renderingdataduringviewtransitionsandinsteadrepositionandscaletherenderedoutputfromthepreviousviewstate.Thiscanbeaccomplishedbyusinganol.layer.Imagewithanol.source.ImageVector.Withthiscombination,"snapshots"ofyourdataarerenderedwhentheviewisnotanimating,andthesesnapshotsarereusedduringviewtransitions.
Theexamplebelowusesanol.layer.Imagewithanol.source.ImageVector.Thoughthisexampleonlyrendersasmallquantityofdata,thiscombinationwouldbeappropriateforapplicationsthatrenderlargequantitiesofrelativelystaticdata.
ol.source.ImageVector
Let'sgobacktothevectorlayerexampletogetearthquakedataontopofaworldmap.
MoMoworkshop
150Imagevectorsource
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<title>OpenLayers3example</title>
<scriptsrc="/loader.js"type="text/javascript"></script>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
}),
newol.layer.Vector({
title:'Earthquakes',
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:3,
fill:newol.style.Fill({color:'white'})
})
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:0,
maxResolution:0.703125
})
});
</script>
</body>
</html>
Tasks
1. Openmap.htmlinyourtexteditorandcopyinthecontentsofthevectorexamplefromabove.Saveyourchangesandconfirmthatthingslookgoodinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
2. Changethevectorlayerinto:
MoMoworkshop
151Imagevectorsource
newol.layer.Image({
title:'Earthquakes',
source:newol.source.ImageVector({
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:3,
fill:newol.style.Fill({color:'white'})
})
})
})
})
3. Reloadhttp://terrestris.github.io/momo3-ws//map.htmlinthebrowserNote-Youwillseethesamevectordatabutdepictedasanimage.Thiswillstillenablethingslikefeaturedetection,butthevectordatawillbelesssharp.Sothisisessentiallyatrade-offbetweenperformanceandquality.
ACloserLook
Let'sexaminethelayercreationtogetanideaofwhatisgoingon.
newol.layer.Image({
title:'Earthquakes',
source:newol.source.ImageVector({
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:3,
fill:newol.style.Fill({color:'white'})
})
})
})
})
Weareusinganol.layer.Imageinsteadofanol.layer.Vector.However,wecanstillusevectordataherethroughol.source.ImageVectorthatconnectstoouroriginalol.source.Vectorvectorsource.Thestyleisprovidedasconfigofol.source.ImageVectorandnotonthelayer.
BonusTasks
1. Verifythatfeaturedetectionstillworksbyregisteringa'singleclick'listeneronyourmapthatcallsforEachFeatureAtPixelonthemap,anddisplaysearthquakeinformationbelowthemapviewport.
MoMoworkshop
152Imagevectorsource
ControlsandinteractionsScalelinecontrolSelectinteractionDrawinteractionModifyinteraction
MoMoworkshop
153Controls
DisplayingaScaleLineAnothertypicalwidgettodisplayonmapsisascalebar.OpenLayers3providesanol.control.ScaleLineforjustthis.
CreatingaScaleLineControl
Tasks
1. Openthemap.htmlinyourtexteditor.2. Somewhereinthemapconfig,addthefollowingcodetocreateanewscalelinecontrolforyourmap:
controls:ol.control.defaults().extend([
newol.control.ScaleLine()
]),
3. Saveyourchangesandopenmap.htmlinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
MovingtheScaleLineControlYoumayfindthescalebarabithardtoreadovertheimagery.Thereareafewapproachestotakeinordertoimprovescalevisibility.Ifyouwanttokeepthecontrolinsidethemapviewport,youcanaddsomestyledeclarationswithintheCSSofyourdocument.Totestthisout,youcanincludeabackgroundcolorandpaddingtothescalebarwithsomethinglikethefollowing:
.ol-scale-line{
background:black;
padding:5px;
}
However,forthesakeofthisexercise,let'ssayyouthinkthemapviewportisgettingunbearablycrowded.Toavoidsuchover-crowding,youcandisplaythescaleinadifferentlocation.Toaccomplishthis,weneedtofirstcreateanadditionalelementinourmarkupandthentellthescalecontroltorenderitselfwithinthisnewelement.
Tasks
1. Createanewblocklevelelementinthe<body>ofyourpage.Tomakethiselementeasytoreferto,we'llgiveitanidattribute.Insertthefollowingmarkupsomewhereinthe<body>ofyourmap.htmlpage.(Placingthescaleelementrightafterthemapviewportelement<divid="map"></div>makessense.):
MoMoworkshop
154Scalelinecontrol
<divid="scale-line"class="scale-line"></div>
2. Nowmodifythecodecreatingthescalecontrolsothatitreferstothescale-lineelement:
controls:ol.control.defaults().extend([
newol.control.ScaleLine({className:'ol-scale-line',target:document.getElementById('scale-line')})
]),
3. Saveyourchangesandopenmap.htmlinyourbrowser:http://terrestris.github.io/momo3-ws//map.html4. "Fix"thepositionofthecontrolwith,forexample,thefollowingCSSrules:
.scale-line{
position:absolute;
top:350px;
}
.ol-scale-line{
position:relative;
bottom:0px;
left:0px;
}
5. Nowsaveyourchangesandviewmap.htmlagaininyourbrowser:http://terrestris.github.io/momo3-ws//map.html
Note-Tocreateacustomcontrolyoucaninherit(byusingol.inherits)fromol.control.Control.Toseeanexampleofthischeckout:http://openlayers.org/en/master/examples/custom-controls.html.
MoMoworkshop
155Scalelinecontrol
SelectingFeatures
Aswe'veseeninthelayersmodule,wecanpullfeaturesasvectorsanddrawthemontopofabasemap.Oneoftheadvantagesofservingvectordataisthatuserscaninteractwiththedata.Inthisexample,wecreateavectorlayerwhereuserscanselectandviewfeatureinformation.
Thepreviousexampledemonstratedtheuseofanol.control.Controlonthemap.ControlshaveavisualrepresentationonthemaporaddDOMelementstothedocument.Anol.interaction.Interactionisresponsibleforhandlinguserinteraction,buttypicallyhasnovisualrepresentation.Thisexampledemonstratestheuseoftheol.interaction.Selectforinteractingwithfeaturesfromvectorlayers.
CreateaVectorLayerandaSelectInteraction
Tasks
1. Let'sstartwiththevectorlayerexamplefromaprevioussection.Openmap.htmlinyourtexteditorandmakesureitlookssomethinglikethefollowing:
MoMoworkshop
156Selectinteraction
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<scriptsrc="/loader.js"type="text/javascript"></script>
<title>OpenLayers3example</title>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
interactions:ol.interaction.defaults().extend([
newol.interaction.Select({
style:newol.style.Style({
image:newol.style.Circle({
radius:5,
fill:newol.style.Fill({
color:'#FF0000'
}),
stroke:newol.style.Stroke({
color:'#000000'
})
})
})
})
]),
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
}),
newol.layer.Vector({
title:'Earthquakes',
source:newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
}),
style:newol.style.Style({
image:newol.style.Circle({
radius:5,
fill:newol.style.Fill({
color:'#0000FF'
}),
stroke:newol.style.Stroke({
color:'#000000'
})
})
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:1
})
});
</script>
</body>
</html>
MoMoworkshop
157Selectinteraction
2. Saveyourchangestomap.htmlandopenthepageinyourbrowser:http://terrestris.github.io/momo3-ws//map.html.Toseefeatureselectioninaction,usethemouse-clicktoselectanearthquake:
MoMoworkshop
158Selectinteraction
DrawingFeaturesNewfeaturescanbedrawnbyusinganol.interaction.Draw.Adrawinteractionisconstructedwithavectorsourceandageometrytype.
CreateaVectorLayerandaDrawInteraction
Tasks
1. Let'sstartwiththeexamplebelow.Openmap.htmlinyourtexteditorandmakesureitlookssomethinglikethefollowing:
MoMoworkshop
159Drawinteraction
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<scriptsrc="/loader.js"type="text/javascript"></script>
<title>OpenLayers3example</title>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varsource=newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
});
vardraw=newol.interaction.Draw({
source:source,
type:'Point'
});
varmap=newol.Map({
interactions:ol.interaction.defaults().extend([draw]),
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
}),
newol.layer.Vector({
title:'Earthquakes',
source:source,
style:newol.style.Style({
image:newol.style.Circle({
radius:5,
fill:newol.style.Fill({
color:'#0000FF'
}),
stroke:newol.style.Stroke({
color:'#000000'
})
})
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:1
})
});
</script>
</body>
</html>
2. Saveyourchangestomap.htmlandopenthepageinyourbrowser:http://terrestris.github.io/momo3-ws//map.html.Toseedrawingofpointgeometriesinaction,clickinthemaptoaddanewfeature:
MoMoworkshop
160Drawinteraction
BonusTasks
1. Createalistenerwhichgetsthenewfeature'sXandYafteritisdrawn.
Solutions
Hereisasolutionforthefirstbonustask.Initweregisteraneventlistenerforthedrawendeventoftheol.interaction.Draw.Thismethodlogsthefeature'sXandYtothedeveloperconsole:
draw.on('drawend',function(evt){
varfeature=evt.feature;
varp=feature.getGeometry();
console.log(p.getCoordinates());
});
MoMoworkshop
161Drawinteraction
ModifyingFeaturesModifyingfeaturesworksbyusinganol.interaction.Selectincombinationwithanol.interaction.Modify.Theyshareacommoncollection(ol.Collection)offeatures.Featuresselectedwiththeol.interaction.Selectbecomecandidatesformodificationswiththeol.interaction.Modify.
CreateaVectorLayerandaModifyInteraction
Tasks
1. Let'sstartwiththeworkingexample.Openmap.htmlinyourtexteditorandmakesureitlookssomethinglikethefollowing:
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<scriptsrc="/loader.js"type="text/javascript"></script>
<title>OpenLayers3example</title>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varsource=newol.source.Vector({
url:'/data/layers/7day-M2.5.json',
format:newol.format.GeoJSON()
});
varstyle=newol.style.Style({
image:newol.style.Circle({
radius:7,
fill:newol.style.Fill({
color:[0,153,255,1]
}),
stroke:newol.style.Stroke({
color:[255,255,255,0.75],
width:1.5
})
}),
zIndex:100000
});
varselect=newol.interaction.Select({style:style});
varmodify=newol.interaction.Modify({
features:select.getFeatures()
});
varmap=newol.Map({
interactions:ol.interaction.defaults().extend([select,modify]),
target:'map',
layers:[
newol.layer.Tile({
title:'GlobalImagery',
source:newol.source.TileWMS({
url:'http://demo.opengeo.org/geoserver/wms',
params:{LAYERS:'nasa:bluemarble',VERSION:'1.1.1'}
})
}),
newol.layer.Vector({
title:'Earthquakes',
source:source,
style:newol.style.Style({
MoMoworkshop
162Modifyinteraction
image:newol.style.Circle({
radius:5,
fill:newol.style.Fill({
color:'#0000FF'
}),
stroke:newol.style.Stroke({
color:'#000000'
})
})
})
})
],
view:newol.View({
projection:'EPSG:4326',
center:[0,0],
zoom:1
})
});
</script>
</body>
</html>
2. Saveyourchangestomap.htmlandopenthepageinyourbrowser:http://terrestris.github.io/momo3-ws//map.html.Toseefeaturemodificationinaction,usethemouse-clicktoselectanearthquakeandthendragtomovethepoint.
ACloserLook
Let'sexaminehowmodifyingfeaturesworks.
varstyle=newol.style.Style({
image:newol.style.Circle({
radius:7,
fill:newol.style.Fill({
color:[0,153,255,1]
}),
stroke:newol.style.Stroke({
color:[255,255,255,0.75],
width:1.5
})
}),
zIndex:100000
});
varselect=newol.interaction.Select({style:style});
varmodify=newol.interaction.Modify({
features:select.getFeatures()
});
Wecreate2interactions,anol.interaction.Selecttoselectthefeaturesbeforemodifyingthem,andanol.interaction.Modifytoactuallymodifythegeometries.Theysharethesameol.Collectionoffeatures.Featuresselectedusingol.interaction.Modifybecomecandidatesformodificationwiththeol.interaction.Modify.Aspreviously,theol.interaction.Selectisconfiguredwithastyleobject,whicheffectivelydefinesthestyleusedfordrawingselectedfeatures.Whentheuserclicksinthemapagain,thefeaturewillbedrawnusingthelayer'sstyle.
MoMoworkshop
163Modifyinteraction
VectorTopicsAnasideonformatsStylingconceptsCustomfeaturestyles
MoMoworkshop
164VectorTopics
WorkingwithVectorFormatsThebaseol.layer.Vectorconstructorprovidesafairlyflexiblelayertype.Bydefault,whenyoucreateanewvectorlayer,noassumptionsaremadeaboutwherethefeaturesforthelayerwillcomefrom,sincethisisthedomainofol.source.Vector.Beforegettingintostylingvectorfeatures,thissectionintroducesthebasicsofvectorformats.
ol.format
Theol.formatclassesinOpenLayers3areresponsibleforparsingdatafromtheserverrepresentingvectorfeatures.Theformatturnsrawfeaturedataintool.Featureobjects.
Considerthetwoblocksofdatabelow.Bothrepresentthesameol.Featureobject(apointinBarcelona,Spain).ThefirstisserializedasGeoJSON(usingtheol.format.GeoJSONparser).ThesecondisserializedasKML(OGCKeyholeMarkupLanguage)(usingtheol.format.KMLparser).
GeoJSONExample
{
"type":"Feature",
"id":"OpenLayers.Feature.Vector_107",
"properties":{},
"geometry":{
"type":"Point",
"coordinates":[-104.98,39.76]
}
}
KMLExample
<?xmlversion="1.0"encoding="utf-8"?>
<kmlxmlns="http://earth.google.com/kml/2.2">
<Placemark>
<Point>
<coordinates>-104.98,39.76</coordinates>
</Point>
</Placemark>
</kml>
MoMoworkshop
165Formats
UnderstandingStyleWhenstylingHTMLelements,youmightuseCSSlikethefollowing:
.someClass{
background-color:blue;
border-width:1px;
border-color:olive;
}
The.someClasstextisaselector(inthiscaseitselectsallelementsthatincludetheclassname'someClass')andtheblockthatfollowsisagroupofnamedpropertiesandvalues,otherwiseknownasstyledeclarations.
LayerstyleAvectorlayercanhavestyles.Morespecifically,avectorlayercanbeconfiguredwithanol.style.Styleobject,anarrayofol.style.Styleobjects,orafunctionthattakesanol.Featureinstanceandaresolutionandreturnsanarrayofol.style.Styleobjects.
Here'sanexampleofavectorlayerconfiguredwithastaticstyle:
varlayer=newol.layer.Vector({
source:newol.source.Vector(),
style:newol.style.Style({
//...
})
});
Andhere'sanexampleofavectorlayerconfiguredwithastylefunctionthatappliesastyletoallfeaturesthathaveanattributenamedclasswithavalueof'someClass':
varlayer=newol.layer.Vector({
source:newol.source.Vector(),
style:function(feature,resolution){
if(feature.get('class')==='someClass'){
//createstyles...
returnstyles;
}
},
});
SymbolizersTheequivalentofadeclarationblockinCSSisasymbolizerinOpenLayers3(thesearetypicallyinstancesofol.styleclasses).Topaintpolygonfeatureswithabluebackgroundanda1pixelwideolivestroke,youwouldusetwosymbolizerslikethefollowing:
newol.style.Style({
fill:newol.style.Fill({
color:'blue'
}),
stroke:newol.style.Stroke({
color:'olive',
width:1
})
});
MoMoworkshop
166Stylingconcepts
Dependingonthegeometrytype,differentsymbolizerscanbeapplied.Linesworklikepolygons,buttheycannothaveafill.Pointscanbestyledwithol.style.Circleorol.style.Icon.Theformerisusedtorendercircleshapes,andthelatterusesgraphicsfromfile(e.g.pngimages).Hereisanexampleforastylewithacircle:
newol.style.Circle({
radius:20,
fill:newol.style.Fill({
color:'#ff9900',
opacity:0.6
}),
stroke:newol.style.Stroke({
color:'#ffcc00',
opacity:0.4
})
});
ol.style.Style
Anol.style.Styleobjecthas4keys:fill,image,strokeandtext.ItalsohasanoptionalzIndexproperty.Thestylefunctionwillreturnanarrayofol.style.Styleobjects.
Ifyouwantallfeaturestobecoloredredexceptforthosethathaveaclassattributewiththevalueof"someClass"(andyouwantthosefeaturescoloredbluewithan1pxwideolivestroke),youwouldcreateastylefunctionthatlookedlikethefollowing(bytheway,itisimportanttocreateobjectsoutsideofthestylefunctionsotheycanbereused,butforsimplicityreasonstheobjectsarecreatedinlineintheexamplebelow):
varprimaryStyles=[
newol.style.Style({
fill:newol.style.Fill({
color:'blue'
}),
stroke:newol.style.Stroke({
color:'olive',
width:1
})
})];
varotherStyle=[newol.style.Style({
fill:newol.style.Fill({
color:'red'
})
})
];
varotherStyles=[
//defineotherstyleshere
]
layer.setStyle(function(feature,resolution){
if(feature.get('class')==='someClass'){
returnprimaryStyles;
}else{
returnotherStyles;
}
});
Note-Itisimportanttocreatethestylearraysoutsideoftheactualstylefunction.Thestylefunctioniscalledmanytimesduringrendering,andyou'llgetsmootheranimationifyourstylefunctionsdon'tcreatealotofgarbage.
Afeaturealsohasastyleconfigoptionthatcantakeafunctionhavingonlyresolutionasargument.Thismakesitpossibletostyleindividualfeatures(basedonresolution).
Pseudo-classes
MoMoworkshop
167Stylingconcepts
CSSallowsforpseudo-classesonselectors.Thesebasicallylimittheapplicationofstyledeclarationsbasedoncontextsthatarenoteasilyrepresentedintheselector,suchasmouseposition,neighboringelements,orbrowserhistory.InOpenLayers3,asomewhatsimilarconceptishavingastyleconfigoptiononanol.interaction.Select.
Anexampleis:
varselect=newol.interaction.Select({
style:newol.style.Style({
fill:newol.style.Fill({
color:'rgba(255,255,255,0.5)'
})
})
});
Withthebasicsofstylingunderyourbelt,it'stimetomoveontostylingvectorlayers.
MoMoworkshop
168Stylingconcepts
StylingVectorLayers1. We'llstartwithaworkingexamplethatdisplaysbuildingfootprintsinavectorlayer.Openyourtexteditorandsavethefollowing
asmap.htmlintherootofyourworkshopdirectory:
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<title>OpenLayers3example</title>
<scriptsrc="/loader.js"type="text/javascript"></script>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.OSM()
}),
newol.layer.Vector({
title:'Buildings',
source:newol.source.Vector({
url:'/data/layers/buildings.kml',
format:newol.format.KML({
extractStyles:false
})
}),
style:newol.style.Style({
stroke:newol.style.Stroke({color:'red',width:2})
})
})
],
view:newol.View({
center:ol.proj.fromLonLat([-122.79264450073244,42.30975194250527]),
zoom:16
})
});
</script>
</body>
</html>
2. Openthismap.htmlfileinyourbrowsertoseebuildingswitharedoutline:http://terrestris.github.io/momo3-ws//map.html3. WithabasicunderstandingofstylinginOpenLayers,wecancreateastylefunctionthatdisplaysbuildingsindifferentcolorsbased
onthesizeoftheirfootprint.Inyourmapinitializationcode,addthefollowingtwostylesarraysandreplacethestyleoptionforthe'Buildings'layerwiththestylefunctionbelow:
MoMoworkshop
169Customstyles
vardefaultStyles=[
newol.style.Style({
fill:newol.style.Fill({color:'navy'}),
stroke:newol.style.Stroke({color:'black',width:1})
})
];
varsmallStyles=[
newol.style.Style({
fill:newol.style.Fill({color:'olive'}),
stroke:newol.style.Stroke({color:'black',width:1})
})
];
functionstyle(feature,resolution){
if(feature.get('shape_area')<3000){
returnsmallStyles;
}else{
returndefaultStyles;
}
}
4. Saveyourchangesandopenmap.htmlinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
5. Nowasafinalstep,let'saddalabeltothebuildings.Forsimplicitywe'reonlyusingalabelandablackoutlineasthestyle.
style:(function(){
varstroke=newol.style.Stroke({
color:'black'
});
vartextStroke=newol.style.Stroke({
color:'#fff',
width:3
});
vartextFill=newol.style.Fill({
color:'#000'
});
returnfunction(feature,resolution){
return[newol.style.Style({
stroke:stroke,
text:newol.style.Text({
font:'12pxCalibri,sans-serif',
text:feature.get('key'),
fill:textFill,
stroke:textStroke
})
})];
};
})()
6. Saveyourchangesandopenmap.htmlinyourbrowser:http://terrestris.github.io/momo3-ws//map.html
MoMoworkshop
170Customstyles
MoMoworkshop
171Customstyles
CustomBuildsConceptsCreateacustombuild
MoMoworkshop
172CustomBuilds
UnderstandingcustombuildsOpenLayers3isabiglibraryprovidingalotoffunctionality.SoitisunlikelythatanapplicationwillneedanduseallthefunctionalityOpenLayers3provides.Custombuilds(a.k.a.application-specificbuilds)areOpenLayers3buildswithjustthefunctionalityyourapplicationneeds.Custombuildsareoftenmuchsmallerthatthefullbuild,socreatingcustombuildsisoftenaverygoodidea.
Requirements
OpenLayers3buildsarecreatedbyusingtheClosureCompiler.ThegoaloftheClosureCompileristocompileJavaScripttobetterJavaScript,thattakeslesstimetodownoadandrunfaster.
TheClosureCompilerisaJavaprogram,sorunningtheCompilerrequiresaJavaVirtualMachine.Sobeforejumpingtothenextsection,andcreatingacustombuild,makesureJavaisinstalledonyourmachine.
YoujustneedtheJavaRuntimeEnvironment,whichyoucandownloadfromtheOracleJavasite.Forexample,forWindows,youwoulddownloadandinstalljre-8u60-windows-i586.exe.
BuildconfigurationfileCreatingacustombuildrequireswritingabuildconfigurationfile.TheformatofbuildconfigurationfilesisJSON.Hereisasimpleexampleofabuildconfigurationfile:
MoMoworkshop
173Concepts
{
"exports":[
"ol.Map",
"ol.View",
"ol.layer.Tile",
"ol.source.OSM"
],
"jvm":[],
"umd":true,
"compile":{
"externs":[
"externs/bingmaps.js",
"externs/closure-compiler.js",
"externs/esrijson.js",
"externs/geojson.js",
"externs/oli.js",
"externs/olx.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js"
],
"define":[
"goog.array.ASSUME_NATIVE_FUNCTIONS=true",
"goog.dom.ASSUME_STANDARDS_MODE=true",
"goog.json.USE_NATIVE_JSON=true"
],
"jscomp_error":[
"*"
],
"jscomp_off":[
"useOfGoogBase",
"unnecessaryCasts",
"lintChecks"
],
"extra_annotation_name":[
"api","observable"
],
"compilation_level":"ADVANCED",
"warning_level":"VERBOSE",
"use_types_for_optimization":true,
"manage_closure_dependencies":true
}
}
Themostrelevantpartofthisconfigurationobjectistheexportsarray.Thisarraydeclaresthefunctions/constructorsyouuseinyourJavaScriptcode.Forexample,theaboveconfigurationfileiswhatyou'duseforthefollowingJavaScriptcode:
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:[0,0],
zoom:4
})
});
MoMoworkshop
174Concepts
CreatingcustombuildsInthissectionwe'regoingtocreateacustombuildforthemapyoucreatedatthelastchapter.
1. Startwiththemap.htmlfileyoucreatedpreviously:
MoMoworkshop
175Createcustombuilds
<!doctypehtml>
<htmllang="en">
<head>
<linkrel="stylesheet"href="/ol.css"type="text/css">
<style>
#map{
height:256px;
width:512px;
}
</style>
<title>OpenLayers3example</title>
<scriptsrc="/loader.js"type="text/javascript"></script>
</head>
<body>
<h1>MyMap</h1>
<divid="map"></div>
<scripttype="text/javascript">
varstyle=(function(){
varstroke=newol.style.Stroke({
color:'black'
});
vartextStroke=newol.style.Stroke({
color:'#fff',
width:3
});
vartextFill=newol.style.Fill({
color:'#000'
});
returnfunction(feature,resolution){
return[newol.style.Style({
stroke:stroke,
text:newol.style.Text({
font:'12pxCalibri,sans-serif',
text:feature.get('key'),
fill:textFill,
stroke:textStroke
})
})];
};
})();
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.OSM()
}),
newol.layer.Vector({
title:'Buildings',
source:newol.source.Vector({
url:'/data/layers/buildings.kml',
format:newol.format.KML({
extractStyles:false
})
}),
style:style
})
],
view:newol.View({
center:ol.proj.fromLonLat([-122.79264450073244,42.30975194250527]),
zoom:16
})
});
</script>
</body>
</html>
2. Createabuildconfigurationfileforthatmap:
MoMoworkshop
176Createcustombuilds
{
"exports":[
"ol.Map",
"ol.View",
"ol.format.KML",
"ol.layer.Tile",
"ol.layer.Vector",
"ol.proj.fromLonLat",
"ol.source.OSM",
"ol.source.Vector",
"ol.style.Fill",
"ol.style.Stroke",
"ol.style.Style",
"ol.style.Text"
],
"jvm":[],
"umd":true,
"compile":{
"externs":[
"externs/bingmaps.js",
"externs/closure-compiler.js",
"externs/esrijson.js",
"externs/geojson.js",
"externs/oli.js",
"externs/olx.js",
"externs/proj4js.js",
"externs/tilejson.js",
"externs/topojson.js"
],
"define":[
"goog.array.ASSUME_NATIVE_FUNCTIONS=true",
"goog.dom.ASSUME_STANDARDS_MODE=true",
"goog.json.USE_NATIVE_JSON=true",
"ol.ENABLE_DOM=false",
"ol.ENABLE_WEBGL=false",
"ol.ENABLE_PROJ4JS=false",
"ol.ENABLE_IMAGE=false",
"goog.DEBUG=false"
],
"jscomp_error":[
"*"
],
"jscomp_off":[
"useOfGoogBase",
"unnecessaryCasts",
"lintChecks"
],
"extra_annotation_name":[
"api","observable"
],
"compilation_level":"ADVANCED",
"warning_level":"VERBOSE",
"use_types_for_optimization":true,
"manage_closure_dependencies":true
}
}
3. CreatethecustombuildusingOpenLayers'sbuild.jsNodescript:
$nodenode_modules/openlayers/tasks/build.jsol-custom.jsonol-custom.js
Thiswillgeneratetheol-custom.jscustombuildattherootofthetheproject.
4. Nowchangemap.htmltousethecustombuild(ol-custom.js)ratherthanthedevelopmentloader.
Sochange
MoMoworkshop
177Createcustombuilds
<scriptsrc="/loader.js"type="text/javascript"></script>
to
<scriptsrc="/ol-custom.js"type="text/javascript"></script>
Thepageshouldnowloadmuchfasterthanbefore!
MoMoworkshop
178Createcustombuilds
ExtJSWorkshopWelcometotheExtJSWorkshop.ThisworkshopisdesignedtodeliveryouafirstinsightintotheJavaScriptframeworkExtJSfordevelopingwebapplications.Asthisworkshopisfurtherintendedforbeginnerswe'llmainlyfocusonthecoreconceptsandcomponentsdeliveredbyExtJSbysimpletasks.Hencewe'lllearnhowtoincludetheframeworkintoabasicHTMLpage,howtousetheViewport,whatuserinterfacecomponentsExtJSprovidestousandhowtoprogrammaticallyinteractwiththesecomponents.
Thesegoalsaresubdividedintothefollowingsetsofmodules:
IntroductiontoExtJSBasicsLayoutsComponentsDataEvents
Let'sstartwiththeintroductiontoExtJS!
MoMoworkshop
179ExtJS
Introduction
WhatisExtJS?
ExtJSisaJavaScriptbasedapplicationframeworkfordevelopinginteractivecross-platformapplicationsandisaproductofSencha.
Usefulresources
APIdocumentation
ThequantityandqualityoftheExtJSAPIdocumentationisoutstanding.
http://docs.sencha.com/extjs/6.0/6.0.0-classic/
Thedocsprovidealistofallclassesontheleftanddetailsonceyouclickonanyclass.InordertounderstandandmakeuseofExtJS,itiscrucialtofullygraspthedocumentation.
Examples
TheexamplesfortheExtJSframeworkcanbefoundhere:
http://examples.sencha.com/extjs/6.0.0/examples/
AswiththeAPIdocumentation,youmayatfirstbeoverwhelmedatthesheermassesofexamples.Itisnonethelessveryusefultoclickthroughsomeofthem,astheshowhowtocombinetheclassesoftheframeworkintosmallworkingapplications.
Other
Ifyouwanttoquicklychecksomeclass,youcane.g.usetheSenchaFiddlewebsite.Specificquestions(andanswers)canbebrowsedintheSenchaForums.
MoMoworkshop
180Introduction
WorkshopsetupTheworkshoprequiresaminimumofpreliminarywork.Pleasefollowthenextstepstosetupyourworkshopenvironment.
Prepareworkshopfolder
Inthisworkshopwe'regoingtolearnthebasicsofExtJSbytheuseofsimpleconsecutiveexercises,wherewe'regoingtocreate(andsave)HTMLfilesonyourlocalcomputer.Inordertohaveacomparablesetup,atfirstwe'llcreateanappropriateworkshopfolder,wheretoputthesefilesin.
Opentheterminalandnavigatetoyourhomefolderwith:
$cd~
Createanewfoldernamedext-workshopwith:
$mkdirext-workshop
Enterthenewlycreateddirectorywith:
$cdext-workshop
Someexampleswillneedsomeadditionalfiles.Downloadthesefilesbyperformingthefollowingsteps:
Reopentheterminalanddownloadtheworkshopfilesintoyourext-workshopfolderwith:
$wgethttp://terrestris.github.io/momo3-ws/en/extjs/materials.tar.gz
Extractthedownloadedarchivefilewith(thiswillcreateafoldernamedmaterialsinyourworkshopdirectory):
$tar-xvzfmaterials.tar.gz
PreparesimpleworkshopHTTPserver
Forthefirstexerciseswewon'thaveanyneedforservingourcodesnippetsviatheHTTPprotocol,butinthelaterpartswe'regoingtousetechnologiesthatrequirealocalHTMLserver.AverysimplewaytoserveacontentofaspecificdirectoryoverawebserveristousetheSimpleHTTPServermoduleprovidedbyPython.Inthenextfewstepswe'regoingtostarttheSimpleHTTPServerintheworkshopdirectorywhereallupcomingexercisefileswillbeservedovertheHTTPprotocol.
(optional)Opentheterminalandnavigatetoyourworkshopfolderwith:
$cd~/ext-workshop
RuntheSimpleHTTPServerwith:
$python-mSimpleHTTPServer
Thisshouldgiveyouthefollowingoutputmeaningthatthefilesofthecurrentdirectoryareservedthroughportnumber8000.
ServingHTTPon0.0.0.0port8000...
Finallyopenawebbrowserandnavigatetohttp://0.0.0.0:8000whichshouldgiveyoualistingofallavailablefilesintheservedwebdirectory.
MoMoworkshop
181WorkshopSetup
TherunningSimpleHTTPServer
Ifyouwanttoquittheserver,youcaneithersimplyquittheterminalorpressCtrl+C.
MoMoworkshop
182WorkshopSetup
BasicsNowthatourdevelopmentenvironmentisready,it'stimetogetstarted.
Inthischapterwe'lllearnhowto:
CreateasimpleHTMLfileandhowtoembedExtJSCreateyourfirstExtJScomponentCreateandconfiguretheExtJSViewport
MoMoworkshop
183Basics
BasicHTMLfilewithExtJSWe'llstarttheworkshopwithcreatingasimpleHTMLpageandembedtheframeworkintoit.
CreatebasicHTMLpage
Basicallywe'llworkwithasingleHTMLfilewe'llextendgraduallywithineachsectiononly.OurinitialfilewilltherebyonlycontainthebasicHTMLtemplateshowingaheading.Let'screatethefilebytheuseofthe(highlyrecommended)texteditoratom.
Exercise
Opentheterminalandnavigatetoyourworkshopdirectory(ifnotalreadydone)with:
$cd~/ext-workshop
Starttheatomeditorwiththeext-workshopdirectoryasproject:
$atom.
Startviewatomeditor.
Havingatomopenedwecancreateanewfilebyopeningthecontext-menuontheprojectfolderext-workshopandselectingNewFile.
Createanewfilenamedindex.htmlintheexercisedirectoryandcopythecontentofthefollowingbasicHTMLtemplateintoit
MoMoworkshop
184IncludeExtJS
basic-template.html
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>ThisisabasicHTMLtemplate</title>
</head>
<body>
<h1>UsethistemplatetocreateyourownHTMLfiles</h1>
</body>
</html>
Reopenthebrowserand(re-)loadtheURLhttp://0.0.0.0:8000toseethechangestakeeffect:
ThebasicHTMLpage.
IncludeExtJSInthenextstepwe'llinserttwoimportantlinesintotheindex.htmlthatwillautomaticallyincludethefullExtJSlibraryintoourbasicHTMLtemplate.TheExtJScodeitselfisalsoavailableonlineviacdnjs,sowedon'tnecessarilyhavetodownloadtheframeworkcodetoourlocalmachinefirst.Asyouwillseeinthenextfewsteps,the(productiveversionofthe)frameworkconsistsoftwofiles:BothaCSS(CascadingStyleSheets)andaJS(JavaScript)file:
Exercise
Includetheexternalfilesinsidetheendofyour<head>elementofyourindex.html:include-ext-cdnjs.html
MoMoworkshop
185IncludeExtJS
<!--includeaCSSstylesheet-->
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<!--includeanexternalJavaScriptfile-->
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
Again,reloadtheURLinthebrowserandkeeptrackofthechanges:
ThebasicHTMLpageafterincludingExtJS.
Forthisworkshopit'ssatisfyingtoincludethefullbuildsoftheframeworkandtoalwaysloadtheminthehead.Thistechniqueallowsustobasicallyforgetabouttheseresourcesforthecourseoftheworkshop.Foraproductionwebsiteyouwouldprobablyloadthefilesinadifferentmanor,andyouwouldrathernotloadtheversionsofthelibrarieswhichcontaineverything.Butthecreationofspecificversionsofthebaselibrariesthatonlyincludewhatyourapplicationactuallyneeds,iswaybeyondthescopeofthisworkshop.
MoMoworkshop
186IncludeExtJS
ExtJSishere!Havingthelastmodulefinished,wehaveasimpleHTMLpagewithoutanyrealuseoftheExtJSframeworkpresent.We'llcontinuebycreatingaverysimpleExtJScomponenttoverifytheframeworkisreadytoworkwith.
Exercise
(Re-)openyourindex.htmlandreplacethe<h1>elementblockwithinthe<body>tagwiththefollowing<script>block:open-window.html
<scripttype="text/javascript">
Ext.onReady(function(){
Ext.create('Ext.window.Window',{
title:'Hello',
height:200,
width:400,
layout:'fit',
bodyPadding:15,
constrain:true,
html:'ExtJSishere!'
}).show();
});
</script>
Reloadthepageinthebrowserandlookwhat'sgoingtohappen:
HelloExtJS.
So,whathavewedonetocreatethissimpleExtJSwindow?
MoMoworkshop
187HelloExtJS
Thecontentsofthe<scripttype="text/javascript">tagwillbeinterpretedasJavaScriptbythebrowserandanyJavaScriptcodeinit'llberunassoonastheinterpreterseesit.InthenextlinewearefinallygoingtoreallyworkwithExtJS.Extistheglobalnamespacethatencapsulatesallclasses,singletonsandmethodsprovidedbytheframework.Bycallingitontherootscope(astheglobalsingletonobject),wehaveaccesstotheglobalmethodsprovidedbyExtJS.HereweexecutethemethodonReady()whichhasananonymousfunctionasargument.Thisfunctionisbeingprocessedassoonasthedocumentisready(butbeforethedocument'sonloadlistenerandbeforeimagesareloaded).
Asalreadymentioned,theExtJSAPIdocumentationisquitesubstantialandreallyhelpfulwhiledevelopingapplications.PleasetakeyourtimetogetfamiliarwiththedocumentationandstartbyinspectingthedocsforthemethodonReady()usedabovebyfollowingthislink.
IntheanonymousfunctionwepasstotheExt.onReady()methodweexecute-onceagainontheExtglobalobject-themethodcreate().WiththehelpofthismethodweinstantiateaExtJSclass(tobemorespecific:asubclassofExt.Base)byitsfullclassname.HerewecreatetheclassExt.window.Window,which,asyoumaynoticed,isafloating,resizableanddraggablewindowcontainingsimpleHTMLtextascontent.Everycomponenthasaindividualsetofconfigurationparameters(e.g.title),whicharepassedtothecreate()methodasthesecondparameter(andbunchedinanobject).Andoveragain:SeethedocumentationforafulllistofallavailableConfigsforthewindowclass.
MoMoworkshop
188HelloExtJS
ExtJSViewportIncontrasttootherJavaScriptframeworks(e.g.jQuery)ExtJSistypicallyusedtoserveasanintegralframeworkthatisusedtobuildfeature-richsinglepageapplications(SPA)andnotassomekindofan"utility"or"helper"frameworkforisolatedchallenges.ThusyouwouldgenerallynotuseExtJStointegrateasinglewindow(ascreatedintheformersection)inyourexistingwebapplication.
WhendevelopinganExtJSapplication,(oneof)themaincomponentsyou'redealingwith,istheExt.container.Viewport.TheViewportclassrepresentsaspecializedcontainerthatautomaticallyresizesitselftothesizeofthedocumentbodyandthereforetheviewableapplicationarea.FurtheronitautomaticallyresizesduetoresizingthebrowserwindowandwillperformsizingandpositioningonitschildcomponentsasyoucanaddotherExtJSUIcomponentsandcontainerstoit.HowthepositioninginsidetheViewporttakesplaceistherebyconfigurablebyasocalledLayout(seenextchapter).
GiventhattheViewportsizestobrowserwindow,it'sreasonabletohaveasingleviewportperExtJSapplicationonly.
Inthissectionwe'regoingcreateasimpleviewportcontainingasetofnestedchildcomponents.Thisviewportwillthenactasthebasictemplateforanyfurtherexerciseinthisworkshop.
Exercise
(Re-)openyourindex.htmlandreplacethecodecreatingtheExt.window.Windowcomponentwiththefollowingsnippetviewport-simple.js
Ext.create('Ext.container.Viewport',{
defaults:{
bodyPadding:15
},
items:[{
title:'Item1',
html:'Content1'
},{
title:'Item2',
html:'Content2'
},{
title:'Item3',
html:'Content3'
},{
title:'Item4',
html:'Content4'
}]
});
Reloadtheapplicationpageinthebrowserandyou'llnoticethefirstelementaryindicationsofasimplefull-screenwebapplication(Trytoresizethebrowserwindow!):
MoMoworkshop
189Viewport
Simpleviewport.
IntheaboveexampleweusedthemethodExt.create()toinstantiatethecomponentExt.container.Viewportverysimilartothepreviousexample.Theresultisastackoffourcomponentscomposedofatitleandahtmlvalue.PerdefaultswedeclarethateachdirectchildcomponentintheViewport-configuredasitem-shouldberenderedwithabodyPaddingof15pixelsadditionally.
Exercise
UsetheExtJSAPIdocumentationfortheViewportclass(here)toanswerthisquestion:IcreatedanExtJSviewportwithacoupleofitemswithoutanyspecificconfigurationsexcepttitleandhtml.Athisjuncturethehtmlparameterincludesaplentyoftextcontent.StrangelyIcouldnotseeanyscrollbars,ifthebrowserwindowgetsresized.Whatiswronghere?
MoMoworkshop
190Viewport
LayoutsAnExtJSapplication(UI)isalwaysmadeupofsinglecomponentsbasedonthebaseclassExt.Components.TheViewportclasswehaveseeninthepreviouschapterissomekindofaspecialtypeofacomponentasitmaycontainothercomponents.OnceExtJScomponentsareinsertedintoasuperiorcontainer,itslayoutpropertiesmustbedefined(accordingtotherequirementbyyourapplication).
Componentarchitecture,source:https://docs.sencha.com/extjs/6.0/core_concepts/images/component_architecture.png
TheLayouttellsthissuperiorcontainer(e.g.theViewport)howtoproperlyarrangeitschildcomponents(e.g.Panel)insizingandpositioning.Asyoumaycorrectlynote,therecentexamplewewereusinghasn'tanyspecificlayoutset,butcouldberenderedinthebrowser.ThishappensbecausethedefaultlayoutforallcontainersisthelayouttypeAutoandthislayoutdoesnotspecifyanyspecialpositioningorsizingrulesforchildelements.ItsimplyrendersthechilditemsasnormalblockelementsintheDOM.
GenerallythelayoutofaContainerhasthebesetviathelayoutconfigurationattribute.Inmostcasesit'ssatisfactorytosetthenameoftherequestedlayoutasasimplestring(e.g.'auto')only,buttherearelayoutsavailablewhereafullobject,specifyingthelayoutoptionsinmoredetail,areallowed.Furthermoreseverallayoutsholdparticularattributesrelatedtothechildcomponentsofthecontainerspecifyinge.g.it'sinnerpositionorsize.
Inthissectionwe'regoingtohaveaquicklooktosomeofthepredefinedlayoutsExtJSprovidestous.Herewefocusonthefollowinglayouts:
ThedescriptionsgivenintheupcomingsubsectionsarebasedontheAPIdocumentation.
ColumnHBoxVBoxAccordionTableBorder
ForafulllistofalllayoutshavealookattheAPIdocumentationortheKitchenSink.
MoMoworkshop
191Layouts
ColumnTheColumnlayoutisthelayoutstyleofchoiceforcreatingstructurallayoutsinamulti-columnformatwherethewidthofeachcolumncanbespecifiedasapercentageorfixedwidth,buttheheightisallowedtovarybasedonthecontent.
Thelayoutdoesnothaveanydirectconfigoptions,butitdoessupportaspecificconfigpropertyofcolumnWidththatcanbeincludedintheconfigofanypaneladdedtoit.ThelayoutwillusethecolumnWidth(ifpresent)orwidthofeachpanelduringlayouttodeterminehowtosizeeachpanel.IfwidthorcolumnWidthisnotspecifiedforagivenpanel,itswidthwilldefaulttothepanel'swidth(orauto).
Thewidthpropertyisalwaysevaluatedaspixelsandmustbeanumbergreaterthanorequalto1.ThecolumnWidthpropertyisalwaysevaluatedasapercentageandmustbeadecimalvaluegreaterthan0andlessthan1(e.g..25).
Exercise(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-column.js
Ext.create('Ext.container.Viewport',{
layout:'column',
defaults:{
bodyPadding:15,
},
items:[{
title:'Item1',
columnWidth:0.3,
html:'Content1'
},{
title:'Item2',
columnWidth:0.2,
html:'Content2'
},{
title:'Item3',
columnWidth:0.2,
html:'Content3'
},{
title:'Item4',
columnWidth:0.3,
html:'Content4'
}]
});
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
192Column
Columnlayout.
MoMoworkshop
193Column
HBoxTheHBoxlayoutsarrangesitemshorizontallyacrossthecontainer.Thislayoutoptionallydividesavailablehorizontalspacebetweenchilditemscontaininganumericflexconfiguration.
Thislayoutmayalsobeusedtosettheheightsofchilditemsbyconfiguringitwiththealignoption.Additionallyyoucanspecifyhowthechilditemsofthecontainerarepackedtogetherbysettingthepackoption.
Exercise
(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-hbox.js
Ext.create('Ext.container.Viewport',{
layout:{
type:'hbox',
pack:'start',
align:'stretch'
},
defaults:{
bodyPadding:'1015',
margin:5,
flex:1,
},
items:[{
title:'Item1',
html:'Content1'
},{
title:'Item2',
flex:1.5,
html:'Content2'
},{
title:'Item3',
html:'Content3'
},{
title:'Item4',
html:'Content4'
}]
});
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
194HBox
HBoxlayout.
MoMoworkshop
195HBox
VBoxTheVBoxlayoutsarrangesitemsverticallyacrossthecontainer.Thislayoutoptionallydividesavailableverticalspacebetweenchilditemscontaininganumericflexconfiguration.
Thislayoutmayalsobeusedtosetthewidthsofchilditemsbyconfiguringitwiththealignoption.Additionallyyoucanspecifyhowthechilditemsofthecontainerarepackedtogetherbysettingthepackoption.
Exercise
(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-vbox.js
Ext.create('Ext.container.Viewport',{
layout:{
type:'vbox',
pack:'start',
align:'stretch'
},
defaults:{
bodyPadding:'1015',
margin:5,
flex:1,
},
items:[{
title:'Item1',
html:'Content1'
},{
title:'Item2',
flex:1.5,
html:'Content2'
},{
title:'Item3',
html:'Content3'
},{
title:'Item4',
html:'Content4'
}]
});
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
196VBox
VBoxlayout.
MoMoworkshop
197VBox
AccordionTheAccordionlayoutisalayoutthatmanagesmultiplePanelsinanexpandableaccordionstylesuchthatbydefaultonlyonepanelcanbeexpandedatanygiventime(setmulti-configtohavemoreopen).EachPanelhasbuilt-insupportforexpandingandcollapsing.
OnlypanelsandallsubclassesofExt.panel.Panelmaybeusedinanaccordionlayoutcontainer.
Exercise
(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-accordion.js
Ext.create('Ext.container.Viewport',{
layout:'accordion',
defaults:{
bodyPadding:15,
border:false
},
items:[{
title:'Item1',
html:'Content1'
},{
title:'Item2',
html:'Content2'
},{
title:'Item3',
html:'Content3'
},{
title:'Item4',
html:'Content4'
}]
});
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
198Accordion
Accordionlayout.
MoMoworkshop
199Accordion
TableTheTablelayoutallowsyoutoeasilyrendercontentintoanHTMLtable.Thetotalnumberofcolumnscanbespecified,androwspanandcolspancanbeusedtocreatecomplexlayoutswithinthetable.
InthecaseofTablelayout,theonlyvalidlayoutconfigpropertiesarecolumnsandtableAttrs.However,theitemsaddedtoalayoutcansupplytheconfigpropertiesrowspan(thenumberofrowsthatthespannedcellneedstocover),colspan(thenumberofcellsthatthecellshouldreplace)andcellCls(aCSSclassnameaddedtothetablecellcontainingtheitem).
ThebasicconceptofbuildingupaTablelayoutisconceptuallyverysimilartobuildingupastandardHTMLtable.Yousimplyaddeachpanel(or"cell")thatyouwanttoincludealongwithanyspanattributesspecifiedasthespecialconfigpropertiesofrowspanandcolspanwhichworkexactlyliketheirHTMLcounterparts.RatherthanexplicitlycreatingandnestingrowsandcolumnsasyouwouldinHTML,yousimplyspecifythetotalcolumncountinthelayoutconfigandstartaddingpanelsintheirnaturalorderfromlefttoright,toptobottom.Thelayoutwillautomaticallyfigureout,basedonthecolumncount,rowspansandcolspans,howtopositioneachpanelwithinthetable.
JustlikewithHTMLtables,yourrowspansandcolspansmustaddupcorrectlyinyouroveralllayoutoryou'llendupwithmissingand/orextracells!
Exercise
(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-table.js
Ext.create('Ext.container.Viewport',{
layout:{
type:'table',
columns:3,
tableAttrs:{
style:{
width:'100%'
}
}
},
defaults:{
bodyPadding:15,
},
items:[{
title:'Item1',
rowspan:1,
colspan:1,
html:'Content1'
},{
title:'Item2',
rowspan:1,
colspan:1,
html:'Content2'
},{
title:'Item3',
rowspan:1,
colspan:1,
html:'Content3'
},{
title:'Item4',
colspan:2,
rowspan:1,
html:'Content4'
}]
});
MoMoworkshop
200Table
Reloadthepageinthebrowserandtakealookattheresult:
Tablelayout.
MoMoworkshop
201Table
BorderTheBorderlayoutisamulti-pane,application-orientedUIlayoutstylethatsupportsmultiplenestedpanels,automaticbarsbetweenregionsandbuilt-inexpandingandcollapsingofregions.
Whenusingthislayoutnote,thatanycontainerusingtheborderlayoutmusthaveachilditemwithregion:'center'.Thischilditeminthecenterregionwillalwaysberesizedtofilltheremainingspacenotusedbytheotherregionsinthelayout.Anychilditemswitharegionofwestoreastmaybeconfiguredwitheitheraninitialwidth,flexoraninitialpercentagewidthvalue.Anychilditemswitharegionofnorthorsouthmaybeconfiguredwitheitheraninitialheight,flexvalueoraninitialpercentageheightvalue.
Exercise
(Re-)openyourindex.htmlandupdatethecodecreatingtheExt.container.Viewportcomponenttomatchthefollowingsnippet:
layout-border.js
Ext.create('Ext.container.Viewport',{
layout:'border',
defaults:{
bodyPadding:15,
collapsible:true,
split:true
},
items:[{
title:'Item1',
region:'north',
height:100,
html:'Content1'
},{
title:'Item2',
region:'east',
width:150,
html:'Content2'
},{
title:'Item3',
region:'west',
width:150,
html:'Content3'
},{
title:'Item4',
region:'center',
html:'Content4'
}]
});
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
202Border
Borderlayout.
MoMoworkshop
203Border
ComponentsComponentsarereferredtotheExtJSclassExt.Componentwhichisthebaseclassforallcomponents.GenerallyspeakingacomponentitselfisapredefinedExtJScompatiblemodulecomposedofHTML,CSSandJavaScript.IntheformerexerciseswealreadymetthecomponentsExt.window.Window,Ext.container.ViewportandExt.panel.Panel(whereasthelatternotexplicit,butit'sthedefaultcomponentintheViewportcontainer).
EveryComponenthasashorthandnamecalledxtype.Thextypeisespeciallyusefulifyouwanttorenderyourapplicationlazily,thatmeansrenderingyourcomponentsatthetimethey'regettingmeaningfulforyourapplication,e.g.creatinganerrormessageatthetimeanerroroccurred.Intheupcomingexampleswe'llusethextypetocreatecomponents.
Atypicalapplication'scomponenthierarchystartswithaviewportatthetop,whichhasothercontainersand/orcomponentsnestedwithinit.
Thecomponenthierarchy,source:https://docs.sencha.com/extjs/6.0/core_concepts/images/component_heirarchy_5.png
Intheupcomingsectionwe'regoingtoinspectsomecomponentsthatmightbeusefulforanyExtJSapplicationyour'regoingtodevelopinthefuture:
ThedescriptionsgivenintheupcomingsubsectionsarebasedontheAPIdocumentation.
PanelImageFormTreeGrid
MoMoworkshop
204Components
PanelAPanelisacontainerdesignedforbuildingstructuredblocksforapplicationorienteduserinterfaces.Panelsare,bytheirinheritancefromExt.container.Container,capableofbeingconfiguredwithalayout(seepreviouschapter)andcontainingchildcomponents.Panelsalsoprovidebuilt-incollapsible,expandableandclosablebehaviorandcanbeeasilydroppedintoanycontainerorlayout,whereasthelayoutandrenderingiscompletelymanagedbytheframework.
Inmostapplicationsthepanelisoneofthemostoftenusedcomponents.Inthenextexercisewe'llextentourexistingviewportbyonlyafewconfigurationsandwillsee,thatwehaveworkedwithpanels(evenwehaven'tspecifiedit)yet.
Exercise
(Re-)openyourindex.htmlandextendtheExt.container.Viewporttomatchthefollowingsnippet:component-panel.js
Ext.create('Ext.container.Viewport',{
layout:'border',
defaults:{
xtype:'panel',
bodyPadding:15,
collapsible:true,
split:false,
margin:5
},
items:[{
region:'north',
collapsible:false,
height:60,
border:false,
html:'Content1'
},{
title:'Item2',
region:'east',
width:'20%',
html:'Content2'
},{
title:'Item3',
region:'west',
width:'20%',
html:'Content3'
},{
region:'center',
collapsible:false,
html:'Content4'
},{
title:'Item5',
region:'south',
maxHeight:350,
collapsed:true,
html:'Content5'
}]
});
MoMoworkshop
205Panel
AdvancedBorderlayout.
Asyoumaynotice,it'shardlytospotanyviewabledifferencetoourpreviousexample.Buthavealookatthedefaultsattributesetintheviewport.Itcontainsanewkeynamedxtype(remember:it'stheshorthandnameforacomponent)withthevaluepanel.Thuseverydirectchildintheviewportwillbeinstantiatedasapanel.
AdvancedpanelconfigurationAPanelmayalsocontainbottomandtoptoolbars,alongwithseparateheader,footerandbodysections.
Exercise
(Re-)openyourindex.htmlandextendthepanelrenderedintheviewportscenterbythefollowingsnippet:component-panel-toolbar.js
tbar:[{
xtype:'button',
text:'Button1',
iconCls:'fafa-repeat'
}]
Reloadthepageinthebrowserandtakealookattheresult:
Paneltoolbar.
MoMoworkshop
206Panel
NestedcomponentsTheExt.ImgcomponentcanbeusedtoinsertanimageintotheExtJShandledlifecycle.Forexampletheclassmakesiteasytochangethesourceoftheimagecontainer.
Exercise
(Re-)openyourindex.htmlandextendtheExt.container.Viewportitemsbythefollowingsnippet:component-image.js
{
region:'north',
collapsible:false,
height:60,
border:false,
bodyPadding:5,
items:[{
xtype:'image',
src:'./materials/ext-logo.png',
height:50
}]
}
Reloadthepageinthebrowserandtakealookattheresult:
Nestedcomponentimageinapanel.
MoMoworkshop
207Image
FormFieldsTheExt.form.Panelpresentsasubclassofthepanelandisespeciallyusefulforbuildinguserinteractionwebformsandforsavingandloadingremotedata.UsuallyyoucombineaformpanelwithsubclassesinheritedfromtheExt.field.Fieldclass.Inthefollowingexamplewe'llgettoknowsomeofthemostimportantfieldsonewoulduseinaform(listedwithxtypesandlinkstotheAPIdocumentation):
textfielddisplayfieldnumberfieldcomboboxcheckboxdatepickersliderfilefieldbutton
Exercise
(Re-)openyourindex.htmlandextendthepanelintheviewport'seastregionbythefollowingsnippet:component-form-fields.js
MoMoworkshop
208Form
{
xtype:'form',
title:'FormPanel',
region:'east',
width:'20%',
autoScroll:true,
defaults:{
anchor:'100%'
},
items:[{
xtype:'textfield',
name:'text',
fieldLabel:'Text',
emptyText:'Enteratext'
},{
xtype:'displayfield',
name:'status',
fieldLabel:'Status',
value:'<spanstyle="color:green;">OK</span>'
},{
xtype:'numberfield',
name:'number',
fieldLabel:'Number',
emptyText:'Enteranumber',
minValue:0,
maxValue:99
},{
xtype:'combo',
name:'combo',
fieldLabel:'Combo',
emptyText:'Selectfromlist',
minValue:0,
maxValue:99,
store:[
'Entry1',
'Entry2',
'Entry3'
]
},{
xtype:'checkbox',
name:'check',
fieldLabel:'Check'
},{
xtype:'datefield',
name:'dateField',
fieldLabel:'DateField'
},{
xtype:'slider',
name:'slider',
fieldLabel:'Slider',
minValue:0,
maxValue:100,
value:25
},{
xtype:'filefield',
name:'upload',
fieldLabel:'Upload'
}]
}
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
209Form
Nestedcomponentimageinapanel.
Asstatedabove,theformisveryusefulifyouwanttosystematicallyreadoutvaluesgivenbytheuserandtoworkwiththemafterwards,e.g.sendingthevaluestoaserverendpoint.Inthenextexamplewe'regoingtocreateanotherusefulformcomponent,theExt.form.FieldSetclass.Thefieldsetisaspecializedcontainerforgroupingfields.Wewe'llnowcreateafieldsetwithatextareaandabutton(ignorethehandlermethodforthemoment,we'llexplaineventsandcomponentqueryinglateron).
Addthefollowingfieldsettothelowerendoftheformfieldwedeclaredabove:
{
xtype:'fieldset',
title:'Inputdata',
layout:'fit',
items:[{
xtype:'textarea',
height:180,
isFormField:false
},{
xtype:'button',
text:'Readinputdata',
handler:function(btn){
varform=btn.up('form'),
textArea=form.down('textarea');
textArea.setValue(
JSON.stringify(
form.getValues(),null,4
)
);
}
}]
}
Reloadthepageinthebrowser,entersomecustomvaluesintheformfieldandpressthebuttonReadinputdata:
MoMoworkshop
210Form
Nestedcomponentimageinapanel.
MoMoworkshop
211Form
TreesTheExt.tree.Panelclassprovidesatree-structuredUIrepresentationoftree-structureddata.AtreepanelmustbeboundtoaExt.data.TreeStore(theExtJSdatapackageincludingstoreswillbehandledinthenextchapter).
Exercise
(Re-)openyourindex.htmlandextendtheExt.container.Viewportitemsbythefollowingsnippet:component-treepanel.js
{
xtype:'treepanel',
width:'20%',
bodyPadding:0,
title:'TreePanel',
region:'west',
rootVisible:false,
store:{
data:{
text:'Root',
children:[{
text:'Child1',
leaf:true
},{
text:'Child2',
leaf:true
},{
text:'Child3',
leaf:true
},{
text:'Child4',
children:[{
text:'GrandChild1',
leaf:true
},{
text:'GrandChild2',
leaf:true
}]
}]
}
}
}
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
212Tree
Treepanelwithdummyitems.
MoMoworkshop
213Tree
GridTheExt.grid.Panelclassisofavailforshowingup(largeamounts)oftabulardata.Therequiredpropertiesofagridpanelareastoreandacolumndefinition.
Exercise
(Re-)openyourindex.htmlandextendtheExt.container.Viewportitemsbythefollowingsnippet:component-grid.js
{
xtype:'gridpanel',
title:'GridPanel',
region:'south',
bodyPadding:0,
maxHeight:350,
collapsed:true,
columns:[{
text:'Firstname',
dataIndex:'firstName',
flex:1
},{
text:'Lastname',
dataIndex:'lastName',
flex:1
},{
text:'Instruments',
dataIndex:'instruments',
flex:1
}],
store:{
data:[{
firstName:'Angus',
lastName:'Young',
instruments:'Guitar'
},{
firstName:'Cliff',
lastName:'Williams',
instruments:'Bassguitar,vocals'
},{
firstName:'Brian',
lastName:'Johnson',
instruments:'Vocals'
},{
firstName:'Stevie',
lastName:'Young',
instruments:'Guitar,vocals'
},{
firstName:'Chris',
lastName:'Slade',
instruments:'Drums,percussion'
}]
}
}
Reloadthepageinthebrowserandtakealookattheresult:
MoMoworkshop
214Grid
Gridpanelwithsomeinlinedata.
FinaloutputFinallyyourExtJSapplicationshouldlooksimilartothis:
Finalapplicationlayout.
MoMoworkshop
215Grid
DataNowwe'vebecameacquaintedwithoneofmostimportantExtJSvisualcomponents,we'regoingtolearnhowonecouldloadremotedataintotheexistingwebapplicationwithoutreloadingthepageitself.Forexamplethismightbeinterestedforyouifyouwanttoimplementapagingfunctionalitytothegrid.Inthiscontextwe'llgetintouchwiththeExtJSdatapackagethatisresponsibleforloading(andsaving)allofthedataintheapplication.Thepackageconsistsofmultipleclasses,buttherearethreethataremoreimportantthanalltheothers:TheExt.data.Model,Ext.data.StoreandExt.data.proxy.Proxy(sub-)classes.
TheExtJSdatapackage,source:http://docs.sencha.com/extjs/6.0/core_concepts/images/data-model.png
Intheforthcomingexerciseswe'regoingtorecreateagridpanelinthecenterofourborderlayoutthat'llcontainanExt.data.StorereadingremotedatawiththeuseofanExt.data.proxy.Ajaxproxy.ThestorewillbeassociatedwithanExt.data.Model.
PreparationModelProxyandstore
MoMoworkshop
216Data
PreparationLet'sstartthissectionbycreatinganothergridpanelinthecenterofourborderlayout.Herewe'regoingtoreplacetheexistingthepanelwiththegridpanelbykeepingthetoolbar.Thetoolbar(anditsbutton)willbeneededinthenextmodule.Thecolumnsdefinitionwillstayunaffectedincomparisontothegridpanelwecreatedintherecentexercise.
Exercise
(Re-)openyourindex.htmlandfindthedeclarationofthecenterregion.Replacethepanelintheborderlayoutscenterwithagrid,butleavethetoolbarandsetthestoretonull(forthemoment).
data-grid.js
{
xtype:'gridpanel',
title:'GridPanelwithremotestore',
region:'center',
collapsible:false,
bodyPadding:0,
columns:[{
text:'Firstname',
dataIndex:'firstName',
flex:1
},{
text:'Lastname',
dataIndex:'lastName',
flex:1
},{
text:'Instruments',
dataIndex:'instruments',
flex:1
}],
store:null,
tbar:[{
xtype:'button',
text:'Button1',
iconCls:'fafa-repeat'
}]
}
Reloadthepageinthebrowserandverifytheemptygridpanelinthelayoutscenter:
MoMoworkshop
217Preparation
Thenewgridpanel.
MoMoworkshop
218Preparation
ModelThecoreofthedatapackageistheExt.data.Modelclass.AModelorEntityrepresentssomeobjectthatyourapplicationmanages,e.g.the(former)membersofarockband.Modelsareusedbystores,whichareinturnusedbymanyofthedata-boundcomponentsinExtJS.Themostsignificantparts(orproperties)ofamodelareFields(theyhandlethemembersofamodel),Proxies(theyhandletheloadingandsavingofmodeldata),Validations(theyhandlevalidationofthedata,e.g.ifafieldhasnot-nullvalue)andAssociations(theyhandletherelationsandlinkagestoothermodelinstances).
PartsoftheExtJSmodelclass,source:http://docs.sencha.com/extjs/6.0/core_concepts/images/model-breakdown.png
Inthisexercisewe'llbuildupasimplemodel,that'llcontainsome(string)fieldsandsimplevalidationforinputdata(ensuringthatallfieldshaveavalue).Asweonlyhavethissinglemodelwedon'twanttomodelanyassociations.PleaserefertotheAPIdocumentationforfurtherdetails.Youbothhavethepossibilitytoassigntheproxyinthemodelorthestore(usingthatmodel).Bothwaysdohaveadvantagesdependingonyourapplicationsetup:Ifyousettheproxyinthemodelitallowsyoutoloadandsaveinstancesofthismodelwithouttheneedofastoreandmultiplestorescouldusethesamemodel.Incontrastdefiningtheproxyinthestoreitallowsyoutousethesamedatamodelinmultiplestores,evenifthestoreswillloadtheirdatafromdifferentsources.Inthisexercisewe'regoingtosettheproxyinthestore(withoutanyspecificreason).
Exercise
(Re-)openyourindex.htmlandinsertthefollowingcodebeforetheinstantiationoftheviewport(line~15)tocreateanewmodelcalledFormerMembers:
data-model.js
MoMoworkshop
219Model
Ext.define('FormerMembers',{
extend:'Ext.data.Model',
fields:[{
name:'firstName',
type:'string'
},{
name:'lastName',
type:'string'
},{
name:'instruments',
type:'string'
}],
validators:{
firstName:'presence',
lastName:'presence',
instruments:'presence'
}
});
MoMoworkshop
220Model
ProxyandstoreAstoretakescareoftheclientsidecachingofmodelobjectsandcanbeconfiguredtoloaddataviaaproxy.Theyprovidedifferentfunctionsforaccessingtheunderlyingmodelinstances(e.g.sorting,filteringorquerying).Toloadandsaveinstancesastoreusesaproxy.Generallyspeakingthedatasourcecanbeeitherlocal(clientproxy)orremote(serverproxy),whereastheclientproxiesload/savetheirdatalocallyandtheserverproxiesload/savetheirdatabysendingrequeststoaremoteserver.
Inthefollowingexercisewe'llcreateanewExt.data.StorereadingdatawithaExt.data.proxy.Ajaxfromaremotesource(Theremotesourceisthepythonserveroursimplewebapplicationliveson,butbeawarethatitcouldbeanyotherremotesource).
Exercise
(Re-)openyourindex.htmlandinsertthefollowingcodeaftertheinstantiationofthemodelFormerMembers(line~34)tocreatethestore.
data-store.js
Ext.create('Ext.data.Store',{
autoLoad:true,
storeId:'formerMembers',
model:'FormerMembers',
proxy:{
type:'ajax',
url:'./materials/former-members.json',
reader:{
type:'json',
rootProperty:'data'
}
}
});
Ifyouwouldreloadthepagenow,youwouldn'tbeabletoseeanychangesinthecenteredgridpanelasthenewlycreatedstoreisn'tregardedasbeingexistenttothegrid.
Findthedeclarationofthegridpanelandupdatethestorepropertyto:
store:'formerMembers'
Finallyreloadthepageandviewtheresults.
MoMoworkshop
221Proxyandstore
Gridwithremotedataloaded.
MoMoworkshop
222Proxyandstore
EventsInExtJSeventsaresignalsfromaclassthatarefiredifanythinghappenedtoaclass.EventsarefiredgloballyintheExtnamespacesothateveryclasscanlistentothose.InourExtJSapplicationwecanusetheseeventstocodespecificreponsesthatwillbeexecutedifacertaineventisbeingfired.NearlyallExtJScomponentsandclassesfiredifferentkindsofeventsattheirlifecycle.Forexample,eachclassinheritedfromExt.Componentfirestheeventaddedafterthecomponenthadbeenaddedtoacontainer.Wecanlistenforthateventbyconfiguringasimplelistenersobject.
Inthissectionwe'llgettoknowthreeeventsfiredbydifferentcomponentstogetabasicideaaboutthepotentialbehindeventsandlisteners.
EventclickEventafterrenderEventchange
MoMoworkshop
223Events
EventclickTheclickeventisbeingfiredwhene.g.abuttonoramenuentryisclicked.
Inthefollowingexercisewe'llusetheclickeventofthebuttonrenderedtothetoolbarinthecenteredpaneltoloadtheremotedataifthebuttonisclickedbytheuser.
Exercise
(Re-)openyourindex.htmlandfindtheinstantiationofthestoreformerMembers(line~29)weintroducedintheformermodule.SetautoLoad:falseinthestore.
Afterreloadingthepageyoushouldnoticethatthegridcontainsnodataanymore.Canyouexplainwhy?
Inthenextfewstepswe'regoingtofetchbackourmissingdatabyreusingthealreadyexistingbuttoninthetoolbarasaLoaddatabutton:
Findthebuttondeclarationwithinthepanelinthecenterregionandrenameitaccordingly(text:'Loaddata').Registeranewlistenertotheclick-eventandpassananonymousfunctiontoit.Thisfunctionwillbecallediftheclickeventisfiredbythebuttonclass:
event-click.js
listeners:{
click:function(btn){
vargridpanel=btn.up('gridpanel');
gridpanel.getStore().load();
}
}
Again,reloadthepageinthebrowserandclicktheupdatedLoaddatabutton.
Loadremotedata`onClick`.
Dissectingtheexample
MoMoworkshop
224Eventclick
Let'shaveamoredetailedlookatthefunctionwepassedtotheclicklistener:
function(btn){...}
Everyeventcanbefiredwithoptionalargumentspassedtothelistener.Here,ouranonymoushandlerfunctionreceivestheargumentbtn,whereatthevariablebtholdsareferencetothebuttoninstancefiringtheclickevent("theclickedbutton").
vargridpanel=this.up('gridpanel');
Remember,wearedealingwithhierarchicallystructuredcomponents.ExtJS(internally)registersallinstantiatedcomponentsinitsExt.ComponentManager.Withinthismanagerwecannavigateacrossandsearchtheapplicationcomponentcomposition.TheexplanationofthemanagerandthecorrespondingExt.ComponentQuerysingletonisfarbeyondthegoalsofthisworkshop,butit'sveryrecommendedtohavealookattheverydetaileddocumentation.Longstoryshort:Eachcomponentprovidesusthemethodup()andeachcontainerthemethodsup(),down()andquery()tosimplynavigateacrossthecomponenthierarchybytheuseofverysimplefilterexpressions.Theeasiestwayonecanthinkofisbuildafilterthatreturnsthefirstxtypeinthelower/upperhierarchylevelofagivencomponent.Havingthisinmind,theuppermethodwillreturnthefirstcomponentofxtypegridpanelintheupperdirectionbasedonthepressedbutton.
gridpanel.getStore().load();
Nowwegotthegridpanel,wecanaccesstheunderlyingstorebyusingthegetStore()methodanddirectlyexecutethemethodload()toloadanylocalorremotedataassociatedwiththestore.
MoMoworkshop
225Eventclick
EventafterrenderTheafterrendereventisbeingfiredafteracomponentisfinallyrendered(totheDOM)andisveryoftenusedifyouwanttomakesure,yourlistenerfunctioniscalledafterthecomponentisrendered.
Inthefollowingexercisewe'llregisteranotherlistenertothebuttonLoaddatathatwillshowupaminimalisticmessagebox(Ext.toast)totheuserafterthebuttonhasbeenrendered.
Exercise
(Re-)openyourindex.htmlandfindthebuttondeclarationwithinthepanelinthecenterregionandweusedintheformerexercise.Registeralistenerfortheeventafterrenderbyappendingthefollowingcodeblocktoitslistenersarray:
event-afterrender.js
afterrender:function(cmp){
Ext.toast({
html:'Click<code>Loaddata</code>!',
title:'Hint',
align:'t',
});
}
Andagain,reloadthepageinthebrowserandyouwillseethetoast.
AsimpleExt.toast.
Dissectingtheexample
Let'shaveamoredetailedlookatthefunctionwepassedtotheafterrenderlistener:
function(cmp){...}
Asalreadyillustrated,eventscanholdextraargumentswhichwillbereceivedinthelistenerfunctionsandsimiliartotheclickevent,theafterrendereventpassesareferencetotherenderedcomponenttotheanonymousfunction.Thevariablecmpthereforereferstothebuttonitself(butisnotusedinourfunction).
MoMoworkshop
226Eventafterrender
Ext.toast({
html:'Click<code>Loaddata</code>!',
title:'Hint',
align:'t',
});
TheExt.toastclassprovidesalightweight,auto-dismissingpop-upnotificationsandisconfigurablebytheuseofaconfigurationobject.Herewesetboththehtmlandtitlekeysforasimplemessageandtitleaswellasthealignkeyforspecifingthealignmentofthetoastmessagetothetopofitsanchor(theviewport).
MoMoworkshop
227Eventafterrender
EventchangeThechangeeventisbeingfiredwhene.g.afileinputfield'svaluehaschanged.
Inthis(final)exercisewe'lladdanewtextfieldtothegridpanelstoolbarinvolvingafilterfunctionthatiscalledoneverychangemadebytheusertothetextfield.
Exercise
(Re-)openyourindex.htmlandfindthetoolbardeclarationinsidethegridpanelrenderedtothecenterregion.
AtfirstwewillmakeuseoftheclassExt.toolbar.Filltoaddanon-renderingplaceholderitemtothetoolbarwherebyallfollowingitemswillbealignedtotherightofthetoolbar.(Otherusefultoolbaritemsyoumayinterestedinaretbseparatorandtbspacer.)
Addtheplaceholdertotheendofthetoolbarbyinsertingthefollowingdeclaration:
{
xtype:'tbfill'
}
Nextwe'lladdatextfieldwiththechangelistenertothetoolbar:event-change.js
{
xtype:'textfield',
emptyText:'FindbyFirstname',
listeners:{
change:function(field,newValue,oldValue){
field.up('gridpanel').getStore().filter({
property:'firstName',
value:newValue||'',
anyMatch:true,
caseSensitive:false
});
}
}
}
Reloadthepageinthebrowserandyoushouldnoticeanewtextfieldintheupperrightofthecenteredgridpanel.
Filtertextfield.
Tryoutthenewlycreatedfilterbyloadingdataintothegridandchangingthetextfieldsvalue.
MoMoworkshop
228Eventchange
Filtertextfield.
Dissectingtheexample
Let'shaveamoredetailedlookatthefunctionwepassedtothechangelistener:
function(field,newValue,oldValue){...}
Ifthevalueofafieldischanged,ouranonymousfunctionisbeingcalledwithpassedargumentsfield,newValueandoldValue.fieldholdsareferencetothetextfielditself,newValuethechanged/prevailingandoldValuetheoriginal/precedingvalue.
field.up('gridpanel').getStore()
Again,weusethemethodsup()togetareferencetothegridpanelandgetStore()togettheassociatedstore(seeeventclickforthedetailedinformation).
.filter({...})
Withthestoreinhandwecanaccessallmethodsprovidedbythe(instantiated)Ext.data.Storeclass.Aswewanttofilterthestorebyaparticularvaluegiveninthetextfield,wecanmakeuseofthemethodfilter().Thismethodfiltersthedatainthestorebyoneormorefieldsandcanbeconfiguredwithadetailedfilterconfiguration(fromExt.util.Filterclass).
{
property:'firstName',
value:newValue||'',
anyMatch:true,
caseSensitive:false
}
Thegivenobjectrepresentsafilterthatisappliedtothefilter()functionandisdefinedtofiltertheproperty(thatisthefieldinthemodeltofilteron)'firstName'.ThevaluetofilterwithisthenewValuegivenbytheeventoranemptystring('')ifthepassedvalueisfalsy(false,0,''null,undefinedorNaN).SettinganyMatchtotrueconfiguresthefiltertomatchthevaluecharactersatanypositioninthestore'svalueandbyhavingcaseSensitivesettofalseweignoreexactcasematching.
MoMoworkshop
229Eventchange
MoMoworkshop
230Eventchange
GeoExt3workshop
WelcometotheGeoExt3workshop,inwhichyou'lllearnhowtouseGeoExt3inyourExtJSapplications.
Thisworkshopiscomposedoutofseveralmodules,whichusuallyaredoneinorder.
Beforedivingintoprogramming,themetainformationchapterhasalotofinformationaboutthisworkshop,theintendedaudienceandhowtobestcreatesolutionsfortheworkshop.Afterwardswe'llworkthroughthefirststepschapter,inwhichyoulearnaboutOpenlayers,ExtJSandGeoExt3.Nowthatweknowtheselibrariesandframeworks,wearereadytousetheGeoExt.component.Mapinthemapchapter.Yourmapmayverywellcontainalotoflayers,thesecanbemanagedwithalayertree.Thenextchapterisallaboutvectorfeatures,you'llcreateagridthatissyncedwithavectorlayerinthemap.ThelastchaptershowsotheraspectsofGeoExtcomponentslikepopups,anembeddableoverviewmap,theutilityclassestotalktoMapFishprintserversandotherpartsofGeoExt.
MoMoworkshop
231GeoExt
MetainformationInthischapterwe'llprovideyouwithsomemetainformationaboutthisworkshop.
Inordertoefficientlyworkthroughtheworkshop,youareadvisedtoreadthefollowingparts:
GeneralinformationabouttheworkshopThetargetaudienceoftheworkshopThegoalswetrytoachieveintheworkshopHowtosetupyourdevelopmentenvironmentsothatyoucanworkontheworkshoptasksefficientlySomefinalnotes(e.g.aboutthechosenstructure)
Let'sstartwithsomegeneralinformationabouttheworkshop.
MoMoworkshop
232Metainformation
About
Authors
Thisworkshopwascreatedbythefollowingindividuals:
MarcJansenDanielKoch
ContributeTheworkshoprepositorycanbefoundathttps://github.com/geoext/geoext3-ws.
Welookforwardtoexternalcontributors.Ifyoufoundanissueorhaveanideaofhowtoimprovetheworkshop,justopenanissuehere.
Usedlibraries
DuringtheworkshopyouwillworkwiththefollowingJavaScriptlibrariesorframeworks:
OpenLayers(v3.13.0):http://openlayers.org/ExtJS(v6.0.0,GPL):https://www.sencha.com/products/extjs/,downloadGeoExt3(c326b01cpre-v3.0.0):http://geoext.github.io/geoext3/
Copyright
Thecopyrightis©GeoExtContributors.
License
TheworkshopcontentispublishedunderCC-BY-SA-4.Thefulltextisavailableonline.
GeoExt3itselfisreleasedunderthetermsoftheGPLv3.
MoMoworkshop
233About
TargetaudienceThisworkshopistargetedatdevelopersthatwanttotryouttheGeoExtlibrary.
GeoExtisbasedonOpenLayersandExtJS,soabitofbackgroundintheseJavaScriptlibraries/frameworksdoesn'thurt.Itisnotnecessarilyneededtohaveadeepunderstandingofthebaselibraries,though.Allexamplesortasksusuallyhighlightthekeyaspectsthatmakeeverythingworktogether.
SomebasicfamiliaritywithJavaScriptisassumed,butagain,wewillnotdivetodeepintothelanguage,sobasicallyanyinterestedpersonwillbeabletounderstandwhatisgoingon.
Inordertoaccomplisheverythingintheworkshop,someOGCservices(suchasWMSandWFS)willbeused.Weshouldprovideyouwithenoughwrittenbackgroundsoyou'llgraspthecoreofthetasks.
Stillunsureifyoucanworkthroughthismaterial?
Westrivetomakethisworkshopasunderstandableaspossible,sopleasetryitout!Ifyoufailorexperienceproblems,justtellusso:Wearereallylookingforwardtogettingfeedback.
MoMoworkshop
234Targetaudience
GoalsThesearethegoalswewanttoreachwiththisworkshop:
LearnthebasicsofGeoExttheoreticallypractically
LearnhowtoworkwiththeAPI-docsLearnaboutotherplaceswheretofindhelpforGeoExtGettoknowcertaincoreclassesofGeoExtIterativelycreateanapplicationLearnhowtodebugincaseoferrors
MoMoworkshop
235Goals
Developmentenvironment
Requiredsoftware
Inordertocompletethisworkshop,youwillneedthefollowingsoftware:
Atexteditor,forexampleAtomorsomeothereditorinwhichyoufeelcomfortable.Abrowser,toreadtheworkshopinstructionsandopenupthetasksyouwillhavetoaccomplish.Node.JS,sothatyoucanruntheworkshopexamples.Node.jswillalsoinstallnpm,whichwewillusetoinstallworkshopdependenciesandtoservetheworkshopslidesasHTML.Ifyouareonlinux,wehavemadeexcellentexperienceswithnvmtoinstallvariousversionsofNode.js.
PreparationstepsDownloadthelatestworkshop-contensfromthisURL:
https://github.com/geoext/geoext3-ws/archive/master.zipExtractthezip-archiveintoadirectoryofyourchoice.Youshouldfindthefollowingfilesanddirectoriesinthegeoext3-ws-master-folder:
LICENSE.md
package.json
README.md
src/
Installthedependenciesoftheworkshopvianpminstall.HerearesomeexamplestepsforaLinux-system:
#createadirectorygx-ws…
mkdir-p~/gx-ws
#…gothere…
cd~/gx-ws
#…grabthezip-archive…
wgethttps://github.com/geoext/geoext3-ws/archive/master.zip
#…unzipthearchive…
unzipmaster.zip
#…changeintotheextractedfolder
cdgeoext3-ws-master
#Installdependenciesvianpm
npminstall
Startingtheworkshop
Issuethefollowingcommandinthedirectorygeoext3-ws-masterfromabove:
npmstart
Thisshouldgiveyouanoutputlikebelow:
MoMoworkshop
236Developmentenvironment
Livereloadserverstartedonport:35729
PressCTRL+Ctoquit...
info:loadingbookconfiguration....OK
info:loadplugingitbook-plugin-image-captions....OK
info:loadplugingitbook-plugin-highlight....OK
info:loadplugingitbook-plugin-search....OK
info:loadplugingitbook-plugin-sharing....OK
info:loadplugingitbook-plugin-fontsettings....OK
info:loadplugingitbook-plugin-livereload....OK
info:>>6pluginsloaded
info:startgenerationwithwebsitegenerator
info:cleanwebsitegeneratorOK
info:generationisfinished
Startingserver...
Servingbookonhttp://localhost:4000
Ifinsteadyouseesomeerrorlikebelow,theworkshopislikelyalreadyrunningonyoursystemorsomeotherapplicationisblockingtheports35729and4000:
...Uhoh.GoterrorlistenEADDRINUSE:::35729...
Error:listenEADDRINUSE:::35729
atObject.exports._errnoException(util.js:870:11)
atexports._exceptionWithHostPort(util.js:893:20)
atServer._listen2(net.js:1237:14)
atlisten(net.js:1273:10)
atServer.listen(net.js:1369:5)
atServer.listen(/home/jansen/.gitbook/versions/2.6.7/node_modules/tiny-lr/lib/server.js:164:15)
atPromise.apply(/home/jansen/.gitbook/versions/2.6.7/node_modules/q/q.js:1078:26)
atPromise.promise.promiseDispatch(/home/jansen/.gitbook/versions/2.6.7/node_modules/q/q.js:741:41)
at/home/jansen/.gitbook/versions/2.6.7/node_modules/q/q.js:1304:14
atflush(/home/jansen/.gitbook/versions/2.6.7/node_modules/q/q.js:108:17)
Youalreadyhaveaserverlisteningon35729
Youshouldstopitandtryagain.
Stoppingtheworkshop
SimplyhitCtrl-Cintheterminalwhereyoustartedtheworkshop,e.g.~/gx-ws/geoext3-ws-master.
MoMoworkshop
237Developmentenvironment
NotesInthissectionwewanttokeepcertainnotesaboutthisworkshop.ItisnotanecessaryrequirementtoreadthissectionifyoujuststartwithGeoExt.
Incaseyouhavequestionsaboutwhywehavestructuredtheworkshopaswehavedoneit,pleasecontinuereading.
Q:WhynotasExt.application?WhynoMVC?
Whydidn'tyoucreatetheexamplesasExt.application()?Anwhyaren'tyouusingtheMVCpattern?
WerecommendtheuseofExt.application/Ext.app.ApplicationandtheuseoftheMVCorMVVMpatternforreal-worldapplications.Forthisintroductoryworkshopwethinkthiswouldcomplicatestuffmorethannecessary.
Q:WhynotwithhelpofSenchaCmd?Whydon'tyouusetheSenchaCmdfortheworkshop?
Weusethesenchatoolquiteofteninourdailywork,butfoundthattheadditionalburdenofinstallationstepswouldbedistractingforthemainfocusofthisworkshop.
MoMoworkshop
238Notes
FirststepsNowthatweknowalltherequiredmetainformationandhavesetupourdevelopmentenvironment,itistimetogetstarted.
Inthischapterwewilllearn5things:
1. WheretosaveourexerciseHTMLfiles.2. HowtoincludeOpenLayersinourexercise.3. HowtoincludeExtJSinourexercise.4. HowtoincludeGeoExtinourexercise.5. Wheretolookformoredocumentation.
MoMoworkshop
239Firststeps
HelloexerciseThroughoutthisworkshop,youwillencountervarioustasks,thatyoushouldaccomplish.MostofthetimeyouwillbeaskedtoeditanHTMLorJavaScript-fileandseeiftheresultisasintended.
Inordertohavecomparableresults,youareadvisedtosaveyourHTMLandanyadditionalfilesinsideofthesrc/exercise/-folder.Ifyoufollowedtheinstructionsforsettingupthedevelopmentenvironment,thisfolderwillbelocatedat:
~/gx-ws/geoext3-ws-master/src/exercise.
Ifyoue.g.storeafilenamedmap.htmlinsidethisdirectory,andyouareservingtheworkshopasrecommended,thanthisfilecanbeaccessedviathefollowingURL:
/map.html
Shallwetackleourfirsttinyexcercise?Okthen,herewego:
Exercises
Createamy-exercise.htmlHTML-fileinthesrc/exercise/-folder,openitwithyourtexteditorandfillitwiththetemplateHTMLfrombelow:
template.html
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>ThisisabasicHTMLtemplate</title>
</head>
<body>
<h1>UsethistemplatetocreateyourownHTMLfiles</h1>
</body>
</html>
SeeifyourfileisavailableinabrowserunderthefollowingURL:/my-exercise.htmlInthebodyoftheHTMLchangethecontentofthefirst<h1>-elementtoread:GeoExtrocks!CheckifanychangestotheHTMLfilearereflectedinyourbrowser.ReloadtheURL/my-exercise.html
Ifeverythingworked,youshouldseesomethinglikeinthefollowingimages.
OurfirstHTML-page
MoMoworkshop
240Helloexercise
Indeed,itdoes!
Pleasenote:
Incaseyouaddedmorefiles(e.g.forupcomingtasks)tothesrc/exercise/folderandtheyarenotinstantlyavailableundertheURL/filename.html
…thenyouhavetostopandstartthefileservingagain.Seethenotesonstarting/stopping(Hint:Ctrl-Cornpmstart)
MoMoworkshop
241Helloexercise
HelloOpenLayersOk,wecancreateandeditHTML-files,andwecanseethechangesinourbrowserbecauseallfilesinsrc/exercises/arealwaysavailableunder.
Let'sseehowwecanincludeOpenLayersinourpagesothatwecanstarttouseit.Inordertodoso,weneedtoincludeaCSSandaJavaScriptfile.
Exercises
Seeifyoufindafolderlib/ol/insideofthesrc/exercise/-folder.Itshouldcontaintwofiles:ol.jsandol.cssCreateanewol-example.htmlfromthebasictemplate
template.html
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>ThisisabasicHTMLtemplate</title>
</head>
<body>
<h1>UsethistemplatetocreateyourownHTMLfiles</h1>
</body>
</html>
Changeol-example.htmltoincludebothfilesinthe<head>.UsethebelowtemplatestoincludeaCSSandaJavaScriptfile.
include-js-css.html
<!--includeaCSSstylesheet-->
<linkrel="stylesheet"href="path/to/file.css"type="text/css">
<!--includeanexternalJavaScriptfile-->
<scriptsrc="path/to/file.js"type="text/javascript"></script>
Verifythat/ol-example.htmlloadsyourfile.Inthe<body>ofthefile,addthefollowingHTML-fragment,whichincludesatinybitofJavaScript:
simple-map.html
<divid="map"style="height:600px"></div>
<scripttype="text/javascript">
varmap=newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.MapQuest({layer:'sat'})
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:4
})
});
</script>
Whenyounowreloadthe/ol-example.htmlURL,youshouldseeanOpenLayersmapcenteredonUlanBator:
MoMoworkshop
242HelloOpenLayers
AverybasicOpenLayersmaa
ToverifywearereallylookingatUlanBator,justchangethelayerstonowconsistanOpenStreetMaplayer,whiche.g.haslabelsandacountryoutline.UsethefollowingJavaScriptsnippetattheappropriateplace:
newol.layer.Tile({
source:newol.source.OSM()
})
Say"hi"totheOSMlayer
MoMoworkshop
243HelloOpenLayers
MoMoworkshop
244HelloOpenLayers
HelloExtJSBeforewecanlearnhowtouseGeoExt,weneedtoseeifwecanuseExtJSinourpage.
Againwe'llneedtoincludetworesourcesinaHTMLpagetobeabletouseExtJS:AndagainitisaCSSandaJavaScriptfile.
Exercises
Createanewext-example.htmlfromthebasictemplate
template.html
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>ThisisabasicHTMLtemplate</title>
</head>
<body>
<h1>UsethistemplatetocreateyourownHTMLfiles</h1>
</body>
</html>
Changeex-example.htmltoincludethefollowingtwofiles:https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-crisp/resources/theme-crisp-all.csshttps://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js
include-js-css.html
<!--includeaCSSstylesheet-->
<linkrel="stylesheet"href="path/to/file.css"type="text/css">
<!--includeanexternalJavaScriptfile-->
<scriptsrc="path/to/file.js"type="text/javascript"></script>
Verifythat/ext-example.htmlloadsyourfile.Doesyourbasicpagelookliketheoneinthefollowingimage?Whydoesthefontlooksodifferent?
MoMoworkshop
245HelloExtJS
Thetemplate-HTMLwiththeExtJSresourcesincluded
Inordertoseeifeverythingwasincludedsuccessfully,let'sinstantiateanExtJSclass.Pleasecopyandpastethefollowingintothe<body>ofthetest-file:
<script>
Ext.onReady(function(){
varwin=Ext.create('Ext.window.Window',{
width:200,
height:200,
title:'ExtJS…',
html:'…iseasy!'
});
win.show();
});
</script>
YoushouldseeanExt.window.Windowlikebelow:
ExtJSiseasy
MoMoworkshop
246HelloExtJS
MoMoworkshop
247HelloExtJS
HelloGeoExtNowthatweknowhowtouseOpenLayersandExtJS,it'stimetojointheselibraries.EnterGeoExt!
We'llstartwiththeresultofthelastexercise,whichwasabasicHTMLfilethatincludedtheresourcestoExtJS.
Exercises
CopythefollowingHTMLintoafilehello-geoext.htmlintheexercises-directory:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>ThisisabasicHTMLtemplate</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
</head>
<body>
<script>
Ext.onReady(function(){
varwin=Ext.create('Ext.window.Window',{
width:200,
height:200,
title:'ExtJS…',
html:'…iseasy!'
});
win.show();
});
</script>
</body>
</html>
AddtheCSSandJavaScriptforOpenLayers:
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.css"type="text/javascript"></script>
AddtheJavaScriptforGeoExt:https://geoext.github.io/geoext3/master/GeoExt.jsMostGeoExtcomponentsdon'tneedspecialCSS.IfyouusethePopup-components,youmaywanttoincludethefollowingCSSfile:http://geoext.github.io/geoext3/master/resources/css/gx-popup.cssVerifythat/hello-geoext.htmlloadsinyourbrowser
AddingourfirstGeoExtcomponent
Exercises
WearenowgoingtocreateaninstanceofGeoExt.component.Mapandrenderitinthewindowwecreatedinthepreviousexample.Inordertodothat:
Removethehtml:'…iseasy!'fromtheExt.window.WindowconfigurationobjectAndaddthefollowinglinesinstead:
MoMoworkshop
248HelloGeoExt
//intheconfigobject:
layout:'fit',
items:[
Ext.create('GeoExt.component.Map',{
map:newol.Map({
target:'map',
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:4
})
})
})
]
Next:Pleasechangethetitleofthewindowandmakeisslightlybigger.
Ifyourexamplelooksliketheonebelow,everythingissetupcorrectly!
HelloGeoExt!
MoMoworkshop
249HelloGeoExt
UsefulresourcesInordertoefficientlyworkwithallthelibrariesusedthroughouttheworkshop,you'llneedknowsomeotherresources:
OpenLayers
Homepage:http://openlayers.org
APIdocumentation
UsethAPIdocsofOpenLayerstoknowaboutavailableclassesandtheirproperties:
http://openlayers.org/en/v3.13.1/apidoc/
TheleftsideoftheAPI-docsisalistofallobjectsinOpenLayers.IfyouclickonthenameofanOpenLayersclassthere,youseetheAPIoftheobject.
Inthesubpagesyoucanfindalistofallproperties,methodsandeventstheclassprovides.YoushouldmakeyourselffamiliarwithhowtonavigatetheAPIdocs.
Examples
Manypeoplelearnbestwhentheyseethepartsofalibraryinaction.OpenLayershasavastamountofpublishedonlineexamples,whichmostlyfocusononeaspectofthelibrary.
http://openlayers.org/en/v3.13.1/examples/
Browsetheexamplesandlearnhowtofindonethatprovidestheinformationyouneed.
Other
OpenLayersalsohaspublishedaworkshopathttp://openlayers.org/workshop/.CommonproblemsthatmayarisewhenusingOpenLayers3areexplainedintheFrequentlyAskedQuestions(FAQ).Forspecificquestionsonecanaskonstackoverflowusingthetag'openlayers-3'.
ExtJS
APIdocumentation
ThequantityandqualityoftheExtJSAPIdocumentationisoutstanding.
http://docs.sencha.com/extjs/6.0/6.0.0-classic/
Thedocsprovidealistofallclassesontheleftanddetailsonceyouclickonanyclass.InordertounderstandandmakeuseofExtJS,itiscrucialtofullygraspthedocumentation.
Examples
TheexamplesfortheExtJSframeworkcanbefoundhere:
http://examples.sencha.com/extjs/6.0.0/examples/
AswiththeAPIdocumentation,youmayatfirstbeoverwhelmedatthesheermassesofexamples.Itisnonethelessveryusefultoclickthroughsomeofthem,astheshowhowtocombinetheclassesoftheframeworkintosmallworkingapplications.
MoMoworkshop
250Usefulresources
Other
Ifyouwanttoquicklychecksomeclass,youcane.g.usetheSenchaFiddlewebsite.Specificquestions(andanswers)canbebrowsedintheSenchaFormums.
GeoExt
Homepage:https://geoext.github.io/geoext3/
APIdocumentation
TheGeoExtAPIdocumentation(generatedwiththesamesoftwareastheExtJSone)canbefoundhere:
http://geoext.github.io/geoext3/master/docs/
IfyouknowyourwayaroundinTheExtJSdocumentation,youwilleasilyunderstandtheGeoExtone.
ThereisalsoaversionoftheAPIofGeoExt,whichincludesalltheclassesfromtheExtJSframework:
http://geoext.github.io/geoext3/master/docs-w-ext/
Examples
The(few)examplesoftheGeoExtlibrarycanbeaccessedfromthehomepage:https://geoext.github.io/geoext3/
MoMoworkshop
251Usefulresources
SummaryThischaptergentlyintroducedyoutoOpenLayers,ExtJSandGeoExt.Youhavelearned…
…howandwheretocreateexercisefiles.…howtoincludetheresourcesofOpenLayers.…howtoincludetheresourcesofExtJS.…howtoincludetheresourcesofGeoExt.…wheretofindmoreinformationonline.
ThenextchapterwillfocusontheGeoExt.component.Mapwhichwehaveseenbrieflyinthefirstexamplewecreated.
MoMoworkshop
252Summary
MapThischapterwillintroduceyoutooneofthemostcentralcomponentsinGeoExt:theGeoExt.component.Map.
Willworkthroughthefollowingpartstolearnaboutthiscomponent:
CreatingabasicexampleDissectingthepartsoftheexampleExploreconfigurationvariants
MoMoworkshop
253Map
BasicexampleWewanttohavealookatafullyworkingexamplefirst.
Exercises
Createanewfilemap.htmlinthesrc/exercise-directory.Pastethefollowinghtml-codeintothefileyouhavejustcreated:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:12
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'fit',
items:mapComponent
});
});
</script>
</body>
</html>
Verifythat/map.htmlloadsinyourbrowserandlookslikethepicturebelow.
MoMoworkshop
254Basicexample
Amapcomponentinafullscreenviewport
Wewillnowdissecttheexampleandexplainwhateachpartdoes.
MoMoworkshop
255Basicexample
DissectingtheexampleLet'slookatthepartsoftheHTMLpage.
TheHTMLskeleton
TheHTMLofthepagelooksasfollows:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
</script>
</body>
</html>
HTML5DOCTYPE
ThefirstlineinthisdocumentisthedoctypeoftheHTML.Byspecifying…
<!DOCTYPEhtml>
…wedeclarethattheHTMLfileshallbehandledasanHTML5document.Werecommendtheusageofthisdoctypetoforcebrowsersintofixedrulesofrenderingthepage.Thiseventuallyalsoreducesinconsistenciesofthebehaviourofthepageinvariousbrowsers.
Declarationofthecharacterset
InordertotellthebrowserthatwehaveencodedourfileasUTF-8,weadda<meta>-tagtothe<head>ofthedocument:
<head>
<metacharset="utf-8">
</head>
Thiswaywecanberelativelysurethatallthecharactersweenterintothedocument(e.g.Germanumlautslikeä,öorü;orУлаанбаатар)arecorrectlydisplayedwhenviewingthesite.
CSSandJavaScriptresources
Alsointhe<head>ofthedocumentweloadexternalJavaScriptandCSSfiles,sowecanuseourneededlibrarieslater.
<head>
<linkrel="stylesheet"href="URL-or-relative-path-to-file"type="text/css">
<scriptsrc="URL-or-relative-path-to-file"type="text/javascript"></script>
</head>
MoMoworkshop
256Dissectingtheexample
Forthisworkshopitwillbeenoughtoalwaysincludethefullbuildsofthelibrary;andtoalwaysloadtheminthe<head>.Thistechniqueallowsustobasicallyforgetabouttheseresourcesforthecourseoftheworkshop.Foraproductionwebsiteyouwouldprobablyloadthefilesinadifferentmanor,andyouwouldrathernotloadtheversionsofthelibrarieswhichcontaineverything.Thecreationofspecificversionsofthebaselibrariesthatonlyincludewhatyourapplicationactuallyneeds,iswaybeyondthescopeofthisworkshop.
<script>-taginthe<body>
OurbodyoftheHTMLfileisreally,really,reallyminimalistic:
<body>
<script>
</script>
</body>
Weonlyincludeone<script>-tagthatwillcontainalltheJavaScriptthatweneedtocreateourmap.ThecontentsofthistagwillbeinterpretedasJavaScript,andthecodewillberunassoonasthebrowserseesit.
JavaScriptcodeforthemapAllourcodetocreatethefullscreenmaplivesinthe<script>-tagintheHTML<body>.
Let'sgothroughallthelinesinthere.
Avariablenamedmap
Thefirstlineintheexamplereads:
varmap;
Thiscreatesaglobalvariablenamedmap,which(atthispoint)hasthevalueundefined.Lateronwewillstoreourinstanceoftheol.Mapinthatvariable.Wehavemadeitglobaltoallowforeasierdebugging(e.g.inthedevelopertoolsofyourbrowser).FortheworkshopitisOKtocreatealotofglobalvariablesforstuffyouwanttoexaminelateron;inproductionsitesitusuallyfrownedupon.
PassingafunctiontoExt.onReady
Thenextlinereads:
Ext.onReady(function(){
//someotherlineswedonotcareaboutnow
});
Theselinespassananonymous(e.g.unnamed)functiontothemethodExt.onReady.ThismethodwillexecutethepassedfunctionassoonastheDocumentisready,e.g.ExternalresourceshaveloadedandtheDOM(DocumentObjectModel)ofthepageisreadytobemanipulated.
Behindthecurtains,whenwecreateinstancesofsomeExtclasses,theywilleventuallyneedtomodifytheDOM.Inordertorunintoproblemswhensuchchangeshappentoearly(remember,allcodeinthe<script>tagisexecutedassoonasitisbeingread),wewraptherealcodetoactuallycreateExtJScomponentsintoafunction.WethensimplytellExtJStodelaytherealworktoalatertime,wheneverythingisready.
Let'shavealookatthepartsinsidethisfunction.
Creatinganol.Map
MoMoworkshop
257Dissectingtheexample
Firstwewanttocreateaninstanceofanol.Map:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:12
})
});
TheselinescreateanOpenLayersmapandconfigureitwithaviewthatiscenteredonUlanBatorandthathasonelayershowingpre-renderedtilesfromtheOpenStreetMapproject.
YoushouldalreadybeslightlyfamiliarwithOpenLayersandcanbasicallyuseanymapthatworkswithoutGeoExt.
Sincewedidnotwritevarmap=…,theassignmentwillhappentotheglobalvariablemap,thatwedeclaredinthefirstlineofthescript-tag.YoucaneasilydebugtheOpenLayersmapthisway.
CreatingaGeoExt.component.Map
NextweusethemethodExt.createwithtwoarguments:thenameoftheclasstocreate,andaconfigurationobjectwithpropertiesfortheinstance.
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map
});
InplainEnglishthislinecouldread
PleasecreateaninstanceoftheclassGeoExt.component.MapandensurethatitisconfiguredwiththeOpenLayersmapIhavestoredinthevariablemap.Onceyouhavedonethat,pleasestorethisinstanceinavariablemapComponent.
Aftertheselineshaveexecuted,wenowhavetwovariables,oneholdingtheplainOpenLayersmap(map),andonethatisnamedmapComponentwhichcontainsaninstanceofaGeoExtclass;andthisinstanceknowsabouttheOpenLayersmap.
CreatingaExt.Viewport
Thefinalfourlinesintheblockread:
varvp=Ext.create('Ext.container.Viewport',{
layout:'fit',
items:mapComponent
});
AgainweuseExt.createtobuildaninstanceofaclass,thistimeoftheExt.container.Viewportclass.FromtheExtJSAPIdocs:
Aspecializedcontainerrepresentingtheviewableapplicationarea(thebrowserviewport).
TheViewportrendersitselftothedocumentbody,andautomaticallysizesitselftothesizeofthebrowserviewportandmanageswindowresizing.TheremayonlybeoneViewportcreatedinapage.
(source)
Thisviewportwillbeasbigasthebrowserviewport.Allit'schildren(configuredviatheitems-key)willbelayedoutaccordingtothefit-layout.Thislayoutensuresthatthechildcomponent(inourcasethemapComponent)willbeasbigastheviewportitself.
MoMoworkshop
258Dissectingtheexample
Trytoresizeyourbrowserwindowandseethattheviewport(andthecontainingmapcomponent)alwaysfilloutthefullareaofthebrowserwindow.
Nextsteps
Let'slookatvariousvariantstoconfigurethethreepartsofourmap.
MoMoworkshop
259Dissectingtheexample
ConfigurationvariantsThischapterlooksatourpossibilitiestocustomizetheappearanceandbehaviourofthemap.
ConfiguringaspectsofOpenLayers
Asyouhaveseen,wehavesimplycreatedaninstanceofol.MapandpassedittotheGeoExt.component.Map.Ifweconfiguretheol.Mapdifferently,thechangesshouldbereflectedinthefinalapplication.
Exercises
ChangethefollowingaspectsoftheOpenLayersmap:
Setadifferentmapcenter.Initiallyzoomtoanotherregion.Addmorelayerstothemap.TrythelayersfromtheseWMScapabilitiesforexample:
http://ows.terrestris.de/osm/service?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilitieshttp://ows.terrestris.de/osm-gray/service?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities
Addanothercontrol.Trythese,forexample:ol.control.ScaleLine
ol.control.MousePosition
ConfiguringaspectsofExtJS
ChangethefollowingaspectsoftheextJScomponents:
Useanotherlayoutfortheviewport.Justrememberthatyouprobablyneedtochangetwoplaces:Thelayoutconfigoftheviewport.Anddependingonthechosenlayout,children(ourmap-component)mayneednewproperties.
WraptheGeoExt.component.Mapinapanelwithatitle.
ConfiguringaspectsofGeoExtOfcourseyoucanalsochangeaspectsdirectlyviaGeoExt:
Setthecenterofthemap,butthistimewithGeoExt.AddalayerwithGeoExt.
MoMoworkshop
260Configurationvariants
SummaryThischapterhasshownyouhowtousetheGeoExt.component.Map.Wefirststartedwithaworkingexample,thatwasthedissectedingreatdetail.YouhavethenlearnedhowtoconfigureyourapplicationbychangingpropertiesofOpenLayers-,ExtJS-andGeoExt-objects.
Thenextchapterwillnowintroduceacomponenttodealwiththepossiblyhierarchicalstructureoflayersinthetree:wewanttoaddalayer-tree.
MoMoworkshop
261Summary
LayertreeNowthatwehaveamap-componentinanExtJSlayout,wenaturallywanttoaddmorelayerstothemap.ButwhenwehavemorethanonelayerintheOpenLayersmap,wealsomaywanttoincludesomecomponenttohandletheindividualvisibilityandtheorderoflayersinthemap.AsOpenLayersdoesnotprovideacontroltoinfluencetheseproperties,wehavetocreateourowncomponent.
GeoExtwantstohelpherebyprovidingthenecessarypartstocreateaTreeofthelayersinthemap-component.
Let'strytoaddalayertree.
MoMoworkshop
262Layertree
PreparelayoutThepreviouschapterstartedfromthefollowingtemplate,whichwenowwanttorecreate.
Exercises
Pleasecreateafilemap.htmlinthesrc/erxercisesdirectoryandpastethefollowing:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:12
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'fit',
items:mapComponent
});
});
</script>
</body>
</html>
Wewanttochangethelayoutoftheviewportasfollows:
MoMoworkshop
263Preparelayout
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent
]
});
Inordertobeusableinaborder-layout,onechildcomponentneedstohavetheregion-propertysettocenter:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center'
});
Ifyouapplytheabovechanges,yourapplicationshouldrenderagaininthebrowser,butsinceweonlyhaveonecomponentintheborder-layout,you'llnotnoticeavisualdifference.Let'sfirstaddaplaceholderpanelwherewewanttoaddthelayertree:
varlayerTreePanel=Ext.create('Ext.panel.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west'
});
//…thispanelalsoneedstobeaddedtotheviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel
]
});
Yourapplicationshouldnowlooklikethefollowing:
Ourplaceholderpanelintheviewport
MoMoworkshop
264Preparelayout
MoMoworkshop
265Preparelayout
CreateaTreePanelWenowhavethelayoutpreparedandsimplyneedtoswitchthecontentsofthewest-panel.
Sincewewanttouseatreetoeventuallycontrolthelayersofthemap,we'lluseanExt.tree.Panelinsteadofthesimplepanel.
Exercises
Next,we'llswitchouttheExt.panel.PanelagainstadedicatedExt.tree.Panel.Ifwelookatthedocumentationforthetree-panel,you'llseeaverybasicexample,whichyoupleaseaddtotheviewportinsteadofourplaceholder.Theexamplefromtheabovepagelookslikethis:
varstore=Ext.create('Ext.data.TreeStore',{
root:{
expanded:true,
children:[
{text:'detention',leaf:true},
{text:'homework',expanded:true,children:[
{text:'bookreport',leaf:true},
{text:'algebra',leaf:true}
]},
{text:'buylotterytickets',leaf:true}
]
}
});
Ext.create('Ext.tree.Panel',{
title:'SimpleTree',
width:200,
height:150,
store:store,
rootVisible:false,
renderTo:Ext.getBody()
});
Trytounderstandwhateachlineoftheabovecodedoesandseewhichlinesyouneedtochangeorremove,sothatyoucanusethetreeinourlayout.
Hints
Somehints(incaseyouhavetroublegettingittowork)Thestore—ascomplicatedasitlooksatfirst—canbeleftasis,youdon'tneedtochangesomethinghere.ThereturnvalueoftheExt.create('Ext.tree.Panel',/**/)calliscurrentlyignored.Youshouldtrytosaveitinavariable(probablytheonefromourbasicsetuplayerTreePanel).Theheightofthetree-panelisunnecessary,wewanttoputthepanelinthewestregion,whichhasfullheightbydefault.Removetheheight-property.TherenderTo-configurationofthetree-panelisalsofinefortheExtJSstandaloneexample,butbadforourcombinationsetup.Inourcase,theviewporttakescareofwheretoactuallyrenderthetree.RemovetherenderTo-property.
Thefinalresultshouldlooklikethis:
MoMoworkshop
266CreateaTreePanel
ThecopyandpastedExt-exampleinourviewport
Solution
Forreference,hereisthefullcodeofthestore,treeandviewportthatleadtotheabovepicture:
varstore=Ext.create('Ext.data.TreeStore',{
root:{
expanded:true,
children:[
{text:'detention',leaf:true},
{text:'homework',expanded:true,children:[
{text:'bookreport',leaf:true},
{text:'algebra',leaf:true}
]},
{text:'buylotterytickets',leaf:true}
]
}
});
varlayerTreePanel=Ext.create('Ext.panel.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:store,
rootVisible:false
});
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel
]
});
MoMoworkshop
267CreateaTreePanel
MoMoworkshop
268CreateaTreePanel
AssignLayersTreestoreInsteadofthehard-codedhierarchicallistofthingsinthetree,wenowwanttolinkthetreewithourmap.
Inordertodothis,weneedtochangethestorethatisusedforthetree.Insteadoftheall-purposeExt.data.TreeStore,we'llusethespecialGeoExtclassGeoExt.data.store.LayersTree
Exercises
Ifyouhaven'tdonealready,setupafilecalledmap.htmlinthesrc/exercisesdirectoryandpastethefollowingcontents:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM()
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:12
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center'
});
varstore=Ext.create('Ext.data.TreeStore',{
root:{
expanded:true,
children:[
{text:'detention',leaf:true},
{text:'homework',expanded:true,children:[
{text:'bookreport',leaf:true},
{text:'algebra',leaf:true}
]},
{text:'buylotterytickets',leaf:true}
]
}
});
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
MoMoworkshop
269AssignLayersTreestore
width:300,
region:'west',
store:store,
rootVisible:false
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel
]
});
});
</script>
</body>
</html>
MakeyourselffamiliarwiththeGeoExt.data.store.LayersTreeclassbystudyingthefollowingAPI-docs:http://geoext.github.io/geoext3/master/docs/#!/api/GeoExt.data.store.LayersTreeCreateaninstanceoftheGeoExt.data.store.LayersTreeclassandpassitthefollowingconfigurationobject:
{
layerGroup:/*thetoplevellayergroupofthemap*/
}
StudytheAPIdocsofol.MaptogettheappropriateLayerGroup:http://openlayers.org/en/v3.13.1/apidoc/ol.Map.htmlIfeverythingworksfine,youshouldseeatreewithone(currentlyunlabeled)leaf.Nexttotheleafyoufindacheckbox,thatreflectstheoverallvisibilityofthelayer.
Theworkingbutcurrentlyunlabeledtree
StudytheGeoExt.data.store.LayersTreeandfindoutwhythereisnolabelnexttothetree-element.Addmorelayerstothemapandseeiftheyallappearinthemapandinthetree.Takee.g.thefollowingWMS:
MoMoworkshop
270AssignLayersTreestore
url:http://ows.terrestris.de/osm/service
layers:OSM-WMS
ReadthedocumentationfortheExtclassExt.tree.plugin.TreeViewDragDrop.Whathappensifyouaddthisplugintothetree?Yourapplicationshouldnowe.g.looklikethis:
Thetreeintheapplication
Solution
Forreference,herearecode-snippetsfortherelevantpartsofthecode:
MoMoworkshop
271AssignLayersTreestore
//layersshouldhaveapropertyfortheirname(configurable)
newol.layer.Tile({
source:newol.source.OSM(),
name:'OpenStreetMap'
});
//Creatinganappropriatetreestore
vartreeStore=Ext.create('GeoExt.data.store.LayersTree',{
layerGroup:map.getLayerGroup()
});
//Usethestoreinthetreeandalsoloadplugin
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:treeStore,
rootVisible:false,
viewConfig:{
plugins:{ptype:'treeviewdragdrop'}
}
});
MoMoworkshop
272AssignLayersTreestore
SummaryThischaptertaughtyouhowtomakeuseoftheclassGeoExt.data.store.LayersTreetocreateatreeshowingthelayersoftheapplication.
Thetree-panelcorrectlyreordersthelayerorderinthemapiftheorderchanges(viadraganddrop)inthetree.Alltree-leafshaveacheckboxtocontrolthevisibilityoftheconnectedlayer.
MoMoworkshop
273Summary
FeaturegridInthischapterwewanttoaddagridcomponenttotheapplication,whichshowsarowforeveryfeatureofavectorlayer.Wealsowanttoaddbasicinteractionbetweenthegridoffeaturesandthemap.
WewillstartintheusualwaybysettingupanExtJSplaceholdercomponentwhichwewillthengraduallybeenhancedorreplaced.
Headovertothepreparelayoutchapter,inwhichwe'lladdanothervisualcomponenttoourapplication.
MoMoworkshop
274Featuregrid
PreparelayoutWewanttoaddagridpaneltoourbasicmapapplicationnow.
Exercises
Preparethemap.htmlfiletocontainthefollowingcode.Thisisbasicallytheresultofthepreviouschapters:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM(),
name:'OpenStreetMap'
}),
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://ows.terrestris.de/osm/service',
params:{
layers:'OSM-WMS'
}
}),
name:'OSMWMS(terrestris)'
})
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:12
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center'
});
vartreeStore=Ext.create('GeoExt.data.store.LayersTree',{
layerGroup:map.getLayerGroup()
});
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:treeStore,
MoMoworkshop
275Preparelayout
rootVisible:false,
viewConfig:{
plugins:{ptype:'treeviewdragdrop'}
}
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel
]
});
});
</script>
</body>
</html>
Ifyouopenthisfileinabrowser(/map.html),theapplicationshouldlooklikeinthefollowingimage:
Ourstartingpoint
Wewanttohaveagridinthesouth,solet'sstartwiththebasicexamplefromtheExtJSGriddocumentation:
MoMoworkshop
276Preparelayout
Ext.create('Ext.data.Store',{
storeId:'simpsonsStore',
fields:['name','email','phone'],
data:[
{name:'Lisa',email:'[email protected]',phone:'555-111-1224'},
{name:'Bart',email:'[email protected]',phone:'555-222-1234'},
{name:'Homer',email:'[email protected]',phone:'555-222-1244'},
{name:'Marge',email:'[email protected]',phone:'555-222-1254'}
]
});
Ext.create('Ext.grid.Panel',{
title:'Simpsons',
store:Ext.data.StoreManager.lookup('simpsonsStore'),
columns:[
{text:'Name',dataIndex:'name'},
{text:'Email',dataIndex:'email',flex:1},
{text:'Phone',dataIndex:'phone'}
],
height:200,
width:400,
renderTo:Ext.getBody()
});
InsteadofusingastoreIdandthenlaterExt.data.StoreManager.lookup('simpsonsStore'),wewillsimplyuseavariabletobeabletoreferencethestore.Sincewewillputthepanelinourborderlayout,wedonotneedtherenderToandwidthproperties.Don'tforgettoassigntheregion:south.We'llalsosavethepanelinavariable.Yourcodeshouldlookroughlylikethefollowing:
Hint
varfeatureStore=Ext.create('Ext.data.Store',{
fields:['name','email','phone'],
data:[
{name:'Lisa',email:'[email protected]',phone:'555-111-1224'},
{name:'Bart',email:'[email protected]',phone:'555-222-1234'},
{name:'Homer',email:'[email protected]',phone:'555-222-1244'},
{name:'Marge',email:'[email protected]',phone:'555-222-1254'}
]
});
varfeaturePanel=Ext.create('Ext.grid.Panel',{
title:'Simpsons',
store:featureStore,
columns:[
{text:'Name',dataIndex:'name'},
{text:'Email',dataIndex:'email',flex:1},
{text:'Phone',dataIndex:'phone'}
],
height:200,
region:'south'
});
OncewehaveaddedthefeaturePaneltotheviewport,ourapplicationshouldlooklikeinthefollowingimage:
MoMoworkshop
277Preparelayout
ThepreparedExtJSlayout
Ofcoursewealsowanttohaveavectorlayerinthemap,whosefeatureswewantinthegridlater.Pleasecreateanewol.layer.Vector,thathasaol.source.GeoJSONconfiguredandloadsthelocaldatainsrc/exercises/data/aimag-centers.json.Pleasestylethepointswithredcircles.Pleasealsozoomthemapalittlebitfurtherout;zoomlevel4shouldbefine.
Hint
varredStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(220,0,0,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(220,0,0,0.8)',
width:3
}),
radius:8
})
})
varvectorLayer=newol.layer.Vector({
source:newol.source.Vector({
url:'data/aimag-centers.json',
format:newol.format.GeoJSON()
}),
name:'Aimag',
style:redStyle
});
Ourapplicationshouldnowlooklikeinthefollowingimage:
MoMoworkshop
278Preparelayout
Ourmapnowalsoshowsthe'Aimag'
MoMoworkshop
279Preparelayout
CreateafeaturegridNowit'stimetochangethegridtonolongershowstaticdatafromTheSimpsons,butinsteadonerowforeveryfeatureofthevectorlayer.
Exercises
Pleasesetupsrc/map.htmltocontainthefollowinglines:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
varredStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(220,0,0,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(220,0,0,0.8)',
width:3
}),
radius:8
})
})
varvectorLayer=newol.layer.Vector({
source:newol.source.Vector({
url:'data/aimag-centers.json',
format:newol.format.GeoJSON()
}),
name:'Aimag',
style:redStyle
});
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM(),
name:'OpenStreetMap'
}),
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://ows.terrestris.de/osm/service',
params:{
layers:'OSM-WMS'
}
}),
name:'OSMWMS(terrestris)'
}),
vectorLayer
],
view:newol.View({
MoMoworkshop
280Createafeaturegrid
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:4
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center'
});
vartreeStore=Ext.create('GeoExt.data.store.LayersTree',{
layerGroup:map.getLayerGroup()
});
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:treeStore,
rootVisible:false,
viewConfig:{
plugins:{ptype:'treeviewdragdrop'}
}
});
varfeatureStore=Ext.create('Ext.data.Store',{
fields:['name','email','phone'],
data:[
{name:'Lisa',email:'[email protected]',phone:'555-111-1224'},
{name:'Bart',email:'[email protected]',phone:'555-222-1234'},
{name:'Homer',email:'[email protected]',phone:'555-222-1244'},
{name:'Marge',email:'[email protected]',phone:'555-222-1254'}
]
});
varfeaturePanel=Ext.create('Ext.grid.Panel',{
title:'Simpsons',
store:featureStore,
columns:[
{text:'Name',dataIndex:'name'},
{text:'Email',dataIndex:'email',flex:1},
{text:'Phone',dataIndex:'phone'}
],
height:200,
region:'south'
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel,
featurePanel
]
});
});
</script>
</body>
</html>
InsteadofagenericExt.data.Store,useaGeoExt.data.store.Features.LookuptheAPIdocumentationonhttp://geoext.github.io/geoext3/master/docsforfurtherdetails.Makesureyoureferenceyourvectorlayerandthemaptoworkonwhenconfiguringthestore.
MoMoworkshop
281Createafeaturegrid
Hint
varfeatureStore=Ext.create('GeoExt.data.store.Features',{
layer:vectorLayer,
map:map
});
NextweneedtoconfigurethecolumnsoftheExt.grid.Panel.LookuptheattributesoftheGeoJSONandchangetheappropriateconfigurationintheconfigobjectfortheExt.grid.Panel.
Hint
//E.g.
columns:[
{text:'Name',dataIndex:'NAME',flex:3},
{text:'Population',dataIndex:'POP',flex:1},
{text:'Id',dataIndex:'AIMAG_ID',flex:1}
]
AdditionallywecanusetheGeoExt.grid.column.SymbolizerclassofGeoExttoincludethestylingofthefeatureinthegrid.Addthefollowinglinetoyourcolumnsdefinition:
{xtype:'gx_symbolizercolumn',width:30}
Whenarowisselectedinthegrid,itisvisuallyhighlighted.Wouldn'titbeniceifthefeatureonthemapwouldalsohaveadifferentstyleonceitsassociatedrowisselected?Assignaselectionchangelisteneronthegridandensurethatthecorrectfeatureishighlightedinthemap.Hint:Createanewstyleandinthecallbackresetthestyleforeveryfeatureandreassignthenewstyletothetothecurrentlyselectedfeature.Useconsole.log(arguments)toseewhatyouhavebeenpassedandhowyoucangetthefeaturefromthepassedarguments.Bonus:Oncethefeaturehasadifferentstyleonthemap,itwouldbeniceifwecouldseethatstyleinthegrid,right?Changetheselectionchangelistenertoalsoupdatethegridoncethestyleofthefeaturehaschanged.
Hint
MoMoworkshop
282Createafeaturegrid
varfeatureGrid=Ext.create('Ext.grid.Panel',{
store:featureStore,
region:'south',
title:'CentersofMongolianAimag',
columns:[
{xtype:'gx_symbolizercolumn',width:30},
{text:'Name',dataIndex:'NAME',flex:3},
{text:'Population',dataIndex:'POP',flex:1},
{text:'AIMAG_ID',dataIndex:'AIMAG_ID',flex:1}
],
listeners:{
selectionchange:function(sm,selected){
//resetallselections
featureStore.each(function(rec){
rec.getFeature().setStyle(null);
});
//highlightgridselectioninmap
Ext.each(selected,function(rec){
rec.getFeature().setStyle(blueStyle);
});
//updatethegridrenderingofthegeometry
sm.view.refresh();
}
},
height:300
});
Yourapplicationshouldnowroughlylooklikedepictedbelow:
Theapplicationwithafeaturegrid
MoMoworkshop
283Createafeaturegrid
SummaryThischapterhastaughtyoualot:
WelearnedhowtocreateandusetheGeoExt.data.store.Featuresclassandconfigureitwithalayerandamap.WelearnedthatsuchstorescanbeaninplacereplacementforExt.data.Store,e.g.todisplaythecontaineddatainagrid.WelearnedaboutGeoExt.grid.column.Symbolizerthatcanbeusedtorenderfeaturesymbolizersingrids.Wealsohaveseenhowconvenientitistouseeventlistenerstoupdatethevisualrepresentationofyourmap(theselectionchangelistener).
ThenextandfinalchapterwillbrieflyintroduceyoutosomeotherGeoExtcomponents.
MoMoworkshop
284Summary
Popups,Overview&moreGeoExthassomemorecomponentsandclassesthatwedidn'ttouchsofar.ThischapterwantstoshowsomeotheraspectsofGeoExt.However,wecannotgothrougheveryclassindetailandwillonlybarelytouchallthepossibilitiesthatGeoExtprovides.
WewillenhanceourcurrentapplicationwithtwoconcreteusagesofGeoExtfunctionality:anembeddedoverview-mapandpopupsforhoveredlocations.
Furthermorewewanttogiveashorttheoreticaloutlookforpossibleenhancementsofyourapplications.Youcanthenexploretheseonyourown.
Let'sstartwithaddingpopupsforhoveredcoordinates,shallwe?
MoMoworkshop
285Popups,Overviewmap&othercomponents
PopupsInthischapterwewanttoaddshortinformativepopupsonthemap.Wewillopenthepopupwhenthemouselastsforacertainamountoftimeataspecificlocation.Insidethepopupweshowtheformattedcoordinatesofthehover-location.TodothiswewillmakeuseofaGeoExteventontheGeoExt.component.MapandoftheclassGeoExt.component.Popup.
Exercises
Preparethemap.htmlfiletocontainthefollowingcode.Thisisbasicallytheresultofthepreviouschapters:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
varredStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(220,0,0,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(220,0,0,0.8)',
width:3
}),
radius:8
})
});
varblueStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(0,0,220,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(0,0,220,0.8)',
width:3
}),
radius:8
})
})
varvectorLayer=newol.layer.Vector({
source:newol.source.Vector({
url:'data/aimag-centers.json',
format:newol.format.GeoJSON()
}),
name:'Aimag',
style:redStyle
});
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
MoMoworkshop
286Popup
layers:[
newol.layer.Tile({
source:newol.source.OSM(),
name:'OpenStreetMap'
}),
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://ows.terrestris.de/osm/service',
params:{
layers:'OSM-WMS'
}
}),
name:'OSMWMS(terrestris)'
}),
vectorLayer
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:4
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center'
});
vartreeStore=Ext.create('GeoExt.data.store.LayersTree',{
layerGroup:map.getLayerGroup()
});
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:treeStore,
rootVisible:false,
viewConfig:{
plugins:{ptype:'treeviewdragdrop'}
}
});
varfeatureStore=Ext.create('GeoExt.data.store.Features',{
layer:vectorLayer,
map:map
});
varfeatureGrid=Ext.create('Ext.grid.Panel',{
store:featureStore,
region:'south',
title:'CentersofMongolianAimag',
columns:[
{xtype:'gx_symbolizercolumn',width:30},
{text:'Name',dataIndex:'NAME',flex:3},
{text:'Population',dataIndex:'POP',flex:1},
{text:'AIMAG_ID',dataIndex:'AIMAG_ID',flex:1}
],
listeners:{
selectionchange:function(sm,selected){
//resetallselections
featureStore.each(function(rec){
rec.getFeature().setStyle(null);
});
//highlightgridselectioninmap
Ext.each(selected,function(rec){
rec.getFeature().setStyle(blueStyle);
});
//updatethegridrenderingofthegeometry
sm.view.refresh();
}
MoMoworkshop
287Popup
},
height:300
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel,
featureGrid
]
});
});
</script>
</body>
</html>
Ifyouopenthisfileinabrowser(/map.html),theapplicationshouldlooklikeinthefollowingimage:
Ourstartingpoint
Forpopupstolookright,weneedsomeCSS.Includethefollowinginthe<head>ofthepage:
MoMoworkshop
288Popup
<linkrel="stylesheet"href="http://geoext.github.io/geoext3/master/resources/css/gx-popup.css"type="text/css">
<style>
.gx-popupp{
padding:5px5px05px;
border-radius:7px;
background-color:rgba(255,255,255,0.85);
border:3pxsolidwhite;
margin:0;
text-align:center;
}
</style>
ConfiguretheexistingGeoExt.component.MapwithpointerRest:true.Onlyifthisconfigurationistrue,themap-componentwillemitthepointerrest&pointerrestoutevents.Readthedocumentationforpointerrest&pointerrestoutRegisteranevent-listeneronpointerrestthatlogsthehoveredcoordinate.UsetheOpenLayersutilitymethodsol.coordinate.toStringHDMSandol.proj.transformontheevt.coordinatetoformatthecoordinate.
Hint
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center',
pointerRest:true
});
mapComponent.on('pointerrest',function(evt){
varcoordinate=evt.coordinate;
varlonlat=ol.proj.transform(coordinate,'EPSG:3857','EPSG:4326')
varhdms=ol.coordinate.toStringHDMS(lonlat);
console.log(hdms);
});
Onceyouhavetheaboveloggingofthecoordinateinplace,wecannowcreateapopup.InstantiatetheclassGeoExt.component.Popupandconfigureitwithyourmap.Youshouldalsoprovideawidth.Storethepopupinanaccessiblevariable;varpopupisagoodchoice.InsideoftheconfiguredcallbackonpointerrestwenowwanttoupdatetheHTMLofthepopup(methodsetHtml)andrepositionitatthecoordinate(methodposition,passthecoordinatesintheviewprojection).Finally,showthepopup.
Hint
mapComponent.on('pointerrest',function(evt){
varcoordinate=evt.coordinate;
varlonlat=ol.proj.transform(coordinate,'EPSG:3857','EPSG:4326')
varhdms=ol.coordinate.toStringHDMS(lonlat);
popup.setHtml('<p>'+hdms+'</p>');
popup.position(coordinate);
popup.show();
});
Youmaynoticethatthepopupstaysinplaceifthemouseleavesthemapviewport,whichisundesiredinmostofthecases.Usetheeventpointerrestouttohidethepopupwheneverthemouseleavesthemap.
MoMoworkshop
289Popup
Hint
mapComponent.on('pointerrestout',popup.hide,popup);
Congratulations,youcannowhappilyhoveranywhereonthemapandbegreetedwiththehoveredcoordinate:
Apopupforahoveredlocation
MoMoworkshop
290Popup
OverviewmapEspeciallywhenzoomedin,itcanbehardtounderstandtheextentofthemappanel.Overview-maps,whichshowtheextentofthemainmaponasmallerscalecanbeveryusefulthen.GeoExtcomeswithausefulcomponenttocreateoverviews:GeoExt.component.OverviewMap.
Exercises
We'llstartagainwiththecodeofmap.htmlfromtheprevioussections.It'salreadysomelineslong:
<!DOCTYPEhtml>
<html>
<head>
<metacharset="utf-8">
<title>Exercise|GeoExtWorkshop</title>
<linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/classic/theme-triton/resources/theme-triton-all.css"
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/extjs/6.0.0/ext-all.js"type="text/javascript"></script>
<linkrel="stylesheet"href="./lib/ol/ol.css"type="text/css">
<linkrel="stylesheet"href="http://geoext.github.io/geoext3/master/resources/css/gx-popup.css"type="text/css">
<scriptsrc="./lib/ol/ol.js"type="text/javascript"></script>
<scriptsrc="https://geoext.github.io/geoext3/master/GeoExt.js"type="text/javascript"></script>
<style>
.gx-popupp{
padding:5px5px05px;
border-radius:7px;
background-color:rgba(255,255,255,0.85);
border:3pxsolidwhite;
margin:0;
text-align:center;
}
</style>
</head>
<body>
<script>
varmap;
Ext.onReady(function(){
varredStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(220,0,0,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(220,0,0,0.8)',
width:3
}),
radius:8
})
});
varblueStyle=newol.style.Style({
image:circle=newol.style.Circle({
fill:newol.style.Fill({
color:'rgba(0,0,220,0.5)'
}),
stroke:newol.style.Stroke({
color:'rgba(0,0,220,0.8)',
width:3
}),
radius:8
})
})
varvectorLayer=newol.layer.Vector({
source:newol.source.Vector({
MoMoworkshop
291Overviewmap
url:'data/aimag-centers.json',
format:newol.format.GeoJSON()
}),
name:'Aimag',
style:redStyle
});
//1)OpenLayers
//
//CreateaninstanceofanOpenLayersmap:
map=newol.Map({
layers:[
newol.layer.Tile({
source:newol.source.OSM(),
name:'OpenStreetMap'
}),
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://ows.terrestris.de/osm/service',
params:{
layers:'OSM-WMS'
}
}),
name:'OSMWMS(terrestris)'
}),
vectorLayer
],
view:newol.View({
center:ol.proj.fromLonLat([106.92,47.92]),
zoom:4
})
});
//2)GeoExt
//
//CreateaninstanceoftheGeoExtmapcomponentwiththatmap:
varmapComponent=Ext.create('GeoExt.component.Map',{
map:map,
region:'center',
pointerRest:true,
pointerRestInterval:750,
pointerRestPixelTolerance:5
});
varpopup=Ext.create('GeoExt.component.Popup',{
map:map,
width:200
});
//Addapointerresthandlertothemapcomponenttorenderthepopup.
mapComponent.on('pointerrest',function(evt){
varcoordinate=evt.coordinate;
varlonlat=ol.proj.transform(coordinate,'EPSG:3857','EPSG:4326')
varhdms=ol.coordinate.toStringHDMS(lonlat);
popup.setHtml('<p>'+hdms+'</p>');
popup.position(coordinate);
popup.show();
});
//hidethepopuponceitisn'tonthemapanylonger
mapComponent.on('pointerrestout',popup.hide,popup);
vartreeStore=Ext.create('GeoExt.data.store.LayersTree',{
layerGroup:map.getLayerGroup()
});
varlayerTreePanel=Ext.create('Ext.tree.Panel',{
title:'Layersoftheapplication',
width:300,
region:'west',
store:treeStore,
rootVisible:false,
MoMoworkshop
292Overviewmap
viewConfig:{
plugins:{ptype:'treeviewdragdrop'}
}
});
varfeatureStore=Ext.create('GeoExt.data.store.Features',{
layer:vectorLayer,
map:map
});
varfeatureGrid=Ext.create('Ext.grid.Panel',{
store:featureStore,
region:'south',
title:'CentersofMongolianAimag',
columns:[
{xtype:'gx_symbolizercolumn',width:30},
{text:'Name',dataIndex:'NAME',flex:3},
{text:'Population',dataIndex:'POP',flex:1},
{text:'AIMAG_ID',dataIndex:'AIMAG_ID',flex:1}
],
listeners:{
selectionchange:function(sm,selected){
//resetallselections
featureStore.each(function(rec){
rec.getFeature().setStyle(null);
});
//highlightgridselectioninmap
Ext.each(selected,function(rec){
rec.getFeature().setStyle(blueStyle);
});
//updatethegridrenderingofthegeometry
sm.view.refresh();
}
},
height:300
});
//3)ExtJS
//
//Createaviewport
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
layerTreePanel,
featureGrid
]
});
});
</script>
</body>
</html>
Ifyouopenthisfileinabrowser(/map.html),theapplicationshouldlooklikeinthefollowingimage,butyoushouldalsobeabletoseepopupswhenhoveringoveramaplocation:
MoMoworkshop
293Overviewmap
Ourstartingpoint
Wewanttheoverviewmaptoliveinthetop-leftcornerofourapplication,rightabovethelayertree.Forthiswewill—asusual—firstpreparethelayoutbeforeweusetheGeoExtcomponent.Createanewpanelthatwewilleventuallyreplacewiththeoverview,butdon'tadditanywhereyet:
varoverviewPanel=Ext.create('Ext.panel.Panel',{
title:'Overview',
layout:'fit',
html:'TODO',
height:300,
width:300
});
Insteadofassigningtheregion:'west'tothelayertreepanel,we'llcreateanewcontainerwiththevbox-layoutandpassthattotheitemsoftheExt.container.Viewport:
varvp=Ext.create('Ext.container.Viewport',{
layout:'border',
items:[
mapComponent,
//belowisthenewwrappingcontainer:
{
xtype:'container',
region:'west',
layout:'vbox',
collapsible:true,
items:[
overviewPanel,
layerTreePanel
]
},
featureGrid
]
});
MoMoworkshop
294Overviewmap
Ifwespecifyflex:1forthelayerTreePanel(theregion-propertyisnolongerneeded),yourapplicationshouldlooklikethis:
Thepreparedlayout
NowitistimetouseGeoExt.component.OverviewMap:CreateaninstanceofthisclassandreadtherelatedAPI-docs.ConfiguretheoverviewPanelwiththecreatedoverviewinsteadofhtml:'TODO'(viaitems).Youmaywanttohaveanotherlayerintheoverview.HowaboutthisWMS?
URL:http://ows.terrestris.de/osm-gray/service
Layers:OSM-WMS
Hint
MoMoworkshop
295Overviewmap
varoverview=Ext.create('GeoExt.component.OverviewMap',{
parentMap:map,
layers:[
newol.layer.Tile({
source:newol.source.TileWMS({
url:'http://ows.terrestris.de/osm-gray/service',
params:{
layers:'OSM-WMS'
}
}),
opacity:0.8
})
]
});
varoverviewPanel=Ext.create('Ext.panel.Panel',{
title:'Overview',
layout:'fit',
items:overview,
height:300,
width:300,
collapsible:true
});
Ifeverythingwentwell,youshouldseeanapplicationlikebelow:
Thefinalapplication
MoMoworkshop
296Overviewmap
Other&summaryCongratulations!Youcreatedquiteanapplicationwithjustaround200linesofJavaScript;andthatincludesplentyofcommentsandwhitespace.
GeoExtstillhasmoretooffer.
Wecouldn'ttalkaboutlegendsinthetree,thesuperusefulGeoExt.OlObject-classorthePrintProviderwhichallowsyoutoserializeyourmaptoaformatunderstandablebythesuperbMapfishPrintServlet(v3).
Makesuretocheckoutallexamples,theAPIdocumentation(alsoavailablewithExtJS-classes)andthecodeongithub.
Thesourceforthisworkshopisalsoongithub.Ifyoufindanerrororoutdatedsection,justopenanissueor—evenbetter—provideuswithapullrequest.
Wehopeyoulikewhatyouhavelearned.
MoMoworkshop
297Other
SynopsisCongratulations!You'vesuccessfullyfinishedtheworkshop"IntroductiontocoretechnologiesbehindtheMoMogeoportal" !
MoMoworkshop
298Synopsis