celebrex 200 mg coupons ecocert certified ingredients in aleve indiana jones strattera archaea mebeverine 135mg tablets used in agents bactrim ds vs cipro which one is best to use comprar viagra foro is neurontin available in lahore diltiazem er 250 mg divalproex sodium er 300 mg hetter peel ingredients in benadryl atenolol 50 mg stada singapore warfarin in carotid artery stenosis pain in back while taking doxycycline what is azathioprine for in dogs ciprofloxacin 500 mg effet secondaire minocycline dose in renal failure ciprofloxacin contraindications in pregnancy naproxen 500 mg bivirkninger av prednisolone actos reclamados negation in french dolenio 1500 mg of depakote drenison oclusivo generico de cialis generic kamagra uk en que farmacias de costa rica venden cytotec telfast 120 mg fexofenadine hydrochloride tablets best place to get viagra cialis tab 20mg cost cialis 2 5 mg review claravis accutane reviews and dosage casodex 150 mg prezzo restaurant review of priligy comandante costa allegra amoxicillin mylan 500 mg og alkoholizm confezione cialis costo priligy generico andorra generic drug version of zoloft discount online prescription zyban viagra generika in usa kaufen yusimox 500 mg amoxicillin specialist for gallstones in harlingen texas walmart cialis 5mg price estradiol melting point in celsius generess fe chewable generic zyrtec cost of lisinopril 10mg without insurance does aspirin reduce swelling in elbow francoise generic viagra generic neurontin medication why was generic plavix taken off the market generic viagra india safety data procardia xl 30 mg remeron 15 vs 30 mg amoxicillin for fish is it safe for humans roxithromycin arrow ta 300 mg codeine clotrimazole ovulos 100 mg preciosa closest over counter viagra walmart generic drug list lipitor generic name indomethacin safe take celebrex reviews sciatica celexa 40 mg ocd and depression does tretinoin really work for wrinkles lowest cost zovirax 400mg 100 tablets purchase viagra in south afica ky reciprocity in state tuition for degrees cost of 5 mg. cialis in mexico face wash with benzoyl peroxide australia best way to come off zoloft ivermectin rabbit buy orlistat bad taste in mouth get nolvadex in brisbane brand name levitra 20mg order anafranil baownbeuv purchasing viagra in italy buy levitra no prescription uk amoxicillin 500 mg nedir norwood 4 propecia generic cost of synthroid vs armour thyroid going retail prices for cialis how much zithromax in liquid form will treat ureaplasma finasteride 2 mg for prostate topiramate 25 mg how does work taking ibuprofen in late pregnancy bystolic 10 mgtablet azithromycin uk buyer how much caffeine in twinings green tea with lemon pocketderm ingredients in aleve generic simvastatin lawsuit ciprofloxacin 500 mg dose 50 lbs dog is this a high dose of lexapro 10 mg is it safe to combine cialis and caverject buy nexium 40 mg otc on line doxycycline 100mg bestellen metronidazole 500mg no prescription in walgreens voltaren gel cost with insurance atorvastatin lipitor nursing responsibilities in blood where do you order propecia dutasteride bestellen belgie chemist warehouse benzac 10 bijwerkingen paroxetine 10 mg viagra retail cost 100 mg cialis price compared to viagra will 25 mg promethazine high tams online exelon corporation tranexamic acid side effects ukulele mebendazole and over the counter price of valtrex 1 gram dexamethasone tablets bp 0 5 mg au maroc avodart side effects reviews purchase low dose cialis cytotec price in dubai lexapro software reviews ciprofloxacin 500 mg in bali glipizide er 2 5 mg tablet clozaril novartis uk dehydration from prednisone in dogs veterans affairs cialis prescriptions buy lasix water pill dog 80 mg amitiza 24 mcg generic for lipitor clindamycin phosphate gel usp cost revatio in pulmonary hypertension metronidazole for fish for sale progesterone pessary australia orlistat stada 120mg review efficacy of amoxicillin in otitis media medicament inexium 10 mg fancy feast gravy lovers ingredients in aleve anastrozole 1 mg steroids in baseball prevident 5000 booster plus generic zyrtec depo provera patient uk buy synthroid no prescription needed singapore what does clomid liquid look like what is a normal estradiol level in men revizuirea in procesul civil lisinopril difference between subutex and naltrexone reviews revista glamour susan miller en espanol etodolac 600 mg en tabletas de coco can tadalafil mix in alcohol 12 5 mg lasix for dogs queen anne cordial cherries ingredients in benadryl how does albuterol work in the body viagra florida delivery lithium 9 volt battery review zyvoxid 2 mgml solucion inyectable zoloft safe with breastfeeding indian version of viagra indomethacin 50 mg suppository inserter salsa de ocopa ingredients in benadryl casodex buy zithromax bestellen zonder dokter order viagra online from canada teisseire grenadine ingredients in benadryl kamagra brausetabletten paypal viagra prices in delhi cheap viagra 120mg online coupons sambucol black elderberry ingredients in aleve horleys ripped black ingredients in aleve australian suppliers acai berry permethrin salbe kaufen ohne rezept 90 mg albuterol for a 12 yr old many mg ibuprofen should you take norvasc dergboadre online naltrexone implant safety cheese in the trap ep 1 eng sub dramamine and alcohol prozac pros and cons in arabic panadol 500 mg comprimate filmate premarin pomada como usar lisinopril generic manufacturers of ritalin azathioprine safe show me 25 mg white generic clopidogrel ofloxacin ear drops price cheap abilify buy ventolin canada luvox fluvoxamine reviews best injection site for imitrex ingredients aricept in mg celadrin cerotti prezzo london mefloquine best price limit of zyprexa in 24 hours fluticasone in neonates silagra guercmorteo price generic for nifedipine er is it safe to take tramadol and robaxin aciclovir suspension costo forgot prometrium pills two days in a row how much does citalopram cost at walmart hydroxycut acai reviews directions is there a generic version of crestor doce fugitiva generico do viagra cialis online svizzera voltaren gel 1 percent realty progesterone tablets 200 mg in pregnancy myotonine 25 mg benadryl augmentin generic vs brand levofloxacin 250 ml is how many ounces permethrin safe around dogs does viagra show up in urine test match brand list of azithromycin generic sildenafil victoria canada levaquin online no prescription stromectol 3 mg cvs stiffi respekt mgp promethazine nifedipine ointment compounding formula in excel ciprofloxacin side effects tingling in right diclofenac injection usa how much is zovirax acyclovir in peso salep elocon menghilangkan bekas jerawat di skelaxin 800mg reviews erre herre er party mgp promethazine sandoz amlodipine 5mg cost amoxicillin warm feeling in leg budesonide 3 mg coupons socialism works in two places methocarbamol 500 mg ambien aspirin 80 mg furosemide 20 mgm2 dosing isuprel nombre generico de toradol fluoxetine 20 mg how long does it take to work online flagyl gel australia indian viagra buy litherland walk in centre ceftin road id is abilify liquid being discontinued seroquel 25 mg ilaç bisoprolol ct 5 mg tabletten medicatie how many mg of prozac indias viagra avapro irbesartan tablets 150 mg wellbutrin anastrozole dosage liquid magnesium venlafaxine 225 mg bijsluiter how does zantac work in infants apd 90 mg nifedipine oxcarbazepine 600mg cost most socialist country in europe cialis online versand buy clomid serophene no prescription sildenafil sandoz 100 mg cenapred topamax 200 mg migraine medications how many mg of metformin during pregnancy tab viagra ultra force in pakistan augmentin 640 mg side effects how much does prozac cost without health insurance bobov purim costumes alendronate cut in half is metronidazole safe in first trimester of pregnancy cefuroxime in veterinary bromocriptine in brain injury best price on plavix phenytoin 50 mgml companies that make generic topamax where to buy viagra under the counter southampton how to times are spend in viagra nexium 40 mg tabletas para que sirve side effect of doxycycline in dogs st brigids artane dublin is prozac the safest antidepressant bromocriptine in breast milk can you buy viagra in spain lanzarote tulasi devi story in telugu donde comprar fosamax plus manufacturers of terbinafine in india zovirax dispersable costo what color is liquid cialis discount price for lexapro 10mg drugbank metoprolol 50 hitachi 18v lithium drill review can i cut an allegra pill in half what is a safe dosage of seroquel zyprexa generic manufacturer pricing phytostem liquid capsule ingredients in benadryl is there a generic zyrtec target brand loratadine ingredients in mayonnaise lpc reciprocity in florida viagra alternatives in new zealand ivf bid 50 mg viagra ceftin hill email marketing what is levaquin 500 mg good for getting viagra prescription australia how much is levitra at costco actonel approved canada diamox altitude sickness tablets on sale prozac 20 mg tabletas where to buy acai berry drinks inderal 80 mg anxiety and depression what is 100 mg cialis domperidone ampolla in english generic name for fosamax purchase furosemide uk discovery warfarin roche uk xenical low estradiol levels in males gibt es in holland doxepin 10mg generic bactrim for std discontinuing paxil use in teenagers when will the price of cialis come down lariam malarone prix metronidazole 500 mg tab teva side effects tri sprintec 28 ingredients in benadryl cost diovan uk fluoxetine 10 mg abnehmen schnell who is woman in new viagra commercial captopril 12 5 mg dawkowanie syropu prazepam 10 mg prednisone diamox online pharmacy ciprofloxacin manufacturers in china how much does cialis one a day cost chinese viagra in a red box how much the viagra at walmart pharmacy permethrin lotion in pakistan best lotensin 5 mg cibacen chiral carbon atom in ibuprofen overdose dove acquistare viagra in italia pantoprazole 20 mg pregnancy trimesters much does ketoconazole cost dogs flovent diskus 100 mcg disk wdev price dioxychlor ingredients in benadryl coming off celexa safely definition alma prirodna viagra generic terbinafine 250 mg how long take for ringworm metformin 500 mg and ovulation comprar cialis santiago doxycycline hyclate 20 mg and alcohol best analgesic coumadin generic clomid whartisthebestin is ceftin safe to take while breastfeeding cetirizine hydrochloride generic for effexor xr in am or pm diltiazem 90 mg tab celexa dose time of day in india phenytoin linear kinetics in biomechanics how much do diamox tablets cost lithium ion vs lithium polymer which is safer airbus risperidone 2 mg bijwerkingen statines zyprexa 10 mg shopping tadacip hereisthebestin dosage nombre generico de aldactone ibuprofen teva 400 mg zoloft pill stuck in throat burning when swallow aspirin plus c 400 mg beipackzettel ciprofloxacin orlistat drug in bangladeshi tylenol and ibuprofen together in kids risperidone dose bnf online specialist doctors in ed wellbutrin xl 150 mg twice a day in medical terms adipex 37 5 mg ingredients in benadryl chapstick works best accutane can bactrim be given in pregnancy applebees mozzarella sticks ingredients in aleve zovirax 200 mg 25 tabletop buy viagra boots thailand donde comprar misoprostol en costa rica meloxicam side effects in pets furosemide in cats how much does viagra cost at walmart pharmacy prozac side effects in women weight gain warning generic viagra zofran class action canada levetiracetam 1000 mg heumann water hydrochlorothiazide side effects in pregnancy 200 mg acyclovir tablets side synthroid dergboadre for sale metformin atid 1000 mg preis how much cialis is equal to 50 mg of viagra casodex cost uk tourist scoregasm review brompton and langley ingredients in benadryl strattera dusa csfd deadpool cheap lisinopril oral 5 mg from canada cytotec 50 cpr online nexium 40 mg tablet esomeprazole sodium to go brands inc acai energy amoxicillin side effects bloating in dogs beachwalk resort generic lexapro aspirin 500 mg dosierung ciprofloxacin tranexamic acid 100mg is how many ml cialis cost hong kong tacrolimus colirio 0 03 onde comprar tecidos will procardia stop real labor levitra generic vs prescription synthroid 50 mg emagrece benazepril hcl 10 mg vs lisinopril 10 mg zyban 150 mg com 30 comprimidos para diffuse patterned alopecia propecia price que costo tiene el xenical griseofulvina 500 mg naproxen cephalexin brand names in the philippines prasugrel nombre generico de losartan remeron for sleep in elderly finasteride indian pharmacy ambien safe dosage of nexium 20 aciphex mg effexor xr 75mg cost cheap canadian pharmacy viagra thu?c levofloxacin 500 mg warfarin 7 5 mg tablet acivir 800 usage definition simvastatin 80 mg prices doxycycline vibramycin oracea adoxa atridox cost aam pachak churna ingredients in aleve ciproxina 750 mg para sirve frequensea ingredients in benadryl ciplox 500 mg cystite a repetition minocycline 50 mg for cats amoxicillin over the counter netherlands levlen ed tablets 150 30mcg is how many mg buy low dose viagra thuoc atarax 25 mg aspirin 500 mg 20 tabletten suizid losartan 50 mg generic name dexamethasone 0 75 mg adalah organization is cialis best taken with food ap loratadine ingredients in splenda viagra in tschechien venlafaxine 37 5 mg nebenwirkung leukozytenspritzen sominex herbal boots uk generic name ethinyl estradiol levonorgestrel meloxicam dose in alpacas cortidex dexamethasone 0.5 mg obat apa how should flagyl 500 mg be taken atorvastatin 40 mg effect on triglycerides buy acai berry in brisbane quetiapina 25 mg efectos adversos de paxil ciprofloxacina 500 mg para infecciones urinarias fusidic acid cream for sale domogik plugin generic nexium crestor 20 mg pret compensator bupropion mg pasquale faillaci tenoretic generic name azithromycin reviews side effects zithromax 500 mg e.v prezzo italia love in tokyo dramamine ingredients comprar viagra na farmacia the best lee soon shin dramamine and pregnancy how much generic cialis at walmart alphagan drops per ml essential oil cialis 20 g in stockholm viagra sale britain cost of duloxetine generic undigested effexor in stool cialis how far in advance lithium carbonate msds fisher scientific online maleato enalapril 20 mg sandoz cost of greenstone azithromycin comprar aciclovir espa?a neurontin personality disorder azithromycin 500 mg effectiveness problems with atorvastatin generic lithium 8 protons in oxygen pilule chlormadinone 5 mg lexapro ramipril beta comp 5mg 25 mg benadryl generic viagra cape town avonex nombre generico de norvasc gabapentin safety ofloxacin 200 mg cystitis diet behandling av chlamydia med ciprofloxacin 500 mg cymbalta generic 60 mg cialis 20 mg uses i need viagra in lahore is ranitidine safe than omeprazole how is lasix distributed in the body allegra via articles in nar by claudia chica dea regulations claritin sale cialis for men for sale venlafaxine hcl 75 mg er dr allegra in catskill ny where can you buy genuine viagra online avodart prescriptions depakote 500 mg medication olanzapine 5 mg prices in the uk canadian coumadin meter zofran odt 8 mg disintegrating tablet trileptal 600 mg nebenwirkungen aurochem tadalafil at superdrug uk anwu 50 mg zoloft loratadine 10 mg arrow conseil where to buy over the counter viagra in puerto rico buy online viagra in canada omeprazole capsulas 20 mg opko health generic for deltasone coreg network uk keppra 250 mg tritace 10 mg cenaclul 2nd round of accutane reviews isotretinoin citalopram hydrobromide drug bank pentoxifilina 400 mg reacciones secundarias plavix glucophage taste disorder quetiapine fumarate how much does it cost bystolic 5 mg for palpitations icd 9 pseudoephedrine 30 mg dosing of cipro should i take atenolol in the morning or night metformin 500 mg po bid prescription hair loss from accutane in women levaquin 750 mg half life strattera 40 mg before aderall diflucan 50 mg side effects implementing comparable with generics for nexium xenical canadian pharmacy walmart 4 dollar list celexa sucralfate canada lexapro for ocd disorder buy renova in australia cialis black 800 review low progesterone in early pregnancy clonidine .1 mg

