annotate rdf/RDFImporter.cpp @ 493:3931711b5671

* RDF importer: add model titles where possible * RDF transform factory: report whether something appears to be RDF or not (so we can avoid trying to load it as something else if the RDF query fails)
author Chris Cannam
date Tue, 25 Nov 2008 13:43:56 +0000
parents 23945cdd7161
children b71116d3c180
rev   line source
Chris@439 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@439 2
Chris@439 3 /*
Chris@439 4 Sonic Visualiser
Chris@439 5 An audio file viewer and annotation editor.
Chris@439 6 Centre for Digital Music, Queen Mary, University of London.
Chris@439 7 This file copyright 2008 QMUL.
Chris@439 8
Chris@439 9 This program is free software; you can redistribute it and/or
Chris@439 10 modify it under the terms of the GNU General Public License as
Chris@439 11 published by the Free Software Foundation; either version 2 of the
Chris@439 12 License, or (at your option) any later version. See the file
Chris@439 13 COPYING included with this distribution for more information.
Chris@439 14 */
Chris@439 15
Chris@439 16 #include "RDFImporter.h"
Chris@439 17
Chris@439 18 #include <map>
Chris@439 19 #include <vector>
Chris@439 20
Chris@439 21 #include <iostream>
Chris@439 22 #include <cmath>
Chris@439 23
Chris@439 24 #include "SimpleSPARQLQuery.h"
Chris@439 25
Chris@439 26 #include "base/ProgressReporter.h"
Chris@439 27 #include "base/RealTime.h"
Chris@439 28
Chris@439 29 #include "data/model/SparseOneDimensionalModel.h"
Chris@439 30 #include "data/model/SparseTimeValueModel.h"
Chris@439 31 #include "data/model/EditableDenseThreeDimensionalModel.h"
Chris@449 32 #include "data/model/NoteModel.h"
Chris@449 33 #include "data/model/RegionModel.h"
Chris@439 34
Chris@439 35 using std::cerr;
Chris@439 36 using std::endl;
Chris@439 37
Chris@439 38 class RDFImporterImpl
Chris@439 39 {
Chris@439 40 public:
Chris@439 41 RDFImporterImpl(QString url, int sampleRate);
Chris@439 42 virtual ~RDFImporterImpl();
Chris@490 43
Chris@490 44 void setSampleRate(int sampleRate) { m_sampleRate = sampleRate; }
Chris@439 45
Chris@439 46 bool isOK();
Chris@439 47 QString getErrorString() const;
Chris@439 48
Chris@490 49 QString getAudioAvailableUrl() const { return m_audioAvailableAt; }
Chris@490 50
Chris@439 51 std::vector<Model *> getDataModels(ProgressReporter *);
Chris@439 52
Chris@439 53 protected:
Chris@439 54 QString m_uristring;
Chris@439 55 QString m_errorString;
Chris@490 56 QString m_audioAvailableAt;
Chris@439 57 int m_sampleRate;
Chris@439 58
Chris@440 59 void getDataModelsSparse(std::vector<Model *> &, ProgressReporter *);
Chris@440 60 void getDataModelsDense(std::vector<Model *> &, ProgressReporter *);
Chris@440 61
Chris@493 62 void getDenseModelTitle(Model *, QString, QString);
Chris@493 63
Chris@440 64 void getDenseFeatureProperties(QString featureUri,
Chris@440 65 int &sampleRate, int &windowLength,
Chris@440 66 int &hopSize, int &width, int &height);
Chris@440 67
Chris@449 68
Chris@449 69 void fillModel(Model *, long, long, bool, std::vector<float> &, QString);
Chris@439 70 };
Chris@439 71
Chris@439 72
Chris@439 73 QString
Chris@439 74 RDFImporter::getKnownExtensions()
Chris@439 75 {
Chris@439 76 return "*.rdf *.n3 *.ttl";
Chris@439 77 }
Chris@439 78
Chris@439 79 RDFImporter::RDFImporter(QString url, int sampleRate) :
Chris@439 80 m_d(new RDFImporterImpl(url, sampleRate))
Chris@439 81 {
Chris@439 82 }
Chris@439 83
Chris@439 84 RDFImporter::~RDFImporter()
Chris@439 85 {
Chris@439 86 delete m_d;
Chris@439 87 }
Chris@439 88
Chris@490 89 void
Chris@490 90 RDFImporter::setSampleRate(int sampleRate)
Chris@490 91 {
Chris@490 92 m_d->setSampleRate(sampleRate);
Chris@490 93 }
Chris@490 94
Chris@439 95 bool
Chris@439 96 RDFImporter::isOK()
Chris@439 97 {
Chris@439 98 return m_d->isOK();
Chris@439 99 }
Chris@439 100
Chris@439 101 QString
Chris@439 102 RDFImporter::getErrorString() const
Chris@439 103 {
Chris@439 104 return m_d->getErrorString();
Chris@439 105 }
Chris@439 106
Chris@490 107 QString
Chris@490 108 RDFImporter::getAudioAvailableUrl() const
Chris@490 109 {
Chris@490 110 return m_d->getAudioAvailableUrl();
Chris@490 111 }
Chris@490 112
Chris@439 113 std::vector<Model *>
Chris@439 114 RDFImporter::getDataModels(ProgressReporter *r)
Chris@439 115 {
Chris@439 116 return m_d->getDataModels(r);
Chris@439 117 }
Chris@439 118
Chris@439 119 RDFImporterImpl::RDFImporterImpl(QString uri, int sampleRate) :
Chris@439 120 m_uristring(uri),
Chris@439 121 m_sampleRate(sampleRate)
Chris@439 122 {
Chris@490 123 SimpleSPARQLQuery::Value value =
Chris@490 124 SimpleSPARQLQuery::singleResultQuery
Chris@490 125 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 126 QString
Chris@490 127 (" PREFIX mo: <http://purl.org/ontology/mo/> "
Chris@490 128 " SELECT ?url FROM <%1> "
Chris@490 129 " WHERE { ?signal a mo:Signal ; mo:available_as ?url } "
Chris@492 130 ).arg(m_uristring),
Chris@490 131 "url");
Chris@490 132
Chris@490 133 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 134 m_audioAvailableAt = value.value;
Chris@490 135 }
Chris@439 136 }
Chris@439 137
Chris@439 138 RDFImporterImpl::~RDFImporterImpl()
Chris@439 139 {
Chris@492 140 SimpleSPARQLQuery::closeSingleSource(m_uristring);
Chris@439 141 }
Chris@439 142
Chris@439 143 bool
Chris@439 144 RDFImporterImpl::isOK()
Chris@439 145 {
Chris@439 146 return (m_errorString == "");
Chris@439 147 }
Chris@439 148
Chris@439 149 QString
Chris@439 150 RDFImporterImpl::getErrorString() const
Chris@439 151 {
Chris@439 152 return m_errorString;
Chris@439 153 }
Chris@439 154
Chris@439 155 std::vector<Model *>
Chris@439 156 RDFImporterImpl::getDataModels(ProgressReporter *reporter)
Chris@439 157 {
Chris@439 158 std::vector<Model *> models;
Chris@439 159
Chris@490 160 if (m_sampleRate == 0) {
Chris@490 161 std::cerr << "RDFImporter::getDataModels: invalid sample rate" << std::endl;
Chris@490 162 return models;
Chris@490 163 }
Chris@490 164
Chris@440 165 getDataModelsDense(models, reporter);
Chris@440 166
Chris@440 167 QString error;
Chris@440 168 if (!isOK()) error = m_errorString;
Chris@440 169 m_errorString = "";
Chris@440 170
Chris@440 171 getDataModelsSparse(models, reporter);
Chris@440 172
Chris@440 173 if (isOK()) m_errorString = error;
Chris@440 174
Chris@440 175 return models;
Chris@440 176 }
Chris@440 177
Chris@440 178 void
Chris@440 179 RDFImporterImpl::getDataModelsDense(std::vector<Model *> &models,
Chris@440 180 ProgressReporter *reporter)
Chris@440 181 {
Chris@440 182 SimpleSPARQLQuery query = SimpleSPARQLQuery
Chris@489 183 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@480 184 QString
Chris@440 185 (
Chris@440 186 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 187 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 188
Chris@493 189 " SELECT ?feature ?feature_signal_type ?value "
Chris@440 190 " FROM <%1> "
Chris@440 191
Chris@440 192 " WHERE { "
Chris@440 193
Chris@493 194 " ?signal af:signal_feature ?feature . "
Chris@440 195
Chris@440 196 " ?feature a ?feature_signal_type ; "
Chris@440 197 " af:value ?value . "
Chris@440 198
Chris@440 199 " } "
Chris@440 200 )
Chris@440 201 .arg(m_uristring));
Chris@440 202
Chris@440 203 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@440 204
Chris@440 205 if (!query.isOK()) {
Chris@440 206 m_errorString = query.getErrorString();
Chris@440 207 return;
Chris@440 208 }
Chris@440 209
Chris@440 210 if (query.wasCancelled()) {
Chris@440 211 m_errorString = "Query cancelled";
Chris@440 212 return;
Chris@440 213 }
Chris@440 214
Chris@440 215 for (int i = 0; i < results.size(); ++i) {
Chris@440 216
Chris@440 217 QString feature = results[i]["feature"].value;
Chris@440 218 QString type = results[i]["feature_signal_type"].value;
Chris@440 219 QString value = results[i]["value"].value;
Chris@440 220
Chris@440 221 int sampleRate = 0;
Chris@440 222 int windowLength = 0;
Chris@440 223 int hopSize = 0;
Chris@440 224 int width = 0;
Chris@440 225 int height = 0;
Chris@440 226 getDenseFeatureProperties
Chris@440 227 (feature, sampleRate, windowLength, hopSize, width, height);
Chris@440 228
Chris@440 229 if (sampleRate != 0 && sampleRate != m_sampleRate) {
Chris@440 230 cerr << "WARNING: Sample rate in dense feature description does not match our underlying rate -- using rate from feature description" << endl;
Chris@440 231 }
Chris@440 232 if (sampleRate == 0) sampleRate = m_sampleRate;
Chris@440 233
Chris@440 234 if (hopSize == 0) {
Chris@440 235 cerr << "WARNING: Dense feature description does not specify a hop size -- assuming 1" << endl;
Chris@440 236 hopSize = 1;
Chris@440 237 }
Chris@440 238
Chris@440 239 if (height == 0) {
Chris@440 240 cerr << "WARNING: Dense feature description does not specify feature signal dimensions -- assuming one-dimensional (height = 1)" << endl;
Chris@440 241 height = 1;
Chris@440 242 }
Chris@440 243
Chris@440 244 QStringList values = value.split(' ', QString::SkipEmptyParts);
Chris@440 245
Chris@440 246 if (values.empty()) {
Chris@440 247 cerr << "WARNING: Dense feature description does not specify any values!" << endl;
Chris@440 248 continue;
Chris@440 249 }
Chris@440 250
Chris@440 251 if (height == 1) {
Chris@440 252
Chris@440 253 SparseTimeValueModel *m = new SparseTimeValueModel
Chris@440 254 (sampleRate, hopSize, false);
Chris@440 255
Chris@440 256 for (int j = 0; j < values.size(); ++j) {
Chris@440 257 float f = values[j].toFloat();
Chris@440 258 SparseTimeValueModel::Point point(j * hopSize, f, "");
Chris@440 259 m->addPoint(point);
Chris@440 260 }
Chris@493 261
Chris@493 262 getDenseModelTitle(m, feature, type);
Chris@440 263
Chris@440 264 models.push_back(m);
Chris@440 265
Chris@440 266 } else {
Chris@440 267
Chris@440 268 EditableDenseThreeDimensionalModel *m =
Chris@440 269 new EditableDenseThreeDimensionalModel(sampleRate, hopSize,
Chris@440 270 height, false);
Chris@440 271
Chris@440 272 EditableDenseThreeDimensionalModel::Column column;
Chris@440 273
Chris@440 274 int x = 0;
Chris@440 275
Chris@440 276 for (int j = 0; j < values.size(); ++j) {
Chris@440 277 if (j % height == 0 && !column.empty()) {
Chris@440 278 m->setColumn(x++, column);
Chris@440 279 column.clear();
Chris@440 280 }
Chris@440 281 column.push_back(values[j].toFloat());
Chris@440 282 }
Chris@440 283
Chris@440 284 if (!column.empty()) {
Chris@440 285 m->setColumn(x++, column);
Chris@440 286 }
Chris@440 287
Chris@493 288 getDenseModelTitle(m, feature, type);
Chris@493 289
Chris@440 290 models.push_back(m);
Chris@440 291 }
Chris@440 292 }
Chris@440 293 }
Chris@440 294
Chris@440 295 void
Chris@493 296 RDFImporterImpl::getDenseModelTitle(Model *m,
Chris@493 297 QString featureUri,
Chris@493 298 QString featureTypeUri)
Chris@493 299 {
Chris@493 300 QString titleQuery = QString
Chris@493 301 (
Chris@493 302 " PREFIX dc: <http://purl.org/dc/elements/1.1/> "
Chris@493 303 " SELECT ?title "
Chris@493 304 " FROM <%1> "
Chris@493 305 " WHERE { "
Chris@493 306 " <%2> dc:title ?title . "
Chris@493 307 " } "
Chris@493 308 ).arg(m_uristring);
Chris@493 309
Chris@493 310 SimpleSPARQLQuery::Value v;
Chris@493 311
Chris@493 312 v = SimpleSPARQLQuery::singleResultQuery
Chris@493 313 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@493 314 titleQuery.arg(featureUri),
Chris@493 315 "title");
Chris@493 316
Chris@493 317 if (v.value != "") {
Chris@493 318 std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal) \"" << v.value.toStdString() << "\"" << std::endl;
Chris@493 319 m->setObjectName(v.value);
Chris@493 320 return;
Chris@493 321 }
Chris@493 322
Chris@493 323 v = SimpleSPARQLQuery::singleResultQuery
Chris@493 324 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@493 325 titleQuery.arg(featureTypeUri),
Chris@493 326 "title");
Chris@493 327
Chris@493 328 if (v.value != "") {
Chris@493 329 std::cerr << "RDFImporterImpl::getDenseModelTitle: Title (from signal type) \"" << v.value.toStdString() << "\"" << std::endl;
Chris@493 330 m->setObjectName(v.value);
Chris@493 331 return;
Chris@493 332 }
Chris@493 333
Chris@493 334 std::cerr << "RDFImporterImpl::getDenseModelTitle: No title available for feature <" << featureUri.toStdString() << ">" << std::endl;
Chris@493 335 }
Chris@493 336
Chris@493 337 void
Chris@440 338 RDFImporterImpl::getDenseFeatureProperties(QString featureUri,
Chris@440 339 int &sampleRate, int &windowLength,
Chris@440 340 int &hopSize, int &width, int &height)
Chris@440 341 {
Chris@489 342 SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource;
Chris@489 343
Chris@440 344 QString dimensionsQuery
Chris@440 345 (
Chris@440 346 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 347 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 348
Chris@440 349 " SELECT ?dimensions "
Chris@440 350 " FROM <%1> "
Chris@440 351
Chris@440 352 " WHERE { "
Chris@440 353
Chris@440 354 " <%2> af:dimensions ?dimensions . "
Chris@440 355
Chris@440 356 " } "
Chris@440 357 );
Chris@440 358
Chris@440 359 SimpleSPARQLQuery::Value dimensionsValue =
Chris@489 360 SimpleSPARQLQuery::singleResultQuery
Chris@489 361 (s, dimensionsQuery.arg(m_uristring).arg(featureUri), "dimensions");
Chris@440 362
Chris@440 363 cerr << "Dimensions = \"" << dimensionsValue.value.toStdString() << "\""
Chris@440 364 << endl;
Chris@440 365
Chris@440 366 if (dimensionsValue.value != "") {
Chris@440 367 QStringList dl = dimensionsValue.value.split(" ");
Chris@440 368 if (dl.empty()) dl.push_back(dimensionsValue.value);
Chris@440 369 if (dl.size() > 0) height = dl[0].toInt();
Chris@440 370 if (dl.size() > 1) width = dl[1].toInt();
Chris@440 371 }
Chris@440 372
Chris@440 373 QString queryTemplate
Chris@440 374 (
Chris@440 375 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@440 376 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@440 377 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@440 378
Chris@440 379 " SELECT ?%3 "
Chris@440 380 " FROM <%1> "
Chris@440 381
Chris@440 382 " WHERE { "
Chris@440 383
Chris@440 384 " <%2> mo:time ?time . "
Chris@440 385
Chris@440 386 " ?time a tl:Interval ; "
Chris@440 387 " tl:onTimeLine ?timeline . "
Chris@440 388
Chris@440 389 " ?map tl:rangeTimeLine ?timeline . "
Chris@440 390
Chris@440 391 " ?map tl:%3 ?%3 . "
Chris@440 392
Chris@440 393 " } "
Chris@440 394 );
Chris@440 395
Chris@440 396 // Another laborious workaround for rasqal's failure to handle
Chris@440 397 // multiple optionals properly
Chris@440 398
Chris@440 399 SimpleSPARQLQuery::Value srValue =
Chris@489 400 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 401 queryTemplate
Chris@440 402 .arg(m_uristring).arg(featureUri)
Chris@440 403 .arg("sampleRate"),
Chris@440 404 "sampleRate");
Chris@440 405 if (srValue.value != "") {
Chris@440 406 sampleRate = srValue.value.toInt();
Chris@440 407 }
Chris@440 408
Chris@440 409 SimpleSPARQLQuery::Value hopValue =
Chris@489 410 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 411 queryTemplate
Chris@440 412 .arg(m_uristring).arg(featureUri)
Chris@440 413 .arg("hopSize"),
Chris@440 414 "hopSize");
Chris@440 415 if (srValue.value != "") {
Chris@440 416 hopSize = hopValue.value.toInt();
Chris@440 417 }
Chris@440 418
Chris@440 419 SimpleSPARQLQuery::Value winValue =
Chris@489 420 SimpleSPARQLQuery::singleResultQuery(s,
Chris@480 421 queryTemplate
Chris@440 422 .arg(m_uristring).arg(featureUri)
Chris@440 423 .arg("windowLength"),
Chris@440 424 "windowLength");
Chris@440 425 if (winValue.value != "") {
Chris@440 426 windowLength = winValue.value.toInt();
Chris@440 427 }
Chris@440 428
Chris@440 429 cerr << "sr = " << sampleRate << ", hop = " << hopSize << ", win = " << windowLength << endl;
Chris@440 430 }
Chris@440 431
Chris@440 432 void
Chris@440 433 RDFImporterImpl::getDataModelsSparse(std::vector<Model *> &models,
Chris@440 434 ProgressReporter *reporter)
Chris@440 435 {
Chris@489 436 SimpleSPARQLQuery::QueryType s = SimpleSPARQLQuery::QueryFromSingleSource;
Chris@489 437
Chris@439 438 // Our query is intended to retrieve every thing that has a time,
Chris@439 439 // and every feature type and value associated with a thing that
Chris@439 440 // has a time.
Chris@439 441
Chris@439 442 // We will then need to refine this big bag of results into a set
Chris@439 443 // of data models.
Chris@439 444
Chris@439 445 // Results that have different source signals should go into
Chris@439 446 // different models.
Chris@439 447
Chris@439 448 // Results that have different feature types should go into
Chris@439 449 // different models.
Chris@439 450
Chris@439 451 // Results that are sparse should go into different models from
Chris@439 452 // those that are dense (we need to examine the timestamps to
Chris@439 453 // establish this -- if the timestamps are regular, the results
Chris@439 454 // are dense -- so we can't do it as we go along, only after
Chris@439 455 // collecting all results).
Chris@439 456
Chris@439 457 // Timed things that have features associated with them should not
Chris@439 458 // appear directly in any model -- their features should appear
Chris@439 459 // instead -- and these should be different models from those used
Chris@439 460 // for timed things that do not have features.
Chris@439 461
Chris@439 462 // As we load the results, we'll push them into a partially
Chris@439 463 // structured container that maps from source signal (URI as
Chris@439 464 // string) -> feature type (likewise) -> time -> list of values.
Chris@439 465 // If the source signal or feature type is unavailable, the empty
Chris@439 466 // string will do.
Chris@439 467
Chris@449 468 QString prefixes = QString(
Chris@439 469 " PREFIX event: <http://purl.org/NET/c4dm/event.owl#>"
Chris@449 470 " PREFIX tl: <http://purl.org/NET/c4dm/timeline.owl#>"
Chris@439 471 " PREFIX mo: <http://purl.org/ontology/mo/>"
Chris@439 472 " PREFIX af: <http://purl.org/ontology/af/>"
Chris@449 473 " PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>"
Chris@449 474 );
Chris@439 475
Chris@449 476 QString queryString = prefixes + QString(
Chris@449 477
Chris@449 478 " SELECT ?signal_source ?timed_thing ?event_type ?value"
Chris@439 479 " FROM <%1>"
Chris@439 480
Chris@439 481 " WHERE {"
Chris@440 482
Chris@440 483 " ?signal mo:available_as ?signal_source ."
Chris@440 484 " ?signal a mo:Signal ."
Chris@440 485
Chris@439 486 " ?signal mo:time ?interval ."
Chris@449 487 " ?interval tl:onTimeLine ?tl ."
Chris@449 488 " ?time tl:onTimeLine ?tl ."
Chris@449 489 " ?timed_thing event:time ?time ."
Chris@440 490 " ?timed_thing a ?event_type ."
Chris@440 491
Chris@439 492 " OPTIONAL {"
Chris@440 493 " ?timed_thing af:feature ?value"
Chris@439 494 " }"
Chris@439 495 " }"
Chris@439 496
Chris@439 497 ).arg(m_uristring);
Chris@439 498
Chris@449 499 QString timeQueryString = prefixes + QString(
Chris@449 500
Chris@449 501 " SELECT ?time FROM <%1> "
Chris@449 502 " WHERE { "
Chris@449 503 " <%2> event:time ?t . "
Chris@449 504 " ?t tl:at ?time . "
Chris@449 505 " } "
Chris@449 506
Chris@449 507 ).arg(m_uristring);
Chris@449 508
Chris@449 509 QString rangeQueryString = prefixes + QString(
Chris@449 510
Chris@449 511 " SELECT ?time ?duration FROM <%1> "
Chris@449 512 " WHERE { "
Chris@449 513 " <%2> event:time ?t . "
Chris@449 514 " ?t tl:beginsAt ?time . "
Chris@449 515 " ?t tl:duration ?duration . "
Chris@449 516 " } "
Chris@449 517
Chris@449 518 ).arg(m_uristring);
Chris@449 519
Chris@449 520 QString labelQueryString = prefixes + QString(
Chris@449 521
Chris@449 522 " SELECT ?label FROM <%1> "
Chris@449 523 " WHERE { "
Chris@449 524 " <%2> rdfs:label ?label . "
Chris@449 525 " } "
Chris@449 526
Chris@449 527 ).arg(m_uristring);
Chris@449 528
Chris@489 529 SimpleSPARQLQuery query(s, queryString);
Chris@439 530 query.setProgressReporter(reporter);
Chris@439 531
Chris@439 532 cerr << "Query will be: " << queryString.toStdString() << endl;
Chris@439 533
Chris@439 534 SimpleSPARQLQuery::ResultList results = query.execute();
Chris@439 535
Chris@439 536 if (!query.isOK()) {
Chris@439 537 m_errorString = query.getErrorString();
Chris@440 538 return;
Chris@439 539 }
Chris@439 540
Chris@439 541 if (query.wasCancelled()) {
Chris@439 542 m_errorString = "Query cancelled";
Chris@440 543 return;
Chris@439 544 }
Chris@439 545
Chris@449 546
Chris@449 547
Chris@449 548 /*
Chris@449 549
Chris@449 550 This function is now only used for sparse data (for dense data
Chris@449 551 we would be in getDataModelsDense instead).
Chris@449 552
Chris@449 553 For sparse data, the determining factors in deciding what model
Chris@449 554 to use are: Do the features have values? and Do the features
Chris@449 555 have duration?
Chris@449 556
Chris@449 557 We can run through the results and check off whether we find
Chris@449 558 values and duration for each of the source+type keys, and then
Chris@449 559 run through the source+type keys pushing each of the results
Chris@449 560 into a suitable model.
Chris@449 561
Chris@449 562 Unfortunately, at this point we do not yet have any actual
Chris@449 563 timing data (time/duration) -- just the time URI.
Chris@449 564
Chris@449 565 What we _could_ do is to create one of each type of model at the
Chris@449 566 start, for each of the source+type keys, and then push each
Chris@449 567 feature into the relevant model depending on what we find out
Chris@449 568 about it. Then return only non-empty models.
Chris@449 569
Chris@449 570
Chris@449 571 */
Chris@449 572
Chris@449 573 // Map from signal source to event type to dimensionality to
Chris@449 574 // presence of duration to model ptr. Whee!
Chris@449 575 std::map<QString, std::map<QString, std::map<int, std::map<bool, Model *> > > >
Chris@449 576 modelMap;
Chris@449 577
Chris@439 578 for (int i = 0; i < results.size(); ++i) {
Chris@439 579
Chris@440 580 QString source = results[i]["signal_source"].value;
Chris@449 581 QString type = results[i]["event_type"].value;
Chris@449 582 QString thinguri = results[i]["timed_thing"].value;
Chris@449 583
Chris@449 584 RealTime time;
Chris@449 585 RealTime duration;
Chris@439 586
Chris@449 587 bool haveTime = false;
Chris@449 588 bool haveDuration = false;
Chris@439 589
Chris@449 590 QString label = SimpleSPARQLQuery::singleResultQuery
Chris@489 591 (s, labelQueryString.arg(thinguri), "label").value;
Chris@449 592
Chris@489 593 SimpleSPARQLQuery rangeQuery(s, rangeQueryString.arg(thinguri));
Chris@450 594 SimpleSPARQLQuery::ResultList rangeResults = rangeQuery.execute();
Chris@450 595 if (!rangeResults.empty()) {
Chris@450 596 // std::cerr << rangeResults.size() << " range results" << std::endl;
Chris@450 597 time = RealTime::fromXsdDuration
Chris@450 598 (rangeResults[0]["time"].value.toStdString());
Chris@450 599 duration = RealTime::fromXsdDuration
Chris@450 600 (rangeResults[0]["duration"].value.toStdString());
Chris@450 601 // std::cerr << "duration string " << rangeResults[0]["duration"].value.toStdString() << std::endl;
Chris@449 602 haveTime = true;
Chris@450 603 haveDuration = true;
Chris@449 604 } else {
Chris@450 605 QString timestring = SimpleSPARQLQuery::singleResultQuery
Chris@489 606 (s, timeQueryString.arg(thinguri), "time").value;
Chris@450 607 if (timestring != "") {
Chris@450 608 time = RealTime::fromXsdDuration(timestring.toStdString());
Chris@449 609 haveTime = true;
Chris@449 610 }
Chris@449 611 }
Chris@439 612
Chris@439 613 QString valuestring = results[i]["value"].value;
Chris@449 614 std::vector<float> values;
Chris@449 615
Chris@439 616 if (valuestring != "") {
Chris@449 617 QStringList vsl = valuestring.split(" ", QString::SkipEmptyParts);
Chris@449 618 for (int j = 0; j < vsl.size(); ++j) {
Chris@449 619 bool success = false;
Chris@449 620 float v = vsl[j].toFloat(&success);
Chris@449 621 if (success) values.push_back(v);
Chris@449 622 }
Chris@439 623 }
Chris@439 624
Chris@449 625 int dimensions = 1;
Chris@449 626 if (values.size() == 1) dimensions = 2;
Chris@449 627 else if (values.size() > 1) dimensions = 3;
Chris@449 628
Chris@449 629 Model *model = 0;
Chris@449 630
Chris@449 631 if (modelMap[source][type][dimensions].find(haveDuration) ==
Chris@449 632 modelMap[source][type][dimensions].end()) {
Chris@449 633
Chris@449 634 /*
Chris@449 635 std::cerr << "Creating new model: source = " << source.toStdString()
Chris@449 636 << ", type = " << type.toStdString() << ", dimensions = "
Chris@449 637 << dimensions << ", haveDuration = " << haveDuration
Chris@449 638 << ", time = " << time << ", duration = " << duration
Chris@449 639 << std::endl;
Chris@449 640 */
Chris@449 641
Chris@449 642 if (!haveDuration) {
Chris@449 643
Chris@449 644 if (dimensions == 1) {
Chris@449 645
Chris@449 646 // std::cerr << "SparseOneDimensionalModel" << std::endl;
Chris@449 647 model = new SparseOneDimensionalModel(m_sampleRate, 1, false);
Chris@449 648
Chris@449 649 } else if (dimensions == 2) {
Chris@449 650
Chris@449 651 // std::cerr << "SparseTimeValueModel" << std::endl;
Chris@449 652 model = new SparseTimeValueModel(m_sampleRate, 1, false);
Chris@449 653
Chris@449 654 } else {
Chris@449 655
Chris@449 656 // We don't have a three-dimensional sparse model,
Chris@449 657 // so use a note model. We do have some logic (in
Chris@449 658 // extractStructure below) for guessing whether
Chris@449 659 // this should after all have been a dense model,
Chris@449 660 // but it's hard to apply it because we don't have
Chris@449 661 // all the necessary timing data yet... hmm
Chris@449 662
Chris@449 663 // std::cerr << "NoteModel" << std::endl;
Chris@449 664 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 665 }
Chris@449 666
Chris@449 667 } else { // haveDuration
Chris@449 668
Chris@449 669 if (dimensions == 1 || dimensions == 2) {
Chris@449 670
Chris@449 671 // If our units are frequency or midi pitch, we
Chris@449 672 // should be using a note model... hm
Chris@449 673
Chris@449 674 // std::cerr << "RegionModel" << std::endl;
Chris@449 675 model = new RegionModel(m_sampleRate, 1, false);
Chris@449 676
Chris@449 677 } else {
Chris@449 678
Chris@449 679 // We don't have a three-dimensional sparse model,
Chris@449 680 // so use a note model. We do have some logic (in
Chris@449 681 // extractStructure below) for guessing whether
Chris@449 682 // this should after all have been a dense model,
Chris@449 683 // but it's hard to apply it because we don't have
Chris@449 684 // all the necessary timing data yet... hmm
Chris@449 685
Chris@449 686 // std::cerr << "NoteModel" << std::endl;
Chris@449 687 model = new NoteModel(m_sampleRate, 1, false);
Chris@449 688 }
Chris@449 689 }
Chris@449 690
Chris@493 691 QString titleQuery = QString
Chris@493 692 (
Chris@493 693 " PREFIX dc: <http://purl.org/dc/elements/1.1/> "
Chris@493 694 " SELECT ?title "
Chris@493 695 " FROM <%1> "
Chris@493 696 " WHERE { "
Chris@493 697 " <%2> dc:title ?title . "
Chris@493 698 " } "
Chris@493 699 ).arg(m_uristring).arg(type);
Chris@493 700 QString title = SimpleSPARQLQuery::singleResultQuery
Chris@493 701 (s, titleQuery, "title").value;
Chris@493 702 if (title != "") model->setObjectName(title);
Chris@493 703
Chris@449 704 modelMap[source][type][dimensions][haveDuration] = model;
Chris@449 705 models.push_back(model);
Chris@449 706 }
Chris@449 707
Chris@449 708 model = modelMap[source][type][dimensions][haveDuration];
Chris@449 709
Chris@449 710 if (model) {
Chris@449 711 long ftime = RealTime::realTime2Frame(time, m_sampleRate);
Chris@449 712 long fduration = RealTime::realTime2Frame(duration, m_sampleRate);
Chris@449 713 fillModel(model, ftime, fduration, haveDuration, values, label);
Chris@439 714 }
Chris@439 715 }
Chris@439 716 }
Chris@439 717
Chris@439 718 void
Chris@449 719 RDFImporterImpl::fillModel(Model *model,
Chris@449 720 long ftime,
Chris@449 721 long fduration,
Chris@449 722 bool haveDuration,
Chris@449 723 std::vector<float> &values,
Chris@449 724 QString label)
Chris@449 725 {
Chris@493 726 // std::cerr << "RDFImporterImpl::fillModel: adding point at frame " << ftime << std::endl;
Chris@492 727
Chris@449 728 SparseOneDimensionalModel *sodm =
Chris@449 729 dynamic_cast<SparseOneDimensionalModel *>(model);
Chris@449 730 if (sodm) {
Chris@449 731 SparseOneDimensionalModel::Point point(ftime, label);
Chris@449 732 sodm->addPoint(point);
Chris@449 733 return;
Chris@449 734 }
Chris@449 735
Chris@449 736 SparseTimeValueModel *stvm =
Chris@449 737 dynamic_cast<SparseTimeValueModel *>(model);
Chris@449 738 if (stvm) {
Chris@449 739 SparseTimeValueModel::Point point
Chris@449 740 (ftime, values.empty() ? 0.f : values[0], label);
Chris@449 741 stvm->addPoint(point);
Chris@449 742 return;
Chris@449 743 }
Chris@449 744
Chris@449 745 NoteModel *nm =
Chris@449 746 dynamic_cast<NoteModel *>(model);
Chris@449 747 if (nm) {
Chris@449 748 if (haveDuration) {
Chris@449 749 float value = 0.f, level = 1.f;
Chris@449 750 if (!values.empty()) {
Chris@449 751 value = values[0];
Chris@449 752 if (values.size() > 1) {
Chris@449 753 level = values[1];
Chris@449 754 }
Chris@449 755 }
Chris@449 756 NoteModel::Point point(ftime, value, fduration, level, label);
Chris@449 757 nm->addPoint(point);
Chris@449 758 } else {
Chris@449 759 float value = 0.f, duration = 1.f, level = 1.f;
Chris@449 760 if (!values.empty()) {
Chris@449 761 value = values[0];
Chris@449 762 if (values.size() > 1) {
Chris@449 763 duration = values[1];
Chris@449 764 if (values.size() > 2) {
Chris@449 765 level = values[2];
Chris@449 766 }
Chris@449 767 }
Chris@449 768 }
Chris@449 769 NoteModel::Point point(ftime, value, duration, level, label);
Chris@449 770 nm->addPoint(point);
Chris@449 771 }
Chris@449 772 return;
Chris@449 773 }
Chris@449 774
Chris@449 775 RegionModel *rm =
Chris@449 776 dynamic_cast<RegionModel *>(model);
Chris@449 777 if (rm) {
Chris@449 778 if (haveDuration) {
Chris@449 779 RegionModel::Point point
Chris@449 780 (ftime, values.empty() ? 0.f : values[0], fduration, label);
Chris@449 781 rm->addPoint(point);
Chris@449 782 } else {
Chris@449 783 // This won't actually happen -- we only create region models
Chris@449 784 // if we do have duration -- but just for completeness
Chris@449 785 float value = 0.f, duration = 1.f;
Chris@449 786 if (!values.empty()) {
Chris@449 787 value = values[0];
Chris@449 788 if (values.size() > 1) {
Chris@449 789 duration = values[1];
Chris@449 790 }
Chris@449 791 }
Chris@449 792 RegionModel::Point point(ftime, value, duration, label);
Chris@449 793 rm->addPoint(point);
Chris@449 794 }
Chris@449 795 return;
Chris@449 796 }
Chris@449 797
Chris@449 798 std::cerr << "WARNING: RDFImporterImpl::fillModel: Unknown or unexpected model type" << std::endl;
Chris@449 799 return;
Chris@449 800 }
Chris@449 801
Chris@490 802 RDFImporter::RDFDocumentType
Chris@490 803 RDFImporter::identifyDocumentType(QString url)
Chris@490 804 {
Chris@490 805 bool haveAudio = false;
Chris@490 806 bool haveAnnotations = false;
Chris@449 807
Chris@490 808 SimpleSPARQLQuery::Value value =
Chris@490 809 SimpleSPARQLQuery::singleResultQuery
Chris@490 810 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 811 QString
Chris@490 812 (" PREFIX mo: <http://purl.org/ontology/mo/> "
Chris@490 813 " SELECT ?url FROM <%1> "
Chris@490 814 " WHERE { ?signal a mo:Signal ; mo:available_as ?url } "
Chris@490 815 ).arg(url),
Chris@490 816 "url");
Chris@490 817
Chris@490 818 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 819 haveAudio = true;
Chris@490 820 }
Chris@490 821
Chris@490 822 value =
Chris@490 823 SimpleSPARQLQuery::singleResultQuery
Chris@490 824 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 825 QString
Chris@490 826 (" PREFIX event: <http://purl.org/NET/c4dm/event.owl#> "
Chris@490 827 " SELECT ?thing FROM <%1> "
Chris@490 828 " WHERE { ?thing event:time ?time } "
Chris@490 829 ).arg(url),
Chris@490 830 "thing");
Chris@490 831
Chris@490 832 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 833 haveAnnotations = true;
Chris@490 834 }
Chris@490 835
Chris@490 836 if (!haveAnnotations) {
Chris@490 837
Chris@490 838 value =
Chris@490 839 SimpleSPARQLQuery::singleResultQuery
Chris@490 840 (SimpleSPARQLQuery::QueryFromSingleSource,
Chris@490 841 QString
Chris@490 842 (" PREFIX af: <http://purl.org/ontology/af/> "
Chris@490 843 " SELECT ?thing FROM <%1> "
Chris@490 844 " WHERE { ?signal af:signal_feature ?thing } "
Chris@490 845 ).arg(url),
Chris@490 846 "thing");
Chris@490 847
Chris@490 848 if (value.type == SimpleSPARQLQuery::URIValue) {
Chris@490 849 haveAnnotations = true;
Chris@490 850 }
Chris@490 851 }
Chris@490 852
Chris@490 853 if (haveAudio) {
Chris@490 854 if (haveAnnotations) {
Chris@490 855 return AudioRefAndAnnotations;
Chris@490 856 } else {
Chris@490 857 return AudioRef;
Chris@490 858 }
Chris@490 859 } else {
Chris@490 860 if (haveAnnotations) {
Chris@490 861 return Annotations;
Chris@490 862 } else {
Chris@490 863 return OtherDocument;
Chris@490 864 }
Chris@490 865 }
Chris@492 866
Chris@492 867 SimpleSPARQLQuery::closeSingleSource(url);
Chris@490 868 }
Chris@490 869
Chris@490 870