Web based gis and domain model integration 3 10 2008 pieter de graef
This presentation is the property of its rightful owner.
Sponsored Links
1 / 64

Web based GIS and domain model integration 3/10/2008, Pieter De Graef PowerPoint PPT Presentation


  • 66 Views
  • Uploaded on
  • Presentation posted in: General

Web based GIS and domain model integration 3/10/2008, Pieter De Graef. What is GeoMajas ?. GeoMajas is: a Web Mapping framework based on a Java server and an AJAX/SVG/VML client that supports editing of complex domain objects and geometries and much more…of course. Where to find GeoMajas ?.

Download Presentation

Web based GIS and domain model integration 3/10/2008, Pieter De Graef

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


Web based gis and domain model integration 3 10 2008 pieter de graef

Web based GIS and domain model integration3/10/2008, Pieter De Graef


What is geomajas

What is GeoMajas ?

  • GeoMajas is:

    • a Web Mapping framework

    • based on a Java server

    • and an AJAX/SVG/VML client

    • that supports editing of complex domain objects and geometries

    • and much more…of course


Where to find geomajas

Where to find GeoMajas ?

  • http://www.geomajas.org/

    • Check out the source code

    • Build using Maven

    • Import projects into Eclipse

    • Run on your favorite servlet engine


Getting started

Getting started

  • Start up eclipse

  • Open the project majas-foss4g

  • Open the folder:majas-foss4g/src/main/webapp/applications/workshop

  • The Ant build file build.xml contains targets for the 5 exercises of this workshop

  • Run the target exercise1:dojo

  • Refresh the project majas-foss4g and start the server

  • Open your Web browser and enter the following URL:http://localhost:8180/majas-foss4g/applications/dojo/html/index.html


Exercise 1 the dojo widget system

Exercise 1: the Dojo widget system

  • The following layout appears:


Exercise 1 the dojo widget system1