Esri CodeCamp III : Using cloud-based GIS platform – ArcGIS Online

Since our last CodeCamp was fully booked, we are having a recursion of that set at 20.9 at our Espoo office.

In the sessions we’re going through ArcGIS Online’s capabilities and key concepts that you need to know to leverage it in your solutions. Note that this time it is a Friday so there is no hurry from sauna ;)

You can find event description and link to register from here.

Agenda:

17:00 – 17:15 Opening words
17:15 – 18:30 Building solutions with ArcGIS Online, part 1
18:30 – 18:45 Break
18:45 – 19:45 Building solutions with ArcGIS Online, part 2
19:45 – 20:00 Wrap it up
20:00 – 23:30 Sauna

 

 

0  

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 3

ArcGIS Online serie

ArcGIS Online from developers perspective

ArcGIS Online JSON behind WebMap

ArcGIS Online Authentication

How to mashup business data with geometries with FeatureServices

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 1

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 2

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 3

In last post I went through how things are working in the hub page and how data is loaded into application. In this post, I will go through MapTourView and its behavior. The view is called MapTour since it is mimicing MapTour story map from Esri.

Application is build using ArcGIS Runtime SDK for Windows Store 10.2 BetaArcGIS Online (subscription) and Excel with Esri Maps for Office and code is shared in GitHub : https://github.com/anttikajanus/MapTourViewer

 The View – MapTourView

