Skip navigation
All Places > PI Developers Club > Blog > 2016 > September
2016

We wrapped up our first Programming Hackathon at an EMEA Users Conference and it was a success! Our Data sponsor juwi O&M (Operation and Maintenance) kindly provided us with PI Data from wind farms.

 

Theme

Innovate around Renewable Energy

 

Statistics

  • 24 Participants formed 6 Teams
  • All teams delivered a working application
  • Teams uploaded ~ 870 MB of results (presentations, source code and data extractions used)

 

Judging criteria

  • Creativity and Originality (20 points)
  • Potential Business Impact (20 points)
  • Technical Implementation (20 points)
  • Data Analysis and Insight (20 points)
  • User Experience (20 points)

 

Watch the Recording of the Judging Session Here!

 

Prizes

 

  • 1st place: GoPro HERO Session action camera, One-time 100% discount to attend the UC over the next year, 1-year free subscription to PI Developers Club
  • 2nd place: Fitbit Blaze, One-time 50% discount to attend the UC over the next year, 1-year free subscription to PI Developers Club
  • 3rd place: JBL Charge 2+ wireless speaker, 1-year free subscription to PI Developers Club

 

Winners

1st place: Team 3GR: Daniel Rafael Wosch, Ionut Buse, Gregor Biering, Jens Prystawik

3GR.jpg

2nd place: Team BG: Ansgar Backhaus, Mikhail Koloskov, Tobias Heying, Julian Weber

BG.jpg

 

3rd place: Team Team 2: David Bauer, Juliette Spinnato, Muhammad Waheed, Ivan Gallo

Team2.jpg

 

Congratulations!!

Introduction

 

On my previous blog posts, it was shown how to develop the Google Maps PI Coresight custom symbol using the Google Maps JavaScript API. On this blog post, we will develop the Google Street View PI Coresight custom symbol using the same API. As a result, the code from both symbols are very similar since they import the same JavaScript library and use the same API. The main difference is that in this blog post, we are going to work with the StreetViewPanorama class.

 

The final library can generate this great Street View image as shown below:

 

 

 

Getting started using the StreetViewPanorama class

 

There is a great and simple sample on Google documentation about how to use this class. In order to initialize this class, you should instantiate the google.maps.StreetViewPanorama class. Here is an example:

 

function initialize() {
  panorama = new google.maps.StreetViewPanorama(
      document.getElementById('street-view'),
      {
        position: {lat: 37.869260, lng: -122.254811},
        pov: {heading: 165, pitch: 0},
        zoom: 1
      });
}

}

 

Therefore, with this function, we can get the heading, pitch, zoom, latitude and longitude values and then initialize the Google Street View display.

 

If we want to update the Google Street View display, you just need to call the setOptions method from the panorama object. Here is an example:

 

var currentPov = {
    heading: 10,
    pitch: 0,
    zoom: 1
};

panorama.setOptions({
    position: { lat: 41, lng: 21 },
    pov: currentPov
});

 

 

 

This same method also allows you to select some other options for the Street Views Controls:

 

panorama.setOptions({
    position: { lat: 41, lng: 21 },
    pov: currentPov,
    addressControlOptions: {
        position: google.maps.ControlPosition.BOTTOM_CENTER
    },
    linksControl: false,
    panControl: false,
    enableCloseButton: false
});

 

Now, we can start developing our PI Coresight custom symbol.

 

PI Coresight Symbol Code

 

All necessary files can be downloaded from this GitHub repository.

 

If you want to understand better how this symbol works under the hood, I strongly suggest reading my previous blogs about developing the Google Maps PI Coresight custom symbol.

 

Here is what the custom symbol should do:

 

  1. Load the Google Maps JavaScript library in case it is not loaded yet
  2. Create a Street View display with some initial values (not from PI).
  3. When the dataUpdate method is called, it creates a list of indexes in order to find the correct values on the data variable. Please refer to the Google Maps blog posts for more information.
  4. Update the StreetView display with new values retrieved from PI

 

Note that in order to update the display the indexes from scope.config needs to be built (they cannot be -1). This happens in two situations:

  • When the indexes are saved with the display. When the PI Coresight display is loaded, it also loads the indexes.
  • When PI Coresight sends data.Rows with Label and Path properties.

 

Finally, here is the code:

 