Exercise 1: the Dojo widget system

  • The Dojo widget system offers the following functionality:

    • Different types of layout containers: ContentPane, SplitContainer, AccordeonContainer, TabContainer

    • Different types of widgets: textfield, checkbox, combobox, colorpalette, spinner, calendar, radionbutton, tree,…

    • General event handling mechanism:dojo.connect(source,event,target,function);

    • Widget hierarchy:dojo.declare("myWidget", [dijit._Widget, dijit._Templated, dijit._Container], {templatePath : "path/to/HTML/template"  postCreate : function () {…}}(widgets use HTML templates for their definition)


Exercise 1 todos

Exercise 1: todos

  • Open the page html/index.html

  • There are 2 TODOs indicated in the page:

    • Log the id in the closure function by using the built-in log facility:log.info(id);

    • Highlight the widget by changing a style attribute:dijit.byId(id).domNode.style.backgroundColor="blue";

  • Reopen the page in the browser and click on each widget to see the result


Exercise 2 helloworld map

Exercise 2: Helloworld map

  • The purpose of this exercise is to demonstrate elementary Majas configuration concepts

  • Serious warning: after running a target for an exercise, your own writings for the previous exercises will be replaced with the solution of those exercises!

  • Select the file applications/workshop/build.xml again, and run the target: exercise2:helloworld

  • Then refresh the majas-foss4g project.


Exercise 2 configuration basics

Exercise 2: configuration basics

  • All configuration is done via XML files

  • Full schema validation, schemas are available at …

  • Uses JAXB for generating configuration code

  • Uses XInclude to split up the configuration over multiple files

  • Configuration changes require a restart !


Exercise 2 application xml

Exercise 2 : application.xml

  • Contains factory classes for raster and vector layers

  • Refers to the other configuration files:

    • layerTree.xml : configuration of layer tree widget

    • maps.xml : configuration of map widgets

    • toolbar.xml : configuration of toolbar widgets

    • tools.xml : configuration of available tools and actions


Exercise 2 adding a raster layer factory

Exercise 2 : adding a raster layer factory

  • Go to directory applications/helloworld

  • Open the file application.xml

  • Insert the following snippet:

    <rasterLayerFactory id="osm">

    <factoryClass>OSMLayerFactory</factoryClass>

    <parameterMap />

    </rasterLayerFactory>

  • factoryClass: java class name, must implement LayerFactory interface and create RasterLayer implementation

  • parameterMap: a list of parameters to be passed to the factory constructor (none in this case)


Exercise 2 overview of available raster layer factories

Exercise 2 : overview of available raster layer factories

  • GoogleLayerFactory :

    • Google Maps API layer

    • type (normal, satellite,…) determined by layer name (see further)

    • Needs the google maps script and key !

    • Spherical Mercator projection (EPSG:900913)

  • OSMLayerFactory :

    • OpenStreetMap tile layer

    • http://tile.openstreetmap.org/<level>/<x>/<y>.png

    • Spherical Mercator projection (EPSG:900913)

  • WMSLayerFactory :

    • Generic OGC WMS layer

    • <parameter name="baseWMSurl" value="<URL of the WMS server>" />

    • <parameter name="format" value="<format parameter>" />

    • <parameter name="version" value ="<version parameter>" />

    • <parameter name="srs" value ="<SRS parameter>" />


Exercise 2 maps xml

Exercise 2: maps.xml

  • Contains the configuration of each map used by this application (usually a main and an overview map)

  • Configurable parts:

    • Layers of the map

    • Layer z-order in the map

    • Layer order in the layer tree

    • Background-color, pan-buttons, scale widget

    • Selection styles for each geometry type (vector layers)

    • Coordinate Reference System (may be different from layer’s CRS !)

    • Maximum scale

    • Initial bounds

    • Discrete map resolutions (optional)

    • Overview map reference (optional, if overview map)


Exercise 2 adding a map

Exercise 2 : adding a map

  • Open the file applications/helloworld/maps.xml

  • Perform the following steps:

    • Choose a background color

    • Choose whether you want pan buttons and/or a scalebar

    • Pick your initial bounds, see next slide !

    • Have a look at the layer configuration


Exercise 2 adding a map1

Exercise 2 : adding a map

y

(0,20 037 508.34)

Spherical Mercator coordinates (m)

x

(0,0)

(-20 037 508.34, 0)

(20 037 508.34, 0)

(0,-20 037 508.34)


Exercise 2 adding a raster layer

Exercise 2: adding a raster layer

  • Open the file layers/osm.xml

  • Perform the following steps:

    • Choose a display name

    • Add the id of the correct raster layer factory

    • Pick an opacity level (opacity is inverse of transparency)

  • How are the resolutions calculated?

    • Resolution (m/pixel) is inverse of scale (pixel/m)

    • Tile is 256 by 256 pixels

    • There are 2n tiles per level, level 0 has 1 tile: the world

    • <distance>/<number of pixels> = (2*20037508.34)/(256*2n)for n=0 to 17


Exercise 2 making the first html page ready

Exercise 2: making the first HTML page ready

  • Open the page helloworld/html/index.html

  • Uncomment the load code:

    • By using dojo.addOnLoad() one can execute some custom JavaScript after the dojo widgets are loaded

    • In GeoMajas, we use this to load the map configuration from the server

  • Add the correct Dojo type to the map

    • dojoType="geomajas.widget.MapWidget"

    • This turns the <div> into a GeoMajas map widget


Exercise 2 have a look at your map

Exercise 2: have a look at your map!

  • Restart your server, open your browser and go to:

  • http://localhost:8180/majas-foss4g/applications/helloworld/html/


Exercise 2 tips and tricks

Exercise 2: tips and tricks

  • You can drag the map to pan (slippy map)

  • You can use the mouse scroller to zoom in and out

  • Optional: If you uncomment the <resolutions> tag in maps.xml and restart the server:

    • the zoom function will now snap to the resolutions in the list, which are these of the tiles

    • The tile images will not be resized, so they should be crystal clear!


Exercise 2 adding a toolbar

Exercise 2: adding a toolbar

  • GeoMajas has 2 styles of toolbar buttons:

    • Tools:

      • Can be selected/deselected by clicking

      • Selection will normally put the map in a certain mode by adding a specific controller to the map, deselection will remove it

      • A controller interprets mouse operations in a specific way by e.g. drawing a rectangle and zooming on release

    • Actions:

      • Can be invoked by clicking

      • An action can be almost anything, but usually also involves sending a command to the server

    • Each action or tool has an image and a tooltip


Exercise 2 predefined actions tools

Exercise 2: predefined actions/tools

Predefined actions


Exercise 2 predefined actions tools1

Exercise 2: predefined actions/tools

Predefined tools


Exercise 2 adding a toolbar1

Exercise 2: adding a toolbar

  • Open the file helloworld/toolbar.xml

  • Add one or more toolRef tags after the toolbar separator, one for each tool

  • Open helloworld/html/index.html, and set the dojoType for the toolbar: "geomajas.widget.DynamicToolbar"

  • Restart the server and navigate to http://localhost:8180/majas-foss4g/applications/helloworld/html/

  • Play around with the tools and actions


Exercise 2 adding an overview map

Exercise 2: adding an overview map

  • Open the file helloworld/maps.xml

  • Copy the map declaration and:

    • Rename the id to helloOverviewMap

    • Put the id of the main map inside the overview tag:<overview>helloMap</overview>

    • Choose a large enough initial bounds area

  • Restart the server and navigate to http://localhost:8180/majas-foss4g/applications/helloworld/html/

  • See how the main map follows if you drag the blue rectangle!


Exercise 3 simple vector map

Exercise 3: Simple vector map

  • The purpose of this exercise is to demonstrate the vector layer capabilities of GeoMajas

  • We will also demonstrate some extra widgets:

    • LayerTree widget

    • Legend widget

    • FeatureListTable widget

    • SearchTable widget

    • Style widgets

  • Open the file applications/workshop/build.xml

  • First we run the target exercise3:simplevectors

  • Refresh the majas-foss4g project!

  • Go to applications/simplevectors


Exercise 3 adding a vector layer model factory

Exercise 3 : adding a vector layer model factory

  • Open the file simplevectors/application.xml

  • Insert the following snippet:

    <layerModelFactory id="worldShape">

    <factoryClass>ShapeInMemLayerModelFactory</factoryClass>

    <parameterMap>

    <parameter name="url" value="file:shapes/countries_world/world_adm0.shp" />

    </parameterMap>

    </layerModelFactory>

  • factoryClass: java class name, must implement LayerFactory interface and create VectorLayer implementation

  • parameterMap: a list of parameters to be passed to the factory constructor (none in this case)


Exercise 3 overview of available layer model factories

Exercise 3: overview of available layer model factories

  • GeotoolsLayerModelFactory:

    • wraps a Geotools datastore

    • parameters are the same as for the datastore (see geotools doc)

  • HibernateLayerModelFactory:

    • uses hibernate to map a layer of POJO objects to a geodatabase

    • no parameters, configuration read from standard hibernate.cfg.xml

  • ShapeInMemLayerModelFactory:

    • an in-memory layer model, initially based on a shape file

    • <parameter name="url" value="file:shapes/world/world.shp" />

    • (the url path is relative to application.xml folder in this case)

  • WKTLayerModelFactory:

    • layer model based on a WKT (Well-known-Text) text file

    • no parameters

  • MaxCoordSimplifierLayerModelFactory:

    • wraps an existing factory and simplifies its geometries

    • <parameter name="delegate-factory" value="ShapeInMemLayerModelFactory" />

    • <parameter name="url" value="file:shapes/world/world.shp" />

    • <parameter name="distance-tolerance" value="0.01" />

    • <parameter name="maximum-coordinates" value="1000" />


Exercise 3 adding a layer tree

Exercise 3: adding a layer tree

  • Now open simplevectors/maps.xml

  • What we have to do here, is to add the new world shapefile to the layerconfiguration.

    • Include the layers/countries_world.xml (layer config)

    • Include the layer in the mapOrder

    • Include the layer in the LayerTree order

  • To find the ID of the world layer, open it’s XML configuration file: layers/countries_world.xml


Exercise 3 adding a layer tree1

Exercise 3: adding a layer tree

  • Open the file simplevectors/html/index.html

    • Add the layer tree to the page by adding the correct dojoType ("geomajas.widget.LayerTree")

  • Restart the server and navigate to: http://localhost:8180/majas-foss4g/applications/simplevectors/html/index.html


Exercise 3

Exercise 3:

visibility

zoom to South-Africa to see some featuresand test new toolbar buttons !

labeling


Exercise 3 adding a legend

Exercise 3: adding a legend

  • The legend widget will show a symbol for all the styles of all the visible vectorlayers

  • Open the file simplevectors/html/index.html

    • Add the legend underneath the layer tree with the indicated width and heigth

    • Put the following code snippet in the postConfiguration() function to connect the legend to its map:

      var legend = dijit.byId("mainLegend");

      var mapWidget = dijit.byId("mainMap");

      legend.setMapModel(mapWidget.getMapModel());

    • Remark: the postConfiguration() can be used to execute some JavaScript after the GeoMajas configuration!

  • Refresh the browser page: http://localhost:8180/majas-foss4g/applications/simplevectors/html/index.html


Exercise 3 adding a legend1

Exercise 3: adding a legend


Exercise 3 styles and filtering

Exercise 3: styles and filtering

  • Open the file layers/countries_world.xml

  • The styles are defined at the bottom, in the <styleDef> tag

  • For each style, there is:

    • The name that will appear in the legend

    • The filter formula to determine on which features the style should be applied

    • The style parameters: fill color, fill opacity, stroke color, stroke opacity and stroke width

  • Change the style definition for south africa, restart the server and see how it looks !


Exercise 3 styles and tables

Exercise 3: styles and tables

  • Navigate to: http://localhost:8180/majas-foss4g/applications/simplevectors/html/more.html

  • Notice the layout has changed completely

  • At the bottom you will see 2 tabs. One empty and one containing a FontStyleWidget.

    • The font style widget can be used to change the style of the labeling

    • Turn on the labels for Africa, and fool around with the style.


Exercise 3 adjusting layer tree

Exercise 3: Adjusting layer tree

  • What still needs to be done, is to determine the tools and actions for the selected layer that are shown at the top:


Exercise 3 predefined actions tools for the layer tree

Exercise 3: predefined actions/tools for the layer tree

  • Actions

    • ShowTableAction : shows a featurelist table

    • RefreshLayerAction : refreshes the layer

  • Tools

    • LayerVisibleTool : makes the layer visible/invisible

    • LayerLabeledTool : shows/hides labels of features

    • LayerSnappableTool : makes features snappable or not


Exercise 3 predefined actions tools for the layer tree1

Exercise 3: predefined actions/tools for the layer tree

  • Open the simplevectors/layertree.xml

  • Add a tool action to the list:

    • ShowTableAction


Exercise 3 adding a feature list table

Exercise 3: adding a feature list table

  • Open the file simplevectors/html/more.html

  • Add the feature list table by inserting the following piece of xml:

    <div id="mainTable" dojoType="geomajas.widget.FeatureListTable" supportEditing=false style="width: 100%; height: 100%; border: 0px solid #888;"></div>

  • Restart the server and navigate to http://localhost:8180/majas-foss4g/applications/simplevectors/html/more.html


Exercise 4 openstreetmap and complex domain modeling

Exercise 4: OpenStreetMap and complex domain modeling

  • The purpose of this exercise is to demonstrate complex domain modelling with GeoMajas

  • An OpenStreetMap dataset has been copied to your local PostGIS database to serve as a simple domain model

  • In this exercise you will work on the Java mapping via HibernateSpatial to set up an editable map

  • First we run the target exercise4:osm

  • Refresh the majas-foss4g project.


Exercise 4 geomajas complex domain modeling

Exercise 4: GeoMajas complex domain modeling

  • Starts from annotated Java classes and database schema

  • Uses Hibernate(Spatial) to manage the O/R mapping

  • Has support for many-to-one, one-to-many, many-to-many relationships

  • Uses metadata FeatureModel for feature access, e.g. translating back-and-forth to JSON

    • Primitive attributes

    • Association attributes


Exercise 4 open streetmap data model subset

Exercise 4: Open StreetMap data model (subset)

k="oneway" v="yes“

k="created_by" v="Potlatch 0.8c“

Node =

+ set of tags

+ set of tags

or

Way =


Exercise 4 osm database schema

Exercise 4: OSM database schema

many-to-one

one-to-many


Exercise 4 osm java classes

Exercise 4: OSM Java classes

  • Many-to-One association:

    @ManyToOne(cascade = { CascadeType.PERSIST, CascadeType.MERGE })

    @JoinColumn(name = "user_id")

    private User user;

    • @join-column: refers to foreign key column

    • cascade: determines what happens if the many-side is persisted: in this case the user will be added too if he/she is new (PERSIST) or updated if he/she already existed but has some properties changed, e.g. the name (MERGE)


Exercise 4 osm java classes1

Exercise 4: OSM Java classes

  • One-to-Many association:

    @OneToMany(mappedBy = "way", fetch = FetchType.EAGER, cascade={CascadeType.ALL})

    @org.hibernate.annotations.Cascade(value = {

    org.hibernate.annotations.CascadeType.ALL,

    org.hibernate.annotations.CascadeType.DELETE_ORPHAN })

    private List<WayTag> tags = new ArrayList<WayTag>();

  • mappedBy : makes the many-side responsible for mapping the relationship

  • fetch : determines fetch type, EAGER means fetch immediately

  • cascade : determines what happens if the one-side is persisted: in this case new tags will be added, updated tags will be updated (ALL) and missing (orphaned) tags will be deleted (DELETE_ORPHAN)


Exercise 4 add an attribute

Exercise 4: Add an attribute

  • Open osm/layers/ways.xml

  • Add the timestamp attribute to the list:

    <attribute>

    <label>Creation Date</label>

    <name>timestamp</name>

    <editable>true</editable>

    <identifying>true</identifying>

    <type>date</type>

    </attribute>


Exercise 4 osm editing

Exercise 4: OSM editing

  • Restart the server and navigate to http://localhost:8180/majas-foss4g/applications/osm/html/index.html

  • Open the featurelist table by selecting the Ways layer and clicking on the table icon

  • Double-click on a row of the table to open the editing dialog

  • Edit different types of data:

    • Timestamp property : date primitive

    • User property : many-to-one

    • Tags property : one-to-many


Exercise 4 osm editing1

Exercise 4: OSM editing


Exercise 4 add constraints

Exercise 4: Add constraints

  • Open osm/layers/ways.xml

  • Find the timestamp attribute in the attribute list, and add a validator after the <type>-tag:

    <validator>

    <constraints>{min:'2004-01-01', max:'2008-12-31'}</constraints>

    <required>true</required>

    <promptMessage>Creation date.</promptMessage>

    <invalidMessage>The required date format is "dd/MM/yyyy".</invalidMessage>

    <rangeMessage>Range: between 01/01/2004 and 31/12/2008.</rangeMessage>

    </validator>


Exercise 4 constraint test

Exercise 4: Constraint test

  • Restart the server and navigate to http://localhost:8180/majas-foss4g/applications/osm/html/index.html

  • Open the featurelist table by selecting the Ways layer and clicking on the table icon

  • Double-click on a row of the table to open the editing dialog

  • Try to fool around with the date


Exercise 5 creating buffers

Exercise 5: Creating buffers

  • How to create and display a buffer around a selection of lines.

  • This problem can be subdivided into many smaller problems:

    • How to get the selected features, and more importantly, their geometries?

    • Since buffer calculation is very heavy, how can we let the server do this for us?

    • How can we show the resulting geometry on the map?


Exercise 5 creating buffers1

Exercise 5: Creating buffers

  • Starting point: a layer with lines:

  • Line-layer: Streets of Washington

  • layerType = 3 (LineString)

  • Run target exercise5: advanced

  • Refresh the majas-foss4g project.

  • Location:

    http://localhost:8080/majas-foss4g/applications/advanced/html/index.html


Exercise 5 creating buffers2

Exercise 5: Creating buffers

  • Todo:

    • Look for selection. If there is no selection in the linelayer, return an error message.

    • If there are streets selected, calculate a buffer around them.

    • Problem: calculating a buffer around multiple lines, will require us to make a union first and then calculate the buffer. This is too heavy a calculation to be done in Javascript. We must let the server calculate this!

    • So we need communication between client and server.

    • Last but not least the result must be rendered. Problem: the streets were stored in lonlat. So we receive a lonlat geometry as buffer on the client => we must first transform this to screenspace!


Exercise 5 creating buffers3

Exercise 5: Creating buffers

  • When the exercise starts, you can see 2 buttons and a textbox at the bottom:

  • Press the « Create Buffer » button and see what happens.

  • A rectangle is drawn! This has nothing to do with current selection.


Exercise 5 creating buffers4

Exercise 5: Creating buffers

  • Open advanced/html/index.html

  • When looking at the code, we see:

    // Execute the command for buffer calculation:

    var command = new JsonCommand("org.geomajas.foss4g.command.CreateBufferCommand", null, false);

    command.addParam("distance", distance);

    command.addParam("streets", selStreets);

    var deferred = majasConfig["dispatcher"].execute(command);

    deferred.addCallback(bufferCallback);

  • This executes the CreateBufferCommand


Exercise 5 creating buffers5

Exercise 5: Creating buffers

  • How a command works:

  • We must fill in the exact Java classname!

  • Add params, with names for which setters can be found in the Javaclass. Example:

  • addParam(‘distance’) results on the server in the setter ‘setDistance’.

  • Last line connects a callback function that will handle the command’s result.


Exercise 5 creating buffers6

Exercise 5: Creating buffers

  • Open the CreateBufferCommand in Java Resources (src/main/java) ("org.geomajas.foss4g.command.CreateBufferCommand")

  • Look at the required fields:

    private LineString[] streets;

    privatefloat distance;

  • Apparently we need to pass the buffer distance as a float, and an array of LineString objects. (the selected streets)

  • Next take a look at the «execute» function.


Exercise 5 creating buffers7

Exercise 5: Creating buffers

  • We see the part that writes out the rectangle:

    // TODO: Temporary buffer, a rectangle - Remove this:

    GeometryFactory factory = new GeometryFactory();

    LinearRing shell = factory.createLinearRing(new Coordinate[] {

    new Coordinate(100, 100), new Coordinate(400, 100),

    new Coordinate(400, 300), new Coordinate(100, 300),

    new Coordinate(100, 100) });

    temp = factory.createPolygon(shell, null);

    // Remove until here.

  • Since this is not correct, we should replace it with the loop above it:


Exercise 5 creating buffers8

Exercise 5: Creating buffers

for (int i = 0; i < streets.length; i++) {

LineString street = streets[i];

if (temp == null) {

temp = street;

} else {

temp = temp.union(street);

}

}

  • What this does, is create the union of all the LineStrings that have reached the server.

  • Restart the server, and check again.

    http://localhost:8080/majas-foss4g/applications/advanced/html/index.html


Exercise 5 creating buffers9

Exercise 5: Creating buffers

  • We create a buffer again:

  • An empty array? Let’s see the Javascript again! An empty array?

  • Indeed command.addParam("streets", selStreets);sends an empty array. We need to find the selection!


Exercise 5 creating buffers10

Exercise 5: Creating buffers

  • Check if there actually is a selection:

  • Find this line:

    // TODO: check if there actualy is a selection!

  • And replace it with:

    var streetLayer = mapWidget.getMapModel().getLayerById("mainMap.streets");

    if (streetLayer.getSelectionStore().getElementCount() == 0) {

    alert("There are no streets selected!");

    return;

    }


Exercise 5 creating buffers11

Exercise 5: Creating buffers

  • Now fill the array with the geometries belonging to the selected features:

  • Find this line:

    // TODO: fill the "selStreets" array with LineStrings.

  • And replace it with:

    var selected = streetLayer.getSelectionStore().getElements();

    var keys = selected.getKeyList();

    for (var i=0; i<keys.length; i++) {

    var street = selected.item(keys[i]);

    selStreets.push(street.getGeometry());

    }

  • Selected is a dictionary, with the feature’s ID as key.


Exercise 5 creating buffers12

Exercise 5: Creating buffers

  • If we now check again, we should see the buffer right?

  • Wrong! Something is still missing. What?


Exercise 5 creating buffers13

Exercise 5: Creating buffers

  • Transformation from world to view!

  • Find the following line:

    // TODO: Something is missing here!

  • And replace it with:

    var transformer = new WorldViewTransformation(mapWidget.getMapView());

    geometry = transformer.worldGeometryToView(geometry);

  • Let’s try again!

    http://localhost:8080/majas-foss4g/applications/advanced/html/index.html


The end

The end!

  • Thank you for your attention!

  • Visit us at http://www.geomajas.org/


  • Login