The view contains three parts. Header with title and navigate back to hub button, main section with selected item details on left and map that shows all features on background and last part is image carousel on bottom of the view. user can select image from the bottom and map is moved to that item and it’s details (text and image) is shown in the left. User can also navigate to next or previous items from selected items control.

<Grid Height="140" HorizontalAlignment="Stretch" Grid.ColumnSpan="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"></ColumnDefinition>
        <ColumnDefinition Width="auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Button x:Name="GoBack" IsEnabled="{Binding CanGoBack}" Style="{StaticResource BackButtonStyle}" />
    <TextBlock x:Name="DisplayName" Style="{StaticResource PageHeaderTextStyle}" Margin="120,0,30,40"></TextBlock>
    <!--<Image Source="../Assets/Logo.png" Grid.Column="1" Margin="20"></Image>-->
</Grid>

The header contains title text that is bound into DisplayName property in ViewModel where it get it from the ArcGIS portal item that presents Feature Service in ArcGIS Online. GoBack-button functionality is also  implemented in the ViewModel. Note  that there is no direct binding in GoBack-buttons Command property or DisplayName’s Text property. Binding is done with x:Name-attributes and some magic from Caliburn.Micro. Caliburn.Micro uses convention based binding that is done under the hoods. Basically it looks property or method with same name from the ViewModel and creates binding background. If you are new to this, check this documentation to get started.

<Grid Grid.ColumnSpan="2" Grid.Row="1">
    <Grid.Resources>
        <core:DataContextProxy x:Key="DataContextProxy" />
    </Grid.Resources>
    <esri:Map x:Name="_map"
                LocationDisplaySettings="{Binding LocationDisplay}"
                core:MapProperties.ZoomTo="{Binding Extent}">
        <esri:ArcGISTiledMapServiceLayer
            ServiceUri="http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer" />
        <esri:GraphicsLayer
            GraphicsSource="{Binding Data.StoryObjects, Source={StaticResource DataContextProxy}, Mode=TwoWay}"
            Renderer="{StaticResource StoryRenderer}" />
    </esri:Map>
</Grid>

The map contains only two layers: Basemap and GraphicsLayer with story graphics. Graphics are bound to GraphicsLayer by using DataContextProxy. Also map’s Extent is bound to map but since you cannot bind into that directly I have used AttachedPropety to do that.