Content of sym-gtreetview.js:

 

(function (CS) {
    var definition = {
        typeName: 'gstreetview',
        datasourceBehavior: CS.DatasourceBehaviors.Multiple,
        iconUrl: 'Images/streetview.svg',
        getDefaultConfig: function () {
            return {
                DataShape: 'Table',
                Height: 400,
                Width: 400,
                HeadingName: 'Heading',
                PitchName: 'Pitch',
                ZoomName: 'Zoom',
                LngName: 'Longitude',
                LatName: 'Latitude',
                LatIndex: -1,
                LngIndex: -1,
                HeadingIndex: -1,
                PitchIndex: -1,
                ZoomIndex: -1,
                AddressControl: true,
                ClickToGo: true,
                DisableDefaultUI: false,
                DisableDoubleClickZoom: false,
                ImageDateControl: true,
                PanControl: true,
                ZoomControl: true
            };
        },
        configOptions: function () {
            return [{
                title: 'Format Symbol',
                mode: 'format'
            }];
        },
        init: init
    };






    window.gStreetViewCallback = function () {
        $(window).trigger('gSvLoaded');
    }


    function loadGoogleMaps() {
        if (window.google == undefined) {
            if (window.googleRequested) {
                setTimeout(function () {
                    window.gStreetViewCallback();
                }, 200);


            }
            else {
                var script_tag = document.createElement('script');
                script_tag.setAttribute("type", "text/javascript");
                script_tag.setAttribute("src", "http://maps.google.com/maps/api/js?sensor=false&callback=gStreetViewCallback");
                (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
                window.googleRequested = true;
            }
        }
        else {
            window.gStreetViewCallback();
        }
    }




    function init(scope, elem) {
        var container = elem.find('#container')[0];
        var id = "gstreetview_" + Math.random().toString(36).substr(2, 16);
        container.id = id;
        scope.id = id;






        scope.currentLatLng = { lat: -23.5626384, lng: -46.6510285 };
        scope.startStreetView = function () {
            if (scope.panorama == undefined) {
                scope.panorama = new google.maps.StreetViewPanorama(document.getElementById(scope.id), {
                    position: scope.currentLatLng,
                    pov: {
                        heading: 0,
                        pitch: 0,
                        zoom: 1
                    }
                });
            }
            scope.updateGoogleStreetViewConfig(scope.config);
        };






        scope.updateGoogleStreetViewConfig = function (config) {
            scope.LatIndex = parseInt(config.LatIndex);
            scope.LngIndex = parseInt(config.LngIndex);
            scope.HeadingIndex = parseInt(config.HeadingIndex);
            scope.PitchIndex = parseInt(config.PitchIndex);
            scope.ZoomIndex = parseInt(config.ZoomIndex);


            if (scope.panorama != undefined) {
                scope.panorama.setOptions({
                    addressControl: config.AddressControl,
                    clickToGo: config.ClickToGo,
                    disableDefaultUI: config.DisableDefaultUI,
                    disableDoubleClickZoom: config.DisableDoubleClickZoom,
                    imageDateControl: config.ImageDateControl,
                    panControl: config.PanControl,
                    zoomControl: config.ZoomControl
                });
            }
        };


        scope.resizeGoogleStreetView = function (width, height) {
            if (scope.panorama != undefined) {
                google.maps.event.trigger(scope.panorama, "resize");
            }
        }






        $(window).bind('gSvLoaded', scope.startStreetView);
        loadGoogleMaps();






        scope.updateIndexes = function (data) {
            var isValid = true;
            scope.config.LatIndex = -1;
            scope.config.LngIndex = -1;
            scope.config.HeadingIndex = -1;
            scope.config.PitchIndex = -1;
            scope.config.ZoomIndex = -1;


            for (var i = 0; i < data.Rows.length; i++) {
                var splitResult = data.Rows[i].Label.split('|');
                if (splitResult[1] == scope.config.LatName) {
                    scope.config.LatIndex = i;
                }
                else if (splitResult[1] == scope.config.LngName) {
                    scope.config.LngIndex = i;
                }
                else if (splitResult[1] == scope.config.HeadingName) {
                    scope.config.HeadingIndex = i;
                }
                else if (splitResult[1] == scope.config.PitchName) {
                    scope.config.PitchIndex = i;
                }
                else if (splitResult[1] == scope.config.ZoomName) {
                    scope.config.ZoomIndex = i;
                }
            }


            var checkIndex = function(attributeName, indexValue)
            {
                if (indexValue == -1) {
                    isValid = false;
                    alert(attributeName + ' attribte was not found');
                }
            }


            checkIndex('Latitude', scope.config.LatIndex);
            checkIndex('Longitude', scope.config.LngIndex);
            checkIndex('Zoom', scope.config.ZoomIndex);
            checkIndex('Pitch', scope.config.PitchIndex);
            checkIndex('Heading', scope.config.HeadingIndex);
            return isValid;




        }




        scope.updateDisplay = function (data) {
            var currentPov = {
                heading: parseFloat(data.Rows[scope.config.HeadingIndex].Value),
                pitch: parseFloat(data.Rows[scope.config.PitchIndex].Value),
                zoom: parseFloat(data.Rows[scope.config.ZoomIndex].Value)
            };


            scope.currentLatLng = { lat: parseFloat(data.Rows[scope.config.LatIndex].Value), lng: parseFloat(data.Rows[scope.config.LngIndex].Value) };


            setTimeout(function () {
                scope.panorama.setOptions({
                    position: scope.currentLatLng,
                    pov: currentPov
                });
            }, 3000);


        }


        scope.dataUpdate = function (data) {
            if ((data == null) || (data.Rows.length == 0)) {
                return;
            }


            if (data.Rows[0].Path) {
                scope.updateIndexes(data);
            }


            if (scope.panorama != undefined) {
                scope.updateDisplay(data);
            }
        }


        return { dataUpdate: scope.dataUpdate, resize: scope.resizeGoogleStreetView, configChange: scope.updateGoogleStreetViewConfig };
    }


    CS.symbolCatalog.register(definition);
})(window.Coresight);

 

 

Content of sym-gtreetview-config.html:

 

<div class="c-side-pane t-toolbar">
    <span style="color:#fff; margin-left:15px">Attributes Names Settings</span>
</div>


<div class="c-config-content">
    Latitude attribute name:
    <input type="text" ng-model="config.LatName">
</div>
<div class="c-config-content">
    Longitude attribute name:
    <input type="text" ng-model="config.LngName">
</div>


<div class="c-config-content">
    Heading attribute name:
    <input type="text" ng-model="config.HeadingName">
</div>
<div class="c-config-content">
    Pitch attribute name:
    <input type="text" ng-model="config.PitchName">
</div>


<div class="c-config-content">
    Zoom attribute name:
    <input type="text" ng-model="config.ZoomName">
</div>


<div class="c-side-pane t-toolbar">
    <span style="color:#fff; margin-left:15px">Street View Options</span>
</div>




<div class="c-config-content">
    Address Control:
    <input type="checkbox" ng-model="config.AddressControl">
</div>


<div class="c-config-content">
    Click To Go:
    <input type="checkbox" ng-model="config.ClickToGo">
</div>


<div class="c-config-content">
    Disable Default UI:
    <input type="checkbox" ng-model="config.DisableDefaultUI">
</div>


<div class="c-config-content">
    Disable Double Click Zoom:
    <input type="checkbox" ng-model="config.DisableDoubleClickZoom">
</div>


<div class="c-config-content">
    Image Date Control:
    <input type="checkbox" ng-model="config.ImageDateControl">
</div>




<div class="c-config-content">
    Pan Control:
    <input type="checkbox" ng-model="config.PanControl">
</div>




<div class="c-config-content">
    Zoom Control:
    <input type="checkbox" ng-model="config.ZoomControl">
</div>

 

Content of sym-gtreetview-template.html:

 

<div id="container" style="width:100%;height:calc(100% - 50px);">


</div>
<center>
    <br /><br />
    <p style="color: white;">Right click here to format this symbol</p>
</center>

 

 

 

 

Conclusions

 

I hope you can find this blog post useful and thank you for reading it.

 

It is always good to remember that the PI Coresight Extensiblity used to develop those custom JavaScript symbols is still in CTP.

PI Web API Useful Links Nexus

Posted by rdavin Employee Sep 21, 2016

Last Updated: October 27, 2016 

 

The purpose of this post is to provide a dynamic list of resources for newcomers to PI Web API.  This is a complement to the PI Web API: Quick Start Guide.  This page is maintained by the PI Developer's Club.

 

Developer's Reference

 

Current Version

  • This Live Library link has documentation about the current and previous releases.
  • PI Web API 2017, Version 1.9.0, Released 2017-05-10
  • Release Notes
    • New Features
      • Supports reading, creating, updating, and deleting security identifiers, mappings, and entries

      • Support reading, creating, updating, and deleting annotations on event frames

      • Permits management of attribute traits

      • Supports addition of an existing element as a child to another element

      • Supports the provision of time zone information as a query parameter for Stream and StreamSet resources

      • Supports gzip compression for inbound data

      • PI Web API Indexed Search Plug-in introduces the following enhancements:

      • Support for FIPS (CTP)

      • Scheduling of simultaneous crawls has been improved: concurrent crawls are now permitted up to the configured limit of MaxConcurrentCrawlers

      • WebId is added to the search source results

      • Improved incremental crawl times for PI Data Archive

 

Previous Versions

  • PI Web API 2016 R2, Version 1.8.0, Released 2016-10-19
    • Release Notes
    • New Features
      • Supports reading, creating, updating, and deleting security identifiers, mappings, and entries
      • Support reading, creating, updating, and deleting annotations on event frames
      • Permits management of attribute traits
      • Supports addition of an existing element as a child to another element
      • Supports the provision of time zone information as a query parameter for Stream and StreamSet resources
      • Supports gzip compression for inbound data
      • Enhancements to PI Web API Indexed Search Plug-in
  • PI Web API 2016 SP1, Version 1.7.1, Released 2016-07-27
    • New Features
      • Support for PI Analysis infrastructure
      • New REST controllers allow you to create and edit Analysis Rules, Templates, Categories , and Time Rules
  • PI Web API 2016, Version 1.7, Released 2016-05-02

 

 

Upcoming Releases

 

Basics of PI Web API

 

Code Samples

 

PI Web API in Action

Some cool projects using PI Web API

 

This page is meant to be evergreen.  If you know of new posts, links, or material of any kind that would enhance the current material, let us know in the comments below.

Best practices with the PI AF SDK were covered many times already, have you missed it? In this post I am providing the resources I find the most useful to learn about the PI AF SDK Best practices.

If you know other valuable resources please let me know in a comment and I will update this table as needed!

 

General Information

Title
Summary
Resource Type
Developing Applications with PI AF SDKThis is our free  course to begin with the PI AF SDK. You can register for a fee and get a course certificate. Learning
New Features and Best Practices with PI AF SDK 2016 Presentation of the new features that are found in the PI AF SDK 2016 (2.8) as well as an overview on existing best practices with the PI AF SDK.Video (43m) + Presentation ( approx. 38 slides)
Optimizing PI AF Server and PI AF SDK Applications for Performance and ScalabilityThis is a presentation from 2013 (formerly vCampus Live) where Chris Manhard and Paul Combellick went over details in PI AF SDK and the AF Server that can really make a difference when building your application.  This is a must-watch video.Video (60m) + Presentation ( approx. 80 slides)
PI AF SDK GuidelinesThis document highlights aspects of the PI AF SDK that are important to understand.  This is a must-read resource.Whitepaper (34 pages)
Getting Started with with the PI AF SDK - Live Library / PDF (Translated in many languages) / Code
The link of this resource points to the content of a set of exercises that came out of out labs delivered during users conferences since 2016.  This was completely rewritten by our documentation team and should be a very good resource to get started with.  There is also associated code available on GitHub. Document / GitHub
Working with the PI AF SDK (see files attached to this post)The link of this resource points to the content of the Hands On Labs of the UC 2015.  The content of the lab Working with the PI AF SDK gives a very good highlight on scenarios that could benefit from optimization such as lazy loading, scheduled tasks, long running applications, multi-user service.  Note: the code is not available for this resource, but the document and the presentation a really worth to read.Document pdf (16 pages)  + powerpoint (38 slides)

 

Information Specific to optimization strategies

TitleSummaryResource Type
Performance of Bulks and Parallel callsTake a deep dive into performance testing of bulk calls, the reading of this thread is for advanced users who want to learn in depth details about how bulk calls and parallel calls can help gaining performances.PI Square Discussion

Filter Blog

By date: By tag: