4/21/2012

Звездочки рейтинга в результатх поиска Google

Наверняка многие из вас замечали, что для некоторых результатов поиска Google показывает текущий рейтинг, который соответствует данному результату на Вашем сайте. Пример того, как выводится подобный результат в Google приведен ниже:


Согласитесь, вероятность того, что такая ссылка имеет больше шансов привлечь внимание пользователя, чем стандартные результаты поиска. Добиться того, чтобы Google отображал результаты подобным образом совсем насложно, используя специальную разметку, которую кроме Google понимают Microsoft, Yahoo!, и другие поисковые системы, которая применяется для результатов рейтингов и обзорово.

Существуют несколько вариантов подобной разметки. Я ограничусь писанием одного из них, про остальные можно почитать здесь: http://support.google.com/webmasters/bin/answer.py?hl=en&answer=172705. Итак, на кадой странице, которая может иметь рейтинг, обязательно должны присутствовать следующие элементы:

1. Название объекта, который оценивается
2. Текущий средний рейтинг
3. Количество голосов или обзоров, на которых строится рейтинг.

Для этого, содержимое страницы, которое содержит информацию о рейтинге, должно быть обравлмлено в тэг с аттрибутаит itemscope и i itemtype="http://data-vocabulary.org/Review-aggregate", например:


HTML тэгу, который обрамляет заголовок объкета надо добавить аттрибут itemprop="itemreviewed". Например:

Мойка "Автобаня"


Средний рейтинг для объекта можно задать в специальном мета-тэге:



Обратите внимание, что Google оперирует шкалой рейтингов от 0 до 5. Если Вы испольщуете другую шкалу, то задать диапазон шкалы можно таким образом:

 




И наконец, количество голосов, на которых базируется рейтинг задается таким образом:

1 отзыв

Для того, чтобы удоставриться, что Вы все сделали правильно, Вам необязательно дожидаться, пока Google проиндексирует страницу. Для проверки нужно ввести ссылку Вашу страницу здесь: http://www.google.com/webmasters/tools/richsnippets

Описанный выше подход успешно применен на сайте отзывов об организациях Киева Отзычик.

Playing video in .NET Windows Forms application

I have a Windows Forms application developed in C#.NET that I used to work on a few years ago which includes functionality that is supposed to display fragments from a video file on form. Everything was fine while the application was working in Windows XP: there are a number of ways to play the video in a .NET application that worked there.One of them, that I was using, is the .NET wrapper for the native windows avifil32 library, which used to play .AVI files just fine. But this approach stopped working in Windows 7. I have also tried Managed DirectX but sing the Managed DirectX was discontinued a while back, it did not work in Windows 7 either. And since I just had to fix the issue I did not feel like rewriting the application with WPF.

Fortunately I found the library called DirectShow.NET, which provides .NET wrappers for native DirectShow calls. There are a number of examples included with the library, so getting it to work was fairly simple. Also, to make my life a little easier I wrote a simple class that allows playing Video in a given Control on a Form (I used PictureBox for this), which I would like to discuss below.

The VideoPlayer class is displayed below. The constructor of the class takes a path to the file that needs to be played and the control where the video should be displayed. The class allows to play fragments from the video starting at a given second and ending at a given second (see method SetFragment, which must be called before the Play method if you just want to play the fragment and not the entire video). There is also a SetRate method that allows to set the speed with which the video will be played.

The use of the class if fairly simple:
1. Download and link to your project the DirectShow.NET library from DirectShow.NET.
2. Copy and paste the VideoPlayer.cs class below and include it into your project.
3. Create a PictureBox control on a form (or any other control that you see fit).
4. Instantiate a VideoPlayer class, providing a path to the file and a reference to a control you just added.
5. Optionally set the frame rate and the fragment start and end positions.
6. Call Play method to start the video and Pause method to stop it.
7. Use the Close method when the video is not needed.

So here comes the code of the VideoPlayer class (a lot which is simply copied and pasted from one of the examples included with DirectShow.NET):

using System;
using System.Collections.Generic;
using System.Text;
using DirectShowLib;
using System.Windows.Forms;
using System.Runtime.InteropServices;


namespace VideoUtils
{
    public class VideoPlayer
    {

        public enum PlayState
        {
            Stopped,
            Paused,
            Running,
            Init
        };

        private Control owner;
        private string FileName;

        private IGraphBuilder graphBuilder = null;
        private IMediaControl mediaControl = null;
        private IMediaEventEx mediaEventEx = null;
        private IVideoWindow videoWindow = null;
        private IBasicAudio basicAudio = null;
        private IBasicVideo basicVideo = null;
        private IMediaSeeking mediaSeeking = null;
        private IMediaPosition mediaPosition = null;
        private IVideoFrameStep frameStep = null;

        private int currentVolume = 0;
        private PlayState currentState = PlayState.Stopped;
        public PlayState state {
            get { return currentState; }
        }

        public VideoPlayer(string file, Control owner)
        {
            this.owner = owner;
            this.FileName = file;

            this.currentState = PlayState.Stopped;
            this.currentVolume = -10000; // muting the audio

            InitializePlayer();
        }

        private void InitializePlayer()
        {
            int hr = 0;

            if (FileName == string.Empty)
                return;

            this.graphBuilder = (IGraphBuilder)new FilterGraph();

            // Have the graph builder construct its the appropriate graph automatically
            hr = this.graphBuilder.RenderFile(FileName, null);
            DsError.ThrowExceptionForHR(hr);

            // QueryInterface for DirectShow interfaces
            this.mediaControl = (IMediaControl)this.graphBuilder;
            this.mediaEventEx = (IMediaEventEx)this.graphBuilder;
            this.mediaSeeking = (IMediaSeeking)this.graphBuilder;
            this.mediaPosition = (IMediaPosition)this.graphBuilder;

            // Query for video interfaces, which may not be relevant for audio files
            this.videoWindow = this.graphBuilder as IVideoWindow;
            this.basicVideo = this.graphBuilder as IBasicVideo;

            // Query for audio interfaces, which may not be relevant for video-only files
            this.basicAudio = this.graphBuilder as IBasicAudio;

            // Have the graph signal event via window callbacks for performance
            // hr = this.mediaEventEx.SetNotifyWindow(owner, WMGraphNotify, IntPtr.Zero);
            DsError.ThrowExceptionForHR(hr);

                // Setup the video window
            hr = this.videoWindow.put_Owner(owner.Handle);
            DsError.ThrowExceptionForHR(hr);

            hr = this.videoWindow.put_WindowStyle(WindowStyle.Child | WindowStyle.ClipSiblings | WindowStyle.ClipChildren);
            DsError.ThrowExceptionForHR(hr);

            GetFrameStepInterface();

            hr = this.videoWindow.SetWindowPosition(0, 0, owner.Width, owner.Height);

            hr = this.basicAudio.put_Volume(this.currentVolume);
        }

        public void Play()
        {
            int hr = 0;
            // Run the graph to play the media file
            hr = this.mediaControl.Run();
            DsError.ThrowExceptionForHR(hr);

            this.currentState = PlayState.Running;
        }

        //
        // Some video renderers support stepping media frame by frame with the
        // IVideoFrameStep interface.  See the interface documentation for more
        // details on frame stepping.
        //
        private bool GetFrameStepInterface()
        {
            int hr = 0;

            IVideoFrameStep frameStepTest = null;

            // Get the frame step interface, if supported
            frameStepTest = (IVideoFrameStep)this.graphBuilder;

            // Check if this decoder can step
            hr = frameStepTest.CanStep(0, null);
            if (hr == 0)
            {
                this.frameStep = frameStepTest;
                return true;
            }
            else
            {
                // BUG 1560263 found by husakm (thanks)...
                // Marshal.ReleaseComObject(frameStepTest);
                this.frameStep = null;
                return false;
            }
        }

        /*
         * Media Related methods
         */

        public void Pause()
        {
            if (this.mediaControl == null)
                return;

            // Toggle play/pause behavior
            if ((this.currentState == PlayState.Paused) || (this.currentState == PlayState.Stopped))
            {
                if (this.mediaControl.Run() >= 0)
                    this.currentState = PlayState.Running;
            }
            else
            {
                if (this.mediaControl.Pause() >= 0)
                    this.currentState = PlayState.Paused;
            }
        }

        public void SetFragment(long start_seconds, long stop_seconds)
        {
            // In Directx time is measured in 100 nanoseconds. 

            DsLong pos_start = new DsLong(start_seconds * 10000000);
            DsLong pos_stop = new DsLong(stop_seconds * 10000000);
            int hr = 0;
            // Seek to the position
            hr = this.mediaSeeking.SetPositions(pos_start, AMSeekingSeekingFlags.AbsolutePositioning, pos_stop, AMSeekingSeekingFlags.AbsolutePositioning);
        }

        public void CloseFile()
        {
            int hr = 0;

            // Stop media playback
            if (this.mediaControl != null)
                hr = this.mediaControl.Stop();

            // Clear global flags
            this.currentState = PlayState.Stopped;

            // Free DirectShow interfaces
            CloseInterfaces();

            // Clear file name to allow selection of new file with open dialog

            // No current media state
            this.currentState = PlayState.Init;

        }

        private void CloseInterfaces()
        {
            int hr = 0;

            try
            {
                lock (this)
                {
                    // Relinquish ownership (IMPORTANT!) after hiding video window
                    hr = this.videoWindow.put_Visible(OABool.False);
                    DsError.ThrowExceptionForHR(hr);
                    hr = this.videoWindow.put_Owner(IntPtr.Zero);
                    DsError.ThrowExceptionForHR(hr);

                    if (this.mediaEventEx != null)
                    {
                        hr = this.mediaEventEx.SetNotifyWindow(IntPtr.Zero, 0, IntPtr.Zero);
                        DsError.ThrowExceptionForHR(hr);
                    }

                    // Release and zero DirectShow interfaces
                    if (this.mediaEventEx != null)
                        this.mediaEventEx = null;
                    if (this.mediaSeeking != null)
                        this.mediaSeeking = null;
                    if (this.mediaPosition != null)
                        this.mediaPosition = null;
                    if (this.mediaControl != null)
                        this.mediaControl = null;
                    if (this.basicAudio != null)
                        this.basicAudio = null;
                    if (this.basicVideo != null)
                        this.basicVideo = null;
                    if (this.videoWindow != null)
                        this.videoWindow = null;
                    if (this.frameStep != null)
                        this.frameStep = null;
                    if (this.graphBuilder != null)
                        Marshal.ReleaseComObject(this.graphBuilder); this.graphBuilder = null;

                    GC.Collect();
                }
            }
            catch
            {
            }
        }

        public int SetRate(double rate)
        {
            int hr = 0;

            // If the IMediaPosition interface exists, use it to set rate
            if (this.mediaPosition != null)
            {
                hr = this.mediaPosition.put_Rate(rate);
            }

            return hr;
        }

    }
}

4/18/2012

Яндекс субботник

Я.Субботник в Киеве, 5 мая

Я.Субботник в Киеве пройдет 5 мая по адресу: Киев, ул. Б.Хмельницкого, 53, отель "Опера", зал "Симфония Гранд Холл".

Регистрация на мероприятие начнется 18 апреля. Количество мест ограничено.

Для тех, кто не попадёт в число участников или не сможет лично присутствовать на Я.Субботнике, будет организована онлайн-трансляция.

Подробную информацию о мероприятии читайте здесь.

4/05/2012

Сайт-справочник организаций и предприятий Киева otzyvchik.com запущен!

Я долго откладывал написание этого сообщения, собирался добавить побольше организаций, написать побольше функционала, но соврешенству нет предела. Поэтому было принято решение запустить сайт таким какой он есть сейчас, пусть люди им начинают пользоваться, и пользователи, то есть вы, сами подскажут, какие организации им наиболее интересны и какие изменения они хотят на сайте увидеть.

Итак, дамы и господа, прошу любить и жаловать - сайт-справочник Киевских предприятий и организаций, и отзывов о них - Отзывчик.

В нынешнем разноообразии заведений и организаций, оказывающих всевозможные услуги, часто бывает сложно сделать выбор: в какой ресторан пойти, где лучше купить свежие продукты, где качественно помоют машину. Озывчик призван помочь в принятии решения, и помогут в этом те люди, которым Вы доверяете - ваши друзья. На отзычике вы можете делиться хорошими и плозими отзывами о люьых заведениях и организациях, добавлять организации и читать отзывы друзей.