<Grid Background="#B2060606" Grid.Row="1" Margin="40" Width="780">
    <FlipView ItemsSource="{Binding StoryObjects}" SelectedItem="{Binding SelectedStoryObject, Mode=TwoWay}">
        <FlipView.ItemTemplate>
            <DataTemplate>
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"></RowDefinition>
                        <RowDefinition Height="auto"></RowDefinition>
                    </Grid.RowDefinitions>
                    <Image Source="{Binding Attributes[URL]}" Grid.RowSpan="2" Stretch="Fill" />
                    <Grid Background="#EE060606" Grid.Row="1">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="auto"></RowDefinition>
                            <RowDefinition Height="auto"></RowDefinition>
                        </Grid.RowDefinitions>
                        <TextBlock Text="{Binding Attributes[Name]}" Margin="10"
                                    FontSize="18" />
                        <TextBlock
                            Text="{Binding Attributes[Caption], Converter={StaticResource htmlTextConverter}, ConverterParameter=1000}"
                            Margin="10, 0, 10, 10"
                            TextWrapping="Wrap" Grid.Row="1" />
                    </Grid>
                </Grid>
            </DataTemplate>
        </FlipView.ItemTemplate>
    </FlipView>
</Grid>

Selected item is visualized in a FlipView that gives all my wanted behavior out of the box like navigation to next or previous item by flipping with touch or by mouse. In the ItemTemplate I have larger Image on background and on top bottom Name of the item and Caption text. Caption might contains some HTML so I clear text from that using a converter.

<GridView Grid.Row="2" Grid.ColumnSpan="2" Background="#B2060606" Margin="10"
            ItemsSource="{Binding StoryObjects}" SelectedItem="{Binding SelectedStoryObject, Mode=TwoWay}">
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal"></VirtualizingStackPanel>
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Image Source="{Binding Attributes[Thumb_URL]}" Width="180" Height="180" />
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
</GridView>

Image carousel is a GridView with horizontal VirtualizingStackPanel. As you can see, ItemsSource and SelectedItem is bound to same properties as a FlipView earlier. The ItemTemplate contains only an Image that gets it’s Source from Graphic’s Attribute Thum_URL.

The ViewModel – MapTourViewModel

MapTourViewModel is derived from MapScreenViewModelBase that is an abstract base class that adds LocationDisplay and Extent properties to ScreenViewModelBase class. To get your location to visible, you need to check Location capability from Package.appxmanifest. Construction of ViewModel goes the same line like MapStoriesViewModel in previous post so I wont go that through.

Enabling navigation support

Navigation behavior is used from Caliburn.Micro‘s NavigationService and is published to the View though GoBack method and CanGoBack property that are part of INavigateBack interface.

public void GoBack()
{
    _navigationService.GoBack();
}

public bool CanGoBack
{
    get
    {
        return _navigationService.CanGoBack;

}

NavigationService gets injected into ViewModel in constructor and it is stored in a private variable. When user selects one ArcGISPortalItem in a hub view and navigates to MapTourViewModel, it also transfers StoryId to ViewModel. When using NavigationService’s WithParam functionality, simple types can be delivered to navigated item. Property that is set on navigation needs to be public and I usually use those to initialize ViewModel on OnActivate method.

 Exposing Stories and SelectedStory properties

Stories are set of Graphic’s that are exposed through StoryObjects property that is used with One-Way bindings from the View. There is nothing special in that but in SelectedStoryObject property’s setter contains Graphic’s Selected state changing and invoking to zooming into just selected item.

public GraphicCollection StoryObjects
{
    get
    {
        return _storyObjects;
    }
    set
    {
        if (Equals(value, _storyObjects))
        {
            return;
        }
        _storyObjects = value;
        NotifyOfPropertyChange(() => StoryObjects);
    }
}

public Graphic SelectedStoryObject
{
    get
    {
        return _selectedStoryObject;
    }
    set
    {
        if (Equals(value, _selectedStoryObject))
        {
            return;
        }

        if (_selectedStoryObject != null)
        {
            _selectedStoryObject.Selected = false;
        }

        _selectedStoryObject = value;
        _selectedStoryObject.Selected = true;

        // Zoom to extent
        SetExtent(SelectedStoryObject);

        NotifyOfPropertyChange(() => SelectedStoryObject);
    }
}

SetExtent method smells but works in this case. It creates new Extent from MapPoint and zooms into it.

private void SetExtent(Graphic storyObject)
{
    // This smells. 
    var point = storyObject.Geometry as MapPoint;
    var extent = new Envelope(point.X - 1000, point.Y - 500, point.X + 500, point.Y + 500);

    Extent = extent;
}

Loading content when page gets activated

Like in earlier post, I load content when ViewModel gets activated. First I load selected item from ArcGIS Online by items id. After selected Feature Services ArcGISPortalItem is loaded, I set header text and load all Graphics that are stored in that service I zoom map to the first graphics location.

protected override async void OnActivate()
{
    try
    {
        var item = await _mapStoryService.GetStoryItem(StoryId);

        if (item == null)
        {
            // No public item found
            // TODO handle
        }

        DisplayName = item.Title;

        var results = await _mapStoryService.GetStoryObjects(item.Url + "/0");

        if (results.Features.Count == 0)
        {
            // No items found from the FeatureService
            // TODO handle
        }

        StoryObjects.AddRange(results.Features);
        SelectedStoryObject = StoryObjects[0];

        IsLoading = false;
        SetExtent(_selectedStoryObject);
    }
    catch (Exception exception)
    {
        throw;
    }

    base.OnActivate();
}

DataAccess – MapStoryService

MapTourViewModel uses GetStoryItem and GetStoryObjects methods from MapStoryService.

Loading item by id

Loading specific item or items by ids can be based on ArcGIS Online items id values. In GetStoryItem method, SearchParameters QueryString is set to id:”<id guid>”. This returns list of results with one or zero items.

private const string IdQueryTemplate = "id:\"{0}\"";

public async Task<ArcGISPortalItem> GetStoryItem(string storyItemId)
{
    var parameters =  new SearchParameters { QueryString = string.Format(IdQueryTemplate, storyItemId) };

    var results = await _portal.SearchItemsAsync(parameters);

    if (results.Results.Count() == 1)
    {
        return results.Results.ToList()[0];
    }

    return null;
}

When SearchItemsAsync is executed, following query is sent  to <portal>/sharing/rest/search endpoint.

GET http://www.arcgis.com/sharing/rest/search?q=id%3A%224fcc494b3e654608bea9b373f3159f46%22&f=json

Response is actually pretty much same that when querying groups items in earlier post.

{
   "query":"id:\"4fcc494b3e654608bea9b373f3159f46\"",
   "total":1,
   "start":1,
   "num":10,
   "nextStart":-1,
   "results":[
      {
         "id":"4fcc494b3e654608bea9b373f3159f46",
         "owner":"kajanus_dev",
         "created":1371005929000,
         "modified":1371316795302,
         "guid":null,
         "name":null,
         "title":"Mission Bay Marsh Reserve",
         "type":"Feature Service",
         "typeKeywords":[
            "ArcGIS Server",
            "Data",
            "Feature Access",
            "Feature Service",
            "Service",
            "Hosted Service"
         ],
         "description":"<div>The reserve protects the only remnant of the salt-water marsh that covered much of the bay before Mission Bay Park was created in the 1950s.</div><div><br /></div>Friends of Mission Bay Marshes is a community-based association of concerned citizens established for the purpose of restoring and preserving the indigenous marshes of Mission Bay in San Diego, California and to promote public awareness of, education about, and involvement with those marshes and their flora, fauna, and ecology.",
         "tags":[
            "StoryMaps",
            "ConceptualDemonstration",
            "MapTourSchema"
         ],
         "snippet":"The reserve protects the only remnant of the salt-water marsh that covered much of the bay before Mission Bay Park was created in the 1950s.",
         "thumbnail":"thumbnail/marsh_creek_low_tide_thumbnail.jpg",
         "documentation":null,
         "extent":[
            [
               -117.23246320000015,
               32.7897344
            ],
            [
               -117.22345619999993,
               32.795569500000006
            ]
         ],
         "spatialReference":null,
         "accessInformation":"Roy Little (Friends of Mission Bay Marshes), Isabelle Kay (UCSD Natural Reserve System), Rupert Essinger (Esri)",
         "licenseInfo":"<p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>No restrictions. The content of service is collected by Friends of Mission Bay Marshes and the UCSD Natural Reserve System.</p><p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>The map features the fantastic photography of Roy Little of Friends of Mission Bay Marshes. The photos are all copyright (c) Roy Little.</p><p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>This service is only for demonstration purposes!</p>",
         "culture":"english (united states)",
         "properties":null,
         "url":"http://services.arcgis.com/1be8I8dpkSL373og/arcgis/rest/services/BayMarsh/FeatureServer",
         "access":"public",
         "size":-1,
         "appCategories":[

         ],
         "industries":[

         ],
         "languages":[

         ],
         "largeThumbnail":null,
         "banner":null,
         "screenshots":[

         ],
         "listed":false,
         "numComments":0,
         "numRatings":0,
         "avgRating":0.0,
         "numViews":45
      }
   ]
}

As you can see from the beginning there is only one item found with that Id as expected. If you want to find multiple items which id you know already you can concatenate id:”<idValue>”  parts with OR operation like this:

"id:"<id1>" OR id:"<id2> OR id:"<id3>"

See more about more advanced searching capabilities from here. From this response application is using title and url values. Title is set into header and url contains REST endpoint of the Feature Service that is queried to get story graphics.

Querying features from FeatureLayer

ArcGIS SDK’s provides Task’s that handles a lot of needed scenarios when working directly with rest endpoint. In ArcGIS Runtime SDK for Windows Store these classes are in ESRI.ArcGIS.Runtime.Tasks namespace and in WPF / SL / Windows Phone API’s they are in ESRI.ArcGIS.Client.Tasks namespace.

public async Task<FeatureSet> GetStoryObjects(string url)
{
    var task = new QueryTask(new Uri(url));
    var query = new Query { OutFields = new OutFields { "*" }, ReturnGeometry = true, Where = "1 [equalsmarkhere] 1" };

    var results = await task.ExecuteAsync(query);
    return results.FeatureSet;
}

Using other tasks from SDK’s follows same pattern with QueryTask usage. When QueryTask is initialized, it takes a url to the map service as a constructor parameter and when QueryTask is executed it takes a Query  parameter class in as a parameter. QueryTask constructs the query from the values set to the parameter class and invokes call to rest endpoint. In this case, I use couple good to know values in Query. First I set OutFields property to contain OutFields instance with only ”*” definition. When using “*” all attributes are returned from the service. To optimize your calls, you should ask only Attributes that you need. By default, query doesn’t return geometry of the features so it is set to True. Lastly in Where clause I set query to return all items from the service. Using ”1 [equalsmarkhere] 1“ in where clause is simplest way to select whole content of the layer.

When task is executed following query is sent:

GET http://services.arcgis.com/1be8I8dpkSL373og/arcgis/rest/services/BayMarsh/FeatureServer/0/query?f=json&outFields=%2A&returnGeometry=true&returnZ=false&returnM=false&spatialRel=esriSpatialRelIntersects&where=1%3D1&returnIdsOnly=false&returnCountOnly=false&returnDistinctValues=false

Response is quite long to show here so I left only one feature in it and cut others.

{
"objectIdFieldName":"FID",
   "globalIdFieldName":"",
   "geometryType":"esriGeometryPoint",
   "spatialReference":{
      "wkid":102100,
      "latestWkid":3857
   },
   "fields":[
      {
         "name":"Name",
         "type":"esriFieldTypeString",
         "alias":"Name",
         "sqlType":"sqlTypeNVarchar",
         "length":2147483647,
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"Caption",
         "type":"esriFieldTypeString",
         "alias":"Caption",
         "sqlType":"sqlTypeNVarchar",
         "length":2147483647,
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"Icon_color",
         "type":"esriFieldTypeString",
         "alias":"Icon_color",
         "sqlType":"sqlTypeNVarchar",
         "length":2147483647,
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"Long",
         "type":"esriFieldTypeDouble",
         "alias":"Long",
         "sqlType":"sqlTypeFloat",
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"Lat",
         "type":"esriFieldTypeDouble",
         "alias":"Lat",
         "sqlType":"sqlTypeFloat",
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"URL",
         "type":"esriFieldTypeString",
         "alias":"URL",
         "sqlType":"sqlTypeNVarchar",
         "length":2147483647,
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"Thumb_URL",
         "type":"esriFieldTypeString",
         "alias":"Thumb_URL",
         "sqlType":"sqlTypeNVarchar",
         "length":2147483647,
         "domain":null,
         "defaultValue":null
      },
      {
         "name":"FID",
         "type":"esriFieldTypeInteger",
         "alias":"FID",
         "sqlType":"sqlTypeInteger",
         "domain":null,
         "defaultValue":null
      }
   ],
   "features":[
      {
         "attributes":{
            "Name":"Welcome to the reserve",
            "Caption":"The reserve is managed jointly. The Kendall Frost Mission Bay Reserve is part of the University of California Natural Reserve System (UC NRS) and is managed by the University of California San Diego (UCSD). The adjacent Northern Wildlife Reserve is managed by the City of San Diego.",
            "Icon_color":"R",
            "Long":-117.2296755,
            "Lat":32.7920955,
            "URL":"http://downloads.esri.com/blogs/places/missionbaymarsh/marsh_intro_picture.jpg",
            "Thumb_URL":"http://downloads.esri.com/blogs/places/missionbaymarsh/marsh_intro_picture_thumbnail.jpg",
            "FID":1
         },
         "geometry":{
            "x":-13049947.7825207,
            "y":3867740.5210794583
         }
      },
<snipped>
      }
   ]
}