Собственно, милости прошу. Заходите, добавляйте организации, пишите отзывы. А если вдруг вам захочется увидеть на сайте что-то новенькое пишите на info@otzyvchik.com.

8/29/2011

Showing text to the right of the input box in Key Survey

Today i have seen a really awesome workaround in one of the Key Survey surveys, where the user has placed the text to the right of the Single Line question input box. Kind of like this:

Note that the word "years" appears to the right of the text input box. And here's hot it's done:

Note that the label for the first answer option is empty and that there is a second answer option with two special tags next to it. Tag <newcolumn/> tells the application that the answer option should appear in the new column (typically this tag is used split the answer options of the same question into multiple columns to prevent scrolling for long lists of answer options. Tag <subheader/> tells that this is not the real answer option and that the application should not show the input box next to it. And both tags used together give you a quick way of showing the text to the right of the input box. (P.S. Some CSS tweaking may be required to make the items appear next to each other, but it is not always required). 

8/28/2011

Converting HTML pages to PDF in pure Java

There are plenty of commercial HTML to PDF converters for the .NET platform (most of which are based on the Internet Explorer libraries that are available in Windows), but HTML to PDF conversion in Java is not that easy. But not impossible. FlyingSaucer allows to convert properly formatted XHTML and CSS2 to PDF (which is in details described here: http://today.java.net/pub/a/today/2007/06/26/generating-pdfs-with-flying-saucer-and-itext.html). However, unfortunately most of the web pages out there are not designed in XHTML. FlyingSuacer when it has to deal with such pages would simply throw an exception and quit. But there also a solution to this limitation, as the developers of FlyingSaucer suggest, we could use one of the existing HTML code cleaners (for example TagSoup, JTidy or HTMLCleaner) for that purpose. Below I would like to show you an example of using HTMLCleaner, FlyingSaucer and iText to convert the HTML to PDF.

package htmltopdf;

import com.lowagie.text.DocumentException;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.htmlcleaner.CleanerProperties;
import org.htmlcleaner.CommentNode;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.HtmlNode;
import org.htmlcleaner.PrettyXmlSerializer;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.TagNodeVisitor;
import org.htmlcleaner.Utils;
import org.xhtmlrenderer.pdf.ITextRenderer;


public class HTMLtoPDF {

    static int cssCounter = 0;
    
    public static void main(String[] args) {
        try {
            final String site = "http://www.keysurvey.co.za";
            final String page = "/company/";
            final String cssUrl = "http://www.keysurvey.co.za";
            
            URL url = new URL(site+page);

            CleanerProperties props = new CleanerProperties();

// HTMLCleaner part
// set some properties to non-default values
            props.setTranslateSpecialEntities(true);
            props.setTransResCharsToNCR(true);
            props.setOmitComments(true);

// do parsing
            TagNode tagNode = new HtmlCleaner(props).clean(url);
            tagNode.traverse(new TagNodeVisitor() {

                public boolean visit(TagNode tagNode, HtmlNode htmlNode) {
                    if (htmlNode instanceof TagNode) {
                        TagNode tag = (TagNode) htmlNode;
                        String tagName = tag.getName();
                        if ("img".equals(tagName)) {
                            String src = tag.getAttributeByName("src");
                            if (src != null && ! src.startsWith("http")) {
                                tag.setAttribute("src", Utils.fullUrl(site, src));
                            }
                        }
                        if ("link".equals(tagName)) {
                            String rel = tag.getAttributeByName("rel");
                            String href = tag.getAttributeByName("href");
                            if (href != null && "stylesheet".equals(rel)) {
                                try {
                                    HttpClient client = new DefaultHttpClient();
                                    String fullUrl = "";
                                    if (href.startsWith("http")) fullUrl = href;
                                    else fullUrl = Utils.fullUrl(cssUrl, href);
                                    HttpGet get = new HttpGet(fullUrl);
                                    HttpResponse response = client.execute(get);
                                    HttpEntity entity = response.getEntity();
                                    if (entity != null) {
                                        InputStream is = entity.getContent();
                                        href = "css" + cssCounter + ".css";
                                        cssCounter++;
                                        OutputStream os = new FileOutputStream(href);
                                        IOUtils.copy(is, os);
                                    }
                                    tag.setAttribute("href", href);
                                } catch (IOException ex) {
                                    Logger.getLogger(HTMLtoPDF.class.getName()).log(Level.SEVERE, null, ex);
                                }
                            }
                        }
                    } else if (htmlNode instanceof CommentNode) {
                        CommentNode comment = ((CommentNode) htmlNode);
                        comment.getContent().append(" -- By HtmlCleaner");
                    }
                    // tells visitor to continue traversing the DOM tree
                    return true;
                }
            });



// serialize to xml file
            new PrettyXmlSerializer(props).writeToFile(
                    tagNode, "page.xhtml", "utf-8");

// FlyingSaucer and iText part
            String inputFile = "page.xhtml";
            String url2 = new File(inputFile).toURI().toURL().toString();
            String outputFile = "firstdoc.pdf";
            OutputStream os = new FileOutputStream(outputFile);

            ITextRenderer renderer = new ITextRenderer();
            renderer.setDocument(url2);
            renderer.layout();
            renderer.createPDF(os);

            os.close();


        } catch (DocumentException ex) {
            Logger.getLogger(HTMLtoPDF.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(HTMLtoPDF.class.getName()).log(Level.SEVERE, null, ex);

        }
    }
}

FlyingSaucer cannot read the CSS files over the web, so we have to save them locally. While parsing the HTML source of the page, for all of the link tags with rel='stylesheet', we need to save a local copy of the css file. Also relative links to the image files need to be replaced with the absolute URLs.

To compile and run the above example you will need the Jars supplied with the FlyingSaucer distribution (it includes XHTMLRenderer and iText), Apache HttpClient, Apache IOUtils and HTMLCleaner.

Overall the code works fairly well on various even poorly formatted pages. There are a few issues that I have noted that may prevent some pages from being rendered to PDF correctly. Here are some of them:
1. Some types of comments (i.e. .ClassName /* some comment */ { ... } ) are not supported by the CSS parser of FlyingSaucer . It simply stops parsing CSS without throwing any exceptions.
2. @import and url() properties of the CSS are not supported.

But overall for many pages the described solution should be goo enough, especially when you need to convert internal pages that you can properly prepare for the PDF conversion.

Добавление кнопки ВКонтакте "Мне нравится" в Blogger

Просмотрел сегодня ряд постов, где люди описывали свои варианты добалвения кнопки ВКонтакте "Мне нравится" к Blogger, но не нашел ни одно 100% работающего варианта. Вернее работающие варинаты сводились к тому, что кнопка работает, когда на одной странице отображается только один пост, в противном сулчае кнопка просто пряталсь. Проблема в том, что скрипт, который по умолчанию предлагает ВКонтакте расчитан на то, что на странице может быть только одна кнопка "Мне нравится".

Итак, вот пошаговая инструкция о том, как показывать кнопку "Мне нравиится" для каждого поста, если у Вас показывается несколько постов на странице.

1. Идем на страницу виджета "Мне нравится" вокнтакте: http://vkontakte.ru/developers.php?o=-1&p=Like и генерируем код с необходимыми настройками.
2. Открываем страницу редактиорвания шаблона в Blogger: Design > Edit HTML. Включаем галочку Expand widget templates:

3. Сразу после тэга вставляем первую часть кода, сгенерированную ВКонтакте. Должно получится нечто вроде:





4. Находим к коде шаблона строку      
<div class="post-footer-line post-footer-line-3">. Ниже этой строки вставляем следюущий код:


Все, теперь внизу каждого вашего поста будет показвать кнопка и работать кнопка "Мне нравится".

5/23/2011

Import images from Picasa web albums to Facebook

I like sharing my photos on Picasa Web Albums: you do not need have an account with Picasa to view the photos, the photos and albums are extremely easy to manage, and the Picasa application itself is probably the best photo management software out there. However, it would be also nice if people on Facebook could see and comment on the photos without having to go to a different site. Until recently, the only proper way to show your Picasa photos on Facebook was to really upload them twice: once to Picasa Web Albums and then to the Facebook albums. Of, course some application existed that made the process of uploading a photo easier, like  the Picasa Uploader - a plugin to the Picasa tool that could upload photos on to Facebook, or Picasa Tab - a Facbook app that allow to create a tab on the profile that would display the Picasa albums (like someone would ever click on that tab),

But today I have found an amazing Facebook app, that does exactly what is needed: it takes a Picasa web album and converts it to the Facebook album. You are also allowed to select individual photos from a Picasa web album and/or upload them into an existing Facebook album instead of creating a new one. The tool is called Picasa Importer.  The process of uploading picture is extremely simple: you provide a link to Picasa album, the tool grabs the photos from it, allows you to select the ones that you want to upload and then creates the Facebook album with the existing photos. Extremely easy and fast.

Hope this will be usedful to someone.

1/03/2011

Распознавание речи при помощи HTK Toolkit, основы

Данная статья является большей частью переводом и адаптацией Tutorial из HTKBook, и представляет собой пошаговое описание построения системы распознавания устного произношения украинских чисел от нуля до девяти. Данная статья дает лишь первое знакомство с HTK Toolkit и позволяет на практике познакомиться с основными компонентами инструментария. Ориентирована статья прежде всего на исследователей, начинающих заниматься проблемами распознавания речи, студентов и аспирантов. Все шаги, описанные ниже могут в принципе быть адаптированы для любого языка. Действия, описанные ниже проводились на операционной системе Windows XP, компиляция HTK Toolkit из исходных кодов проводилась при помощи Visual Studio 2005. Я не буду вдаваться в подробности того, для чего нужна та или иная команда или как они работаю «изнутри», т.к. это достаточно детально описано в HTKBook.

Шаг 1. Скачиваем и устанавливаем HTK Toolkit

HTK Toolkit версии 3.4.1 можно скачать с сайта http://htk.eng.cam.ac.uk/download.shtml
после регистрации на этом сайте.  Регистрируемся на сайте, качаем исходники  HTK, распаковываем их в любую директорию на локальном диске. Самый простой способ собрать HTK, если у вас есть Visual Studio 2003, 2005 или 2008 – при помощи утилиты HTK34xVSxCompile, которую можно скачать отсюда: http://www.co.it.pt/~aveiga/htk/ . Распаковываем архив с HTK34xVSxCompile в директорию, куда прежде был распакован HTK. В директории с HTK появятся два файла: HMMIRest.c и HTK34xVSxCompile.bat. Для сборки достаточно запустить на выполнение HTK34xVSxCompile.bat, и если у Вас в системе есть все необходимое, то сборка пройдет без проблем и в директории с HTK появится новая директория bin.win32, в которой будут все необходимые выполняемые файлы HTK Toolkit. Для того, чтобы создать нашу собственную языковую модель для распознавания, рекомендую скопировать все содержимое из директории bin.win32 в новую директорию, например numbers_UA. Далее, если не указано иначе, все действия производятся в директории numbers_UA, где есть все выполняемые файлы HTK.

Шаг 2. Строим словарь и грамматику.

Система, которую мы строим, должна распознавать любую последовательность украинских чисел от 0 до 9. Для описания грамматики создаем файл grammar со следующим содержимым:

(SENT-START <(ОДИН | ДВА | ТРИ | ЧОТИРИ | П'ЯТЬ | ШІСТЬ | СІМ | ВІСІМ | ДЕВ'ЯТЬ | НУЛЬ)> SENT-END)

Далее необходимо на основе грамматики создать сеть слов (word net). Для этого выполняем команду

HParse.exe grammar wnet

В результате выполнения команды будет создан файл wnet,  в котором будет содержаться  сеть слов.

Следующим шагом руками создаем список слов и словарь. Создаем в текстовом редакторе файл wlist со следующим содержимым:

SENT-END
SENT-START
ВІСІМ
ДВА
ДЕВ'ЯТЬ
НУЛЬ
ОДИН
П'ЯТЬ
СІМ
ТРИ
ЧОТИРИ
ШІСТЬ

Создаем также словарь с фонетической транскрипцией для каждого из приведенных выше слов. В текстовом редакторе создаем файл digits-ua.dic, внутри которого пишем:

SENT-END   [] sil
SENT-START [] sil
ВІСІМ    VV I SS I M sp
ДВА  D V AA sp
ДЕВ'ЯТЬ  D E V J A TT sp
НУЛЬ N U LL sp
ОДИН O D Y N sp
П'ЯТЬ    P J A TT sp
СІМ  SS I M sp
ТРИ  T R YY sp
ЧОТИРИ   CH OE T YY R Y sp
ШІСТЬ    SHI I SS TT sp

Для того, чтобы привести словарь к форме пригодной для HTK Toolkit, необходимо выполнить команду

HDMan.exe –m –w wlist –n monophones1 –l dlog dict digits-ua.dic

Эта команда создаст файл фонетического словаря dict и список фонем monophones1.

Шаг 3. Запись и кодирование обучающих данных.

Для того, чтобы построить модель скрытых цепей Маркова для системы распознавания речи, необходимо систему обучить. Для этого необходимо записать набор обучающих данных. Обучающими данными в нашем случае будет набор фраз, которые Вы можете записать самостоятельно. Сгенерировать набор фраз, соответствующих целевой грамматике, можно сгенерировать при помощи команды

HSGen –l –n 10 wnet dict > trainprompts

Эта команда создаст файл trainprompts со списком из 10 обучающих фраз (можно и больше, но 10 в принципе достаточно для обучения нашей системы). Файл может иметь приблизительно следующий вид:

1. SENT-START ОДИН ТРИ ЧОТИРИ ШІСТЬ ОДИН SENT-END
2. SENT-START ВІСІМ SENT-END
3. SENT-START ВІСІМ СІМ ОДИН ТРИ SENT-END

Записать обучающие фразы можно при помощи программы HSLab, которая входит в состав HTK Toolkit. Для записи каждой из 10 тренировочных фраз необходимо вызвать команду

HSLab <имя записи>

В качестве имени записей можно использовать имена вроде s01, s02,…, s10. У меня не получилось заставить программу HSLab создавать новый файл для новой записи без перезапуска программы, так что для записи каждой новой фразы имеет смысл выйти из HSLab при помощи кнопки Quit и запустить ее заново с новым именем записи. Для записи в программе HSLab надо нажать кнопку Rec, произнести фразу и нажать клавишу Stop. В результате записи в текущей директории будут созданы файлы s01_0, s02_0 и т.д.

После того, как данные записаны необходимо создать файл с описанием записанных данных. Его можно создать руками в текстовом редакторе. Назовем файл words.mlf и запишем в него следующее содержимое:

#!MLF!#
"*/s01.lab"
ОДИН
ТРИ
ЧОТИРИ
ШІСТЬ
ОДИН
.
"*/s02.lab"
ВІСІМ
.
"*/s03.lab"
ВІСІМ
СІМ
ОДИН
ТРИ
.

Для каждой фразы из набора обучающих данных  необходимо разбить фразу на отдельные слова и записать каждое слово с новой строки. В конце каждой фразы, кроме последней, в отдельной строке должна быть точка.

Для следующего шага нам понадобится еще один текстовый файл. Назовем его mkphones0.led. Сгенерируем фонетическую транскрипцию для записанных обучающих данных при помощи команды

HLed.exe –l * -d dict –l phones0.mlf mkphones0.led words.mlf

В результате выполнения будет создан файл phones0.mlf с фонетической транскрипцией для обучающих фраз.

Следующим шагом необходимо закодировать записанные обучающие данные, выделив из них характеристические векторы, основанные на коэффициентах мел-кепстр. Для этого создаем конфигурационный файл config со следующим содержимым:

TARGETKIND = MFCC_0
TARGETRATE = 100000.0
SAVECOMPRESSED = T
SAVEWITHCRC = T
WINDOWSIZE = 250000.0
USEHAMMING = T
PREEMCOEF = 0.97
NUMCHANS = 26
CEPLIFTER = 22
NUMCEPS = 12
ENORMALISE = F

Список файлов для кодирования задаем в текстовом файле codetr.scp, со следующим содержимым:

s01_0    s01.mfc
s02_0    s02.mfc
s03_0    s03.mfc
s04_0    s04.mfc
s05_0    s05.mfc
s06_0    s06.mfc
s07_0    s08.mfc
s09_0    s09.mfc
s10_0    s10.mfc

Кодирование данных осуществляем при помощи команды

HCopy –T 1 –C config –S codetr.scp


Шаг 4. Обучение скрытых моделей Маркова

В текстовом редакторе создаем файл прототип модели Маркова для системы распознавания названием proto, внутри которого помещаем текст:

~o 39
~h "proto"
     5
     2
         39
              0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
         39
                        1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
     3
         39
              0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
         39
                        1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
     4
         39
              0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
         39
                        1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
     5
         0.0 1.0 0.0 0.0 0.0
         0.0 0.6 0.4 0.0 0.0
         0.0 0.0 0.6 0.4 0.0
         0.0 0.0 0.0 0.7 0.3
         0.0 0.0 0.0 0.0 0.0
               

Также создаем файл со списком закодированных файлов с обучающими данными train.scp:

s01.mfc
s02.mfc
s03.mfc
s04.mfc
s05.mfc
s06.mfc
s08.mfc
s09.mfc
s10.mfc

В файле config первую строку заменяем на

TARGETKIND = MFCC_0_D_A

Далее в текущей директории создаем поддиректории hmm0, hmm1, hmm2, …, hmm9. В последствии в эти директории будут сохранены созданные модели. Создаем прототип модели при помощи команды

HCompV.exe –C config –f 0.01 –m –S train.scp –M hmm0 proto

В результате выполнения в директории hmm0 будут созданы файлы proto и vFloors. В основной директории создаем файл monophones0 копированием файла monophones1 и удалением из него строки sp. В директории hmm0 создаем файл hmmdefs, в котором будут описаны модели для кажой из фонем из файла monophones0, включая sil. Для этого внутри файла hmmdefs для каждой фонемы записываем строку типа

~h "sil"

(в кавычках пишем название фонемы, так как оно записано в monophones0), ниже этой строчки копируем из файла hmm0/proto секцию, начинающуюся со строки <BEGINHMM> и заканчивающуюся строкой <ENDHMM>. Эта секция должна быть одинаковой для каждой фонемы..

Также в директории hmm0 создаем файл macros, в содержимое которого пишем первые три строки из файла hmm0/proto и все содержимое файла vFloors.

После этого последовательно выполняем команды

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm0/macros –H hmm0/hmmdefs –M hmm1 monophones0

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm1/macros –H hmm1/hmmdefs –M hmm2 monophones0

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm2/macros –H hmm2/hmmdefs –M hmm3 monophones0

Следующим шагом к текущим моделям нужно добавить модель короткой паузы. Для этого копируем содержимое директории hmm3 в директорию hmm4, и в файле hmm4/hmmdefs копируем описание модели для фонемы sil и создаем такое же  описание для фонемы sp (~hsp”). В основной директории создаем файл sel.hed с содержимым

AT 2 4 0.2 {sil.transP}
AT 4 2 0.2 {sil.transP}
AT 1 3 0.3 {sp.transP}
TI silst {sil.state[3],sp.state[2]}

И выполняем команду

HHEd.exe –H hmm4/macros –H hmm4/hmmdefs –M hmm5 sil.hed monophones1

После этого уточняем модель командами

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm5/macros –H hmm5/hmmdefs –M hmm6 monophones1

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm6/macros –H hmm6/hmmdefs –M hmm7 monophones1

Далее, в файл dict добавляем строку silence sil и создаем уточненную фонетическую транскрипцию, выполнением команды

HVite –l * -o SWT –b silence –C config –a –H hmm7/macros –H hmm7/hmmdefs –I aligned.mlf –m –t 250.0 –y lab –I words.mlf –S train.scp dict monophones1

Результатом будет файл aligned.mlf, в котором будут более точные фонетический транскрипции для всех обучающих данных. Некоторые из обучающих данных могут быть пропущены. Для того, чтобы в дальнейшем избежать ошибок, надо скопировать их транскрипции из файла phones0.mlf в aligned.mlf. После этого модель еще дважды нужно уточнить командами

HERest.exe –C config –I aligned.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm7/macros –H hmm7/hmmdefs –M hmm9 monophones1

HERest.exe –C config –I phones0.mlf –t 250.0 150.0 1000.0 –S train.scp –H hmm8/macros –H hmm8/hmmdefs –M hmm9 monophones1


Шаг 5. Распознавание речи на основе созданной модели.

Создаем текстовый файл config2 копированием файла config. В конце файла config2 дописываем строки:

SOURCERATE=625.0
SOURCEKIND=HAUDIO
SOURCEFORMAT=HTK
ENORMALISE=F
USESILDET=T
MEASURESIL=F
OUTSILWARN=T

Переключаем консоль Windows в кодировку Win1251 (в русскоязычных Windows она по умолчанию 866) командой

chcp 1251

Запускаем программу распознавания речи в «живом» режиме командой

HVite.exe –H hmm9/macros –H hmm9/hmmdefs –C config2 –w wdnet –p 0.0 –s 5.0 dict monophones1

Сразу после запуска программа проведет измерение уровней входящего сигнала. Для этого после отображения приглашения

READY[1]>
Please speak sentence – measuring levels

Необходимо произнести любое предложение. Обычно измерение уровня длится около 4 секунд. По окончанию измерения программа отобразит строку Level measurement completed. Если такая строка не появляется в течение 5-6 секунд, следует остановить программу нажатием Ctrl+C (другого способа я не нашел) и запустить ее заново.

Далее последовательно произносите последовательности чисел, которые система должна распознать и смотрите, как они появляются на экране.

11/15/2010

Киев с высоты 28 этажа

Оказывается на центр Киева тоже можно посмотреть с высоты! За торговым центром Олимпийский есть офисное здание, где работает один из моих друзей. В этом здании лифтом можно добраться до 23 этажа и дальше по лестнице подняться на балкон 28 этажа. Оттуда открывается замечательный вид на Киев, почти как в Нью Йорке :).


11/14/2010

Сайт поиска по товарам, услугам и организациям Мариуполя - infomega.com.ua

С сегодняшнего дня официально запущен поисковый сервис по товарам, услугам и организациям города Мариуполь (пока только Мариуполя, но в дальнейшем будем расширяться на другие города). - infomega.com.ua. Сайт представляет собой справочник коммерческих и некоммерческих организаций Мариуполя, а также предоставляемых ими товаров и услуг. На сегодняшний день сервис предоставляет поиск по более чем 4000 организаций и 6000 товаров и услуг благодаря базе, предоставленной справочной службой 15-50. Результаты поиска в удобном виде отображаются на карте города, присутствует также возможность поиска рядом с заданным адресом или объектом. Одной из уникальных особенностей сервиса является предоставляемая посетителям возможность оценивать организации и оставлять о них свои отзывы. Результаты поиска в последствии сортируются в зависимости от рейтинга организации. Таким образом мы пытались помочь пользователю решить проблему выбора в большом наборе однотипных товаров и услуг. И для того, чтобы оценить организацию или оставить комментарий, не обязательно регистрироваться на сайте, можно воспользоваться своей учетной запись в Google, Facebook, LiveJournal, LinkeIn, Flikr или своим OpenID.

5/02/2010

Прогулка по Бостону

По приезду в Бостон, мы поселились в домике, который наша компания снимает специально для таких приезжих как мы в Weymouth, одном из пригородов Бостона. Несмотря на то, что жили мы от Бостона относительно недалеко, мы сделали туда всего несколько вылазок, т.к. основную часть времени я был занят работой.

Бостон отличается от большинства других американских городов тем, что здесь сразу понимаешь, что это – город с историей. Такое количество старинных зданий и памятников историческим событиям пожалуй вряд ли найдешь в каком либо другом месте в Америке. Центр Бостона довольно компактный, так что основные его достопримечательности можно осмотреть совершенно спокойно за пару дней. Передвигаться по Бостону, как и по большинству других крупных городов лучше пешком (во всяком случае по центральной его части), т.к. с парковкой здесь наверняка будут проблемы. Оставить автомобиль лучше на одной из многоэтажных платных парковок, хотя в выходной день можно поискать и место для бесплатной парковки на улице – в выходные дни машин здесь значительно меньше.

Мы начали свое знакомство с Бостоном с прибрежной его части. Отсюда открываются замечательные виды на океан, а дали виднеется международные аэропорт Бостон Логан, где буквально каждую минуту можно наблюдать садящийся или взлетающий самолет. Если есть желание с набережной можно отправиться на экскурсию по океану и посмотреть на китов (Whale watch). Рядом с океанским побережьем находится Бостонский Аквариум. Снаружи его находится открытый резервуар с водой где можно бесплатно полюбоваться на тюленей.

От аквариума идем к Faneuil Hall - это площадь, которая с 1742 года была рыночной площадью и площадью собраний, рядом с которой находится собственно здание Faneuil Hall, построенное в том же 1742 году, которое на сегодняшний день является национальным историческим памятником. На противоположном конце площади расположен Quincy Market, здание построенное в 1824-1826 годах и названное в честь мэра Josiah Quincy. Сейчас здесь наверное самый большой выбор бостонских сувениров.

Напротив площади Faneuil Hall расположена площадь Government Center plaza, в центре которой находится здание муниципального управления Бостона. Квадратное девятиэтажное здание значительно отличается от стоящих вокруг исторических зданий и площадей, и, говорят было даже признано самым уродливым зданием в Бостоне.
От площади Faneuil Hall вдоль Congress street. движемся в центральную часть города.

Здесь, на пересечении Congress и State street, в окружении небоскребов затерялось маленькое 3-х этажное здание со шпилем и золотым орлом над входом - это Old State House, или старый дом правительства штата. Здание было построено в 1713 году и на сегодняшний день является старейшим общественным зданием, сохранившимся в Бостоне. Сейчас здесь музей, а у правительства штата есть «новый» дом, к которому мы доберемся чуть позже.

Вдоль Washignton street едем к перекрестку с School street. Здесь на углу расположен книжный магазин, а перед ним в маленьком сквере – памятник ирландским эмигрантам, бежавшим от голода в Ирландии в Америку. Поворачиваем на School street и идем к старому зданию муниципального управления – Old City Hall. Не в пример новому зданию городского управления, это здание, построенное в 1865 году и выдержанное во французском имперском стиле является очень массивным и красивым.

Дальше идем к пересечению с Tremont street. Здесь, в самом центре Бостона, расположено удивительное старинное кладбище – Granary Burying Ground. Четырехсотлетние надгробия в самом центре города, окруженные высокими зданиями выглядят поистине удивительно, но ничуть не жутко. Кладбище открыто для всех и если бы не надгробия выглядело бы обыкновенным сквером или парком. Местные жители говорят, что у коренных жителей Бостона обязательно найдется хотя бы один, пусть отдаленный родственник, покоящийся на этом кладбище.

Напротив кладбища находится небольшая, но очень уютная церковь. Внутри церкви сиденья разделены между собой перегородками, таким образом, что каждая семья как бы отделена от других, и дети могут сидеть напротив своих родителей. Очевидно, что церковь - для элиты.

Дальше идем к Park street, за которой начинается огромный парк – Boston Common. Вдоль Park street поднимаемся к новому зданию правительства штата – New State House. Его видно издалека – большое задние с сияющим на солнце золотым куполом. Именно здесь сейчас творят законы для штат Массачусетс. А прямо напротив здания – на углу открытая студия новостей.

Далее плавно прогуливаемся через парк. Зеленый остров в середине города, окруженный со всех сторон жилыми и офисными зданиями по-своему успокаивает. В парке довольно много белок, которые практически не боятся людей. В центре парка – каток. По центральной улице парка доходим по пересечения с Charles street, переходя которую попадаем в Public Garden – продолжение парка с большим озером и красивым мостиком в самом центре. В конце Public Garde находится величественный памятник первому президенту Джорджу Вашингтону. Интересная особенность памятников историческим людям в Бостоне: если человек изображен всадником на коне и у коня подняты обе передние ноги, как например на памятнике Вашингтону, то это означает, что человек погиб в бою, если у коня поднята одна передняя нога, то человек получил ранения в бою и скончался позднее от ран, а если конь стоит всеми четырьмя ногами на земле, то человек умер от естественных причин.

Выходя из парка попадаем на Arlington street, поворачиваем налево и идем до пересечения с Boylson street, где на углу перекрестка расположен старинный готический костел – Arlington street church. Вдоль Boylson street расположены неверятно дорогие и фешенебельные магазины и бутики, а вдоль улицы припаркованы дорогие автомобили. Вдали слева виднеется самый высокий небоскреб Бостона - John Hancock tower. Чтобы подобраться ближе к нему идем два квартала до Clarendon street, а на ней поворачиваем налево. Между Boylston street и St. James avenue расположена старинная церковь – Trinity church, основанная в 1733 году, стены и башни которой отражаются в зеркальных стенах небоскреба. За церковью – небольшая площадь Copley Square, напротив которой – довольно мрачное здание центральной бостонской библиотеки.

Дальше по Boylston street идем ко второму самому высокому зданию Бостона – Prudential tower, находящемуся во главе целого комплекса зданий – Prudential Center. В небоскребе всего 52 этажа, и на 50-м этаже расположена открытая для общественности площадка для обозрения, из которой открывается сногсшибательный панорамный вид на Бостон и Гарвард. Prudential tower стал последней точкой нашего первого Бостонского маршрута.

Что еще мы видели в Бостоне?
В один из наших следующих приездов мы посетили музей Массачусетского Технологического Института, где представлены новые и не очень разработки и изобретения американских ученых. Наряду с новейшими роботами и космическими технологиями там можно увидеть и такие странные вещи, как например пресс для радиоприемников. В целом – довольно интересно, рекомендую посетить если вы интересуетесь технологиями. Нам кстати туда посчастливилось попасть совершенно бесплатно, так как мы пришли за 15 минут до закрытия музея, но к сожалении осматривать экспозицию пришлось бегло и в спешке.

Где в Бостоне поесть?
Очень рекомендую лобстеров в No Name Restaurant расположенный на самом берегу океана по адресу 15 Fish Pier St. К тому же само место относительно тихое и здесь не встретишь много туристов, а лобстеры будут выловлены из океана наверняка не позднее утра текущего дня.

Из Киева в Америку

В феврале 2010 года выпала нам с женой возможность провести месяц в Америке. Две недели из этого месяца мне предстояла коммандировка, а остальные две недели мы планировали провести в отпуске, путешествуя по Калифорнии. В итоге этот месяц получился незабываемым и ниже я постараюсь описать все подробности этого увлекательного путешествия.

Маршрут перелета Киев - Бостон был довольно стандартным, с одной пересадкой в Лондоне и не прдвещал ничего необычного. Вылетали мы из Киева около 9 утра и уже к вечеру того же дня (по Американскому времени) должны были быть в Бостоне. Перелет до Лондона действительно прошел без приключений, а в Лондоне стало инетресно. Моя жена обладает замечательной интуицией, которая ее практически никогда не подводит. Еще на подлете до Лондона она начала мне жаловаться, что ей мол кажется что что-то будет не так, что что-то с нами по дороге должно случиться. По мере приближения времени вылета из Лондона это предчуствие у нее усиливалось. Итак, где-то за час до вылета рейса Лондон-Бостон мы прошли ручной досмотр личных вещей (американцы, как оказалось не пускают в самолет людей, не ощупав их и не порывшись в их рюкзаках и сумочках) и прошли в зал ожидания ждать наш самолет. Странным показадлсь то, что за полчаса до вылета информация о рейсе так и не появлялась на табло.


Буквально сразу же после появления информации на табло прозвучало объявление о том, что рейс задерживается. Такие объявления звучали еще где-то в течение часа, после чего было сказано, что рейс отменяется по техническим причинам. То, что на первый взгляд казалось неприятным обстоятельством обернулось для нас, пусть не очень значительным, но интересным приключением. Более того, авиакомпания просила нас забрать свой багаж. Интересность ситуации состояла также в том, что для того, чтобы забрать багаж нужно было пересечь британскую границу, т.е. пройти паспортный контроль, что по причине отсутствия у нас британских виз было невозможным. Идем к представителям  British Airways, по пути прихватываем еще двух пассажиров из Харькова, которые летели в Бостон к дочери и очень слабо могли объяснятся по-английски. Тут нужно отдать должное British Airways, они сразу же выдали нам билеты на утренний рейс, а кроме того, выделили номер в гостинице с бесплатным ужином и завтраком и билеты на автобус до гостиницы. Идем к стойкам паспортного контроля, и тут нам совершенно без проблем и лишних вопросов выдают визу в Великобртитаню на 24 часа!

Мы выбираемся наконец из аэропорта, забрав свой багаж, и садимся в автобус, который идет к нашей гостинице. Водитель надо сказать ехал так, как будто очень сильно куда-то опаздывал, я честно сказать, даже не думал, что автобус может так проходить повороты. В гостинице, поздоровавшись с парнем на ресепшне по-английски, протягиваем ему наши паспорта, после чего он говорит «А теперь перейдем на русский». Наши всюду! Заранее скажу, что людей, говорящих по-русски, за границей намного больше, чем этого можно ожидать, нам они по ходу путешествия попадались на каждом шагу.

Гостиница оказалась тоже гораздо лучше, чем можно было бы предположить для пассажиров эконом-класса. Нам достался номер стоимость 209 фунтов за ночь. При этом, парень на ресепшн был так любезен, что выдал нам пароль для доступа в Интернет также совершенно бесплатно. Более того, ужин и завтрак были в ресторане с самообслуживанием, где совершенно без ограничений можно было брать все, что сможешь есть, и качество еды было на высоком уровне.

Единственное разочарование, гостиница, в которой нас поселили находилась рядом с аэропортом Хитроу, то есть на расстоянии около часа езды на метро от центра Лондона. Метро перестает ходить в полночь, а к моменту когда мы поселились в гостиницу было уже около 11 вечера. А такси до центра обошлось бы в сумму около 100 фунтов, что казалось невероятно дорого. Пожалуй, это было единственное маленькое разочарование – ведь 24 часовую британскую визу можно было бы использовать гораздо более активно!. С другой стороны, мы замечательно выспались и отдохнули. На следующее утро, мы без приключений вылетели рейсом на Бостон. 



4/13/2010

Capturing Geo location information in surveys and showing it in the reports with Key Survey

Sometimes it could be a very good idea to capture where you survey respondents come from. Key Survey has a few plugins designed specifically for the purpose of capturing respondent location and displaying it on a Map in the report. This post will give you step-by-step instructions on how to capture geographical information and display it in the report.

First you will need to create a placeholder question in the survey to capture the geographical information and the respondent’s IP address. All this could be stored in a one single line question with the following answer options:
1. IP address
2. Latitude
3. Longitude (these are required to display the information on the map properly)
4. Country
5. State/Region
6. City
In most cases you will not want your respondent to see this information, so you can make the question hidden. This question should be located on the first page of the survey.
On the example below I have created a simple survey with one Geo Location question and one real question.



After the questions have been created we need to set up the plugins. To do this, go to Rules and click the Plugins button. On the plugins page click the Add new plugin button, expand IP Address Tools and click insert next to IP address saver. We will need to capture the respondent’s IP address to determine his geographic location. As IP address question put Q1.A1, this is where we want to have the IP address stored.
After that add another plugin, called Respondent Location. Put the relevant answer line identifiers as parameters. In the ens the plugin setup should look similar to this:



That all we need to capture the respondent location. Now you can take the Master URL of the survey and open take the survey to verify that the geo location information is captured. The results in the report by respondent mode should look approximately as follows:



No after we’d want to display capture information in a report on a map. To do this, go back to the plugins page and add a new plugin: either Respondent location map (Long. and Lat.) or Respondent Location Map (IP address). Both plugins are located in the Summary report section. The difference between the two is that the first plugin will use the longitude and latitude information that has been captured in the survey and the second plugin will show the respondent location based on the IP address saved internally in the system. In other words we could have used the second plugin without setting up any other plugins for this survey, but assuming we wanted also to capture respondent’s Country, State and City to use for further analysis, other two plugins are required.
So for the report we will set up the plugin based on the IP address. The only required parameter for this plugin is location for the map within the report. This should be a question identifier above which the map should be displayed. If you want the map to be displayed on top of the report, put Q1 as this required parameter. Other parameters are optional and you can find more information about them in the help documentation. In the end the plguin settings for the survey should look similar to the following:



Now if we go to the report we should see something similar to the following:

For more information on the the WorldApp's Key Survey please visit http://www.keysurvey.com