Response can be cut into three parts: general informatinfield information and features. In general information defines items spatial reference and identification fields. Field information contains list of fields that are got from the service and their description. This information is useful when you want to create dynamical behavior in your app depending what service / features are used. Alias information is also good way to provide users understandable definition of the field. If you are publishing FeatureServices using Esri Maps for Microsoft Office you cannot control aliases or domains (range or code value domains) so if you want to create more sophisticated services, currently you need to create them in ArcMap. Features collection contains Features that drops in given Where clause. Feature contains a geometry since we asked for it and attribute information.

Query results are returned as a QueryResult that contains found features in FeatureSet property. You can access fields and features returned from the service.

Summary

This is it. Now I have gone through the implementation of this application. In this post, there was couple controls using same properties from ViewModel and executed QueryTask to get all Features/Graphics from a FeatureService. This example application is no way near of polished application but I hope that going through it you got some information how you can work with ArcGIS Online, it’s content management capabilities and using that content dynamically from custom client.

0  

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 2

ArcGIS Online serie

ArcGIS Online from developers perspective

ArcGIS Online JSON behind WebMap

ArcGIS Online Authentication

How to mashup business data with geometries with FeatureServices

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 1

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 2

Building Map Tour Viewer app for Windows Store with ArcGIS Online – Part 3

In last post I went through how things are working in the ArcGIS Online and Feature Service side. In this post I concentrate on application story hub page and its implementation. Communication between application and ArcGIS Online is also looked briefly.

Application is build using ArcGIS Runtime SDK for Windows Store 10.2 BetaArcGIS Online (subscription) and Excel with Esri Maps for Office and code is shared in GitHub : https://github.com/anttikajanus/MapTourViewer

The View – MapStoriesView

When application is launched, “Loading content” indicator is shown at the page. This indicator is set to visible when application is loading it’s content from the ArcGIS Online. After All data is loaded, indicator is removed from the view and user can interact with the application. The View contains two sections – Header where the title and subtitle information is shown and a content area where list of story maps are shown. Header contains application title information on the left side and there is a place for a logo on the right side. Application gets its Title and Subtitle information from the ArcGISPortalGroup that is loaded first when the application is started.

<Grid Height="140" HorizontalAlignment="Stretch" Grid.ColumnSpan="2">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*"></ColumnDefinition>
        <ColumnDefinition Width="auto"></ColumnDefinition>
    </Grid.ColumnDefinitions>
    <Grid Margin="120,0,30,10">
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <TextBlock Text="{Binding GroupInfo.Title}" Style="{StaticResource PageHeaderTextStyle}"
                    Margin="0,0,0,0" />
        <TextBlock Text="{Binding GroupInfo.Snippet}" Style="{StaticResource PageSubheaderTextStyle}"
                    Grid.Row="1" Margin="0,5,0,0">
        </TextBlock>
    </Grid>
    <!--<Image Source="Assets/Logo.png" Grid.Column="1" Margin="20"></Image>-->
</Grid>

In the main content area, set of  Stories are shown in  a GridView. Each item shows Title, Thumbnail image, Description, Creator and Created date from individual Feature Service item that are loaded as a ArcGISPortalItems after the group information is loaded. All information is updated in the ArcGIS Online by editing items and changes are reflected into application on every start up.

<GridView ItemsSource="{Binding Stories}" SelectedItem="{Binding SelectedStory, Mode=TwoWay}" Grid.Row="1" Grid.ColumnSpan="2" Margin="40,10,0,10">
    <GridView.ItemTemplate>
        <DataTemplate>
            <Grid Width="597" Height="800">
                <Grid.RowDefinitions>
                    <RowDefinition Height="100" />
                    <RowDefinition Height="auto" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <TextBlock Text="{Binding Title}" Style="{StaticResource HeaderTextStyle}"
                            VerticalAlignment="Center" TextWrapping="NoWrap" Margin="5">
                </TextBlock>

                <Grid Grid.Row="1" HorizontalAlignment="Left" Width="597" Height="399">
                    <Border Background="{StaticResource ListViewItemPlaceholderBackgroundThemeBrush}">
                        <Image Source="{Binding ThumbnailUri}" Stretch="UniformToFill" />
                    </Border>
                </Grid>
                <StackPanel Grid.Row="2"
                            Background="{StaticResource ListViewItemOverlayBackgroundThemeBrush}">
                    <TextBlock
                        Text="{Binding Description, Converter={StaticResource htmlTextConverter}, ConverterParameter=2000}"
                        Foreground="{StaticResource ListViewItemOverlaySecondaryForegroundThemeBrush}"
                        Style="{StaticResource CaptionTextStyle}" TextWrapping="Wrap"
                        Margin="15,0,15,10" />
                    <TextBlock Margin="15,0,15,10" Style="{StaticResource CaptionTextStyle}">
                        <Run Text="Created by : "></Run>
                        <Run Text="{Binding Owner}" Foreground="Gold"></Run>
                        <Run Text=" at "></Run>
                        <Run Text="{Binding CreationDate}" Foreground="DodgerBlue"></Run>
                    </TextBlock>
                </StackPanel>
            </Grid>
        </DataTemplate>
    </GridView.ItemTemplate>
    <GridView.ItemsPanel>
        <ItemsPanelTemplate>
            <VirtualizingStackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </GridView.ItemsPanel>
</GridView>

I think that I should mention couple things about the code in here. First is that the Thumbnails in ArcGIS Online is stored as 133 x 199 pixels so I am using that information to scale the image to three times to keep ration same.  Another part is the Description that contains HTML text styling, that you can use when editing field through ArcGIS Online, but here I wanted to rip all styling away so I am using a converter to do that. That converter is some old one that I used in Windows Phone project when showing RSS-feed so it is not written for this but seems to do the job. Here you could take an advantage styling and show description as HTML.

The ViewModel – MapStoriesViewModel

I derive my ViewModels from ScreenViewModelBase that extends Caliburn.Micros Screen class. INotifyPropertyChanged is implemented by the Screen. I have added 2 new general properties for ViewModels: IsLoading and IsContentLoaded. IsLoading is used to give state information and IsContentLoaded is used to define if the content needs to be loaded.

Construction and Dependency Injection

When ViewModel gets constructed, MapStoryService and NavigationService dependencies gets injected by the Caliburn.Micros Inversion of Control functionality that also handles the instance creation. Dependency mapping is done in the app.xam.cs file that you need to implement when using Caliburn.Micro. Read more about application bootstrapping from here. Here is how classes are registered in this application.

protected override void Configure()
{
    _container = new WinRTContainer();
    _container.RegisterWinRTServices();

    // This is main page, use single instance
    _container.Singleton<MapStoriesViewModel>();

    // Create new instance for every MapTour 
    _container.PerRequest<MapTourViewModel>();

    // Create all services as a singletons
    _container.Singleton<MapStoryService>();           
}

NavigationService is Caliburn.Micro’s service so it gets hooked when calling _container.RegisterWinRTServices() method and other classes are implemented in this application. Here is the MapStoriesViewModels constructor where I get needed service instances and those references are stored into private variables for later use.

public MapStoriesViewModel(MapStoryService mapStoryService, INavigationService navigationService)
{
    _mapStoryService = mapStoryService;
    _navigationService = navigationService;
    Stories = new BindableCollection<ArcGISPortalItem>();
    IsLoading = true;
}

Exposing Models from ViewModel

MapStoriesViewModel gives content to View through GroupInfo and Stories properties. These properties get set when the data is asynchronously loaded to them.

public ArcGISPortalGroup GroupInfo
{
    get
    {
        return _groupInfo;
    }
    set
    {
        if (Equals(value, _groupInfo))
        {
            return;
        }
        _groupInfo = value;
        NotifyOfPropertyChange(() => GroupInfo);
    }
}

public BindableCollection<ArcGISPortalItem> Stories
{
    get
    {
        return _stories;
    }
    set
    {
        if (Equals(value, _stories))
        {
            return;
        }
        _stories = value;
        NotifyOfPropertyChange(() => Stories);
    }
}

As you can see, I am using ArcGISPortalItem and ArcGISPortalGroup as Models and exposing those directly from the ViewModel. I think that in this case this is fine but keep in mind that when you are exposing models directly from the ViewModel you lose an abstraction between View and Model that could be considered as violation of MVVM pattern. I could have properties for each property that is needed to being exposed but this way I can get everything to View faster and my ViewModel is not bloated with properties. Anyway, just keep thing in mind and make decision accordingly how to expose information from items to View.

Navigating to selected story

When user selects a story from the list, application moves to another page where the map story data is visualized for the user. NavigateToItem method is called when SelectedStory is changed. It uses Caliburn.Micros NavigationService to move to another page with Id of selected item. I’ll show how this parameter is handled in MapStoryViewModel in next post.

private void NavigateToItem(ArcGISPortalItem itemToNavigate)
{
    _navigationService.UriFor<MapTourViewModel>().WithParam(x => x.StoryId, itemToNavigate.Id).Navigate();
}

Loading content when page gets activated

When the page gets activated it launches OnActivated event in ViewModel. This is default behavior that comes in when using Caliburn.Micro and it’s a good place to start loading content in many cases. Since MapStoriesViewModel is used as a singleton content might be already loaded so first I check that if it needs to be loaded. If it content needs to be loaded I call LoadContent method that contains actual loading. After content is loaded ViewModel is set into loaded state.

protected async override void OnActivate()
{
    if (IsContentLoaded == false)
    {
        await LoadContent();
        IsLoading = false;
        IsContentLoaded = true;
    }
    else
    {
        SelectedStory = null;
    }

    base.OnActivate();
}

Content is loaded by using MapStoryService that abstracts data access from the ViewModel. First group information is fetched from the ArcGIS Online and that, loaded group is used to get all stories.

private async Task LoadContent()
{
    try
    {
        var group = await _mapStoryService.GetGroupInformation();
        if (group == null)
        {
            // Group for application was not found
            // TODO handle
        }

        GroupInfo = group;

        var items = await _mapStoryService.GetStories(group);
        if (items == null)
        {
            // There were no public Feature Services in this group
            // TODO handle
        }

        Stories.AddRange(items);
    }
    catch (Exception exception)
    {
        // TODO handle

        Debug.WriteLine(exception);
        throw;
    }
}

Data access – MapStoryService

MapStoryService contains methods to query ArcGIS Online and getting information from there. It uses ArcGISPortal class to do actual queries and each method creates SearchParameters for that things that are searched. MapStoriesViewModel uses two methods – GetGroupInformation and GetStories.

ArcGISPortal abstracts ArcGIS Portal REST API from the user but it is important to know how things works under the hood. You can find REST API documentation from here where search is defined here.

Searching groups

Searching is based on a query. In query you can define all kind of parameters and options but in this application, I am using simple queries. Read more about construction query from here.

ArcGISPortal class contains SearchGroupsAsync method that takes an SearchParameters instance that defines information what is searched. Here I am just creating a query based on Title and Owner information of the group.

private const string TitleGroupQueryTemplate = "title:\"{0}\" AND owner:\"kajanus_dev\"";

public async Task<ArcGISPortalGroup> GetGroupInformation()
{
    var parameters = new SearchParameters()
                            {
                                QueryString = string.Format(TitleGroupQueryTemplate, "Story Maps")
                            };

    var results = await _portal.SearchGroupsAsync(parameters);
    if (results.Results.Count() == 1)
    {
        return results.Results.ToList()[0];
    }

    return null;
}

After query is executed and results are returned, I return only first item to the application. I have here an assumption that there is only one group defined with name containing “Story Maps” and that is created by me.

When SearchGroupsAsync gets invoked, ArcGISPortal sends a GET to REST API endpoint <portal>/sharing/rest/community/groups.

GET http://www.arcgis.com/sharing/rest/community/groups?q=title%3A%22Story%20Maps%22%20AND%20owner%3A%22kajanus_dev%22&f=json

The response contains query information like used query, total amount of results, start index where results was taken from and number (num) of items that were maximally loaded. This information is used to define if there are more results that might be needed to load and nextStart contains index from where next should be started. If it is -1 then there are no more results to query. Response also contains collection of groups in results.

{
   "query":"title:\"Story Maps\" AND owner:\"kajanus_dev\"",
   "total":1,
   "start":1,
   "num":10,
   "nextStart":-1,
   "results":[
      {
         "id":"bdbbe57e937045b58e8b70e607585b00",
         "title":"Story maps",
         "isInvitationOnly":false,
         "owner":"kajanus_dev",
         "description":"Currently this group is used to share FeatureServices that follows the same schema as Map Tour Story Template. These services are dynamically loaded into the clients and the maps are constructed dynamically by the clients. <div><br /></div><div>This is conceptual demo where ArcGIS Online items, in this case FeatureServices, are shared into clients and the content of the application is changing when changes are done to Group or FeatureServices in the group.</div><div><br /></div><div>Demonstration purposes only!</div>",
         "snippet":"Collection of maps that tells a story.",
         "tags":[
            "StoryMaps",
            "Story",
            "Story Maps",
            "Developer",
            "ConseptualDemo"
         ],
         "phone":null,
         "sortField":"title",
         "sortOrder":"asc",
         "isViewOnly":false,
         "thumbnail":null,
         "created":1371113712000,
         "modified":1371315826000,
         "access":"public"
      }
   ]
}

This response gets parsed by the ArcGISPortal and ArcGISPortalGroup is constructed from it. If you look the response, you can here see HTML in description that I was talking earlier. This behavior is shared with all general fields in ArcGIS Online items. Read more about Group data from here.

Searching items that belongs in certain group

After group is loaded that contains the map stories, groups content is loaded by it’s ID. When Feature Service items where shared with the group, items got marked to belong into that group.

private const string ItemsByGroupIdQueryTemplate = "group:\"{0}\"";

public async Task<List<ArcGISPortalItem>> GetStories(ArcGISPortalGroup group, string sortField = "created")
{
    // Here we could also include type:"Feature Service" definition so filtering would be done in the AGOL
    var itemsParameters = new SearchParameters
    {
        QueryString =
            string.Format(
                ItemsByGroupIdQueryTemplate, group.Id),
        SortField = sortField
    };

    var results = await _portal.SearchItemsAsync(itemsParameters);

    // Filter returned items to contain only FeatureServices. Could be filtered on query too.
    var featureServices = results.Results.Where(x => x.Type == ItemType.FeatureService);
    return featureServices.ToList();
}

In this query, I want to sort results by their creation time that is called “created” items. Actual search is done by ArcGISPortals SearchItemsAsync method that returns a list of ArcGISPortalItems. This application only works with Feature Services so I take only ArcGISPortalItems that are type of FeatureService. 

When SearchItemsAsync gets invoked, search query is sent to <portal>/sharing/rest/search endpoint.

GET http://www.arcgis.com/sharing/rest/search?q=group%3A%22bdbbe57e937045b58e8b70e607585b00%22&sortField=created&sortOrder=asc&f=json

The response is quite long so I only include one item of results. Response is constructed the same as in previous one. It is just returned different object (Item) as a result. Read more about items data and fields from here.

{
   "query":"group:\"bdbbe57e937045b58e8b70e607585b00\"",
   "total":4,
   "start":1,
   "num":10,
   "nextStart":-1,
   "results":[
      {
         "id":"4fcc494b3e654608bea9b373f3159f46",
         "owner":"kajanus_dev",
         "created":1371005929000,
         "modified":1371316795302,
         "guid":null,
         "name":null,
         "title":"Mission Bay Marsh Reserve",
         "type":"Feature Service",
         "typeKeywords":[
            "ArcGIS Server",
            "Data",
            "Feature Access",
            "Feature Service",
            "Service",
            "Hosted Service"
         ],
         "description":"<div>The reserve protects the only remnant of the salt-water marsh that covered much of the bay before Mission Bay Park was created in the 1950s.</div><div><br /></div>Friends of Mission Bay Marshes is a community-based association of concerned citizens established for the purpose of restoring and preserving the indigenous marshes of Mission Bay in San Diego, California and to promote public awareness of, education about, and involvement with those marshes and their flora, fauna, and ecology.",
         "tags":[
            "StoryMaps",
            "ConceptualDemonstration",
            "MapTourSchema"
         ],
         "snippet":"The reserve protects the only remnant of the salt-water marsh that covered much of the bay before Mission Bay Park was created in the 1950s.",
         "thumbnail":"thumbnail/marsh_creek_low_tide_thumbnail.jpg",
         "documentation":null,
         "extent":[
            [
               -117.23246320000015,
               32.7897344
            ],
            [
               -117.22345619999993,
               32.795569500000006
            ]
         ],
         "spatialReference":null,
         "accessInformation":"Roy Little (Friends of Mission Bay Marshes), Isabelle Kay (UCSD Natural Reserve System), Rupert Essinger (Esri)",
         "licenseInfo":"<p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>No restrictions. The content of service is collected by Friends of Mission Bay Marshes and the UCSD Natural Reserve System.</p><p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>The map features the fantastic photography of Roy Little of Friends of Mission Bay Marshes. The photos are all copyright (c) Roy Little.</p><p style='padding: 0px; font-size: 12px; line-height: 18px; font-family: Verdana, Helvetica, sans-serif; background-color: rgb(255, 255, 255);'>This service is only for demonstration purposes!</p>",
         "culture":"english (united states)",
         "properties":null,
         "url":"http://services.arcgis.com/1be8I8dpkSL373og/arcgis/rest/services/BayMarsh/FeatureServer",
         "access":"public",
         "size":-1,
         "appCategories":[

         ],
         "industries":[

         ],
         "languages":[

         ],
         "largeThumbnail":null,
         "banner":null,
         "screenshots":[

         ],
         "listed":false,
         "numComments":0,
         "numRatings":0,
         "avgRating":0.0,
         "numViews":45
      },
   ]
}

I think that most of the stuff is selfexplaning but lets check how the thumbnail information is used since it is not so clear. Thumbnail information is relative path to the actual image from items uri. For example when image is shown in the View, the image is fetched from http://www.arcgis.com/sharing/rest/content/items/4fcc494b3e654608bea9b373f3159f46/info/thumbnail/marsh_creek_low_tide_thumbnail.jpg. Thumbnail uri is constructed <portal>/sharing/rest/content/items/<id>/<textFromJSON>. 

It seems that there are some new properties in the response that are not used in the SDK at the moment like largerThumbnail that would be interesting from this applications point of view.

Summary

In this post we digged a bit into how searching is done with the ArcGIS Online and how results information can be used to get more related information. Also I went through how the hub page is implemented. In next post I will go into how MapStory part is working and how selected FeatureService gets loaded into the View.

 

2